HDLRuby 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +139 -79
- data/lib/HDLRuby/hdr_samples/constant_in_function.rb +2 -1
- data/lib/HDLRuby/hdr_samples/with_ref_expr.rb +30 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer.rb +32 -37
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb +103 -69
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb +10 -10
- data/lib/HDLRuby/hdr_samples/with_sequencer_func.rb +63 -0
- data/lib/HDLRuby/hdrcc.rb +1 -1
- data/lib/HDLRuby/hruby_high.rb +23 -5
- data/lib/HDLRuby/hruby_low_without_subsignals.rb +50 -11
- data/lib/HDLRuby/hruby_types.rb +5 -5
- data/lib/HDLRuby/hruby_values.rb +7 -7
- data/lib/HDLRuby/hruby_verilog.rb +101 -17
- data/lib/HDLRuby/hruby_verilog_name.rb +49 -42
- data/lib/HDLRuby/std/fsm.rb +10 -1
- data/lib/HDLRuby/std/sequencer.rb +281 -53
- data/lib/HDLRuby/std/sequencer_func.rb +533 -0
- data/lib/HDLRuby/std/std.rb +1 -0
- data/lib/HDLRuby/version.rb +1 -1
- data/tuto/tutorial_sw.md +267 -61
- metadata +5 -3
- data/lib/HDLRuby/hdr_samples/with_register_stack.rb +0 -150
data/tuto/tutorial_sw.md
CHANGED
@@ -273,30 +273,30 @@ If everything was OK, a file named `my_circuit.v` should have appeared in the `w
|
|
273
273
|
```verilog
|
274
274
|
`timescale 1ps/1ps
|
275
275
|
|
276
|
-
module _v0_1(
|
277
|
-
input
|
278
|
-
input
|
279
|
-
input [15:0]
|
280
|
-
input
|
281
|
-
input [7:0]
|
282
|
-
output [7:0]
|
276
|
+
module _v0_1( clk, rst, addr, data_in, data_out );
|
277
|
+
input clk;
|
278
|
+
input rst;
|
279
|
+
input [15:0] addr;
|
280
|
+
input ce;
|
281
|
+
input [7:0] data_in;
|
282
|
+
output [7:0] data_out;
|
283
283
|
|
284
284
|
|
285
285
|
endmodule
|
286
286
|
```
|
287
287
|
|
288
|
-
The syntax looks indeed a little bit different from HDLRuby, but you should be able to recognize the description of the circuit. The name of the
|
288
|
+
The syntax looks indeed a little bit different from HDLRuby, but you should be able to recognize the description of the circuit. The name of the module is different though, this is because HDLRuby supports any Unicode character for names and to avoid compatibility problems, it recreates the names when generating Verilog. But, just for the fun, please replace `:addr` in the HDLRuby file with `:☺` and regenerate Verilog HDL from it... It works! And the result is:
|
289
289
|
|
290
290
|
```verilog
|
291
291
|
`timescale 1ps/1ps
|
292
292
|
|
293
|
-
module _v0_1(
|
294
|
-
input
|
295
|
-
input
|
296
|
-
input [15:0]
|
297
|
-
input
|
298
|
-
input [7:0]
|
299
|
-
output [7:0]
|
293
|
+
module _v0_1( clk, rst, _v1_, data_in, data_out );
|
294
|
+
input clk;
|
295
|
+
input rst;
|
296
|
+
input [15:0] _v1_;
|
297
|
+
input ce;
|
298
|
+
input [7:0] data_in;
|
299
|
+
output [7:0] data_out;
|
300
300
|
|
301
301
|
|
302
302
|
endmodule
|
@@ -357,17 +357,17 @@ Three new files should have appeared in the `work` directory: `_v10_5.v`, `_v8_4
|
|
357
357
|
```verilog
|
358
358
|
`timescale 1ps/1ps
|
359
359
|
|
360
|
-
module _v0_3(
|
361
|
-
input
|
362
|
-
input
|
363
|
-
input [15:0]
|
364
|
-
input
|
365
|
-
input
|
366
|
-
input [7:0]
|
367
|
-
output [7:0]
|
360
|
+
module _v0_3( clk, rst, addr, ce0, ce1, data_in, data_out );
|
361
|
+
input clk;
|
362
|
+
input rst;
|
363
|
+
input [15:0] addr;
|
364
|
+
input ce0;
|
365
|
+
input ce1;
|
366
|
+
input [7:0] data_in;
|
367
|
+
output [7:0] data_out;
|
368
368
|
|
369
|
-
_v8_4
|
370
|
-
_v10_5
|
369
|
+
_v8_4 my_circuit0();
|
370
|
+
_v10_5 my_circuit1();
|
371
371
|
|
372
372
|
endmodule
|
373
373
|
```
|
@@ -497,14 +497,14 @@ When the compile succeeds (no error message), two new files appear in `work`, na
|
|
497
497
|
```verilog
|
498
498
|
`timescale 1ps/1ps
|
499
499
|
|
500
|
-
module _v0_3(
|
500
|
+
module _v0_3( clk, rst, addr, ce0, ce1, data_in, data_out );
|
501
501
|
input _v1_clk;
|
502
502
|
input _v2_rst;
|
503
|
-
input [15:0]
|
504
|
-
input
|
505
|
-
input
|
506
|
-
input [7:0]
|
507
|
-
output [7:0]
|
503
|
+
input [15:0] addr;
|
504
|
+
input ce0;
|
505
|
+
input ce1;
|
506
|
+
input [7:0] data_in;
|
507
|
+
output [7:0] data_out;
|
508
508
|
wire _v8_0;
|
509
509
|
wire _v9_1;
|
510
510
|
wire [15:0] _v10_2;
|
@@ -518,29 +518,29 @@ module _v0_3( _v1_clk, _v2_rst, _v3_addr, _v4_ce0, _v5_ce1, _v6_data_in, _v7_dat
|
|
518
518
|
wire [7:0] _v18_10;
|
519
519
|
wire [7:0] _v19_11;
|
520
520
|
|
521
|
-
_v20_4
|
522
|
-
_v23_5
|
523
|
-
assign _v8_0 =
|
521
|
+
_v20_4 my_circuit0(.clk(_v8_0),.rst(_v9_1),.addr(_v10_2),.ce(_v11_3),.data_in(_v12_4),.data_out(_v18_10));
|
522
|
+
_v23_5 my_circuit1(.clk(_v13_5),.rst(_v14_6),.addr(_v15_7),.ce(_v16_8),.data_in(_v17_9),.data_out(_v19_11));
|
523
|
+
assign _v8_0 = clk;
|
524
524
|
|
525
|
-
assign _v9_1 =
|
525
|
+
assign _v9_1 = rst;
|
526
526
|
|
527
|
-
assign _v10_2 =
|
527
|
+
assign _v10_2 = addr;
|
528
528
|
|
529
|
-
assign _v11_3 =
|
529
|
+
assign _v11_3 = ce0;
|
530
530
|
|
531
|
-
assign _v12_4 =
|
531
|
+
assign _v12_4 = data_in;
|
532
532
|
|
533
|
-
assign _v13_5 =
|
533
|
+
assign _v13_5 = clk;
|
534
534
|
|
535
|
-
assign _v14_6 =
|
535
|
+
assign _v14_6 = rst;
|
536
536
|
|
537
|
-
assign _v15_7 =
|
537
|
+
assign _v15_7 = addr;
|
538
538
|
|
539
|
-
assign _v16_8 =
|
539
|
+
assign _v16_8 = ce1;
|
540
540
|
|
541
|
-
assign _v17_9 =
|
541
|
+
assign _v17_9 = data_in;
|
542
542
|
|
543
|
-
assign
|
543
|
+
assign data_out = (_v18_10 + _v19_11);
|
544
544
|
|
545
545
|
|
546
546
|
endmodule
|
@@ -599,7 +599,7 @@ With HDL like Verilog HDL, there is a real distinction between wires and storage
|
|
599
599
|
|
600
600
|
__Note__: one may ask what about the `variable` construct in VHDL: those are syntactic intermediates used for simplifying the descriptions. HDLRuby also supports such a `variable` construct, but those are simply the variables of the Ruby language. Therefore, these variables can hold not only values but any Ruby or HDLRuby constructs.
|
601
601
|
|
602
|
-
In HDLRuby, there are
|
602
|
+
In HDLRuby, there are four kinds of signals: the input ports, the output ports, the inout ports, and the inner signals. The first three kinds of signals have already been described in the section about declaring a [circuit](#circuit-declare). At that time they were called ports because it is what such constructs are called in common HDL. However, in HDLRuby there is no distinction at all between ports and signals, hence from now on, we will use the term `signal` in general, and the term `port` when we only want to refer to the interface of a circuit. The inner signals can be used inside a circuit and are usually used as an intermediate for internal computations. They are declared like the ports, but using `inner` instead of `input`, `output`, or `inout`. For example, the following code declares a module named `a_circuit` with two 1-bit inputs, one 8-bit output, and one 4-bit inner signal.
|
603
603
|
|
604
604
|
```ruby
|
605
605
|
system(:a_circuit) do
|
@@ -737,9 +737,11 @@ This section will explain the following about sequencers:
|
|
737
737
|
|
738
738
|
* [How to write a structured programming algorithm in a sequencer.](#322-how-to-write-a-structured-programming-algorithm-in-a-sequencer)
|
739
739
|
|
740
|
-
* [How to
|
740
|
+
* [How to write and use a sequencer function.](#323-how-to-write-and-use-a-sequencer-function)
|
741
|
+
|
742
|
+
* [How to use enumerators in a sequencer.](#324-how-to-use-enumerators-in-a-sequencer)
|
741
743
|
|
742
|
-
* [What happens when there are several sequencers?](#
|
744
|
+
* [What happens when there are several sequencers?](#325-what-happens-when-there-are-several-sequencers)
|
743
745
|
|
744
746
|
|
745
747
|
#### 3.2.1 How to declare and control a sequencer
|
@@ -952,7 +954,7 @@ In detail here is a list of the control statements you can use within a sequence
|
|
952
954
|
|
953
955
|
* `sif(<condition>) <block>`: executes `block` if `condition` is true (i.e., different from 0).
|
954
956
|
|
955
|
-
|
957
|
+
* `selsif(<condition>) <block>`: executes `block` if the previous `sif` and `selsif` conditions are false and if the current `condition` is true.
|
956
958
|
|
957
959
|
* `selse <block>`: executes `block` if the conditions of the previous `sif` and `selsif` are false (i.e., equals 0). It is also used for giving the default block for the `scase` statements (see below).
|
958
960
|
|
@@ -970,6 +972,8 @@ In detail here is a list of the control statements you can use within a sequence
|
|
970
972
|
|
971
973
|
* `scontinue`: ends the current iteration.
|
972
974
|
|
975
|
+
* `steps(<num>)`: performs `num` times `step` (`num` can be any expression). Useful for waiting a certain number of cycles.
|
976
|
+
|
973
977
|
> __IMPORTANT__: each control statement requires one clock cycle for each branch they perform, independently of what their block contains. For example, in the following code, the value of `y` will be set one cycle after `x` is set to 0, and one cycle before `z` is set to 1.
|
974
978
|
|
975
979
|
```ruby
|
@@ -1096,7 +1100,7 @@ clk <= 1
|
|
1096
1100
|
```
|
1097
1101
|
|
1098
1102
|
> __IMPORTANT__: as said when presenting HDLRuby, this language is implemented on top of the Ruby language, and is fully compatible with it. For instance, you can write any Ruby code within HDLRuby constructs (e.g., `def`), and you can write HDLRuby code within Ruby constructs. However, there is an important difference: Ruby code is executed at compile time (i.e., when hdrcc runs) and does not produce any hardware, whereas HDLRuby code is the description of the hardware that will be produced and will be then executed either through simulation or after production physically.
|
1099
|
-
Then, what calling `clk!` do is paste the HDLRuby code in place. Here it is used to shorten the code: instead of
|
1103
|
+
Then, what calling `clk!` do is paste the HDLRuby code in place. Here it is used to shorten the code: instead of each time setting the clock to 0, advancing time, then setting it to 1 again, writing `clk!` is enough to obtain the same result.
|
1100
1104
|
It is from this capability to mix Ruby and HDLRuby that comes the *meta programmability* of HDLRuby.
|
1101
1105
|
|
1102
1106
|
Finally, when you simulate with the following command:
|
@@ -1110,6 +1114,38 @@ You should obtain the following kind of resulting VCD file:
|
|
1110
1114
|
![hruby_simulator.vcd](fact_vcd.png)
|
1111
1115
|
|
1112
1116
|
|
1117
|
+
#### But in structured programming, it is better to use local variables!
|
1118
|
+
|
1119
|
+
Indeed, in the factorial program, signals `val` and `res` are only used within the sequencer, so why declare them outside it? The code would be more clear if they were declared more locally, i.e., *inside* it, and even better, inside its main loop. So let us modify it as follows:
|
1120
|
+
|
1121
|
+
```ruby
|
1122
|
+
system :fact do
|
1123
|
+
input :clk, :start, :req
|
1124
|
+
[5].input :data_in
|
1125
|
+
output :ack
|
1126
|
+
[32].output :data_out
|
1127
|
+
|
1128
|
+
sequencer(clk,start) do
|
1129
|
+
sloop do
|
1130
|
+
[4].inner :val
|
1131
|
+
[24].inner :res
|
1132
|
+
|
1133
|
+
ack <= 0
|
1134
|
+
swhile(req != 1)
|
1135
|
+
val <= data_in
|
1136
|
+
res <= 1
|
1137
|
+
swhile(val>1) do
|
1138
|
+
res <= res*val
|
1139
|
+
val <= val - 1
|
1140
|
+
end
|
1141
|
+
data_out <= res
|
1142
|
+
ack <= 1
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
```
|
1147
|
+
|
1148
|
+
You can simulate it again, and you should obtain exactly the same result. However, if you try to access `res` or `val` outside the main loop, then an error will be raised.
|
1113
1149
|
|
1114
1150
|
|
1115
1151
|
#### Now about `sfor`
|
@@ -1208,16 +1244,128 @@ You should obtain the following time chart:
|
|
1208
1244
|
|
1209
1245
|
|
1210
1246
|
|
1247
|
+
#### 3.2.3. How to write and use a sequencer function.
|
1248
|
+
|
1249
|
+
Why not necessarily associated with algorithmic, it is common in software to define functions for code reuse and implementation of recursive algorithms. HDLRuby also provided such kinds of functions, with all the software features, including recursion, using the construct `sdef`. Such a function is defined as follows:
|
1250
|
+
|
1251
|
+
```ruby
|
1252
|
+
sdef :<name> do |<arguments>|
|
1253
|
+
<body>
|
1254
|
+
end
|
1255
|
+
```
|
1256
|
+
|
1257
|
+
In the code above, `name` is the name of the function, `arguments` is a list of arguments and `body` is the code of the function that can be any kind of HDLRuby sequencer code. For returning a value from a function, the `sreturn(<value>)` command is used. For example, the following describes a function computing the factorial of its argument `n`:
|
1258
|
+
|
1259
|
+
```ruby
|
1260
|
+
sdef :fact do |n|
|
1261
|
+
sif(n>1) { sreturn(n*fact(n-1) }
|
1262
|
+
selse { sreturn(1) }
|
1263
|
+
end
|
1264
|
+
```
|
1265
|
+
|
1266
|
+
There is a lot to unpack from this small example:
|
1267
|
+
|
1268
|
+
1. Such a function can be defined outside or inside a module, but can only be called within a sequencer.
|
1269
|
+
|
1270
|
+
2. The arguments of a function, here `n`, do not have any defined type: their type, and consequently the final implementation of the function, is determined by the data type of the actual arguments when the function is called. For example, in the following code, the data type of the actual argument `val` is 16-bit unsigned, hence that will be the data type of `n`.
|
1271
|
+
|
1272
|
+
```ruby
|
1273
|
+
[16].inner :val, :res
|
1274
|
+
|
1275
|
+
sequencer do
|
1276
|
+
val <= 5
|
1277
|
+
res <= fact(val)
|
1278
|
+
end
|
1279
|
+
```
|
1280
|
+
|
1281
|
+
3. When a function is recursive, a stack is created to store the arguments and the sequencer states for returning at each recursion. The size of this stack is by default set to the bit width of the largest argument, e.g., for `fact` it is set for supporting 16 recursions. In case of stack overflow, i.e., there were too many recursive calls, the recursion is stopped, and the execution of the sequencer proceeds from just after the last call.
|
1282
|
+
|
1283
|
+
4. The behavior of the stack can be controlled in two ways when defining a function as follows:
|
1284
|
+
|
1285
|
+
```ruby
|
1286
|
+
sdef(:<name>,<size>, proc <error_handler>) do |<arguments>|
|
1287
|
+
<body>
|
1288
|
+
end
|
1289
|
+
```
|
1290
|
+
|
1291
|
+
In the code above, `size` is the forced size of the stack, and `error_handler` is a block of code that will be executed when a stack overflow occurs. Both arguments are optional, but if the error handler is provided, then the size must also be provided. For example, the code of the factorial can be rewritten as follows for forcing the stack to support 64 recursions:
|
1292
|
+
|
1293
|
+
```ruby
|
1294
|
+
sdef(:fact,64) do |n|
|
1295
|
+
sif(n>1) { sreturn(n*fact(n-1) }
|
1296
|
+
selse { sreturn(1) }
|
1297
|
+
end
|
1298
|
+
```
|
1299
|
+
|
1300
|
+
It can also be defined as follows to support only 8 recursions but to set a signal named `stack_overflow` to 1 when a stack overflow happens:
|
1301
|
+
|
1302
|
+
```ruby
|
1303
|
+
sdef(:fact, 8, proc { stack_overflow <= 1 }) do |n|
|
1304
|
+
sif(n>1) { sreturn(n*fact(n-1) }
|
1305
|
+
selse { sreturn(1) }
|
1306
|
+
end
|
1307
|
+
```
|
1308
|
+
|
1309
|
+
Here is a full example using the factorial:
|
1310
|
+
|
1311
|
+
```ruby
|
1312
|
+
sdef(:fact,8,proc { stack_overflow_error <= 1 }) do |n|
|
1313
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
1314
|
+
selse { sreturn(1) }
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
|
1318
|
+
# Checking the fact function.
|
1319
|
+
system :module_with_fact do
|
1320
|
+
|
1321
|
+
inner :clk,:rst
|
1322
|
+
|
1323
|
+
[16].inner :val
|
1324
|
+
[16].inner :res
|
1325
|
+
inner stack_overflow_error: 0
|
1326
|
+
|
1327
|
+
sequencer(clk.posedge,rst) do
|
1328
|
+
5.stimes do |i|
|
1329
|
+
val <= i
|
1330
|
+
res <= fact(val)
|
1331
|
+
end
|
1332
|
+
hprint("stack_overflow_error=",stack_overflow_error,"\n")
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
timed do
|
1336
|
+
clk <= 0
|
1337
|
+
rst <= 0
|
1338
|
+
!10.ns
|
1339
|
+
clk <= 1
|
1340
|
+
!10.ns
|
1341
|
+
clk <= 0
|
1342
|
+
rst <= 1
|
1343
|
+
!10.ns
|
1344
|
+
clk <= 1
|
1345
|
+
!10.ns
|
1346
|
+
clk <= 0
|
1347
|
+
rst <= 0
|
1348
|
+
!10.ns
|
1349
|
+
clk <= 1
|
1350
|
+
repeat(500) do
|
1351
|
+
!10.ns
|
1352
|
+
clk <= ~clk
|
1353
|
+
end
|
1354
|
+
end
|
1355
|
+
end
|
1356
|
+
```
|
1357
|
+
|
1358
|
+
|
1211
1359
|
---
|
1212
1360
|
|
1213
1361
|
So now, you know:
|
1214
1362
|
|
1215
|
-
* How to describe fully
|
1363
|
+
* How to describe fully-fledged algorithms with sequencers using conditional and control statements as well as functions, including recursive ones.
|
1216
1364
|
|
1217
1365
|
Also, if you are not familiar with languages like Ruby or Python, the `sfor` may look great compared to what C can provide. But this is just the beginning, HDLRuby can do better than that.
|
1218
1366
|
|
1219
1367
|
|
1220
|
-
#### 3.2.
|
1368
|
+
#### 3.2.4. How to use enumerators in a sequencer
|
1221
1369
|
|
1222
1370
|
If sometimes you program with Ruby, you may know about enumerators: they are objects used for processing iteratively several elements of objects. The HDLRuby's sequencer provides the same concept: it is possible to build hardware enumerators for any enumerable objects and they will run like Ruby's.
|
1223
1371
|
|
@@ -1284,12 +1432,12 @@ That's all that we will explain here, the remaining is exactly like Ruby. Moreov
|
|
1284
1432
|
|
1285
1433
|
sequencer(clk,start) do
|
1286
1434
|
16.stimes {|i| ar[i] <= sin }
|
1287
|
-
ar.ssort
|
1288
|
-
16.stimes {|i| sout <=
|
1435
|
+
res = ar.ssort
|
1436
|
+
16.stimes {|i| sout <= res[i] }
|
1289
1437
|
end
|
1290
1438
|
```
|
1291
1439
|
|
1292
|
-
In this example, `16.stimes` generates an enumerator over the `0..7` range, and is a way to build an enumerator from an integer value.
|
1440
|
+
In this example, `16.stimes` generates an enumerator over the `0..7` range, and is a way to build an enumerator from an integer value. In addition, please notice the use of the Ruby variable `res` for accessign the signal containing the sort result.
|
1293
1441
|
|
1294
1442
|
* Apply a 4-point FIR filter over an array obtained from input signal `sin` with 0-padding at the beginning and output the result to `sout`
|
1295
1443
|
|
@@ -1309,7 +1457,29 @@ That's all that we will explain here, the remaining is exactly like Ruby. Moreov
|
|
1309
1457
|
end
|
1310
1458
|
```
|
1311
1459
|
|
1312
|
-
In this example, `[_h00]*3` builds an array of three 8-bit zeros for the padding, `seach` creates the iterators over this padding. This iterator is added to one over `ar` which creates a global iterator over them all. `seach_slice` and `sreduce` work the same way their Ruby equivalent `each_slice` and `reduce` do.
|
1460
|
+
In this example, `[_h00]*3` builds an array of three 8-bit zeros for the padding, `seach` creates the iterators over this padding. This iterator is added to one over `ar` which creates a global iterator over them all. `seach_slice` and `sreduce` work the same way their Ruby equivalent `each_slice` and `reduce` do. Here, since the result is a single byte, it is directly assigned to the output `sout`, but the following could also have been possible:
|
1461
|
+
|
1462
|
+
```ruby
|
1463
|
+
input :clk,:start
|
1464
|
+
[8].input :sin
|
1465
|
+
[8].output :sout
|
1466
|
+
|
1467
|
+
bit[8][-4].inner coefs: [_h01,_h05,_h0A,_hFE]
|
1468
|
+
bit[8][-16].inner : ar
|
1469
|
+
|
1470
|
+
res = nil
|
1471
|
+
|
1472
|
+
sequencer(clk,start) do
|
1473
|
+
16.stime {|i| ar[i] <= sin }
|
1474
|
+
res = ([_h00]*3).seach + ar.seach).seach_slice(4).sreduce(_h00) do |a,b,c,d|
|
1475
|
+
a*coefs[0] + b * coefs[1] + c * coefs[2] + d * coefs[3]
|
1476
|
+
end
|
1477
|
+
end
|
1478
|
+
|
1479
|
+
sout <= res
|
1480
|
+
```
|
1481
|
+
|
1482
|
+
Please notice that since the Ruby variable `res` is used outside the sequencer, it must also be declared outside (`res = nil`).
|
1313
1483
|
|
1314
1484
|
|
1315
1485
|
|
@@ -1354,7 +1524,7 @@ But this is not all: contrary to software, hardware is inherently parallel, a pr
|
|
1354
1524
|
|
1355
1525
|
|
1356
1526
|
|
1357
|
-
#### 3.2.
|
1527
|
+
#### 3.2.5. What happens when there are several sequencers?
|
1358
1528
|
|
1359
1529
|
|
1360
1530
|
#### General considerations
|
@@ -1698,7 +1868,7 @@ In software, when you want to do parallelism you usually need specific libraries
|
|
1698
1868
|
|
1699
1869
|
* [A sequencer is not a program](#41-a-sequencer-is-not-a-program)
|
1700
1870
|
|
1701
|
-
* [Adding parallelism is genuine](#42-parallel-control-statements-and-enumerators)
|
1871
|
+
* [Adding parallelism is genuine](#42-parallel-control-statements-functions-and-enumerators)
|
1702
1872
|
|
1703
1873
|
### 4.1. A sequencer is not a program
|
1704
1874
|
|
@@ -1736,7 +1906,7 @@ sequencer(clk,start) do
|
|
1736
1906
|
> __WARNING__: in hardware design with HDLRuby (and with all similar languages like Verilog VHDL or VHDL,) it is assumed that a clock is slow enough for the relevant combinatorial circuits to complete computation before the next cycle. If this is not the case, the resulting circuits will not function properly. Fortunately, the synthesis frameworks usually provide tools for verifying these timings.
|
1737
1907
|
|
1738
1908
|
|
1739
|
-
### 4.2. Parallel control statements and enumerators
|
1909
|
+
### 4.2. Parallel control statements, functions, and enumerators
|
1740
1910
|
|
1741
1911
|
Sequencers provide many constructs for easy control and enumeration. However, those constructs are sequential by construction. What if you would like to do the same in parallel? This is possible, but there is an important restriction:
|
1742
1912
|
|
@@ -1840,14 +2010,46 @@ Yes, this is becoming confusing because it is hard to know what Ruby does when e
|
|
1840
2010
|
* `zip`
|
1841
2011
|
|
1842
2012
|
|
2013
|
+
#### 4.2.3. Can functions be parallel too?
|
2014
|
+
|
2015
|
+
As explained before, the body of a `sdef` function can contain any kind of sequencer code, hence parallel code is also possible. Yet, calling and returning from such a function are themselves sequential procedures that require several cycles to be performed. Hence, there exists a parallel version of `sdef` whose call and return as well as its body execution is fully combinatorial. It is declared as follows:
|
2016
|
+
|
2017
|
+
```ruby
|
2018
|
+
hdef :<name> do |<arguments>|
|
2019
|
+
<body>
|
2020
|
+
end
|
2021
|
+
```
|
2022
|
+
|
2023
|
+
As you can see, the declaration is identical to the `sdef` one. However the behavior is different, and this new kind of function has the following limitations:
|
2024
|
+
|
2025
|
+
1. It cannot contain any sequential code, i.e., constructs like `step`, `sif` or `sloop`.
|
2026
|
+
|
2027
|
+
2. It does not support the `sreturn` constructs either, instead, it is the last value computed by the function that is returned.
|
2028
|
+
|
2029
|
+
3. It cannot be recursive.
|
2030
|
+
|
2031
|
+
Here is an example of a parallel-compatible function counting the number of ones in the input argument:
|
2032
|
+
|
2033
|
+
```ruby
|
2034
|
+
hdef :popcount do |n|
|
2035
|
+
n.each.reduce(_b0.as(n.type),&:+)
|
2036
|
+
end
|
2037
|
+
```
|
2038
|
+
|
2039
|
+
In the code above, it can be seen that no return is provided but instead, it will be the result of the parallel sum result that will be returned since it is the last computation of the function. Also, the data type of the sum is obtained from the argument `n` (`as(n.type)`).
|
2040
|
+
|
1843
2041
|
---
|
1844
2042
|
|
1845
2043
|
Now you know:
|
1846
2044
|
|
1847
|
-
* How to
|
2045
|
+
* How to introduce parallelism within a sequencer.
|
1848
2046
|
|
2047
|
+
* How to define a parallel-compatible function using `hdef`
|
1849
2048
|
|
1850
|
-
|
2049
|
+
But, by the way, why bother with sequential code if parallel one is faster?
|
2050
|
+
|
2051
|
+
|
2052
|
+
#### 4.2.4. Parallel is faster, so why do sequential computations?
|
1851
2053
|
|
1852
2054
|
Why parallel dataflow computations are indeed faster than sequential ones, in theory, they also have some drawbacks.
|
1853
2055
|
|
@@ -1898,7 +2100,11 @@ First, we must make things clear:
|
|
1898
2100
|
|
1899
2101
|
> Processes in hardware has very little (nothing?) to do with any kind of software process.
|
1900
2102
|
|
1901
|
-
In hardware, a process is a list of data flow statements that are activated (we would say *executed* if they were software instructions) on a common condition.
|
2103
|
+
In hardware, a process is a list of data flow statements that are activated (we would say *executed* if they were software instructions) on a common condition.
|
2104
|
+
|
2105
|
+
> By data flow statements, we mean all the assignment statements, the `hif`, `helse`, `hcase` and `hwhen` statements, and the calls to `hdef` functions.
|
2106
|
+
|
2107
|
+
Depending on the activation condition, there are three kinds of processes:
|
1902
2108
|
|
1903
2109
|
* The connection processes (in HDLRuby they are considered to be processes)
|
1904
2110
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: HDLRuby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lovic Gauthier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -195,11 +195,12 @@ files:
|
|
195
195
|
- lib/HDLRuby/hdr_samples/with_reconf.rb
|
196
196
|
- lib/HDLRuby/hdr_samples/with_reduce.rb
|
197
197
|
- lib/HDLRuby/hdr_samples/with_ref_array.rb
|
198
|
-
- lib/HDLRuby/hdr_samples/
|
198
|
+
- lib/HDLRuby/hdr_samples/with_ref_expr.rb
|
199
199
|
- lib/HDLRuby/hdr_samples/with_sequencer.rb
|
200
200
|
- lib/HDLRuby/hdr_samples/with_sequencer_deep.rb
|
201
201
|
- lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb
|
202
202
|
- lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb
|
203
|
+
- lib/HDLRuby/hdr_samples/with_sequencer_func.rb
|
203
204
|
- lib/HDLRuby/hdr_samples/with_sequencer_sync.rb
|
204
205
|
- lib/HDLRuby/hdr_samples/with_str2value.rb
|
205
206
|
- lib/HDLRuby/hdr_samples/with_subsums.rb
|
@@ -370,6 +371,7 @@ files:
|
|
370
371
|
- lib/HDLRuby/std/pipeline.rb
|
371
372
|
- lib/HDLRuby/std/reconf.rb
|
372
373
|
- lib/HDLRuby/std/sequencer.rb
|
374
|
+
- lib/HDLRuby/std/sequencer_func.rb
|
373
375
|
- lib/HDLRuby/std/sequencer_sync.rb
|
374
376
|
- lib/HDLRuby/std/std.rb
|
375
377
|
- lib/HDLRuby/std/task.rb
|
@@ -1,150 +0,0 @@
|
|
1
|
-
require 'soft/stacks.rb'
|
2
|
-
|
3
|
-
include HDLRuby::High::Soft
|
4
|
-
|
5
|
-
|
6
|
-
# A system testing the bram-based stack.
|
7
|
-
system :register_stack_test do
|
8
|
-
|
9
|
-
widthA = 3
|
10
|
-
size = 2**widthA
|
11
|
-
widthD = 8
|
12
|
-
|
13
|
-
|
14
|
-
input :clk,:rst,:ce
|
15
|
-
[2].inner :cmd
|
16
|
-
[widthD].inner :din,:dout
|
17
|
-
inner :empty, :full
|
18
|
-
douts = size.times.map { |i| [widthD].inner :"dout#{i}" }
|
19
|
-
|
20
|
-
register_stack(widthA,widthD,size).(:stackI).(clk,rst,ce,cmd,din,dout,empty,full,*douts)
|
21
|
-
|
22
|
-
[widthD].inner :count # Additional counter for the test.
|
23
|
-
|
24
|
-
timed do
|
25
|
-
clk <= 0
|
26
|
-
ce <= 0
|
27
|
-
rst <= 0
|
28
|
-
cmd <= READ
|
29
|
-
din <= 0
|
30
|
-
!10.ns
|
31
|
-
clk <= 1
|
32
|
-
!10.ns
|
33
|
-
clk <= 0
|
34
|
-
rst <= 1
|
35
|
-
!10.ns
|
36
|
-
clk <= 1
|
37
|
-
!10.ns
|
38
|
-
clk <= 0
|
39
|
-
rst <= 0
|
40
|
-
ce <= 1
|
41
|
-
din <= 1
|
42
|
-
!10.ns
|
43
|
-
clk <= 1
|
44
|
-
repeat(9) do
|
45
|
-
!10.ns
|
46
|
-
clk <= 0
|
47
|
-
cmd <= PUSH
|
48
|
-
din <= din + 1
|
49
|
-
!10.ns
|
50
|
-
clk <= 1
|
51
|
-
end
|
52
|
-
!10.ns
|
53
|
-
clk <= 0
|
54
|
-
!10.ns
|
55
|
-
clk <= 1
|
56
|
-
din <= -1
|
57
|
-
repeat(8) do
|
58
|
-
!10.ns
|
59
|
-
clk <= 0
|
60
|
-
cmd <= READ
|
61
|
-
din <= din + 1
|
62
|
-
!10.ns
|
63
|
-
clk <= 1
|
64
|
-
end
|
65
|
-
!10.ns
|
66
|
-
clk <= 0
|
67
|
-
!10.ns
|
68
|
-
clk <= 1
|
69
|
-
count <= 0
|
70
|
-
repeat(4) do
|
71
|
-
!10.ns
|
72
|
-
clk <= 0
|
73
|
-
din <= 1
|
74
|
-
cmd <= POP
|
75
|
-
!10.ns
|
76
|
-
clk <= 1
|
77
|
-
!10.ns
|
78
|
-
clk <= 0
|
79
|
-
count <= count + 1
|
80
|
-
din <= count
|
81
|
-
cmd <= PUSH
|
82
|
-
!10.ns
|
83
|
-
clk <= 1
|
84
|
-
end
|
85
|
-
!10.ns
|
86
|
-
clk <= 0
|
87
|
-
!10.ns
|
88
|
-
clk <= 1
|
89
|
-
repeat(9) do
|
90
|
-
!10.ns
|
91
|
-
clk <= 0
|
92
|
-
din <= 1
|
93
|
-
cmd <= POP
|
94
|
-
!10.ns
|
95
|
-
clk <= 1
|
96
|
-
end
|
97
|
-
!10.ns
|
98
|
-
clk <= 0
|
99
|
-
din <= _b8hAA
|
100
|
-
cmd <= PUSH
|
101
|
-
!10.ns
|
102
|
-
clk <= 1
|
103
|
-
!10.ns
|
104
|
-
clk <= 0
|
105
|
-
din <= 7
|
106
|
-
cmd <= WRITE
|
107
|
-
!10.ns
|
108
|
-
clk <= 1
|
109
|
-
din <= -1
|
110
|
-
repeat(8) do
|
111
|
-
!10.ns
|
112
|
-
clk <= 0
|
113
|
-
cmd <= WRITE
|
114
|
-
din <= din + 1
|
115
|
-
!10.ns
|
116
|
-
clk <= 1
|
117
|
-
end
|
118
|
-
!10.ns
|
119
|
-
clk <= 0
|
120
|
-
din <= size-1
|
121
|
-
cmd <= READ
|
122
|
-
!10.ns
|
123
|
-
clk <= 1
|
124
|
-
!10.ns
|
125
|
-
clk <= 0
|
126
|
-
din <= -3
|
127
|
-
cmd <= POP
|
128
|
-
!10.ns
|
129
|
-
clk <= 1
|
130
|
-
!10.ns
|
131
|
-
clk <= 0
|
132
|
-
din <= 31
|
133
|
-
cmd <= PUSH
|
134
|
-
!10.ns
|
135
|
-
clk <= 1
|
136
|
-
!10.ns
|
137
|
-
clk <= 0
|
138
|
-
din <= 3
|
139
|
-
cmd <= POP
|
140
|
-
!10.ns
|
141
|
-
clk <= 1
|
142
|
-
!10.ns
|
143
|
-
clk <= 0
|
144
|
-
din <= size-1
|
145
|
-
cmd <= READ
|
146
|
-
!10.ns
|
147
|
-
clk <= 1
|
148
|
-
end
|
149
|
-
|
150
|
-
end
|