HDLRuby 2.11.12 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.html +3274 -0
- data/README.md +660 -128
- data/ext/hruby_sim/hruby_sim_calc.c +2 -0
- data/lib/HDLRuby/backend/hruby_allocator.rb +2 -2
- data/lib/HDLRuby/backend/hruby_c_allocator.rb +7 -7
- data/lib/HDLRuby/hdr_samples/constant_in_function.rb +2 -1
- data/lib/HDLRuby/hdr_samples/mei8_bench.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_bram.rb +3 -3
- data/lib/HDLRuby/hdr_samples/with_bram_frame_stack.rb +105 -0
- data/lib/HDLRuby/hdr_samples/with_bram_stack.rb +69 -0
- data/lib/HDLRuby/hdr_samples/with_ref_expr.rb +30 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer.rb +185 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_deep.rb +91 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb +439 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb +89 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_func.rb +63 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_sync.rb +120 -0
- data/lib/HDLRuby/hdrcc.rb +16 -3
- data/lib/HDLRuby/hdrlib.rb +1 -1
- data/lib/HDLRuby/hruby_db.rb +2 -2
- data/lib/HDLRuby/hruby_high.rb +61 -25
- data/lib/HDLRuby/hruby_high_fullname.rb +3 -1
- data/lib/HDLRuby/hruby_low.rb +2 -2
- data/lib/HDLRuby/hruby_low2c.rb +58 -43
- data/lib/HDLRuby/hruby_low2hdr.rb +66 -40
- data/lib/HDLRuby/hruby_low2high.rb +86 -44
- data/lib/HDLRuby/hruby_low2seq.rb +26 -18
- data/lib/HDLRuby/hruby_low2sym.rb +14 -13
- data/lib/HDLRuby/hruby_low2vhd.rb +78 -43
- data/lib/HDLRuby/hruby_low_bool2select.rb +61 -46
- data/lib/HDLRuby/hruby_low_casts_without_expression.rb +56 -44
- data/lib/HDLRuby/hruby_low_cleanup.rb +18 -16
- data/lib/HDLRuby/hruby_low_fix_types.rb +64 -32
- data/lib/HDLRuby/hruby_low_mutable.rb +53 -118
- data/lib/HDLRuby/hruby_low_resolve.rb +26 -31
- data/lib/HDLRuby/hruby_low_with_bool.rb +33 -16
- data/lib/HDLRuby/hruby_low_with_port.rb +3 -3
- data/lib/HDLRuby/hruby_low_with_var.rb +23 -9
- data/lib/HDLRuby/hruby_low_without_concat.rb +19 -13
- data/lib/HDLRuby/hruby_low_without_namespace.rb +47 -32
- data/lib/HDLRuby/hruby_low_without_parinseq.rb +18 -12
- data/lib/HDLRuby/hruby_low_without_select.rb +36 -23
- data/lib/HDLRuby/hruby_low_without_subsignals.rb +79 -39
- data/lib/HDLRuby/hruby_rcsim.rb +79 -64
- data/lib/HDLRuby/hruby_rsim.rb +64 -15
- data/lib/HDLRuby/hruby_rsim_mute.rb +2 -3
- data/lib/HDLRuby/hruby_rsim_vcd.rb +28 -25
- data/lib/HDLRuby/hruby_types.rb +5 -5
- data/lib/HDLRuby/hruby_values.rb +19 -8
- data/lib/HDLRuby/hruby_verilog.rb +191 -65
- data/lib/HDLRuby/hruby_verilog_name.rb +49 -42
- data/lib/HDLRuby/soft/stacks.rb +219 -0
- data/lib/HDLRuby/std/bram.rb +9 -5
- data/lib/HDLRuby/std/clocks.rb +1 -1
- data/lib/HDLRuby/std/fsm.rb +39 -10
- data/lib/HDLRuby/std/sequencer.rb +2085 -0
- data/lib/HDLRuby/std/sequencer_func.rb +533 -0
- data/lib/HDLRuby/std/sequencer_sync.rb +400 -0
- data/lib/HDLRuby/std/std.rb +13 -0
- data/lib/HDLRuby/version.rb +1 -1
- data/tuto/adder_sat_flags_vcd.png +0 -0
- data/tuto/addsub_vcd.png +0 -0
- data/tuto/alu_vcd.png +0 -0
- data/tuto/bit_pong_vcd.png +0 -0
- data/tuto/checksum_vcd.png +0 -0
- data/tuto/circuit_hdr.odg +0 -0
- data/tuto/circuit_hdr.png +0 -0
- data/tuto/circuit_hie.odg +0 -0
- data/tuto/circuit_hie.png +0 -0
- data/tuto/circuit_view.odg +0 -0
- data/tuto/circuit_view.png +0 -0
- data/tuto/clock_counter_vcd.png +0 -0
- data/tuto/counter_ext_vcd.png +0 -0
- data/tuto/fact_vcd.png +0 -0
- data/tuto/hw_flow.odg +0 -0
- data/tuto/hw_flow.png +0 -0
- data/tuto/maxxer_vcd.png +0 -0
- data/tuto/pingpong0_vcd.png +0 -0
- data/tuto/pingpong1_vcd.png +0 -0
- data/tuto/pingpong2_vcd.png +0 -0
- data/tuto/ram_vcd.png +0 -0
- data/tuto/serializer_vcd.png +0 -0
- data/tuto/sw_flow.odg +0 -0
- data/tuto/sw_flow.png +0 -0
- data/tuto/the_counter_vcd.png +0 -0
- data/tuto/tutorial_sw.html +2359 -0
- data/tuto/tutorial_sw.md +2890 -0
- data/tuto/tutorial_sw.pdf +0 -0
- data/tuto/tutorial_sw_jp.md +417 -0
- metadata +46 -2
data/README.md
CHANGED
|
@@ -3,6 +3,37 @@
|
|
|
3
3
|
HDLRuby is a library for describing and simulating digital electronic
|
|
4
4
|
systems.
|
|
5
5
|
|
|
6
|
+
__Note__:
|
|
7
|
+
|
|
8
|
+
If you are new to HDLRuby, it is recommended that you consult first the following tutorial (even if you are a hardware person):
|
|
9
|
+
|
|
10
|
+
* [HDLRuby tutorial for software people](tuto/tutorial_sw.md)
|
|
11
|
+
|
|
12
|
+
__What's new__
|
|
13
|
+
|
|
14
|
+
For HDLRuby version 3.1.0:
|
|
15
|
+
|
|
16
|
+
* [Functions for sequencers](#sequencer-specific-function-std-sequencer_func-rb) supporting recursion;
|
|
17
|
+
|
|
18
|
+
* The `function` keyword was replaced with `hdef` for better consistency with the new functions for sequencers;
|
|
19
|
+
|
|
20
|
+
* The `steps` command was added for waiting several steps in a sequencer;
|
|
21
|
+
|
|
22
|
+
* Verilog HDL code generation was improved to preserve as much as possible the original names of the signals;
|
|
23
|
+
|
|
24
|
+
* Several bug fixes for the sequencers.
|
|
25
|
+
|
|
26
|
+
For HDLRuby version 3.0.0:
|
|
27
|
+
|
|
28
|
+
* This section;
|
|
29
|
+
|
|
30
|
+
* [The sequencers](#sequencer-software-like-hardware-coding-stdsequencerrb) for software-like hardware design;
|
|
31
|
+
|
|
32
|
+
* A [tutorial](tuto/tutorial_sw.md) for software people;
|
|
33
|
+
|
|
34
|
+
* The stable [standard libraries](#standard-libraries) are loaded by default.
|
|
35
|
+
|
|
36
|
+
|
|
6
37
|
__Install__:
|
|
7
38
|
|
|
8
39
|
The recommended installation method is from rubygem as follows:
|
|
@@ -27,7 +58,7 @@ __Warning__:
|
|
|
27
58
|
|
|
28
59
|
## Using the HDLRuby compiler
|
|
29
60
|
|
|
30
|
-
'hdrcc' is the HDLRuby compiler. It takes as input an HDLRuby file, checks it, and can produce as output a Verilog HDL, VHDL, or a YAML low-level
|
|
61
|
+
'hdrcc' is the HDLRuby compiler. It takes as input an HDLRuby file, checks it, and can produce as output a Verilog HDL, VHDL, or a YAML low-level description of HW components but it can also simulate the input description.
|
|
31
62
|
|
|
32
63
|
|
|
33
64
|
__Usage__:
|
|
@@ -49,68 +80,76 @@ Where:
|
|
|
49
80
|
| `-v, --verilog` | Output in Verilog HDL format |
|
|
50
81
|
| `-V, --vhdl` | Output in VHDL format |
|
|
51
82
|
| `-s, --syntax` | Output the Ruby syntax tree |
|
|
52
|
-
| `-C, --clang` | Output the C code of the standalone simulator
|
|
53
|
-
| `-S, --sim` | Perform the simulation with the default engine
|
|
54
|
-
| `--csim` | Perform the simulation with the standalone engine
|
|
55
|
-
| `--rsim` | Perform the simulation with the Ruby engine
|
|
56
|
-
| `--rcsim` | Perform the simulation with the
|
|
83
|
+
| `-C, --clang` | Output the C code of the standalone simulator |
|
|
84
|
+
| `-S, --sim` | Perform the simulation with the default engine |
|
|
85
|
+
| `--csim` | Perform the simulation with the standalone engine |
|
|
86
|
+
| `--rsim` | Perform the simulation with the Ruby engine |
|
|
87
|
+
| `--rcsim` | Perform the simulation with the Hybrid engine |
|
|
57
88
|
| `--vcd` | Make the simulator generate a VCD file |
|
|
58
89
|
| `-d, --directory` | Specify the base directory for loading the HDLRuby files |
|
|
59
90
|
| `-D, --debug` | Set the HDLRuby debug mode |
|
|
60
91
|
| `-t, --top system`| Specify the top system describing the circuit to compile |
|
|
61
92
|
| `-p, --param x,y,z` | Specify the generic parameters |
|
|
62
|
-
| `--
|
|
93
|
+
| `--get-samples` | Copy the sample directory (hdr_samples) to current one, then exit |
|
|
94
|
+
| `--version` | Show the version number, then exit |
|
|
63
95
|
| `-h, --help` | Show the help message |
|
|
64
96
|
|
|
65
97
|
__Notes__:
|
|
66
98
|
|
|
67
99
|
* If no top system is given, it is automatically looked for from the input file.
|
|
68
100
|
* If no option is given, it simply checks the input file.
|
|
69
|
-
*
|
|
101
|
+
* If you are new to HDLRuby, or if you want to see how new features work, we strongly encourage to get a local copy of the test HDLRuby sample using:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
hdrcc --get-samples
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then in your current directory (folder) the `hdr_samples` subdirectory will appear that contains several HDLRuby example files. Details about the samples can be found here: [samples](#sample-hdlruby-descriptions).
|
|
108
|
+
|
|
70
109
|
|
|
71
110
|
__Examples__:
|
|
72
111
|
|
|
73
112
|
* Compile system named `adder` from `adder.rb` input file and generate a low-level YAML description into directory `adder`:
|
|
74
113
|
|
|
75
|
-
```
|
|
114
|
+
```bash
|
|
76
115
|
hdrcc --yaml --top adder adder.rb adder
|
|
77
116
|
```
|
|
78
117
|
|
|
79
|
-
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into directory `adder`:
|
|
118
|
+
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into the directory `adder`:
|
|
80
119
|
|
|
81
|
-
```
|
|
120
|
+
```bash
|
|
82
121
|
hdrcc -v adder.rb adder
|
|
83
122
|
```
|
|
84
123
|
|
|
85
124
|
* Compile system `adder` whose bit width is generic from `adder_gen.rb` input file to a 16-bit circuit low-level VHDL description into directory `adder`:
|
|
86
125
|
|
|
87
|
-
```
|
|
126
|
+
```bash
|
|
88
127
|
hdrcc -V -t adder --param 16 adder_gen.rb adder
|
|
89
128
|
```
|
|
90
129
|
|
|
91
130
|
* Compile system `multer` with inputs and output bit width is generic from `multer_gen.rb` input file to a 16x16->32-bit circuit whose low-level YAML description into directory `multer`:
|
|
92
131
|
|
|
93
|
-
```
|
|
132
|
+
```bash
|
|
94
133
|
hdrcc -y -t multer -p 16,16,32 multer_gen.rb multer
|
|
95
134
|
```
|
|
96
135
|
|
|
97
|
-
* Simulate the circuit described in file `counter_bench.rb` using the default
|
|
136
|
+
* Simulate the circuit described in file `counter_bench.rb` using the default simulation engine and putting the simulator's files in the directory `counter`:
|
|
98
137
|
|
|
99
|
-
```
|
|
138
|
+
```bash
|
|
100
139
|
hdrcc -S counter_bench.rb counter
|
|
101
140
|
```
|
|
102
141
|
|
|
103
|
-
As a policy, the default simulation
|
|
142
|
+
As a policy, the default simulation engine is set to the fastest one (currently it is the hybrid engine).
|
|
104
143
|
|
|
105
144
|
* Run in interactive mode.
|
|
106
145
|
|
|
107
|
-
```
|
|
146
|
+
```bash
|
|
108
147
|
hdrcc -I
|
|
109
148
|
```
|
|
110
149
|
|
|
111
150
|
* Run in interactive mode using pry as UI.
|
|
112
151
|
|
|
113
|
-
```
|
|
152
|
+
```bash
|
|
114
153
|
hdrcc -I pry
|
|
115
154
|
```
|
|
116
155
|
|
|
@@ -166,13 +205,13 @@ The second specificity of HDLRuby is that it supports natively all the features
|
|
|
166
205
|
|
|
167
206
|
__Notes__:
|
|
168
207
|
|
|
169
|
-
- It is still possible to extend HDLRuby to support hardware descriptions of a higher level than RTL, please refer to section [Extending HDLRuby](#
|
|
208
|
+
- It is still possible to extend HDLRuby to support hardware descriptions of a higher level than RTL, please refer to section [Extending HDLRuby](#extending-hdlruby) for more details.
|
|
170
209
|
- In this document, HDLRuby constructs will often be compared to their Verilog HDL or VHDL equivalents for simpler explanations.
|
|
171
210
|
|
|
172
211
|
## Introduction
|
|
173
212
|
|
|
174
213
|
This introduction gives a glimpse of the possibilities of the language.
|
|
175
|
-
However, we do recommend consulting the section about the [high-level programming features](#
|
|
214
|
+
However, we do recommend consulting the section about the [high-level programming features](#high-level-programming-features) to have a more complete view of the advanced possibilities of this language.
|
|
176
215
|
|
|
177
216
|
At first glance, HDLRuby appears like any other HDL (like Verilog HDL or VHDL), for instance, the following code describes a simple D-FF:
|
|
178
217
|
|
|
@@ -187,7 +226,7 @@ system :dff do
|
|
|
187
226
|
end
|
|
188
227
|
```
|
|
189
228
|
|
|
190
|
-
As
|
|
229
|
+
As can be seen in the code above, `system` is the keyword used for describing a digital circuit. This keyword is the equivalent of the Verilog HDL `module`. In such a system, signals are declared using a `<type>.<direction>` construct where `type` is the data type of the signal (e.g., `bit` as in the code above) and `direction` indicates if the signal is an input, an output, an inout or an inner one; and executable blocks (similar to `always` block of Verilog HDL) are described using the `par` keyword when they are parallel and `seq` when they are sequential (i.e., with respectively non-blocking and blocking assignments).
|
|
191
230
|
|
|
192
231
|
After such a system has been defined, it can be instantiated. For example, a single instance of the `dff` system named `dff0` can be declared as follows:
|
|
193
232
|
|
|
@@ -195,7 +234,7 @@ After such a system has been defined, it can be instantiated. For example, a sin
|
|
|
195
234
|
dff :dff0
|
|
196
235
|
```
|
|
197
236
|
|
|
198
|
-
The ports of this instance can then be accessed to be used like any other signals, e.g., `dff0.d` for
|
|
237
|
+
The ports of this instance can then be accessed to be used like any other signals, e.g., `dff0.d` for accessing the `d` input of the FF.
|
|
199
238
|
|
|
200
239
|
Several instances can also be declared in a single statement. For example, a 2-bit counter based on the previous `dff` circuits can be described as follows:
|
|
201
240
|
|
|
@@ -230,7 +269,7 @@ system :counter2 do
|
|
|
230
269
|
end
|
|
231
270
|
```
|
|
232
271
|
|
|
233
|
-
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected
|
|
272
|
+
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected in declaration order. Please notice that it is also possible to connect only a subset of the ports while declaring, as well as to reconnect already connected ports in further statements.
|
|
234
273
|
|
|
235
274
|
While a circuit can be generated from the code given above, a benchmark must
|
|
236
275
|
be provided to test it. Such a benchmark is described by constructs called
|
|
@@ -285,8 +324,7 @@ end
|
|
|
285
324
|
```
|
|
286
325
|
|
|
287
326
|
Then, it often happens that a system will end up with only one instance.
|
|
288
|
-
In such a case, the system declaration can be omitted and an instance
|
|
289
|
-
can be directly declared as follows:
|
|
327
|
+
In such a case, the system declaration can be omitted, and an instance can be directly declared as follows:
|
|
290
328
|
|
|
291
329
|
```ruby
|
|
292
330
|
instance :dff_single do
|
|
@@ -297,8 +335,7 @@ instance :dff_single do
|
|
|
297
335
|
end
|
|
298
336
|
```
|
|
299
337
|
|
|
300
|
-
In the example above, `dff_single` is an instance describing, again, a
|
|
301
|
-
D-FF, but whose system is anonymous.
|
|
338
|
+
In the example above, `dff_single` is an instance describing, again, a D-FF, but whose system is anonymous.
|
|
302
339
|
|
|
303
340
|
Furthermore, generic parameters can be used for anything in HDLRuby.
|
|
304
341
|
For instance, the following code describes an 8-bit register without any parameterization:
|
|
@@ -395,7 +432,7 @@ system :shifter do |n|
|
|
|
395
432
|
end
|
|
396
433
|
```
|
|
397
434
|
|
|
398
|
-
As
|
|
435
|
+
As can be seen in the above examples, in HDLRuby, any construct is an object and therefore include methods. For instance, declaring a signal of a given `type` and direction (input, output, or inout) is done as follows so that `direction` is a method of the type, and the signal names are the arguments of this method (symbols or string are supported.)
|
|
399
436
|
|
|
400
437
|
```ruby
|
|
401
438
|
<type>.<direction> <list of symbols representing the signal>
|
|
@@ -430,7 +467,7 @@ system :sumprod_16_3456 do
|
|
|
430
467
|
end
|
|
431
468
|
```
|
|
432
469
|
|
|
433
|
-
The description above is straightforward, but it would be necessary to rewrite it if another circuit with different bit
|
|
470
|
+
The description above is straightforward, but it would be necessary to rewrite it if another circuit with different bit widths or coefficients is to be designed. Moreover, if the number of coefficients is large an error in the expression will be easy to make and hard to find. A better approach would be to use a generic description of such a circuit as follows:
|
|
434
471
|
|
|
435
472
|
```ruby
|
|
436
473
|
system :sumprod do |typ,coefs|
|
|
@@ -446,7 +483,7 @@ end
|
|
|
446
483
|
In the code above, there are two generic parameters,
|
|
447
484
|
`typ`, which indicates the data type of the circuit, and `coefs`, which is assumed to be an array of coefficients. Since the number of inputs depends on the number of provided coefficients, it is declared as an array of `width` bit signed whose size is equal to the number of coefficients.
|
|
448
485
|
|
|
449
|
-
The description of the sum of products may be more difficult to understand for people not familiar with the Ruby language. The `each_with_index` method iterates over the coefficients adding their index as iteration variable, the resulting operation (i.e., the iteration loop) is then modified by the `reduce` method that accumulates the code passed as arguments. This code, starting by `|sum,coef,i|` simply performs the addition of the current accumulation result (`sum`) with the product of the current coefficient (`coef`) and input (`ins[i]`, where `i` is the index) in the iteration. The argument `_b0` initializes the sum to `0`.
|
|
486
|
+
The description of the sum of products may be more difficult to understand for people not familiar with the Ruby language. The `each_with_index` method iterates over the coefficients adding their index as an iteration variable, the resulting operation (i.e., the iteration loop) is then modified by the `reduce` method that accumulates the code passed as arguments. This code, starting by `|sum,coef,i|` simply performs the addition of the current accumulation result (`sum`) with the product of the current coefficient (`coef`) and input (`ins[i]`, where `i` is the index) in the iteration. The argument `_b0` initializes the sum to `0`.
|
|
450
487
|
|
|
451
488
|
While slightly longer than the previous description, this description allows declaring a circuit implementing a sum of products with any bit width and any number of coefficients. For instance, the following code describes a signed 32-bit sum of products with 16 coefficients (just random numbers here).
|
|
452
489
|
|
|
@@ -456,7 +493,7 @@ sumprod(signed[32], [3,78,43,246, 3,67,1,8, 47,82,99,13, 5,77,2,4]).(:my_circuit
|
|
|
456
493
|
|
|
457
494
|
As seen in the code above, when passing a generic argument for instantiating a generic system, the name of the instance is put between brackets for avoiding confusion.
|
|
458
495
|
|
|
459
|
-
While the description `sumprod` is already usable in a wide range of cases, it still uses
|
|
496
|
+
While the description `sumprod` is already usable in a wide range of cases, it still uses standard addition and multiplication. However, there are cases where specific components are to be used for these operations, either for the sake of performance, compliance with constraints, or because functionally different operations are required (e.g., saturated computations). This can be solved by using functions implementing such computation in place of operators, for example, as follows:
|
|
460
497
|
|
|
461
498
|
```ruby
|
|
462
499
|
system :sumprod_func do |typ,coefs|
|
|
@@ -473,7 +510,7 @@ end
|
|
|
473
510
|
Where `add` and `mult` are functions implementing the required specific operations. HDLRuby functions are equivalent to the Verilog HDL ones. In our example, an addition that saturates at 1000 could be described as follows:
|
|
474
511
|
|
|
475
512
|
```ruby
|
|
476
|
-
|
|
513
|
+
hdef :add do |x,y|
|
|
477
514
|
inner :res
|
|
478
515
|
seq do
|
|
479
516
|
res <= x + y
|
|
@@ -491,7 +528,7 @@ With HDLRuby functions, the result of the last statement in the return value, in
|
|
|
491
528
|
With functions, it is enough to change their content to obtain a new kind of circuit without changing the main code. This approach suffers from two drawbacks though: first, the level of saturation is hard coded in the function, and second, it would be preferable to be able to select the function to execute instead of modifying its code. For the first problem, a simple approach is to add an argument to the function given the saturation level. Such an add function would therefore be as follows:
|
|
492
529
|
|
|
493
530
|
```ruby
|
|
494
|
-
|
|
531
|
+
hdef :add do |max, x, y|
|
|
495
532
|
inner :res
|
|
496
533
|
seq do
|
|
497
534
|
res <= x + y
|
|
@@ -500,9 +537,9 @@ function :add do |max, x, y|
|
|
|
500
537
|
end
|
|
501
538
|
```
|
|
502
539
|
|
|
503
|
-
It would however be necessary to add this argument when invoking the function, e.g., `add(1000,sum,mult(...))`. While this argument is relevant for addition with saturation, it is not for the other kind of addition operations, and hence, the code of `sumprod` is
|
|
540
|
+
It would however be necessary to add this argument when invoking the function, e.g., `add(1000,sum,mult(...))`. While this argument is relevant for addition with saturation, it is not for the other kind of addition operations, and hence, the code of `sumprod` is no general purpose.
|
|
504
541
|
|
|
505
|
-
HDLRuby provides two ways to address such issues. First, it is possible to pass code as an argument. In the case of `sumprod
|
|
542
|
+
HDLRuby provides two ways to address such issues. First, it is possible to pass code as an argument. In the case of `sumprod`, it would then be enough to add two arguments that perform the required addition and multiplication. The example is below:
|
|
506
543
|
|
|
507
544
|
```ruby
|
|
508
545
|
system :sumprod_proc do |add,mult,typ,coefs|
|
|
@@ -582,12 +619,7 @@ sumprod(sat(16,1000),
|
|
|
582
619
|
```
|
|
583
620
|
|
|
584
621
|
|
|
585
|
-
Lastly note, HDLRuby is also a language with supports reflection for
|
|
586
|
-
all its constructs. For example, the system of an instance can be accessed
|
|
587
|
-
using the `systemT` method, and this latter can be used to create
|
|
588
|
-
other instances. For example, previously, `dff_single` was declared with
|
|
589
|
-
an anonymous system (i.e., it cannot be accessed by name). This system
|
|
590
|
-
can however be used as follows to generate another instance:
|
|
622
|
+
Lastly note, HDLRuby is also a language with supports reflection for all its constructs. For example, the system of an instance can be accessed using the `systemT` method, and this latter can be used to create other instances. For example, previously, `dff_single` was declared with an anonymous system (i.e., it cannot be accessed by name). This system can however be used as follows to generate another instance:
|
|
591
623
|
|
|
592
624
|
```ruby
|
|
593
625
|
dff_single.systemT.instantiate(:dff_not_single)
|
|
@@ -596,12 +628,8 @@ dff_single.systemT.instantiate(:dff_not_single)
|
|
|
596
628
|
In the above example, `dff_not_single` is declared to be an instance
|
|
597
629
|
of the same system as `dff_single`.
|
|
598
630
|
|
|
599
|
-
This reflection capability can also be used for instance, for accessing the
|
|
600
|
-
|
|
601
|
-
(`cur_block`), the current process (`cur_behavior`) and so on.
|
|
602
|
-
The standard library of HDLRuby includes several hardware constructs
|
|
603
|
-
like finite state machine descriptors and is mainly based on using these
|
|
604
|
-
reflection features.
|
|
631
|
+
This reflection capability can also be used for instance, for accessing the data type of a signal (`sig.type`), but also the current basic block (`cur_block`), the current process (`cur_behavior`), and so on.
|
|
632
|
+
The standard library of HDLRuby includes several hardware constructs like finite state machine descriptors and is mainly based on using these reflection features.
|
|
605
633
|
|
|
606
634
|
|
|
607
635
|
|
|
@@ -625,10 +653,10 @@ From there, we will describe in more detail each construct of HDLRuby.
|
|
|
625
653
|
## Naming rules
|
|
626
654
|
<a name="names"></a>
|
|
627
655
|
|
|
628
|
-
Several constructs in HDLRuby are referred to by name, e.g., systems and signals. When such constructs are declared, their names are to be specified by Ruby symbols starting with a
|
|
656
|
+
Several constructs in HDLRuby are referred to by name, e.g., systems and signals. When such constructs are declared, their names are to be specified by Ruby symbols starting with a lowercase. For example, `:hello` is a valid name declaration, but `:Hello` is not.
|
|
629
657
|
|
|
630
658
|
After being declared, the construct can be referred to by using the name directly (i.e., without the `:` of Ruby symbols). For example, if a construct
|
|
631
|
-
has been declared with `:hello` as name, it will be afterward referred to by `hello`.
|
|
659
|
+
has been declared with `:hello` as the name, it will be afterward referred to by `hello`.
|
|
632
660
|
|
|
633
661
|
## Systems and signals
|
|
634
662
|
|
|
@@ -646,11 +674,10 @@ system(:box) {}
|
|
|
646
674
|
|
|
647
675
|
__Notes__:
|
|
648
676
|
|
|
649
|
-
- Since this is Ruby code, the body can also be delimited by the `do` and `end`
|
|
650
|
-
Ruby keywords (in which case the parentheses can be omitted) are as follows:
|
|
677
|
+
- Since this is Ruby code, the body can also be delimited by the `do` and `end` Ruby keywords (in which case the parentheses can be omitted) are as follows:
|
|
651
678
|
|
|
652
679
|
```ruby
|
|
653
|
-
system :box
|
|
680
|
+
system :box does
|
|
654
681
|
end
|
|
655
682
|
```
|
|
656
683
|
|
|
@@ -677,8 +704,7 @@ Now, since `bit` is the default data type in HDLRuby, it can be omitted as follo
|
|
|
677
704
|
input :clk
|
|
678
705
|
```
|
|
679
706
|
|
|
680
|
-
The following is a more complete example: it is the code of a system describing an 8-bit data, 16-bit address memory whose interface includes a 1-bit input
|
|
681
|
-
clock (`clk`), a 1-bit signal for selecting reading or writing access (`rwb`), a 16-bit address input (`addr`), and an 8-bit data inout — the remaining of the code describes the content and the behavior of the memory.
|
|
707
|
+
The following is a more complete example: it is the code of a system describing an 8-bit data, 16-bit address memory whose interface includes a 1-bit input clock (`clk`), a 1-bit signal for selecting reading or writing access (`rwb`), a 16-bit address input (`addr`), and an 8-bit data inout — the remaining of the code describes the content and the behavior of the memory.
|
|
682
708
|
|
|
683
709
|
```ruby
|
|
684
710
|
system :mem8_16 do
|
|
@@ -773,8 +799,7 @@ It is also possible to connect multiple signals of an instance using the call op
|
|
|
773
799
|
<instance name>.(<signal name0>: <target0>, ...)
|
|
774
800
|
```
|
|
775
801
|
|
|
776
|
-
For example, the following code connects signals `clk` and `rst` of instance
|
|
777
|
-
`mem8_16I` to signals `clk` and `rst` of the current system. As seen in this example, this method allows partial connection since the address and the data buses are not connected yet.
|
|
802
|
+
For example, the following code connects signals `clk` and `rst` of instance `mem8_16I` to signals `clk` and `rst` of the current system. As seen in this example, this method allows partial connection since the address and the data buses are not connected yet.
|
|
778
803
|
|
|
779
804
|
```ruby
|
|
780
805
|
mem8_16I.(clk: clk, rst: rst)
|
|
@@ -921,7 +946,7 @@ Where:
|
|
|
921
946
|
* `<name>` is the name of the scope.
|
|
922
947
|
* `<code>` is the code within the scope.
|
|
923
948
|
|
|
924
|
-
Contrary to the case of scopes without a name, signals and instances declared within a named scope can be accessed outside using this name as a reference. For example, in the code below signal `sig` declared within scope named `scop` is accessed outside it using `scop.sig`:
|
|
949
|
+
Contrary to the case of scopes without a name, signals, and instances declared within a named scope can be accessed outside using this name as a reference. For example, in the code below signal `sig` declared within scope named `scop` is accessed outside it using `scop.sig`:
|
|
925
950
|
|
|
926
951
|
```ruby
|
|
927
952
|
sub :scop do
|
|
@@ -948,7 +973,7 @@ end
|
|
|
948
973
|
In addition, it is possible to declare inner signals within an execution block.
|
|
949
974
|
While such signals will be physically linked to the system, they are only accessible within the block they are declared into. This permits a tighter scope for signals, which improves the readability of the code and make it possible to declare several signals with identical names provided their respective scopes are different.
|
|
950
975
|
|
|
951
|
-
An event represents a specific change
|
|
976
|
+
An event represents a specific change in the state of a signal.
|
|
952
977
|
For example, a rising edge of a clock signal named `clk` will be represented by the event `clk.posedge`. In HDLRuby, events are obtained directly from
|
|
953
978
|
expressions using the following methods: `posedge` for a rising edge, `negedge` for a falling edge, and `edge` for any edge.
|
|
954
979
|
Events are described in more detail in section [Events](#events).
|
|
@@ -989,7 +1014,7 @@ system :with_sequential_behavior do
|
|
|
989
1014
|
end
|
|
990
1015
|
```
|
|
991
1016
|
|
|
992
|
-
A sub-block can also have a different execution mode if it is declared using `seq`, which will force sequential execution mode
|
|
1017
|
+
A sub-block can also have a different execution mode if it is declared using `seq`, which will force sequential execution mode and `par` which will force parallel execution mode. For example, in the following code a parallel sub-block is declared within a sequential one:
|
|
993
1018
|
|
|
994
1019
|
```ruby
|
|
995
1020
|
system :with_sequential_behavior do
|
|
@@ -1002,7 +1027,7 @@ system :with_sequential_behavior do
|
|
|
1002
1027
|
end
|
|
1003
1028
|
```
|
|
1004
1029
|
|
|
1005
|
-
|
|
1030
|
+
Sunblocks have their scope so that it is possible to declare signals without colliding with existing ones. For example, it is possible to
|
|
1006
1031
|
declare three different inner signals all called `sig` as follows:
|
|
1007
1032
|
|
|
1008
1033
|
```ruby
|
|
@@ -1117,7 +1142,7 @@ unshift do
|
|
|
1117
1142
|
end
|
|
1118
1143
|
```
|
|
1119
1144
|
|
|
1120
|
-
For example the following code inserts two statements at the beginning of the current block:
|
|
1145
|
+
For example, the following code inserts two statements at the beginning of the current block:
|
|
1121
1146
|
|
|
1122
1147
|
```ruby
|
|
1123
1148
|
par do
|
|
@@ -1129,7 +1154,7 @@ par do
|
|
|
1129
1154
|
end
|
|
1130
1155
|
```
|
|
1131
1156
|
|
|
1132
|
-
The code above will
|
|
1157
|
+
The code above will result in the following block:
|
|
1133
1158
|
|
|
1134
1159
|
```ruby
|
|
1135
1160
|
par do
|
|
@@ -1151,7 +1176,7 @@ In HDLRuby, dynamically reconfigurable devices are modeled by instances having m
|
|
|
1151
1176
|
<instance>.choice(<list of named systems>)
|
|
1152
1177
|
```
|
|
1153
1178
|
|
|
1154
|
-
For example, assuming systems `sys0`, `sys1
|
|
1179
|
+
For example, assuming systems `sys0`, `sys1`, and `sys2` have been previously declared a device named `dev012` able to be reconfigured to one of these three systems would be declared as follows (the connections of the instance, omitted in the example, can be done as usual):
|
|
1155
1180
|
|
|
1156
1181
|
```ruby
|
|
1157
1182
|
sys0 :dev012 # dev012 is at first a standard instance of sys0
|
|
@@ -1183,9 +1208,8 @@ These reconfiguration commands are treated as regular RTL statements in HDLRuby
|
|
|
1183
1208
|
|
|
1184
1209
|
|
|
1185
1210
|
## Events
|
|
1186
|
-
<a name="events"></a>
|
|
1187
1211
|
|
|
1188
|
-
Each behavior of a system is associated with a list of events, called a sensitivity list, that specifies when the behavior is to be executed. An event is associated with a signal and represents the
|
|
1212
|
+
Each behavior of a system is associated with a list of events, called a sensitivity list, that specifies when the behavior is to be executed. An event is associated with a signal and represents the instant when the signal reaches a given state.
|
|
1189
1213
|
|
|
1190
1214
|
There are three kinds of events: positive edge events represent the instants when their corresponding signals vary from 0 to 1, and negative edge events
|
|
1191
1215
|
represent the instants when their corresponding signals vary from 1 to 0 and the change events represent the instants when their corresponding signals vary.
|
|
@@ -1211,7 +1235,6 @@ __Note:__
|
|
|
1211
1235
|
- The `change` keyword can be omitted.
|
|
1212
1236
|
|
|
1213
1237
|
## Statements
|
|
1214
|
-
<a name="statements"></a>
|
|
1215
1238
|
|
|
1216
1239
|
Statements are the basic elements of a behavioral description. They are regrouped in blocks that specify their execution mode (parallel or sequential).
|
|
1217
1240
|
There are four kinds of statements: the transmit statement which computes expressions and sends the result to the target signals, the control statement
|
|
@@ -1224,7 +1247,7 @@ __Note__:
|
|
|
1224
1247
|
|
|
1225
1248
|
### Transmit statement
|
|
1226
1249
|
|
|
1227
|
-
A transmit statement is declared using the arrow operator `<=` within a behavior. Its right value is the expression to compute and its left value is a reference to the target signals (or parts of signals), i.e., the signals (or
|
|
1250
|
+
A transmit statement is declared using the arrow operator `<=` within a behavior. Its right value is the expression to compute and its left value is a reference to the target signals (or parts of signals), i.e., the signals (or parts of signals) that receive the computation result.
|
|
1228
1251
|
|
|
1229
1252
|
For example, the following code transmits the value `3` to signal `s0` and the sum of the values of signals `i0` and `i1` to the first four bits of signal `s1`:
|
|
1230
1253
|
|
|
@@ -1282,7 +1305,7 @@ end
|
|
|
1282
1305
|
|
|
1283
1306
|
### helsif
|
|
1284
1307
|
|
|
1285
|
-
In addition to `helse
|
|
1308
|
+
In addition to `helse`, it is possible to set additional conditions to an `hif` using the `helsif` keyword as follows:
|
|
1286
1309
|
|
|
1287
1310
|
```ruby
|
|
1288
1311
|
hif <condition 0> do
|
|
@@ -1297,7 +1320,7 @@ end
|
|
|
1297
1320
|
#### About loops
|
|
1298
1321
|
|
|
1299
1322
|
HDLRuby does not include any hardware construct for describing loops. This might look poor compared to the other HDL, but it is important to understand
|
|
1300
|
-
that the current synthesis tools do not really synthesize hardware from such loops but instead preprocess them (e.g., unroll them) to synthesizable
|
|
1323
|
+
that the current synthesis tools do not really synthesize hardware from such loops but instead preprocess them (e.g., unroll them) to synthesizable loopless hardware. In HDLRuby, such features are natively supported by the Ruby loop constructs (`for`, `while`, and so on), but also by advanced Ruby constructs like the enumerators (`each`, `times`, and so on).
|
|
1301
1324
|
|
|
1302
1325
|
__Notes__:
|
|
1303
1326
|
|
|
@@ -1416,11 +1439,10 @@ While the underlying structure of any HDLRuby type is the bit vector, complex ty
|
|
|
1416
1439
|
<a name="expressions"></a>
|
|
1417
1440
|
|
|
1418
1441
|
Expressions are any construct that represents a value associated with a type.
|
|
1419
|
-
They include [immediate values](#values), [reference to signals](#references) and operations among other expressions using [expression operators](#operators).
|
|
1442
|
+
They include [immediate values](#immediate-values), [reference to signals](#references) and operations among other expressions using [expression operators](#expression-operators).
|
|
1420
1443
|
|
|
1421
1444
|
|
|
1422
1445
|
### Immediate values
|
|
1423
|
-
<a name="values"></a>
|
|
1424
1446
|
|
|
1425
1447
|
The immediate values of HDLRuby can represent vectors of `bit`, `unsigned`, and `signed`, and integer or floating-point numbers. They are prefixed by a `_` character and include a header that indicates the vector type and the base used for representing the value, followed by a numeral representing the value. The bit width of a value is obtained by default from the width of the numeral, but it is also possible to specify it in the header. In addition, the character `_` can be put anywhere in the number for increasing the readability, it will be ignored.
|
|
1426
1448
|
|
|
@@ -1464,9 +1486,9 @@ _o144
|
|
|
1464
1486
|
|
|
1465
1487
|
__Notes__:
|
|
1466
1488
|
|
|
1467
|
-
- `_01100100` used to be considered
|
|
1489
|
+
- `_01100100` used to be considered equivalent to `_b01100100`, however, due to compatibility troubles with a recent version of Ruby it is considered deprecated.
|
|
1468
1490
|
|
|
1469
|
-
- Ruby immediate values can also be used, their bit width is automatically adjusted to match the data type of the expression they are used in. Please notice this
|
|
1491
|
+
- Ruby immediate values can also be used, their bit width is automatically adjusted to match the data type of the expression they are used in. Please notice this adjustment may change the value of the immediate, for example, the following code will set `sig` to 4 instead of 100:
|
|
1470
1492
|
|
|
1471
1493
|
```ruby
|
|
1472
1494
|
[3..0].inner :sig
|
|
@@ -1475,7 +1497,6 @@ __Notes__:
|
|
|
1475
1497
|
|
|
1476
1498
|
|
|
1477
1499
|
### References
|
|
1478
|
-
<a name="references"></a>
|
|
1479
1500
|
|
|
1480
1501
|
References are expressions used to designate signals or a part of signals.
|
|
1481
1502
|
|
|
@@ -1517,7 +1538,6 @@ end
|
|
|
1517
1538
|
```
|
|
1518
1539
|
|
|
1519
1540
|
### Expression operators
|
|
1520
|
-
<a name="operators"></a>
|
|
1521
1541
|
|
|
1522
1542
|
The following table gives a summary of the operators available in HDLRuby.
|
|
1523
1543
|
More details are given for each group of operators in the subsequent sections.
|
|
@@ -1594,10 +1614,9 @@ __Notes__:
|
|
|
1594
1614
|
|
|
1595
1615
|
- The operator precedence is the one of Ruby.
|
|
1596
1616
|
|
|
1597
|
-
- Ruby does not allow to override the `&&`, the
|
|
1617
|
+
- Ruby does not allow to override the `&&`, the `||`, and the `?:` operators so they are not present in HDLRuby. Instead of the `?:` operator, HDLRuby provides the more general multiplex operator `mux`. However, HDLRuby does not provide any replacement for the `&&` and the `||` operators, please refer to section [Logic operators](#logic-and-shift-operators) for a justification for this issue.
|
|
1598
1618
|
|
|
1599
1619
|
#### Assignment operators
|
|
1600
|
-
<a name="assignment"></a>
|
|
1601
1620
|
|
|
1602
1621
|
The assignment operators can be used with any type. They are the connection and the transmission operators both being represented by `<=`.
|
|
1603
1622
|
|
|
@@ -1608,7 +1627,7 @@ __Note__:
|
|
|
1608
1627
|
#### Arithmetic operators
|
|
1609
1628
|
<a name="arithmetic"></a>
|
|
1610
1629
|
|
|
1611
|
-
The arithmetic operators can only be used on vectors of `bit`, `unsigned` or `signed` values, `integer
|
|
1630
|
+
The arithmetic operators can only be used on vectors of `bit`, `unsigned` or `signed` values, `integer`, or `float` values. These operators are `+`, `-`, `*`, `%` and the unary arithmetic operators are `-` and `+`. They have the same meaning as their Ruby equivalents.
|
|
1612
1631
|
|
|
1613
1632
|
#### Comparison operators
|
|
1614
1633
|
<a name="comparison"></a>
|
|
@@ -1625,7 +1644,6 @@ __Notes__:
|
|
|
1625
1644
|
|
|
1626
1645
|
|
|
1627
1646
|
#### Logic and shift operators
|
|
1628
|
-
<a name="logic"></a>
|
|
1629
1647
|
|
|
1630
1648
|
In HDLRuby, the logic operators are all bitwise. For performing Boolean computations, it is necessary to use single-bit values. The bitwise logic binary operators are `&`, `|`, and `^`, and the unary one is `~`. They have the same meaning as their Ruby equivalents.
|
|
1631
1649
|
|
|
@@ -1650,24 +1668,22 @@ For example, for rotating the left signal `sig` 3 times, the following code can
|
|
|
1650
1668
|
sig.rl(3)
|
|
1651
1669
|
```
|
|
1652
1670
|
|
|
1653
|
-
It is possible to perform other kinds of shifts or rotations using the selection and
|
|
1654
|
-
selection operators](#concat) for more details about these operators.
|
|
1671
|
+
It is possible to perform other kinds of shifts or rotations using the selection and concatenation operators. Please refer to section [Concatenation and selection operators](#concatenation-and-selection-operators) for more details about these operators.
|
|
1655
1672
|
|
|
1656
1673
|
|
|
1657
1674
|
#### Conversion operators
|
|
1658
|
-
<a name="conversion"></a>
|
|
1659
1675
|
|
|
1660
1676
|
The conversion operators are used to change the type of an expression.
|
|
1661
1677
|
There are two kinds of such operators: the type pun that does not change the raw value of the expression and the type cast that changes the raw value.
|
|
1662
1678
|
|
|
1663
|
-
The type puns include `to_bit`, `to_unsigned
|
|
1679
|
+
The type puns include `to_bit`, `to_unsigned`, and `to_signed` that convert expressions of any type to vectors of respectively `bit`, `unsigned`, and `signed` elements. For example, the following code converts an expression of hierarchical type to an 8-bit signed vector:
|
|
1664
1680
|
|
|
1665
1681
|
```ruby
|
|
1666
1682
|
[ up: signed[3..0], down: unsigned[3..0] ].inner :sig
|
|
1667
1683
|
sig.to_bit <= _b01010011
|
|
1668
1684
|
```
|
|
1669
1685
|
|
|
1670
|
-
The type casts change both the type and the value and are used to adjust the width of the types. They can only be applied to vectors of `bit`, `signed`, or `unsinged` and can only increase the bit width (bit width can be truncated using the selection operator, please refer to the
|
|
1686
|
+
The type casts change both the type and the value and are used to adjust the width of the types. They can only be applied to vectors of `bit`, `signed`, or `unsinged` and can only increase the bit width (bit width can be truncated using the selection operator, please refer to the next section).
|
|
1671
1687
|
These operators comprise the bit width conversions: `ljust`, `rjust`, `zext` and `sext`.
|
|
1672
1688
|
|
|
1673
1689
|
More precisely, the bit width conversions operate as follows:
|
|
@@ -1701,7 +1717,6 @@ More precisely, the bit width conversions operate as follows:
|
|
|
1701
1717
|
|
|
1702
1718
|
|
|
1703
1719
|
#### Concatenation and selection operators
|
|
1704
|
-
<a name="concat"></a>
|
|
1705
1720
|
|
|
1706
1721
|
Concatenation and selection are done using the `[]` operator as follows:
|
|
1707
1722
|
|
|
@@ -1716,7 +1731,7 @@ Concatenation and selection are done using the `[]` operator as follows:
|
|
|
1716
1731
|
sig2 <= [sig0, sig1]
|
|
1717
1732
|
```
|
|
1718
1733
|
|
|
1719
|
-
- when this operator is applied to an expression of `bit`, `unsigned
|
|
1734
|
+
- when this operator is applied to an expression of `bit`, `unsigned`, or `signed` vector type while taking as argument a range, it selects the bits corresponding to this range. If only one bit is to be selected, the offset of this bit can be used instead. For example, the following code selects bits from 3 to 1 of `sig0` and bit 4 of `sig1`:
|
|
1720
1735
|
|
|
1721
1736
|
```ruby
|
|
1722
1737
|
[7..0].inner :sig0
|
|
@@ -1808,7 +1823,7 @@ __Multiplicative operators:__
|
|
|
1808
1823
|
Like Verilog HDL, HDLRuby provides function constructs for reusing code. HDLRuby functions are declared as follows:
|
|
1809
1824
|
|
|
1810
1825
|
```ruby
|
|
1811
|
-
|
|
1826
|
+
hdef :<function name> do |<arguments>|
|
|
1812
1827
|
<code>
|
|
1813
1828
|
end
|
|
1814
1829
|
```
|
|
@@ -1886,7 +1901,7 @@ system :sys do
|
|
|
1886
1901
|
end
|
|
1887
1902
|
```
|
|
1888
1903
|
|
|
1889
|
-
As another example, following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
|
1904
|
+
As another example, the following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
|
1890
1905
|
|
|
1891
1906
|
```ruby
|
|
1892
1907
|
def too_bad
|
|
@@ -1912,12 +1927,11 @@ Ruby functions can be compared to the macros of the C languages: they are more f
|
|
|
1912
1927
|
|
|
1913
1928
|
|
|
1914
1929
|
## Time
|
|
1915
|
-
<a name="time"></a>
|
|
1916
1930
|
|
|
1917
1931
|
### Time values
|
|
1918
1932
|
<a name="time_val"></a>
|
|
1919
1933
|
|
|
1920
|
-
In HDLRuby, time values can be created using the time operators: `s` for seconds, `ms` for a millisecond, `us` for microseconds, `ns` for nanoseconds, `ps` for picoseconds. For example, the
|
|
1934
|
+
In HDLRuby, time values can be created using the time operators: `s` for seconds, `ms` for a millisecond, `us` for microseconds, `ns` for nanoseconds, `ps` for picoseconds. For example, the following are all indicating one second:
|
|
1921
1935
|
|
|
1922
1936
|
```ruby
|
|
1923
1937
|
1.s
|
|
@@ -1931,7 +1945,7 @@ In HDLRuby, time values can be created using the time operators: `s` for seconds
|
|
|
1931
1945
|
### Time behaviors and time statements
|
|
1932
1946
|
<a name="time_beh"></a>
|
|
1933
1947
|
|
|
1934
|
-
Like the other HDL, HDLRuby provides specific statements that model the advance of time. These statements are not synthesizable and are used for simulating the environment of a hardware component. For sake of clarity, such statements are only allowed in explicitly non-synthesizable behavior declared using the `timed` keyword as follows.
|
|
1948
|
+
Like the other HDL, HDLRuby provides specific statements that model the advance of time. These statements are not synthesizable and are used for simulating the environment of a hardware component. For the sake of clarity, such statements are only allowed in explicitly non-synthesizable behavior declared using the `timed` keyword as follows.
|
|
1935
1949
|
|
|
1936
1950
|
```ruby
|
|
1937
1951
|
timed do
|
|
@@ -1987,7 +2001,6 @@ sequential blocks. The execution semantic is the following:
|
|
|
1987
2001
|
|
|
1988
2002
|
|
|
1989
2003
|
## High-level programming features
|
|
1990
|
-
<a name="highfeat"></a>
|
|
1991
2004
|
|
|
1992
2005
|
### Using Ruby in HDLRuby
|
|
1993
2006
|
|
|
@@ -2042,7 +2055,7 @@ typedef :<type name> do |<list of generic parameters>|
|
|
|
2042
2055
|
end
|
|
2043
2056
|
```
|
|
2044
2057
|
|
|
2045
|
-
For example, the following code describes a bit-vector type with generic number of bits `width`:
|
|
2058
|
+
For example, the following code describes a bit-vector type with a generic number of bits `width`:
|
|
2046
2059
|
|
|
2047
2060
|
```ruby
|
|
2048
2061
|
type(:bitvec) { |width| bit[width] }
|
|
@@ -2072,7 +2085,7 @@ system :subsys, sys(1,2) do
|
|
|
2072
2085
|
end
|
|
2073
2086
|
```
|
|
2074
2087
|
|
|
2075
|
-
This way of inheriting can only be done with fully specialized systems though. For partially specialized systems, `include` must be used instead. For example, if `sys` specialized with only one value, can be used in generic `subsys_gen` as follows:
|
|
2088
|
+
This way of inheriting can only be done with fully specialized systems though. For partially specialized systems, `include` must be used instead. For example, if `sys` is specialized with only one value, can be used in generic `subsys_gen` as follows:
|
|
2076
2089
|
|
|
2077
2090
|
```ruby
|
|
2078
2091
|
system :subsys_gen do |param|
|
|
@@ -2333,7 +2346,7 @@ end
|
|
|
2333
2346
|
Please notice, that in the code above, the left value has been cast to a plain bit-vector to avoid the infinite recursive call of the `*` operator.
|
|
2334
2347
|
|
|
2335
2348
|
Operators can also be overloaded with generic types. However, in such a case, the generic argument must also be present in the list of arguments of the overloaded operators.
|
|
2336
|
-
For instance, let us consider the following fixed-point type of variable width (and whose decimal point is set at
|
|
2349
|
+
For instance, let us consider the following fixed-point type of variable width (and whose decimal point is set at half of its bit range):
|
|
2337
2350
|
|
|
2338
2351
|
```ruby
|
|
2339
2352
|
typedef(:fixed) do |width|
|
|
@@ -2381,7 +2394,7 @@ Several enumerators are also provided for accessing the internals of the current
|
|
|
2381
2394
|
|
|
2382
2395
|
### Global signals
|
|
2383
2396
|
|
|
2384
|
-
HDLRuby allows
|
|
2397
|
+
HDLRuby allows the declaration of global signals the same way system's signals are declared but outside the scope of any system. After being declared, these signals are accessible directly from within any hardware construct.
|
|
2385
2398
|
|
|
2386
2399
|
To ease the design of standardized libraries, the following global signals are defined by default:
|
|
2387
2400
|
|
|
@@ -2400,9 +2413,9 @@ __Note__:
|
|
|
2400
2413
|
### Defining and executing Ruby methods within HDLRuby constructs
|
|
2401
2414
|
<a name="method"></a>
|
|
2402
2415
|
|
|
2403
|
-
Like with any Ruby program it is possible to define and execute methods anywhere in HDLRuby using the standard Ruby syntax. When defined, a method is attached to the enclosing HDLRuby construct. For instance, when defining a method when declaring a system, it can be used within this system only, while when defining a method outside any construct, it can be used everywhere in the HDLRuby description.
|
|
2416
|
+
Like with any Ruby program, it is possible to define and execute methods anywhere in HDLRuby using the standard Ruby syntax. When defined, a method is attached to the enclosing HDLRuby construct. For instance, when defining a method when declaring a system, it can be used within this system only, while when defining a method outside any construct, it can be used everywhere in the HDLRuby description.
|
|
2404
2417
|
|
|
2405
|
-
A method can include HDLRuby code in which case the resulting hardware is appended to the current construct. For example, the following code adds a connection between `sig0` and `sig1` in system `sys0`, and transmission between `sig0` and `sig1` in the behavior of `sys1`.
|
|
2418
|
+
A method can include HDLRuby code in which case the resulting hardware is appended to the current construct. For example, the following code adds a connection between `sig0` and `sig1` in the system `sys0`, and transmission between `sig0` and `sig1` in the behavior of `sys1`.
|
|
2406
2419
|
|
|
2407
2420
|
```ruby
|
|
2408
2421
|
def some_arrow
|
|
@@ -2471,7 +2484,7 @@ end
|
|
|
2471
2484
|
```
|
|
2472
2485
|
|
|
2473
2486
|
|
|
2474
|
-
While requiring
|
|
2487
|
+
While requiring caution, a properly designed method can be very useful for clean code reuse. For example, the following method allows to start the execution of a block after a given number of cycles:
|
|
2475
2488
|
|
|
2476
2489
|
```ruby
|
|
2477
2490
|
def after(cycles,rst = $rst, &code)
|
|
@@ -2525,9 +2538,8 @@ When describing a system, it is possible to disconnect or completely undefine a
|
|
|
2525
2538
|
|
|
2526
2539
|
|
|
2527
2540
|
## Extending HDLRuby
|
|
2528
|
-
<a name="extend"></a>
|
|
2529
2541
|
|
|
2530
|
-
Like any Ruby
|
|
2542
|
+
Like any Ruby class, the constructs of HDLRuby can be dynamically extended. If it is not recommended to change their internal structure, it is possible to add methods to them for an extension.
|
|
2531
2543
|
|
|
2532
2544
|
### Extending HDLRuby constructs globally
|
|
2533
2545
|
|
|
@@ -2653,8 +2665,7 @@ This way, calling directly `to_low` will automatically use `my_generation`.
|
|
|
2653
2665
|
|
|
2654
2666
|
|
|
2655
2667
|
|
|
2656
|
-
# Standard
|
|
2657
|
-
<a name="library"></a>
|
|
2668
|
+
# Standard libraries
|
|
2658
2669
|
|
|
2659
2670
|
The standard libraries are included in the module `Std`.
|
|
2660
2671
|
They can be loaded as follows, where `<library name>` is the name of the
|
|
@@ -2670,9 +2681,23 @@ After the libraries are loaded, the module `Std` must be included as follows:
|
|
|
2670
2681
|
include HDLRuby::High::Std
|
|
2671
2682
|
```
|
|
2672
2683
|
|
|
2684
|
+
> However, `hdrcc` loads the stable components of the standard library by default, so you do not need to require nor include anything more to use them. In the current version, the stable components are the followings:
|
|
2685
|
+
|
|
2686
|
+
- `std/clocks.rb`
|
|
2687
|
+
|
|
2688
|
+
- `std/fixpoint.rb`
|
|
2689
|
+
|
|
2690
|
+
- `std/decoder.rb`
|
|
2691
|
+
|
|
2692
|
+
- `std/fsm.rb`
|
|
2693
|
+
|
|
2694
|
+
- `std/sequencer.rb`
|
|
2695
|
+
|
|
2696
|
+
- `std/sequencer_sync.rb`
|
|
2673
2697
|
|
|
2674
2698
|
|
|
2675
|
-
|
|
2699
|
+
|
|
2700
|
+
## Clocks: `std/clocks.rb`
|
|
2676
2701
|
<a name="clocks"></a>
|
|
2677
2702
|
|
|
2678
2703
|
The `clocks` library provides utilities for easier handling of clock synchronizations.
|
|
@@ -2692,9 +2717,9 @@ system :dff_slow do
|
|
|
2692
2717
|
end
|
|
2693
2718
|
```
|
|
2694
2719
|
|
|
2695
|
-
__Note__: this library
|
|
2720
|
+
__Note__: this library generates all the RTL code for the circuit handling the frequency division.
|
|
2696
2721
|
|
|
2697
|
-
## Counters
|
|
2722
|
+
## Counters: `std/counters.rb`
|
|
2698
2723
|
<a name="counters"></a>
|
|
2699
2724
|
|
|
2700
2725
|
This library provides two new constructs for implementing synthesizable wait statements.
|
|
@@ -2711,12 +2736,12 @@ Where:
|
|
|
2711
2736
|
* `<clock>` is the clock to use, this argument can be omitted.
|
|
2712
2737
|
* `<reset>` is the signal used to reset the counter used for waiting, this argument can be omitted.
|
|
2713
2738
|
|
|
2714
|
-
This statement can be used either inside or outside a clocked behavior. When used within a clocked behavior, the clock event of the behavior is used for the counter unless specified otherwise. When used outside such
|
|
2739
|
+
This statement can be used either inside or outside a clocked behavior. When used within a clocked behavior, the clock event of the behavior is used for the counter unless specified otherwise. When used outside such behavior, the clock is the global default clock `$clk`. In both cases, the reset is the global reset `$rst` unless specified otherwise.
|
|
2715
2740
|
|
|
2716
2741
|
The second construct is the `before` statement that activates a block until a given number of clock cycles is passed. Its syntax and usage are identical to the `after` statement.
|
|
2717
2742
|
|
|
2718
2743
|
|
|
2719
|
-
## Decoder
|
|
2744
|
+
## Decoder: `std/decoder.rb`
|
|
2720
2745
|
<a name="decoder"></a>
|
|
2721
2746
|
|
|
2722
2747
|
This library provides a new set of control statements for easily describing an instruction decoder.
|
|
@@ -2727,13 +2752,13 @@ A decoder can be declared anywhere in the code describing a system using the `de
|
|
|
2727
2752
|
decoder(<signal>) <block>
|
|
2728
2753
|
```
|
|
2729
2754
|
|
|
2730
|
-
Where `signal` is the signal to decode and `block` is a procedure block (i.e., Ruby `proc`) describing the decoding procedure. This procedure block can contain any code supported by a standard behavior
|
|
2755
|
+
Where `signal` is the signal to decode and `block` is a procedure block (i.e., Ruby `proc`) describing the decoding procedure. This procedure block can contain any code supported by a standard behavior but also supports the `entry` statement that describes a pattern of a bit vector to decode and the corresponding action to perform when the signal matches this pattern. The syntax of the `entry` statement is the following:
|
|
2731
2756
|
|
|
2732
2757
|
```ruby
|
|
2733
2758
|
entry(<pattern>) <block>
|
|
2734
2759
|
```
|
|
2735
2760
|
|
|
2736
|
-
Where `pattern` is a string describing the pattern to match
|
|
2761
|
+
Where `pattern` is a string describing the pattern to match the entry, and `block` is a procedure block describing the actions (some HDLRuby code) that are performed when the entry matches. The string describing the pattern can include `0` and `1` characters for specifying a specific value for the corresponding bit, or any alphabetical character for specifying a field in the pattern. The fields in the pattern can then be used by name in the block describing the action. When a letter is used several times within a pattern, the corresponding bits are concatenated and used as a multi-bit signal in the block.
|
|
2737
2762
|
|
|
2738
2763
|
For example, the following code describes a decoder for signal `ir` with two entries, the first one computing the sum of fields `x` and `y` and assigning the result to signal `s` and the second one computing the sum of fields `x` `y` and `z` and assigning the result to signal `s`:
|
|
2739
2764
|
|
|
@@ -2746,12 +2771,12 @@ end
|
|
|
2746
2771
|
|
|
2747
2772
|
It can be noticed for field `z` in the example above that the bits are not required to be contiguous.
|
|
2748
2773
|
|
|
2749
|
-
## FSM
|
|
2774
|
+
## FSM: `std/fsm.rb`
|
|
2750
2775
|
<a name="fsm"></a>
|
|
2751
2776
|
|
|
2752
2777
|
This library provides a new set of control statements for easily describing a finite state machine (FSM).
|
|
2753
2778
|
|
|
2754
|
-
A finite state machine can be declared anywhere provided it is outside a behavior using the `fsm` keyword as follows:
|
|
2779
|
+
A finite state machine can be declared anywhere in a system provided it is outside a behavior using the `fsm` keyword as follows:
|
|
2755
2780
|
|
|
2756
2781
|
```ruby
|
|
2757
2782
|
fsm(<event>,<reset>,<mode>) <block>
|
|
@@ -2794,7 +2819,7 @@ goto(cond,:st_a,:st_b,:st_c)
|
|
|
2794
2819
|
|
|
2795
2820
|
Several goto statements can be used, the last one having priority provided it is taken (i.e., its condition corresponds to one of the target states). If no goto is taken, the next transition is the next declared one.
|
|
2796
2821
|
|
|
2797
|
-
For example, the following code describes
|
|
2822
|
+
For example, the following code describes an FSM describing a circuit that checks if two buttons (`but_a` and `but_b`) are pressed and released in sequence for activating an output signal (`ok`):
|
|
2798
2823
|
|
|
2799
2824
|
```ruby
|
|
2800
2825
|
fsm(clk.posedge,rst,:sync) do
|
|
@@ -2818,7 +2843,7 @@ fsm(clk.posedge,rst,:sync) do
|
|
|
2818
2843
|
end
|
|
2819
2844
|
```
|
|
2820
2845
|
|
|
2821
|
-
__Note__: the goto statements
|
|
2846
|
+
__Note__: the goto statements act globally, i.e., they are independent of the place where they are declared within the state. For example for both following statements, the next state will always be `st_a` whatever `cond` maybe:
|
|
2822
2847
|
|
|
2823
2848
|
```ruby
|
|
2824
2849
|
state(:st_0) do
|
|
@@ -2837,7 +2862,7 @@ That is to say, for a conditional `goto` for `st_1` the code should have been wr
|
|
|
2837
2862
|
end
|
|
2838
2863
|
```
|
|
2839
2864
|
|
|
2840
|
-
The use of `goto` makes the design of FSM shorter for a majority of the cases, be sometimes, a finer control is required. For that purpose it is also possible to configure the FSM
|
|
2865
|
+
The use of `goto` makes the design of FSM shorter for a majority of the cases, be sometimes, a finer control is required. For that purpose, it is also possible to configure the FSM in `static` mode where the `next_state` statement indicates implicitly the next state. Putting in static mode is done by passing `:static` as an argument when declaring the FSM. For example, the following FSM uses `next_state` to specify explicitly the next states depending on some condition signals `cond0` and `cond1`:
|
|
2841
2866
|
|
|
2842
2867
|
```ruby
|
|
2843
2868
|
fsm(clk.posedge,rst,:static)
|
|
@@ -2850,8 +2875,511 @@ fsm(clk.posedge,rst,:static)
|
|
|
2850
2875
|
end
|
|
2851
2876
|
```
|
|
2852
2877
|
|
|
2878
|
+
## Sequencer (software-like hardware coding):: `std/sequencer.rb`
|
|
2879
|
+
<a name="sequencer"></a>
|
|
2880
|
+
|
|
2881
|
+
This library provides a new set of control statements for describing the behavior of a circuit. Behind the curtain, these constructs build a finite state machine where states are deduced from the control points within the description.
|
|
2882
|
+
|
|
2883
|
+
A sequencer can be declared anywhere in a system provided it is outside a behavior using the `sequencer` keyword as follows:
|
|
2884
|
+
|
|
2885
|
+
```ruby
|
|
2886
|
+
sequencer(<clock>,<start>) <block>
|
|
2887
|
+
```
|
|
2888
|
+
|
|
2889
|
+
Where `clock` is the clock signal advancing the execution of the sequence, `start` is the signal starting the execution, and `block` is the description of the sequence to be executed. Both `clock` and `start` can also be events (i.e., `posedge` or `negedge`).
|
|
2890
|
+
|
|
2891
|
+
A sequence is a specific case of a `seq` block that includes the following software-like additional constructs:
|
|
2892
|
+
|
|
2893
|
+
- `step`: wait until the next event (given argument `event` of the sequencer).
|
|
2894
|
+
|
|
2895
|
+
- `steps(<num>)`: perform `num` times `step` (`num` can be any expression).
|
|
2896
|
+
|
|
2897
|
+
- `sif(<condition>) <block>`: executes `block` if `condition` is met.
|
|
2898
|
+
|
|
2899
|
+
- `selsif(<condition>) <block>`: executes `block` if the previous `sif` or `selsif` condition is not met and if the current `condition` is met.
|
|
2900
|
+
|
|
2901
|
+
- `selse <block>`: executes `block` if the condition of the previous `sif` statement is not met.
|
|
2902
|
+
|
|
2903
|
+
- `swait(<condition>)`: waits until that `condition` is met.
|
|
2904
|
+
|
|
2905
|
+
- `swhile(<condition>) <block>`: executes `block` while `condition` is met.
|
|
2906
|
+
|
|
2907
|
+
- `sfor(<enumerable>) <block>`: executes `block` on each element of `enumerable`. This latter can be any enumerable Ruby object or any signal. If the signal is not hierarchical (e.g., bit vector), the iteration will be over each bit.
|
|
2908
|
+
|
|
2909
|
+
- `sbreak`: ends current iteration.
|
|
2910
|
+
|
|
2911
|
+
- `scontinue`: goes to the next step of the iteration.
|
|
2912
|
+
|
|
2913
|
+
- `sterminate`: ends the execution of the sequence.
|
|
2914
|
+
|
|
2915
|
+
It is also possible to use enumerators (iterators) similar to the Ruby `each` using the following methods within sequences:
|
|
2916
|
+
|
|
2917
|
+
- `<object>.seach`: `object` any enumerable Ruby object or any signal. If a block is given, it works like `sfor`, otherwise, it returns a HDLRuby enumerator (please see [enumerator](#hdlruby-enumerators-and-enumerable-objects-stdsequencerrb) for details about them).
|
|
2918
|
+
|
|
2919
|
+
- `<object>.stimes`: can be used on integers and is equivalent to `seach` on each integer from 0 up to `object-1`.
|
|
2920
|
+
|
|
2921
|
+
- `<object>.supto(<last>)`: can be used on integers and is equivalent to `seach` on each integer from `object` up to `last`.
|
|
2922
|
+
|
|
2923
|
+
- `<object>.sdownto(<last>)`: can be used on an integer and is equivalent to `seach` on each integer from `object` down to `last`.
|
|
2924
|
+
|
|
2925
|
+
The objects that support these methods are called _enumerable_ objects. They include the HDLRuby signals, the HDLRuby enumerators, and all the Ruby enumerable objects (e.g., ranges, arrays).
|
|
2926
|
+
|
|
2927
|
+
|
|
2928
|
+
Here are a few examples of sequencers synchronized in the positive edge of `clk` and starting when `start` becomes one. The first one computes the Fibonacci series until 100, producing a new term in signal `v` at each cycle:
|
|
2929
|
+
|
|
2930
|
+
```ruby
|
|
2931
|
+
require 'std/sequencer.rb'
|
|
2932
|
+
include HDLRuby::High::Std
|
|
2933
|
+
|
|
2934
|
+
system :a_circuit do
|
|
2935
|
+
inner :clk, :start
|
|
2936
|
+
[16].inner :a, :b
|
|
2937
|
+
|
|
2938
|
+
sequencer(clk.posedge,start) do
|
|
2939
|
+
a <= 0
|
|
2940
|
+
b <= 1
|
|
2941
|
+
swhile(v < 100) do
|
|
2942
|
+
b <= a + b
|
|
2943
|
+
a <= b - a
|
|
2944
|
+
end
|
|
2945
|
+
end
|
|
2946
|
+
end
|
|
2947
|
+
```
|
|
2948
|
+
|
|
2949
|
+
The second one computes the square of the integers from 10 to 100, producing one result per cycle in signal `a`:
|
|
2950
|
+
|
|
2951
|
+
```ruby
|
|
2952
|
+
inner :clk, :start
|
|
2953
|
+
[16].inner :a
|
|
2954
|
+
|
|
2955
|
+
sequencer(clk.posedge,start) do
|
|
2956
|
+
10.supto(100) { |i| a <= i*i }
|
|
2957
|
+
end
|
|
2958
|
+
```
|
|
2959
|
+
|
|
2960
|
+
The third one reverses the content of memory `mem` (the result will be "!dlrow olleH"):
|
|
2961
|
+
|
|
2962
|
+
```ruby
|
|
2963
|
+
inner :clk, :start
|
|
2964
|
+
bit[8][-12].inner mem: "Hello world!"
|
|
2965
|
+
|
|
2966
|
+
sequencer(clk.posedge,start) do
|
|
2967
|
+
mem.size.stimes do |i|
|
|
2968
|
+
[8].inner :tmp
|
|
2969
|
+
tmp <= mem[i]
|
|
2970
|
+
mem[i] <= mem[-i-1]
|
|
2971
|
+
mem[-i-1] <= tmp
|
|
2972
|
+
end
|
|
2973
|
+
end
|
|
2974
|
+
```
|
|
2975
|
+
|
|
2976
|
+
The fourth one computes the sum of all the elements of memory `mem` but stops if the sum is larger than 16:
|
|
2977
|
+
|
|
2978
|
+
```ruby
|
|
2979
|
+
inner :clk, :start
|
|
2980
|
+
bit[8][-8].inner mem: [ _h02, _h04, _h06, _h08, _h0A, _h0C, _h0E ]
|
|
2981
|
+
bit[8] :sum
|
|
2982
|
+
|
|
2983
|
+
sequencer(clk.posedge,start) do
|
|
2984
|
+
sum <= 0
|
|
2985
|
+
sfor(mem) do |elem|
|
|
2986
|
+
sum <= sum + elem
|
|
2987
|
+
sif(sum > 16) { sterminate }
|
|
2988
|
+
end
|
|
2989
|
+
end
|
|
2990
|
+
```
|
|
2991
|
+
|
|
2992
|
+
|
|
2993
|
+
### HDLRuby enumerators and enumerable objects: `std/sequencer.rb`
|
|
2994
|
+
|
|
2995
|
+
HDLRuby enumerators are objects for generating iterations within sequencers. They are created using the method `seach` on enumerable objects as presented in the previous section.
|
|
2996
|
+
|
|
2997
|
+
The enumerators can be controlled using the following methods:
|
|
2998
|
+
|
|
2999
|
+
- `size`: returns the number of elements the enumerator can access.
|
|
3000
|
+
|
|
3001
|
+
- `type`: returns the type of the elements accessed by the enumerator.
|
|
3002
|
+
|
|
3003
|
+
- `seach`: returns the current enumerator. If a block is given, performs the iteration instead of returning an enumerator.
|
|
3004
|
+
|
|
3005
|
+
- `seach_with_index`: returns an enumerator over the elements of the current enumerator associated with their index position. If a block is given, performs the iteration instead of returning an enumerator.
|
|
3006
|
+
|
|
3007
|
+
- `seach_with_object(<obj>)`: returns an enumerator over the elements of the current enumerator associated with object `obj` (any object, HDLRuby or not, can be used). If a block is given, performs the iteration instead of returning an enumerator.
|
|
3008
|
+
|
|
3009
|
+
- `with_index`: identical to `seach_with_index`.
|
|
3010
|
+
|
|
3011
|
+
- `with_object(<obj>)`: identical to `seach_with_object`.
|
|
3012
|
+
|
|
3013
|
+
- `clone`: create a new enumerator on the same elements.
|
|
3014
|
+
|
|
3015
|
+
- `speek`: returns the current element pointed by the enumerator without advancing it.
|
|
3016
|
+
|
|
3017
|
+
- `snext`: returns the current element pointed by the enumerator and goes to the next one.
|
|
3018
|
+
|
|
3019
|
+
- `srewind`: restart the enumeration.
|
|
3020
|
+
|
|
3021
|
+
- `+`: concatenation of enumerators.
|
|
3022
|
+
|
|
3023
|
+
It is also possible to define a custom enumerator using the following command:
|
|
3024
|
+
|
|
3025
|
+
```ruby
|
|
3026
|
+
<enum> = senumerator(<typ>,<size>) <block>
|
|
3027
|
+
```
|
|
3028
|
+
|
|
3029
|
+
Where `enum` is a Ruby variable referring to the enumerator, `typ` is the data type and `size` is the number of the elements to enumerate, and `block` is the block that implements the access to an element by index. For example, an enumerator on a memory could be defined as follows:
|
|
3030
|
+
|
|
3031
|
+
```ruby
|
|
3032
|
+
bit[8][-8].inner mem: [ _h01, _h02, _h03, _h04, _h30, _h30, _h30, _h30 ]
|
|
3033
|
+
[3].inner :addr
|
|
3034
|
+
[8].inner :data
|
|
3035
|
+
|
|
3036
|
+
data <= mem[addr]
|
|
3037
|
+
|
|
3038
|
+
mem_enum = senumerator(bit[8],8) do |i|
|
|
3039
|
+
addr <= i
|
|
3040
|
+
step
|
|
3041
|
+
data
|
|
3042
|
+
end
|
|
3043
|
+
```
|
|
3044
|
+
|
|
3045
|
+
In the code above, `mem_enum` is the variable referring to the resulting enumerator built for accessing memory `mem`. For the access, it is assumed that one cycle must be waited for after the address is set, and therefore a `step` command is added in the access procedure before `data` can be returned.
|
|
3046
|
+
|
|
3047
|
+
With this basis, several algorithms have been implemented using enumerators and are usable for all the enumerable objects. All these algorithms are HW implantation of the Ruby Enumerable methods. They are accessible using the corresponding ruby method prefixed by character `s`. For example, the HW implementation of the ruby `all?` method is generated by the `sall?` method. In details:
|
|
3048
|
+
|
|
3049
|
+
- `sall?`: HW implementation of the Ruby `all?` method. Returns a single-bit signal. When 0 this value means false and when 1 it means true.
|
|
3050
|
+
|
|
3051
|
+
- `sany?`: HW implementation of the Ruby `any?` method. Returns a single-bit signal. When 0 this value means false and when 1 it means true.
|
|
3052
|
+
|
|
3053
|
+
- `schain`: HW implementation of the Ruby `chain`.
|
|
3054
|
+
|
|
3055
|
+
- `smap`: HW implementation of the Ruby `map` method. When used with a block returns a vector signal containing each computation result.
|
|
3056
|
+
|
|
3057
|
+
- `scompact`: HW implementation of the Ruby `compact` method. However, since there is no nil value in HW, use 0 instead for compacting. Returns a vector signal containing the compaction result.
|
|
3058
|
+
|
|
3059
|
+
- `scount`: HW implementation of the Ruby `count` method. Returns a signal whose bit width matches the size of the enumerator containing the count result.
|
|
3060
|
+
|
|
3061
|
+
- `scycle`: HW implementation of the Ruby `cycle` method.
|
|
3062
|
+
|
|
3063
|
+
- `sfind`: HW implementation of the Ruby `find` method. Returns a signal containing the found element, or 0 if not found.
|
|
3064
|
+
|
|
3065
|
+
- `sdrop`: HW implementation of the Ruby `drop` method. Returns a vector signal containing the remaining elements.
|
|
3066
|
+
|
|
3067
|
+
- `sdrop_while`: HW implementation of the Ruby `drop_while` method. Returns a vector signal containing the remaining elements.
|
|
3068
|
+
|
|
3069
|
+
- `seach_cons`: HW implementation of the Ruby `each_cons` method.
|
|
3070
|
+
|
|
3071
|
+
- `seach_slice`: HW implementation of the Ruby `each_slice` method.
|
|
3072
|
+
|
|
3073
|
+
- `seach_with_index`: HW implementation of the Ruby `each_with_index` method.
|
|
3074
|
+
|
|
3075
|
+
- `seach_with_object`: HW implementation of the Ruby `each_with_object` method.
|
|
3076
|
+
|
|
3077
|
+
- `sto_a`: HW implementation of the Ruby `to_a` method. Returns a vector signal containing all the elements of the enumerator.
|
|
3078
|
+
|
|
3079
|
+
- `sselect`: HW implementation of the Ruby `select` method. Returns a vector signal containing the selected elements.
|
|
3080
|
+
|
|
3081
|
+
- `sfind_index`: HW implementation of the Ruby `find_index` method. Returns the index of the found element or -1 if not.
|
|
3082
|
+
|
|
3083
|
+
- `sfirst`: HW implementation of the Ruby `first` method. Returns a vector signal containing the first elements.
|
|
3084
|
+
|
|
3085
|
+
- `sinclude?`: HW implementation of the Ruby `include?` method. Returns a single-bit signal. When 0 this value means false and when 1 it means true.
|
|
3086
|
+
|
|
3087
|
+
- `sinject`: HW implementation of the Ruby `inject` method. Return a signal of the type of elements containing the computation result.
|
|
3088
|
+
|
|
3089
|
+
- `smax`: HW implementation of the Ruby `max` method. Return a vector signal containing the found max values.
|
|
3090
|
+
|
|
3091
|
+
- `smax_by`: HW implementation of the Ruby `max_by` method. Return a vector signal containing the found max values.
|
|
3092
|
+
|
|
3093
|
+
- `smin`: HW implementation of the Ruby `min` method. Return a vector signal containing the found min values.
|
|
3094
|
+
|
|
3095
|
+
- `smin_by`: HW implementation of the Ruby `min_by` method. Return a vector signal containing the found min values.
|
|
3096
|
+
|
|
3097
|
+
- `sminmax`: HW implementation of the Ruby `minmax` method. Returns a 2-element vector signal containing the resulting min and max values.
|
|
3098
|
+
|
|
3099
|
+
- `sminmax_by`: HW implementation of the Ruby `minmax_by` method. Returns a 2-element vector signal containing the resulting min and max values.
|
|
3100
|
+
|
|
3101
|
+
- `snone?`: HW implementation of the Ruby `none?` method. Returns a single-bit signal. When 0 this value means false and when 1 it means true.
|
|
3102
|
+
|
|
3103
|
+
- `sone?`: HW implementation of the Ruby `one?` method. Returns a single-bit signal. When 0 this value means false and when 1 it means true.
|
|
3104
|
+
|
|
3105
|
+
- `sreject`: HW implementation of the Ruby `reject` method. Returns a vector signal containing the remaining elements.
|
|
3106
|
+
|
|
3107
|
+
- `sreverse_each`: HW implementation of the Ruby `reverse_each` method.
|
|
3108
|
+
|
|
3109
|
+
- `ssort`: HW implementation of the Ruby `sort` method. Returns a vector signal containing the sorted elements.
|
|
3110
|
+
|
|
3111
|
+
- `ssort_by`: HW implementation of the Ruby `sort_by` method. Returns a vector signal containing the sorted elements.
|
|
3112
|
+
|
|
3113
|
+
- `ssum`: HW implementation of the Ruby `sum` method. Returns a signal with the type of elements containing the sum result.
|
|
3114
|
+
|
|
3115
|
+
- `stake`: HW implementation of the Ruby `take` method. Returns a vector signal containing the taken elements.
|
|
3116
|
+
|
|
3117
|
+
- `stake_while`: HW implementation of the Ruby `take_while` method. Returns a vector signal containing the taken elements.
|
|
3118
|
+
|
|
3119
|
+
- `suniq`: HW implementation the Ruby `uniq` method. Returns a vector signal containing the selected elements.
|
|
3120
|
+
|
|
3121
|
+
|
|
3122
|
+
|
|
3123
|
+
### Shared signals, arbiters, and monitors: `std/sequencer_sync.rb`
|
|
3124
|
+
<a name="shared"></a>
|
|
3125
|
+
|
|
3126
|
+
#### Shared signals
|
|
3127
|
+
|
|
3128
|
+
Like any other process, It is not possible for several sequencers to write to the same signal. This is because there would be race competition that may destroy physically the device if such operations were authorized. In standard RTL design, this limitation is overcome by implementing three-state buses, multiplexers, and arbiters. However, HDLRuby sequencers support another kind of signal called the *shared signals* that abstract the implementation details for avoiding race competition.
|
|
3129
|
+
|
|
3130
|
+
The shared signals are declared like the other kind of signals from their type. The syntax is the following:
|
|
3131
|
+
|
|
3132
|
+
```ruby
|
|
3133
|
+
<type>.shared <list of names>
|
|
3134
|
+
```
|
|
3135
|
+
|
|
3136
|
+
They can also have an initial (and default) value when declared as follows:
|
|
3137
|
+
|
|
3138
|
+
```ruby
|
|
3139
|
+
<type>.shared <list of names with initialization>
|
|
3140
|
+
```
|
|
3141
|
+
|
|
3142
|
+
For example, the following code declares two 8-bit shared signals `x` and `y` and two signed 16-bit shared signals initialized to 0 `u` and `v`:
|
|
3143
|
+
|
|
3144
|
+
```ruby
|
|
3145
|
+
[8].shared :x, :y
|
|
3146
|
+
signed[8].shared u: 0, v: 0
|
|
3147
|
+
```
|
|
3148
|
+
|
|
3149
|
+
A shared signal can then be read and written to by any sequencer from anywhere in the subsequent code of the current scope. However, they cannot be written to outside a sequencer. For example, the following code is valid:
|
|
3150
|
+
|
|
3151
|
+
```ruby
|
|
3152
|
+
input :clk, :start
|
|
3153
|
+
[8].inner :val0, :val1
|
|
3154
|
+
[8].shared :x, :y
|
|
3155
|
+
|
|
3156
|
+
val0 <= x+y
|
|
3157
|
+
par(clk.posedge) { val1 <= x+y }
|
|
3158
|
+
|
|
3159
|
+
sequencer(clk.posedge,start) do
|
|
3160
|
+
10.stimes { |i| x <= i }
|
|
3161
|
+
end
|
|
3162
|
+
|
|
3163
|
+
sequencer(clk.posedge,start) do
|
|
3164
|
+
5.stimes { |i| x <= i*2 ; y <= i*2 }
|
|
3165
|
+
end
|
|
3166
|
+
```
|
|
3167
|
+
|
|
3168
|
+
But the following code is not valid:
|
|
3169
|
+
|
|
3170
|
+
```ruby
|
|
3171
|
+
[8].shared w: 0
|
|
3172
|
+
|
|
3173
|
+
par(clk.posedge) { w <= w + 1 }
|
|
3174
|
+
```
|
|
3175
|
+
|
|
3176
|
+
By default, a shared signal acknowledges writing from the first sequencer that accesses it in order of declaration, the others are ignored. In the first example given above, that means for signal `x` the value is always the one written by the first sequencer, i.e., from 0 to 9 changing once per clock cycle. However, the value of signal `y` is set by the second sequencer since it is this one only that writes to this signal.
|
|
3177
|
+
|
|
3178
|
+
This default behavior of shared signal avoids race competition but is not very useful in practice. For better control, it is possible to select which sequencer is to be acknowledged for writing. This is done by setting the number of the sequencer which can write the signal that controls the sharing accessed as follows:
|
|
3179
|
+
|
|
3180
|
+
```ruby
|
|
3181
|
+
<shared signal>.select
|
|
3182
|
+
```
|
|
3183
|
+
|
|
3184
|
+
The select value starts from 0 for the first sequencer writing to the shared signal, and is increased by one per writing sequencer. For example, in the first example, for selecting the second sequencer for writing to `x` the following code can be added after this signal is declared:
|
|
3185
|
+
|
|
3186
|
+
```ruby
|
|
3187
|
+
x.select <= 1
|
|
3188
|
+
```
|
|
3189
|
+
|
|
3190
|
+
This value can be changed at runtime too. For example, instead of setting the second sequencer, it is possible to switch the sequencer every clock cycle as follows:
|
|
3191
|
+
|
|
3192
|
+
```ruby
|
|
3193
|
+
par(clk.posedge) { x.select <= x.select + 1 }
|
|
3194
|
+
```
|
|
3195
|
+
|
|
3196
|
+
__Note__: this select sub signal is a standard RTL signal that has the same properties and limitations as the other ones, i.e., this is not a shared signal itself.
|
|
3197
|
+
|
|
3198
|
+
|
|
3199
|
+
#### Arbiters
|
|
3200
|
+
|
|
3201
|
+
Usually, it is not the signals that we want to share, but the resources they drive. For example, in a CPU, it is an ALU that is shared as a whole rather than each of its inputs separately. In order to support such cases and ease the handling of shared signals, the library also provides the *arbiter* components. This component is instantiated like a standard module as follows, where `name` is the name of the arbiter instance:
|
|
3202
|
+
|
|
3203
|
+
```ruby
|
|
3204
|
+
arbiter(:<name>).(<list of shared signal>)
|
|
3205
|
+
```
|
|
3206
|
+
|
|
3207
|
+
When instantiated, an arbiter will take control of the select sub-signals of the shared signals (hence, you cannot control the selection yourself for them any longer). In return, it provides the possibility of requiring or releasing access to the shared signals. Requiring access is done by sending the value 1 to the arbiter, and releasing is done by sending the value 0. If a sequencer writes to a shared signal under arbitration without requiring access first, the write will simply be ignored.
|
|
3208
|
+
|
|
3209
|
+
The following is an example of an arbiter that controls access to shared signals `x` and `y` and two sequencers acquiring and releasing accesses to them:
|
|
3210
|
+
|
|
3211
|
+
```ruby
|
|
3212
|
+
input :clk, :start
|
|
3213
|
+
[8].shared x, y
|
|
3214
|
+
arbiter(:ctrl_xy).(x,y)
|
|
3215
|
+
|
|
3216
|
+
sequencer(clk.posedge,start) do
|
|
3217
|
+
ctrl_xy <= 1
|
|
3218
|
+
x <= 0 ; y <= 0
|
|
3219
|
+
5.stime do |i|
|
|
3220
|
+
x <= x + 1
|
|
3221
|
+
y <= y + 2
|
|
3222
|
+
end
|
|
3223
|
+
ctrl_xy <= 0
|
|
3224
|
+
end
|
|
3225
|
+
|
|
3226
|
+
sequencer(clk.posedge,start) do
|
|
3227
|
+
ctrl_xy <= 1
|
|
3228
|
+
x <= 2; y <= 1
|
|
3229
|
+
10.stime do |i|
|
|
3230
|
+
x <= x + 2
|
|
3231
|
+
y <= y + 1
|
|
3232
|
+
end
|
|
3233
|
+
ctrl_xy <= 0
|
|
3234
|
+
end
|
|
3235
|
+
```
|
|
3236
|
+
|
|
3237
|
+
In the example, both sequencers require access to signals `x` and `y` before accessing them and then releasing the access.
|
|
3238
|
+
|
|
3239
|
+
Requiring access does not guarantee that the access will be granted by the arbiter though. In the access is not granted, the write access will be ignored.
|
|
3240
|
+
The default access granting policy of an arbiter is the priority in the order of sequencer declaration. I.e., if several sequencers are requiring one at the same time, then the one declared the earliest in the code gains write access. For example, with the code given above, the first sequencer has to write access to `x` and `y`, and since after five write cycles it releases access, the second sequencer can then write to these signals. However, not obtaining write access does not block the execution of the sequencer, simply, its write access to the corresponding shared signals is simply ignored. In the example, the second sequencer will do its first five loop cycles without any effect and have only its five last ones that change the shared signals. To avoid such a behavior, it is possible to check if the write access is granted using arbiter sub signal `acquired`: if this signal is one in the current sequencer, that means the access is granted, otherwise it is 0. For example the following will increase signal `x` only if write access is granted:
|
|
3241
|
+
|
|
3242
|
+
```ruby
|
|
3243
|
+
hif(ctrl_xy.acquired) { x <= x + 1 }
|
|
3244
|
+
```
|
|
2853
3245
|
|
|
2854
|
-
|
|
3246
|
+
The policy of an arbiter can be changed using command policy. You can either provide a new priority table, containing the number of the sequencers in order of priority (the first one having higher priority. The number of a sequencer is assigned in order of declaration provided it uses the arbiter. For example, in the previous code, the second sequencer can be given higher priority by adding the following code after having declared the arbiter:
|
|
3247
|
+
|
|
3248
|
+
```ruby
|
|
3249
|
+
ctrl_xy.policy([1,0])
|
|
3250
|
+
```
|
|
3251
|
+
|
|
3252
|
+
It is also possible to set a more complex policy by providing to the policy method a block of code whose argument is a vector indicating which sequencer is currently requiring write access to the shared signals and whose result will be the number of the sequencer to grant the access. This code will be executed each time write access is actually performed. For example, in the previous code, a policy switch priorities at each access can be implemented as follows:
|
|
3253
|
+
|
|
3254
|
+
```ruby
|
|
3255
|
+
inner priority_xy: 0
|
|
3256
|
+
inner grant_xy
|
|
3257
|
+
ctrl_xy.policy do |acq|
|
|
3258
|
+
hcase(acq)
|
|
3259
|
+
hwhen(_b01) do
|
|
3260
|
+
grant_xy <= 0
|
|
3261
|
+
priority_xy <= ~priority_xy
|
|
3262
|
+
end
|
|
3263
|
+
hwhen(_b10) do
|
|
3264
|
+
grant_xy <= 1
|
|
3265
|
+
priority_xy <= ~priority_xy
|
|
3266
|
+
end
|
|
3267
|
+
hwhen(_b11) do
|
|
3268
|
+
grant_xy <= priority_xy
|
|
3269
|
+
priority_xy <= ~priority_xy
|
|
3270
|
+
end
|
|
3271
|
+
grant_xy
|
|
3272
|
+
end
|
|
3273
|
+
```
|
|
3274
|
+
|
|
3275
|
+
As seen in the code above, each bit of the `acq` vector is one when the corresponding sequencer required an access or 0 otherwise, bit 0 corresponds to sequencer 0, bit 1 to sequencer 1 and so on.
|
|
3276
|
+
|
|
3277
|
+
|
|
3278
|
+
#### Monitors
|
|
3279
|
+
|
|
3280
|
+
Arbiters are especially useful when we can ensure that the sequencers accessing the same resource do not overlap or when they do not need to synchronize with each other. If such synchronizations are required, instead of arbiters, it is possible to use the *monitor* components.
|
|
3281
|
+
|
|
3282
|
+
The monitor component is instantiated like the arbiters as follows:
|
|
3283
|
+
|
|
3284
|
+
```ruby
|
|
3285
|
+
monitor(:<name>).(<list of shared signals>)
|
|
3286
|
+
```
|
|
3287
|
+
|
|
3288
|
+
Monitors are used exactly the same ways as arbiters (including the write access granting policies) but block the execution of the sequencers that require write access until the access is granted. If we take the example of code with two sequencers given as an illustration of arbiter usage, replacing the arbiter with a monitor as follows will lock the second sequencer until it can write to shared variables `x` and `y` ensuring that all its loop cycles have the specified result:
|
|
3289
|
+
|
|
3290
|
+
```ruby
|
|
3291
|
+
monitor(:ctrl_xy).(x,y)
|
|
3292
|
+
```
|
|
3293
|
+
|
|
3294
|
+
Since monitors lock processes, they automatically insert a step. Hence to avoid confusion, acquiring access to a monitor is done by using the method `lock` instead of assigning 1, and releasing is done by using the method `unlock` instead of assigning 0. Hence, when using a monitor, the previous arbiter-based code should be rewritten as follows:
|
|
3295
|
+
|
|
3296
|
+
```ruby
|
|
3297
|
+
input :clk, :start
|
|
3298
|
+
[8].shared x, y
|
|
3299
|
+
monitor(:ctrl_xy).(x,y)
|
|
3300
|
+
|
|
3301
|
+
sequencer(clk.posedge,start) do
|
|
3302
|
+
ctrl_xy.lock
|
|
3303
|
+
x <= 0 ; y <= 0
|
|
3304
|
+
5.stime do |i|
|
|
3305
|
+
x <= x + 1
|
|
3306
|
+
y <= y + 2
|
|
3307
|
+
end
|
|
3308
|
+
ctrl_xy.unlock
|
|
3309
|
+
end
|
|
3310
|
+
|
|
3311
|
+
sequencer(clk.posedge,start) do
|
|
3312
|
+
ctrl_xy.lock
|
|
3313
|
+
x <= 2; y <= 1
|
|
3314
|
+
10.stime do |i|
|
|
3315
|
+
x <= x + 2
|
|
3316
|
+
y <= y + 1
|
|
3317
|
+
end
|
|
3318
|
+
ctrl_xy.unlock
|
|
3319
|
+
end
|
|
3320
|
+
```
|
|
3321
|
+
|
|
3322
|
+
### Sequencer-specific function: `std/sequencer_func.rb`
|
|
3323
|
+
|
|
3324
|
+
HDLRuby function defined by `hdef` can be used in sequencer like any other HDLRuyby construct. But like the process constructs `hif` and so on, the body of these functions cannot include any sequencer-specific constructs.
|
|
3325
|
+
|
|
3326
|
+
However, it is possible to define functions that do support the sequencer constructs using `sdef` instead of `hdef` as follows:
|
|
3327
|
+
|
|
3328
|
+
```ruby
|
|
3329
|
+
sdef :<function name> do |<arguments>|
|
|
3330
|
+
<sequencer code>
|
|
3331
|
+
end
|
|
3332
|
+
```
|
|
3333
|
+
|
|
3334
|
+
Such functions can be defined anywhere in a HDLRuby description, but can only be called within a sequencer.
|
|
3335
|
+
|
|
3336
|
+
As additional features, since the `sdef` function is made to support the software-like code description of the sequencers, it also supports recursion. For example, a function describing a factorial can be described as follows:
|
|
3337
|
+
|
|
3338
|
+
```ruby
|
|
3339
|
+
sdef(:fact) do |n|
|
|
3340
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
|
3341
|
+
selse { sreturn(1) }
|
|
3342
|
+
end
|
|
3343
|
+
```
|
|
3344
|
+
|
|
3345
|
+
As seen in the code above, a new construct `sreturn` can be used for returning a value from anywhere inside the function.
|
|
3346
|
+
|
|
3347
|
+
When a recursion is present, HDLRuby automatically defines a stack for storing the return state and the arguments of the function. The size of the stack is heuristically set to the maximum number of bits of the arguments of the function when it is recursively called. For example, for the previous `fact` function, if when called, `n` is 16-bit, the stack will be able to hold 16 recursions. If this heuristic does not match the circuit's needs, the size can be forced as a second argument when defining the function. For example, the following code set the size to 32 whatever the arguments are:
|
|
3348
|
+
|
|
3349
|
+
```ruby
|
|
3350
|
+
sdef(:fact,32) do |n|
|
|
3351
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
|
3352
|
+
selse { sreturn(1) }
|
|
3353
|
+
end
|
|
3354
|
+
```
|
|
3355
|
+
|
|
3356
|
+
__Notes__:
|
|
3357
|
+
|
|
3358
|
+
* A call to such a function and a return take respectively one and two cycles of the sequencer.
|
|
3359
|
+
|
|
3360
|
+
* For now, there is no tail call optimization.
|
|
3361
|
+
|
|
3362
|
+
* In case of stack overflow (the number of recursive calls exceeds the size of the sack), the current recursion is terminated and the sequencer goes on its execution. It is possible to add a process that is to be executed in such a case as follows:
|
|
3363
|
+
|
|
3364
|
+
```ruby
|
|
3365
|
+
sdef(:<name>,<depth>, proc <block>) do
|
|
3366
|
+
<function code>
|
|
3367
|
+
end
|
|
3368
|
+
```
|
|
3369
|
+
|
|
3370
|
+
Where `block` contains the code of the stack overflow process. For now, this process cannot contain a sequencer construct. For example, the previous factorial function can be modified as follows so that signal `stack_overflow` is set to 1 in case of overflow:
|
|
3371
|
+
|
|
3372
|
+
```ruby
|
|
3373
|
+
sdef(:fact,32, proc { stack_overflow <= 1 }) do |n|
|
|
3374
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
|
3375
|
+
selse { sreturn(1) }
|
|
3376
|
+
end
|
|
3377
|
+
```
|
|
3378
|
+
|
|
3379
|
+
With the code above, the only restriction is that the signal `stack_overflow` is declared before the function `fact` is called.
|
|
3380
|
+
|
|
3381
|
+
|
|
3382
|
+
## Fixed-point (fixpoint): `std/fixpoint.rb`
|
|
2855
3383
|
<a name="fixpoint"></a>
|
|
2856
3384
|
|
|
2857
3385
|
This library provides a new fixed point set of data types. These new data types can be bit vectors, unsigned or signed values and are declared respectively as follows:
|
|
@@ -2876,14 +3404,14 @@ In addition to the fixed point data type, a method is added to the literal objec
|
|
|
2876
3404
|
<litteral>.to_fix(<number of bits after the decimal point>)
|
|
2877
3405
|
```
|
|
2878
3406
|
|
|
2879
|
-
For example, the following code converts a floating-point value to a fixed
|
|
3407
|
+
For example, the following code converts a floating-point value to a fixed-point value with 16 bits after the decimal point:
|
|
2880
3408
|
|
|
2881
3409
|
```
|
|
2882
3410
|
3.178.to_fix(16)
|
|
2883
3411
|
```
|
|
2884
3412
|
|
|
2885
3413
|
|
|
2886
|
-
## Channel
|
|
3414
|
+
## Channel: `std/channel.rb`
|
|
2887
3415
|
<a name="channel"></a>
|
|
2888
3416
|
|
|
2889
3417
|
This library provides a unified interface to complex communication protocols through a new kind of component called the channels that abstract the details of communication protocols. The channels can be used similarly to the ports of a system and are used through a unified interface so that changing the kind of channel, i.e., the communication protocol, does not require any modification of the code.
|
|
@@ -2923,9 +3451,9 @@ The access points to a channel can also be handled individually by declaring por
|
|
|
2923
3451
|
* `output <name>`: declares a port for writing to the channel and associates them to `name` if any
|
|
2924
3452
|
* `inout <name>`: declares a port for reading and writing to the channel and associates them to `name` if any
|
|
2925
3453
|
|
|
2926
|
-
Such port can then be accessed using the same `read` and `write` method of a channel, the difference being that they can also be configured for new access procedures using the `wrap` method:
|
|
3454
|
+
Such a port can then be accessed using the same `read` and `write` method of a channel, the difference being that they can also be configured for new access procedures using the `wrap` method:
|
|
2927
3455
|
|
|
2928
|
-
* `wrap(<args>) <code>`: creates a new port whose read or write procedure has the elements of `<args>` and the ones produced by `<code>`
|
|
3456
|
+
* `wrap(<args>) <code>`: creates a new port whose read or write procedure has the elements of `<args>` and the ones produced by `<code>` assigned to the arguments of the read or write procedure.
|
|
2929
3457
|
|
|
2930
3458
|
For example, assuming `mem` is a channel whose read and write access have as argument the target address and data signals, the following code creates a port for always accessing at address 0:
|
|
2931
3459
|
|
|
@@ -2939,7 +3467,7 @@ Some channels may include several branches, they are accessed by name using the
|
|
|
2939
3467
|
|
|
2940
3468
|
* `branch(<name>)`: gets branch named `name` from the channel. This name can be any ruby object (e.g., a number) but it will be converted internally to a ruby symbol.
|
|
2941
3469
|
|
|
2942
|
-
A branch is a full-
|
|
3470
|
+
A branch is a full-fledged channel and is used identically. For instance, the following code gets access to branch number 0 of channel `ch`, gets its inputs port, reads it, and put the result in signal `val` on the rising edges of signal `clk`:
|
|
2943
3471
|
|
|
2944
3472
|
```ruby
|
|
2945
3473
|
br = ch.branch(0)
|
|
@@ -3061,17 +3589,18 @@ end
|
|
|
3061
3589
|
|
|
3062
3590
|
__Note__:
|
|
3063
3591
|
|
|
3064
|
-
* The code of the circuits, in the examples `producer8`, `consumer8`, and `producer_consummer8` is independent of the content of the channel. For example, the sample `with_channel.rb` (please see [
|
|
3592
|
+
* The code of the circuits, in the examples `producer8`, `consumer8`, and `producer_consummer8` is independent of the content of the channel. For example, the sample `with_channel.rb` (please see [samples](#sample-hdlruby-descriptions)) uses the same circuits with a channel implementing handshaking.
|
|
3065
3593
|
|
|
3594
|
+
<!---
|
|
3066
3595
|
|
|
3067
3596
|
## Pipeline
|
|
3068
3597
|
<a name="pipeline"></a>
|
|
3069
3598
|
|
|
3070
3599
|
This library provides a construct for an easy description of pipeline architectures.
|
|
3071
3600
|
|
|
3601
|
+
-->
|
|
3072
3602
|
|
|
3073
3603
|
# Sample HDLRuby descriptions
|
|
3074
|
-
<a name="sample"></a>
|
|
3075
3604
|
|
|
3076
3605
|
Several samples HDLRuby descriptions are available in the following directory:
|
|
3077
3606
|
|
|
@@ -3079,15 +3608,22 @@ path/to/HDLRuby/lib/HDLRuby/hdr\_samples
|
|
|
3079
3608
|
|
|
3080
3609
|
For the gem install, the path to HDLRuby can be found using the following:
|
|
3081
3610
|
|
|
3082
|
-
```
|
|
3611
|
+
```bash
|
|
3083
3612
|
gem which HDLRuby
|
|
3084
3613
|
```
|
|
3085
3614
|
|
|
3615
|
+
But you can also import the samples to your local directory with the following command (recommended):
|
|
3616
|
+
|
|
3617
|
+
```bash
|
|
3618
|
+
hdrcc --get-samples
|
|
3619
|
+
```
|
|
3620
|
+
|
|
3086
3621
|
The naming convention of the samples is the following:
|
|
3087
3622
|
|
|
3088
3623
|
* `<name>.rb`: default type of sample.
|
|
3089
3624
|
* `<name>_gen.rb`: generic parameters are required for processing the sample.
|
|
3090
3625
|
* `<name>_bench.rb`: sample including a simulation benchmark, these are the only samples that can be simulated using `hdrcc -S`. Please notice that such a sample cannot be converted to VHDL or Verilog HDL yet.
|
|
3626
|
+
* `with_<name>.rb`: sample illustrating a single aspect of HDLRuby or one of its library, usually includes a benchmark.
|
|
3091
3627
|
|
|
3092
3628
|
|
|
3093
3629
|
# Contributing
|
|
@@ -3097,10 +3633,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/civol/
|
|
|
3097
3633
|
|
|
3098
3634
|
# To do
|
|
3099
3635
|
|
|
3100
|
-
* Test the compatibility of the HDLRuby framework with the Microsoft Windows environments.
|
|
3101
|
-
* Add the generation of VHDL and Verilog code for the time behaviors.
|
|
3102
|
-
* Provide targets for the `Reconf` library.
|
|
3103
|
-
* Add a standard wave output for the simulator.
|
|
3104
3636
|
* Find and fix the (maybe) terrifying number of bugs.
|
|
3105
3637
|
* Add a GUI (any volunteer to do it?).
|
|
3106
3638
|
|