HDLRuby 2.11.11 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.html +3274 -0
- data/README.md +608 -99
- data/ext/hruby_sim/hruby_rcsim_build.c +27 -0
- data/ext/hruby_sim/hruby_sim.h +3 -0
- data/ext/hruby_sim/hruby_sim_calc.c +2 -0
- data/ext/hruby_sim/hruby_sim_core.c +17 -5
- data/ext/hruby_sim/hruby_sim_stack_calc.c +1 -1
- data/ext/hruby_sim/hruby_sim_tree_calc.c +8 -1
- data/ext/hruby_sim/hruby_sim_vcd.c +24 -7
- data/ext/hruby_sim/hruby_sim_vizualize.c +9 -1
- 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 +3 -1
- data/lib/HDLRuby/hdr_samples/counter_dff_bench.rb +3 -1
- data/lib/HDLRuby/hdr_samples/huge_rom.rb +1 -1
- data/lib/HDLRuby/hdr_samples/mei8.rb +11 -11
- data/lib/HDLRuby/hdr_samples/mei8_bench.rb +12 -12
- data/lib/HDLRuby/hdr_samples/neg_arith_bench.rb +4 -4
- data/lib/HDLRuby/hdr_samples/rom_nest.rb +1 -1
- data/lib/HDLRuby/hdr_samples/ruby_fir_hw.rb +4 -4
- data/lib/HDLRuby/hdr_samples/struct.rb +44 -10
- data/lib/HDLRuby/hdr_samples/with_bram.rb +45 -0
- 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_casts.rb +3 -3
- data/lib/HDLRuby/hdr_samples/with_concat.rb +6 -6
- data/lib/HDLRuby/hdr_samples/with_connector_memory.rb +2 -2
- data/lib/HDLRuby/hdr_samples/with_def.rb +10 -3
- data/lib/HDLRuby/hdr_samples/with_define_operator.rb +44 -0
- data/lib/HDLRuby/hdr_samples/with_fixpoint.rb +12 -12
- data/lib/HDLRuby/hdr_samples/with_init.rb +3 -3
- data/lib/HDLRuby/hdr_samples/with_leftright.rb +21 -0
- data/lib/HDLRuby/hdr_samples/with_reduce.rb +13 -13
- data/lib/HDLRuby/hdr_samples/with_ref_array.rb +6 -6
- data/lib/HDLRuby/hdr_samples/with_register_stack.rb +150 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer.rb +190 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_deep.rb +91 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb +405 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb +89 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_sync.rb +120 -0
- data/lib/HDLRuby/hdr_samples/with_subsums.rb +3 -3
- data/lib/HDLRuby/hdr_samples/with_terminate.rb +3 -3
- data/lib/HDLRuby/hdr_samples/with_to_a.rb +10 -10
- data/lib/HDLRuby/hdr_samples/with_values.rb +3 -3
- data/lib/HDLRuby/hdrcc.rb +29 -3
- data/lib/HDLRuby/hdrlib.rb +1 -1
- data/lib/HDLRuby/hruby_bstr.rb +10 -5
- data/lib/HDLRuby/hruby_db.rb +2 -2
- data/lib/HDLRuby/hruby_high.rb +152 -47
- data/lib/HDLRuby/hruby_high_fullname.rb +3 -1
- data/lib/HDLRuby/hruby_low.rb +189 -18
- data/lib/HDLRuby/hruby_low2c.rb +129 -54
- 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 +80 -44
- 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 +65 -32
- data/lib/HDLRuby/hruby_low_mutable.rb +83 -119
- data/lib/HDLRuby/hruby_low_resolve.rb +38 -30
- 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 +45 -19
- data/lib/HDLRuby/hruby_low_without_namespace.rb +47 -32
- data/lib/HDLRuby/hruby_low_without_parinseq.rb +32 -16
- data/lib/HDLRuby/hruby_low_without_select.rb +37 -24
- data/lib/HDLRuby/hruby_low_without_subsignals.rb +280 -0
- data/lib/HDLRuby/hruby_rcsim.rb +158 -134
- data/lib/HDLRuby/hruby_rsim.rb +194 -20
- data/lib/HDLRuby/hruby_rsim_mute.rb +2 -3
- data/lib/HDLRuby/hruby_rsim_vcd.rb +125 -50
- data/lib/HDLRuby/hruby_values.rb +48 -33
- data/lib/HDLRuby/hruby_verilog.rb +90 -48
- data/lib/HDLRuby/soft/stacks.rb +219 -0
- data/lib/HDLRuby/std/bram.rb +26 -0
- data/lib/HDLRuby/std/clocks.rb +1 -1
- data/lib/HDLRuby/std/fixpoint.rb +2 -2
- data/lib/HDLRuby/std/fsm.rb +48 -11
- data/lib/HDLRuby/std/function_generator.rb +2 -2
- data/lib/HDLRuby/std/sequencer.rb +1857 -0
- data/lib/HDLRuby/std/sequencer_sync.rb +400 -0
- data/lib/HDLRuby/std/std.rb +12 -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 +2684 -0
- data/tuto/tutorial_sw.pdf +0 -0
- data/tuto/tutorial_sw_jp.md +417 -0
- metadata +49 -3
- data/lib/HDLRuby/hdr_samples/sumprod.rb +0 -29
data/README.md
CHANGED
@@ -3,6 +3,25 @@
|
|
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.0.0:
|
15
|
+
|
16
|
+
* This section;
|
17
|
+
|
18
|
+
* [The sequencers](#sequencer-software-like-hardware-coding-stdsequencerrb) for software-like hardware design;
|
19
|
+
|
20
|
+
* A [tutorial](tuto/tutorial_sw.md) for software people;
|
21
|
+
|
22
|
+
* The stable [standard libraries](#standard-libraries) are loaded by default.
|
23
|
+
|
24
|
+
|
6
25
|
__Install__:
|
7
26
|
|
8
27
|
The recommended installation method is from rubygem as follows:
|
@@ -20,14 +39,14 @@ git clone HDLRuby
|
|
20
39
|
__Warning__:
|
21
40
|
|
22
41
|
- This is still preliminary work which may change before we release a stable version.
|
23
|
-
- It is highly recommended to have both basic
|
42
|
+
- It is highly recommended to have both basic knowledge of the Ruby language and hardware description languages before using HDLRuby.
|
24
43
|
|
25
44
|
|
26
45
|
# Compiling HDLRuby descriptions
|
27
46
|
|
28
47
|
## Using the HDLRuby compiler
|
29
48
|
|
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
|
49
|
+
'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
50
|
|
32
51
|
|
33
52
|
__Usage__:
|
@@ -49,68 +68,76 @@ Where:
|
|
49
68
|
| `-v, --verilog` | Output in Verilog HDL format |
|
50
69
|
| `-V, --vhdl` | Output in VHDL format |
|
51
70
|
| `-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 Hybris engine
|
71
|
+
| `-C, --clang` | Output the C code of the standalone simulator |
|
72
|
+
| `-S, --sim` | Perform the simulation with the default engine |
|
73
|
+
| `--csim` | Perform the simulation with the standalone engine |
|
74
|
+
| `--rsim` | Perform the simulation with the Ruby engine |
|
75
|
+
| `--rcsim` | Perform the simulation with the Hybris engine |
|
57
76
|
| `--vcd` | Make the simulator generate a VCD file |
|
58
77
|
| `-d, --directory` | Specify the base directory for loading the HDLRuby files |
|
59
78
|
| `-D, --debug` | Set the HDLRuby debug mode |
|
60
79
|
| `-t, --top system`| Specify the top system describing the circuit to compile |
|
61
80
|
| `-p, --param x,y,z` | Specify the generic parameters |
|
62
|
-
| `--
|
81
|
+
| `--get-samples` | Copy the sample directory (hdr_samples) to current one, then exit |
|
82
|
+
| `--version` | Show the version number, then exit |
|
63
83
|
| `-h, --help` | Show the help message |
|
64
84
|
|
65
85
|
__Notes__:
|
66
86
|
|
67
87
|
* If no top system is given, it is automatically looked for from the input file.
|
68
88
|
* If no option is given, it simply checks the input file.
|
69
|
-
*
|
89
|
+
* 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:
|
90
|
+
|
91
|
+
```bash
|
92
|
+
hdrcc --get-samples
|
93
|
+
```
|
94
|
+
|
95
|
+
Then in your current directory (folder) the `hdr_samples` subdirectory will appear that contains several HDLRuby example files. For details about the samples can be found there: [samples](#sample-hdlruby-descriptions).
|
96
|
+
|
70
97
|
|
71
98
|
__Examples__:
|
72
99
|
|
73
100
|
* Compile system named `adder` from `adder.rb` input file and generate a low-level YAML description into directory `adder`:
|
74
101
|
|
75
|
-
```
|
102
|
+
```bash
|
76
103
|
hdrcc --yaml --top adder adder.rb adder
|
77
104
|
```
|
78
105
|
|
79
106
|
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into directory `adder`:
|
80
107
|
|
81
|
-
```
|
108
|
+
```bash
|
82
109
|
hdrcc -v adder.rb adder
|
83
110
|
```
|
84
111
|
|
85
112
|
* 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
113
|
|
87
|
-
```
|
114
|
+
```bash
|
88
115
|
hdrcc -V -t adder --param 16 adder_gen.rb adder
|
89
116
|
```
|
90
117
|
|
91
118
|
* 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
119
|
|
93
|
-
```
|
120
|
+
```bash
|
94
121
|
hdrcc -y -t multer -p 16,16,32 multer_gen.rb multer
|
95
122
|
```
|
96
123
|
|
97
|
-
* Simulate the circuit described in file `counter_bench.rb` using the default
|
124
|
+
* Simulate the circuit described in file `counter_bench.rb` using the default simulation engine and putting the simulator's files in directory `counter`:
|
98
125
|
|
99
|
-
```
|
126
|
+
```bash
|
100
127
|
hdrcc -S counter_bench.rb counter
|
101
128
|
```
|
102
129
|
|
103
|
-
As a policy, the default simulation
|
130
|
+
As a policy, the default simulation engine is set to the fastest one (currently it is the hybrid engine).
|
104
131
|
|
105
132
|
* Run in interactive mode.
|
106
133
|
|
107
|
-
```
|
134
|
+
```bash
|
108
135
|
hdrcc -I
|
109
136
|
```
|
110
137
|
|
111
138
|
* Run in interactive mode using pry as UI.
|
112
139
|
|
113
|
-
```
|
140
|
+
```bash
|
114
141
|
hdrcc -I pry
|
115
142
|
```
|
116
143
|
|
@@ -166,13 +193,13 @@ The second specificity of HDLRuby is that it supports natively all the features
|
|
166
193
|
|
167
194
|
__Notes__:
|
168
195
|
|
169
|
-
- It is still possible to extend HDLRuby to support hardware descriptions of a higher level than RTL, please refer to section [Extending HDLRuby](#
|
196
|
+
- 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
197
|
- In this document, HDLRuby constructs will often be compared to their Verilog HDL or VHDL equivalents for simpler explanations.
|
171
198
|
|
172
199
|
## Introduction
|
173
200
|
|
174
201
|
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](#
|
202
|
+
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
203
|
|
177
204
|
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
205
|
|
@@ -187,7 +214,7 @@ system :dff do
|
|
187
214
|
end
|
188
215
|
```
|
189
216
|
|
190
|
-
As
|
217
|
+
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
218
|
|
192
219
|
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
220
|
|
@@ -195,7 +222,7 @@ After such a system has been defined, it can be instantiated. For example, a sin
|
|
195
222
|
dff :dff0
|
196
223
|
```
|
197
224
|
|
198
|
-
The ports of this instance can then be accessed to be used like any other signals, e.g., `dff0.d` for
|
225
|
+
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
226
|
|
200
227
|
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
228
|
|
@@ -230,7 +257,7 @@ system :counter2 do
|
|
230
257
|
end
|
231
258
|
```
|
232
259
|
|
233
|
-
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected by declaration order. Please notice that it is also possible to connect only a subset of the ports while declaring and to reconnect already connected ports in further statements.
|
260
|
+
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected by declaration order. Please notice that it is also possible to connect only a subset of the ports while declaring, and to reconnect already connected ports in further statements.
|
234
261
|
|
235
262
|
While a circuit can be generated from the code given above, a benchmark must
|
236
263
|
be provided to test it. Such a benchmark is described by constructs called
|
@@ -285,8 +312,7 @@ end
|
|
285
312
|
```
|
286
313
|
|
287
314
|
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:
|
315
|
+
In such a case, the system declaration can be omitted, and an instance can be directly declared as follows:
|
290
316
|
|
291
317
|
```ruby
|
292
318
|
instance :dff_single do
|
@@ -395,7 +421,7 @@ system :shifter do |n|
|
|
395
421
|
end
|
396
422
|
```
|
397
423
|
|
398
|
-
As
|
424
|
+
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
425
|
|
400
426
|
```ruby
|
401
427
|
<type>.<direction> <list of symbols representing the signal>
|
@@ -430,14 +456,14 @@ system :sumprod_16_3456 do
|
|
430
456
|
end
|
431
457
|
```
|
432
458
|
|
433
|
-
The description above is straightforward, but it would be necessary to rewrite it if another circuit with different bit
|
459
|
+
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
460
|
|
435
461
|
```ruby
|
436
462
|
system :sumprod do |typ,coefs|
|
437
463
|
typ[coefs.size].input :ins
|
438
464
|
typ.output :o
|
439
465
|
|
440
|
-
o <= coefs.each_with_index.reduce(
|
466
|
+
o <= coefs.each_with_index.reduce(_b0) do |sum,(coef,i)|
|
441
467
|
sum + ins[i]*coef
|
442
468
|
end
|
443
469
|
end
|
@@ -446,7 +472,7 @@ end
|
|
446
472
|
In the code above, there are two generic parameters,
|
447
473
|
`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
474
|
|
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 `
|
475
|
+
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
476
|
|
451
477
|
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
478
|
|
@@ -456,14 +482,14 @@ sumprod(signed[32], [3,78,43,246, 3,67,1,8, 47,82,99,13, 5,77,2,4]).(:my_circuit
|
|
456
482
|
|
457
483
|
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
484
|
|
459
|
-
While the description `sumprod` is already usable in a wide range of cases, it still uses
|
485
|
+
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 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
486
|
|
461
487
|
```ruby
|
462
488
|
system :sumprod_func do |typ,coefs|
|
463
489
|
typ[coefs.size].input ins
|
464
490
|
typ.output :o
|
465
491
|
|
466
|
-
o <= coefs.each_with_index.reduce(
|
492
|
+
o <= coefs.each_with_index.reduce(_b0) do
|
467
493
|
|sum,(coef,i)|
|
468
494
|
add(sum, mult(ins[i]*coef))
|
469
495
|
end
|
@@ -502,14 +528,14 @@ end
|
|
502
528
|
|
503
529
|
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 not general-purpose any longer.
|
504
530
|
|
505
|
-
HDLRuby provides two ways to address such issues. First, it is possible to pass code as an argument. In the case of `sumprod
|
531
|
+
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
532
|
|
507
533
|
```ruby
|
508
534
|
system :sumprod_proc do |add,mult,typ,coefs|
|
509
535
|
typ[coefs.size].input ins
|
510
536
|
typ.output :o
|
511
537
|
|
512
|
-
o <= coefs.each_with_index.reduce(
|
538
|
+
o <= coefs.each_with_index.reduce(_b0) do
|
513
539
|
|sum,(coef,i)|
|
514
540
|
add.(sum, mult.(ins[i]*coef))
|
515
541
|
end
|
@@ -539,10 +565,8 @@ A second possible approach provided by HDLRuby is to declare a new data type wit
|
|
539
565
|
signed[16].typedef(:sat16_1000)
|
540
566
|
|
541
567
|
sat16_1000.define_operator(:+) do |x,y|
|
542
|
-
|
543
|
-
|
544
|
-
res <= x + y
|
545
|
-
( res <= 1000 ).hif(res > 1000)
|
568
|
+
tmp = x + y
|
569
|
+
mux(tmp > 1000,tmp,1000)
|
546
570
|
end
|
547
571
|
end
|
548
572
|
```
|
@@ -565,10 +589,8 @@ end
|
|
565
589
|
|
566
590
|
|
567
591
|
sat.define_operator(:+) do |width,max, x,y|
|
568
|
-
|
569
|
-
|
570
|
-
res <= x + y
|
571
|
-
( res <= max ).hif(res > max)
|
592
|
+
tmp = x + y
|
593
|
+
mux(tmp > max, tmp, max)
|
572
594
|
end
|
573
595
|
end
|
574
596
|
```
|
@@ -629,10 +651,10 @@ From there, we will describe in more detail each construct of HDLRuby.
|
|
629
651
|
## Naming rules
|
630
652
|
<a name="names"></a>
|
631
653
|
|
632
|
-
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
|
654
|
+
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.
|
633
655
|
|
634
656
|
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
|
635
|
-
has been declared with `:hello` as name, it will be afterward referred to by `hello`.
|
657
|
+
has been declared with `:hello` as the name, it will be afterward referred to by `hello`.
|
636
658
|
|
637
659
|
## Systems and signals
|
638
660
|
|
@@ -654,7 +676,7 @@ __Notes__:
|
|
654
676
|
Ruby keywords (in which case the parentheses can be omitted) are as follows:
|
655
677
|
|
656
678
|
```ruby
|
657
|
-
system :box
|
679
|
+
system :box does
|
658
680
|
end
|
659
681
|
```
|
660
682
|
|
@@ -925,7 +947,7 @@ Where:
|
|
925
947
|
* `<name>` is the name of the scope.
|
926
948
|
* `<code>` is the code within the scope.
|
927
949
|
|
928
|
-
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`:
|
950
|
+
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`:
|
929
951
|
|
930
952
|
```ruby
|
931
953
|
sub :scop do
|
@@ -952,7 +974,7 @@ end
|
|
952
974
|
In addition, it is possible to declare inner signals within an execution block.
|
953
975
|
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.
|
954
976
|
|
955
|
-
An event represents a specific change
|
977
|
+
An event represents a specific change in the state of a signal.
|
956
978
|
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
|
957
979
|
expressions using the following methods: `posedge` for a rising edge, `negedge` for a falling edge, and `edge` for any edge.
|
958
980
|
Events are described in more detail in section [Events](#events).
|
@@ -993,7 +1015,7 @@ system :with_sequential_behavior do
|
|
993
1015
|
end
|
994
1016
|
```
|
995
1017
|
|
996
|
-
A sub-block can also have a different execution mode if it is declared using `seq`, which will force sequential execution mode
|
1018
|
+
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:
|
997
1019
|
|
998
1020
|
```ruby
|
999
1021
|
system :with_sequential_behavior do
|
@@ -1006,7 +1028,7 @@ system :with_sequential_behavior do
|
|
1006
1028
|
end
|
1007
1029
|
```
|
1008
1030
|
|
1009
|
-
|
1031
|
+
Sunblocks have their scope so that it is possible to declare signals without colliding with existing ones. For example, it is possible to
|
1010
1032
|
declare three different inner signals all called `sig` as follows:
|
1011
1033
|
|
1012
1034
|
```ruby
|
@@ -1121,7 +1143,7 @@ unshift do
|
|
1121
1143
|
end
|
1122
1144
|
```
|
1123
1145
|
|
1124
|
-
For example the following code inserts two statements at the beginning of the current block:
|
1146
|
+
For example, the following code inserts two statements at the beginning of the current block:
|
1125
1147
|
|
1126
1148
|
```ruby
|
1127
1149
|
par do
|
@@ -1133,7 +1155,7 @@ par do
|
|
1133
1155
|
end
|
1134
1156
|
```
|
1135
1157
|
|
1136
|
-
The code above will
|
1158
|
+
The code above will result in the following block:
|
1137
1159
|
|
1138
1160
|
```ruby
|
1139
1161
|
par do
|
@@ -1155,7 +1177,7 @@ In HDLRuby, dynamically reconfigurable devices are modeled by instances having m
|
|
1155
1177
|
<instance>.choice(<list of named systems>)
|
1156
1178
|
```
|
1157
1179
|
|
1158
|
-
For example, assuming systems `sys0`, `sys1
|
1180
|
+
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):
|
1159
1181
|
|
1160
1182
|
```ruby
|
1161
1183
|
sys0 :dev012 # dev012 is at first a standard instance of sys0
|
@@ -1187,9 +1209,8 @@ These reconfiguration commands are treated as regular RTL statements in HDLRuby
|
|
1187
1209
|
|
1188
1210
|
|
1189
1211
|
## Events
|
1190
|
-
<a name="events"></a>
|
1191
1212
|
|
1192
|
-
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
|
1213
|
+
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.
|
1193
1214
|
|
1194
1215
|
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
|
1195
1216
|
represent the instants when their corresponding signals vary from 1 to 0 and the change events represent the instants when their corresponding signals vary.
|
@@ -1215,7 +1236,6 @@ __Note:__
|
|
1215
1236
|
- The `change` keyword can be omitted.
|
1216
1237
|
|
1217
1238
|
## Statements
|
1218
|
-
<a name="statements"></a>
|
1219
1239
|
|
1220
1240
|
Statements are the basic elements of a behavioral description. They are regrouped in blocks that specify their execution mode (parallel or sequential).
|
1221
1241
|
There are four kinds of statements: the transmit statement which computes expressions and sends the result to the target signals, the control statement
|
@@ -1228,7 +1248,7 @@ __Note__:
|
|
1228
1248
|
|
1229
1249
|
### Transmit statement
|
1230
1250
|
|
1231
|
-
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
|
1251
|
+
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.
|
1232
1252
|
|
1233
1253
|
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`:
|
1234
1254
|
|
@@ -1286,7 +1306,7 @@ end
|
|
1286
1306
|
|
1287
1307
|
### helsif
|
1288
1308
|
|
1289
|
-
In addition to `helse
|
1309
|
+
In addition to `helse`, it is possible to set additional conditions to an `hif` using the `helsif` keyword as follows:
|
1290
1310
|
|
1291
1311
|
```ruby
|
1292
1312
|
hif <condition 0> do
|
@@ -1420,13 +1440,12 @@ While the underlying structure of any HDLRuby type is the bit vector, complex ty
|
|
1420
1440
|
<a name="expressions"></a>
|
1421
1441
|
|
1422
1442
|
Expressions are any construct that represents a value associated with a type.
|
1423
|
-
They include [immediate values](#values), [reference to signals](#references) and operations among other expressions using [expression operators](#operators).
|
1443
|
+
They include [immediate values](#immediate-values), [reference to signals](#references) and operations among other expressions using [expression operators](#expression-operators).
|
1424
1444
|
|
1425
1445
|
|
1426
1446
|
### Immediate values
|
1427
|
-
<a name="values"></a>
|
1428
1447
|
|
1429
|
-
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.
|
1448
|
+
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.
|
1430
1449
|
|
1431
1450
|
The vector type specifiers are the followings:
|
1432
1451
|
|
@@ -1438,7 +1457,7 @@ The vector type specifiers are the followings:
|
|
1438
1457
|
|
1439
1458
|
The base specifiers are the followings:
|
1440
1459
|
|
1441
|
-
- `b`: binary,
|
1460
|
+
- `b`: binary,
|
1442
1461
|
|
1443
1462
|
- `o`: octal,
|
1444
1463
|
|
@@ -1446,21 +1465,30 @@ The base specifiers are the followings:
|
|
1446
1465
|
|
1447
1466
|
- `h`: hexadecimal.
|
1448
1467
|
|
1449
|
-
For example, all the following immediate values represent an 8-bit `
|
1468
|
+
For example, all the following immediate values represent an 8-bit `hundred` (either in unsigned or signed representation):
|
1450
1469
|
|
1451
1470
|
```ruby
|
1452
1471
|
_bb01100100
|
1453
|
-
|
1472
|
+
_b8b110_0100
|
1454
1473
|
_b01100100
|
1455
|
-
_01100100
|
1456
1474
|
_u8d100
|
1457
1475
|
_s8d100
|
1458
1476
|
_uh64
|
1459
1477
|
_s8o144
|
1460
1478
|
```
|
1461
1479
|
|
1480
|
+
Finally, it is possible to omit either the type specifier, the default being unsigned bit, or the base specifier, the default being binary. For example, all the following immediate values represent an 8-bit unsigned `hundred`:
|
1481
|
+
|
1482
|
+
```ruby
|
1483
|
+
_b01100100
|
1484
|
+
_h64
|
1485
|
+
_o144
|
1486
|
+
```
|
1487
|
+
|
1462
1488
|
__Notes__:
|
1463
1489
|
|
1490
|
+
- `_01100100` used to be considered as equivalent to `_b01100100`, however due to compatibility troubles with recent version of Ruby it is considered deprecated.
|
1491
|
+
|
1464
1492
|
- 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 adjusting may change the value of the immediate, for example, the following code will set `sig` to 4 instead of 100:
|
1465
1493
|
|
1466
1494
|
```ruby
|
@@ -1470,7 +1498,6 @@ __Notes__:
|
|
1470
1498
|
|
1471
1499
|
|
1472
1500
|
### References
|
1473
|
-
<a name="references"></a>
|
1474
1501
|
|
1475
1502
|
References are expressions used to designate signals or a part of signals.
|
1476
1503
|
|
@@ -1512,7 +1539,6 @@ end
|
|
1512
1539
|
```
|
1513
1540
|
|
1514
1541
|
### Expression operators
|
1515
|
-
<a name="operators"></a>
|
1516
1542
|
|
1517
1543
|
The following table gives a summary of the operators available in HDLRuby.
|
1518
1544
|
More details are given for each group of operators in the subsequent sections.
|
@@ -1589,10 +1615,9 @@ __Notes__:
|
|
1589
1615
|
|
1590
1616
|
- The operator precedence is the one of Ruby.
|
1591
1617
|
|
1592
|
-
- Ruby does not allow to override the `&&`, the `||` and the `?:` operators so that 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) for a justification about this issue.
|
1618
|
+
- Ruby does not allow to override the `&&`, the `||` and the `?:` operators so that 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 about this issue.
|
1593
1619
|
|
1594
1620
|
#### Assignment operators
|
1595
|
-
<a name="assignment"></a>
|
1596
1621
|
|
1597
1622
|
The assignment operators can be used with any type. They are the connection and the transmission operators both being represented by `<=`.
|
1598
1623
|
|
@@ -1620,7 +1645,6 @@ __Notes__:
|
|
1620
1645
|
|
1621
1646
|
|
1622
1647
|
#### Logic and shift operators
|
1623
|
-
<a name="logic"></a>
|
1624
1648
|
|
1625
1649
|
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.
|
1626
1650
|
|
@@ -1646,11 +1670,10 @@ sig.rl(3)
|
|
1646
1670
|
```
|
1647
1671
|
|
1648
1672
|
It is possible to perform other kinds of shifts or rotations using the selection and the concatenation operators. Please refer to section [Concatenation and
|
1649
|
-
selection operators](#
|
1673
|
+
selection operators](#concatenation-and-selection-operators) for more details about these operators.
|
1650
1674
|
|
1651
1675
|
|
1652
1676
|
#### Conversion operators
|
1653
|
-
<a name="conversion"></a>
|
1654
1677
|
|
1655
1678
|
The conversion operators are used to change the type of an expression.
|
1656
1679
|
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,7 +1685,7 @@ The type puns include `to_bit`, `to_unsigned` and `to_signed` that convert expre
|
|
1662
1685
|
sig.to_bit <= _b01010011
|
1663
1686
|
```
|
1664
1687
|
|
1665
|
-
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
|
1688
|
+
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).
|
1666
1689
|
These operators comprise the bit width conversions: `ljust`, `rjust`, `zext` and `sext`.
|
1667
1690
|
|
1668
1691
|
More precisely, the bit width conversions operate as follows:
|
@@ -1696,7 +1719,6 @@ More precisely, the bit width conversions operate as follows:
|
|
1696
1719
|
|
1697
1720
|
|
1698
1721
|
#### Concatenation and selection operators
|
1699
|
-
<a name="concat"></a>
|
1700
1722
|
|
1701
1723
|
Concatenation and selection are done using the `[]` operator as follows:
|
1702
1724
|
|
@@ -1907,7 +1929,6 @@ Ruby functions can be compared to the macros of the C languages: they are more f
|
|
1907
1929
|
|
1908
1930
|
|
1909
1931
|
## Time
|
1910
|
-
<a name="time"></a>
|
1911
1932
|
|
1912
1933
|
### Time values
|
1913
1934
|
<a name="time_val"></a>
|
@@ -1982,7 +2003,6 @@ sequential blocks. The execution semantic is the following:
|
|
1982
2003
|
|
1983
2004
|
|
1984
2005
|
## High-level programming features
|
1985
|
-
<a name="highfeat"></a>
|
1986
2006
|
|
1987
2007
|
### Using Ruby in HDLRuby
|
1988
2008
|
|
@@ -2309,7 +2329,7 @@ Where:
|
|
2309
2329
|
* `type` is the type from which the operation is overloaded.
|
2310
2330
|
* `op` is the operator that is overloaded (e.g., `+`)
|
2311
2331
|
* `args` are the arguments of the operation.
|
2312
|
-
* `operation description` is an HDLRuby
|
2332
|
+
* `operation description` is an HDLRuby expression of the new operation.
|
2313
2333
|
|
2314
2334
|
For example, for `fix32` a 32-bit (decimal point at 16-bit) fixed-point type defined as follows:
|
2315
2335
|
|
@@ -2520,7 +2540,6 @@ When describing a system, it is possible to disconnect or completely undefine a
|
|
2520
2540
|
|
2521
2541
|
|
2522
2542
|
## Extending HDLRuby
|
2523
|
-
<a name="extend"></a>
|
2524
2543
|
|
2525
2544
|
Like any Ruby classes, 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.
|
2526
2545
|
|
@@ -2648,8 +2667,7 @@ This way, calling directly `to_low` will automatically use `my_generation`.
|
|
2648
2667
|
|
2649
2668
|
|
2650
2669
|
|
2651
|
-
# Standard
|
2652
|
-
<a name="library"></a>
|
2670
|
+
# Standard libraries
|
2653
2671
|
|
2654
2672
|
The standard libraries are included in the module `Std`.
|
2655
2673
|
They can be loaded as follows, where `<library name>` is the name of the
|
@@ -2665,9 +2683,23 @@ After the libraries are loaded, the module `Std` must be included as follows:
|
|
2665
2683
|
include HDLRuby::High::Std
|
2666
2684
|
```
|
2667
2685
|
|
2686
|
+
> However, `hdrcc` does load the stable components of the standard library by default, so that you do not need to require nor include anything more to use them. In the current version, the stable components are the followings:
|
2687
|
+
|
2688
|
+
- `std/clocks.rb`
|
2689
|
+
|
2690
|
+
- `std/fixpoint.rb`
|
2691
|
+
|
2692
|
+
- `std/decoder.rb`
|
2693
|
+
|
2694
|
+
- `std/fsm.rb`
|
2668
2695
|
|
2696
|
+
- `std/sequencer.rb`
|
2669
2697
|
|
2670
|
-
|
2698
|
+
- `std/sequencer_sync.rb`
|
2699
|
+
|
2700
|
+
|
2701
|
+
|
2702
|
+
## Clocks: `std/clocks.rb`
|
2671
2703
|
<a name="clocks"></a>
|
2672
2704
|
|
2673
2705
|
The `clocks` library provides utilities for easier handling of clock synchronizations.
|
@@ -2689,7 +2721,7 @@ end
|
|
2689
2721
|
|
2690
2722
|
__Note__: this library does generate all the RTL code for the circuit handling the division of the frequency.
|
2691
2723
|
|
2692
|
-
## Counters
|
2724
|
+
## Counters: `std/counters.rb`
|
2693
2725
|
<a name="counters"></a>
|
2694
2726
|
|
2695
2727
|
This library provides two new constructs for implementing synthesizable wait statements.
|
@@ -2706,12 +2738,12 @@ Where:
|
|
2706
2738
|
* `<clock>` is the clock to use, this argument can be omitted.
|
2707
2739
|
* `<reset>` is the signal used to reset the counter used for waiting, this argument can be omitted.
|
2708
2740
|
|
2709
|
-
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
|
2741
|
+
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.
|
2710
2742
|
|
2711
2743
|
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.
|
2712
2744
|
|
2713
2745
|
|
2714
|
-
## Decoder
|
2746
|
+
## Decoder: `std/decoder.rb`
|
2715
2747
|
<a name="decoder"></a>
|
2716
2748
|
|
2717
2749
|
This library provides a new set of control statements for easily describing an instruction decoder.
|
@@ -2728,7 +2760,7 @@ Where `signal` is the signal to decode and `block` is a procedure block (i.e., R
|
|
2728
2760
|
entry(<pattern>) <block>
|
2729
2761
|
```
|
2730
2762
|
|
2731
|
-
Where `pattern` is a string describing the pattern to match
|
2763
|
+
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 are used as a signal multi-bit signal in the block.
|
2732
2764
|
|
2733
2765
|
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`:
|
2734
2766
|
|
@@ -2741,12 +2773,12 @@ end
|
|
2741
2773
|
|
2742
2774
|
It can be noticed for field `z` in the example above that the bits are not required to be contiguous.
|
2743
2775
|
|
2744
|
-
## FSM
|
2776
|
+
## FSM: `std/fsm.rb`
|
2745
2777
|
<a name="fsm"></a>
|
2746
2778
|
|
2747
2779
|
This library provides a new set of control statements for easily describing a finite state machine (FSM).
|
2748
2780
|
|
2749
|
-
A finite state machine can be declared anywhere provided it is outside a behavior using the `fsm` keyword as follows:
|
2781
|
+
A finite state machine can be declared anywhere in a system provided it is outside a behavior using the `fsm` keyword as follows:
|
2750
2782
|
|
2751
2783
|
```ruby
|
2752
2784
|
fsm(<event>,<reset>,<mode>) <block>
|
@@ -2789,7 +2821,7 @@ goto(cond,:st_a,:st_b,:st_c)
|
|
2789
2821
|
|
2790
2822
|
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.
|
2791
2823
|
|
2792
|
-
For example, the following code describes
|
2824
|
+
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`):
|
2793
2825
|
|
2794
2826
|
```ruby
|
2795
2827
|
fsm(clk.posedge,rst,:sync) do
|
@@ -2813,8 +2845,481 @@ fsm(clk.posedge,rst,:sync) do
|
|
2813
2845
|
end
|
2814
2846
|
```
|
2815
2847
|
|
2848
|
+
__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:
|
2849
|
+
|
2850
|
+
```ruby
|
2851
|
+
state(:st_0) do
|
2852
|
+
goto(:st_a)
|
2853
|
+
end
|
2854
|
+
state(:st_1) do
|
2855
|
+
hif(cond) { goto(:st_a) }
|
2856
|
+
end
|
2857
|
+
```
|
2858
|
+
|
2859
|
+
That is to say, for a conditional `goto` for `st_1` the code should have been written as follows:
|
2860
|
+
|
2861
|
+
```ruby
|
2862
|
+
state(:st_1) do
|
2863
|
+
goto(cond,:st_a)
|
2864
|
+
end
|
2865
|
+
```
|
2866
|
+
|
2867
|
+
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 is `static` mode where the `next_state` statement that 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`:
|
2868
|
+
|
2869
|
+
```ruby
|
2870
|
+
fsm(clk.posedge,rst,:static)
|
2871
|
+
state(:st_0) do
|
2872
|
+
next_state(:st_1)
|
2873
|
+
state(:st_1) do
|
2874
|
+
hif(cond) { next_state(:st_1) }
|
2875
|
+
helse { next_state(:st_0) }
|
2876
|
+
end
|
2877
|
+
end
|
2878
|
+
```
|
2879
|
+
|
2880
|
+
## Sequencer (software-like hardware coding):: `std/sequencer.rb`
|
2881
|
+
<a name="sequencer"></a>
|
2882
|
+
|
2883
|
+
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.
|
2884
|
+
|
2885
|
+
A sequencer can be declared anywhere in a system provided it is outside a behavior using the `sequencer` keyword as follows:
|
2886
|
+
|
2887
|
+
```ruby
|
2888
|
+
sequencer(<clock>,<start>) <block>
|
2889
|
+
```
|
2890
|
+
|
2891
|
+
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`).
|
2892
|
+
|
2893
|
+
A sequence is a specific case of a `seq` block that includes the following software-like additional constructs:
|
2894
|
+
|
2895
|
+
- `step`: wait until the next event (given argument `event` of the sequencer).
|
2896
|
+
|
2897
|
+
- `sif(<condition>) <block>`: executes `block` if `condition` is met.
|
2898
|
+
|
2899
|
+
- `selsif(<condition>) <block>`: executes `block` if previous `sif` or `selsif` condition is not met and if 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 after the address is sets, 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.
|
2816
3052
|
|
2817
|
-
|
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 processes, 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 where authorized. In standard RTL design, this limitation is overcome by implementing three-state buses, multiplexers and arbiters. However, HDLRuby sequencers supports another kind of signals 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 declare 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 signals 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 acknowledge writing from the first sequencer that access 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 a 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 follow 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 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 drives. For example, in a CPU, it is an ALU that is shared as a whole rather than each of its input 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 returns, 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 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 guaranties 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 order of sequencer declaration. I.e., if several sequencer are requiring at the same time, then the one declared the earliest in the code gains write access. For example, with the code given about, the first sequencer have 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 simple 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 current sequencer, that means the access is granted, otherwise its 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
|
+
```
|
3245
|
+
|
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 assign 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 executing each time an 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 corresponding to sequencer 0, bit 1 to sequencer 1 and so one.
|
3276
|
+
|
3277
|
+
|
3278
|
+
#### Monitors
|
3279
|
+
|
3280
|
+
Arbiters a especially useful when we can ensure that the sequencers accessing the same resource do not overlap or when the 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 are 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 blocks the execution of the sequencers that require a write access until the access is granted. If we take the example of code with two sequencers given as illustration of arbiter usage, replacing the arbiter by 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 locks processes, it automatically insert a step, hence to avoid confusion, aquiring access to a monitor is done by using method `lock` instead of assigning 1, and releasing is done by using method `unlock` instead of assigning 0. Hence, when using a monitor, the previous arbiter-based code should be rewriten 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
|
+
## Fixed-point (fixpoint): `std/fixpoint.rb`
|
2818
3323
|
<a name="fixpoint"></a>
|
2819
3324
|
|
2820
3325
|
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:
|
@@ -2839,14 +3344,14 @@ In addition to the fixed point data type, a method is added to the literal objec
|
|
2839
3344
|
<litteral>.to_fix(<number of bits after the decimal point>)
|
2840
3345
|
```
|
2841
3346
|
|
2842
|
-
For example, the following code converts a floating-point value to a fixed
|
3347
|
+
For example, the following code converts a floating-point value to a fixed-point value with 16 bits after the decimal point:
|
2843
3348
|
|
2844
3349
|
```
|
2845
3350
|
3.178.to_fix(16)
|
2846
3351
|
```
|
2847
3352
|
|
2848
3353
|
|
2849
|
-
## Channel
|
3354
|
+
## Channel: `std/channel.rb`
|
2850
3355
|
<a name="channel"></a>
|
2851
3356
|
|
2852
3357
|
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.
|
@@ -2886,9 +3391,9 @@ The access points to a channel can also be handled individually by declaring por
|
|
2886
3391
|
* `output <name>`: declares a port for writing to the channel and associates them to `name` if any
|
2887
3392
|
* `inout <name>`: declares a port for reading and writing to the channel and associates them to `name` if any
|
2888
3393
|
|
2889
|
-
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:
|
3394
|
+
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:
|
2890
3395
|
|
2891
|
-
* `wrap(<args>) <code>`: creates a new port whose read or write procedure has the elements of `<args>` and the ones produced by `<code>`
|
3396
|
+
* `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.
|
2892
3397
|
|
2893
3398
|
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:
|
2894
3399
|
|
@@ -2902,7 +3407,7 @@ Some channels may include several branches, they are accessed by name using the
|
|
2902
3407
|
|
2903
3408
|
* `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.
|
2904
3409
|
|
2905
|
-
A branch is a full-
|
3410
|
+
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`:
|
2906
3411
|
|
2907
3412
|
```ruby
|
2908
3413
|
br = ch.branch(0)
|
@@ -3024,17 +3529,18 @@ end
|
|
3024
3529
|
|
3025
3530
|
__Note__:
|
3026
3531
|
|
3027
|
-
* 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 [
|
3532
|
+
* 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.
|
3028
3533
|
|
3534
|
+
<!---
|
3029
3535
|
|
3030
3536
|
## Pipeline
|
3031
3537
|
<a name="pipeline"></a>
|
3032
3538
|
|
3033
3539
|
This library provides a construct for an easy description of pipeline architectures.
|
3034
3540
|
|
3541
|
+
-->
|
3035
3542
|
|
3036
3543
|
# Sample HDLRuby descriptions
|
3037
|
-
<a name="sample"></a>
|
3038
3544
|
|
3039
3545
|
Several samples HDLRuby descriptions are available in the following directory:
|
3040
3546
|
|
@@ -3042,15 +3548,22 @@ path/to/HDLRuby/lib/HDLRuby/hdr\_samples
|
|
3042
3548
|
|
3043
3549
|
For the gem install, the path to HDLRuby can be found using the following:
|
3044
3550
|
|
3045
|
-
```
|
3551
|
+
```bash
|
3046
3552
|
gem which HDLRuby
|
3047
3553
|
```
|
3048
3554
|
|
3555
|
+
But you can also import the samples to your local directory with the following command (recommended):
|
3556
|
+
|
3557
|
+
```bash
|
3558
|
+
hdrcc --get-samples
|
3559
|
+
```
|
3560
|
+
|
3049
3561
|
The naming convention of the samples is the following:
|
3050
3562
|
|
3051
3563
|
* `<name>.rb`: default type of sample.
|
3052
3564
|
* `<name>_gen.rb`: generic parameters are required for processing the sample.
|
3053
3565
|
* `<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.
|
3566
|
+
* `with_<name>.rb`: sample illustrating a single aspect of HDLRuby or one of its library, usually includes a benchmark.
|
3054
3567
|
|
3055
3568
|
|
3056
3569
|
# Contributing
|
@@ -3060,10 +3573,6 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/civol/
|
|
3060
3573
|
|
3061
3574
|
# To do
|
3062
3575
|
|
3063
|
-
* Test the compatibility of the HDLRuby framework with the Microsoft Windows environments.
|
3064
|
-
* Add the generation of VHDL and Verilog code for the time behaviors.
|
3065
|
-
* Provide targets for the `Reconf` library.
|
3066
|
-
* Add a standard wave output for the simulator.
|
3067
3576
|
* Find and fix the (maybe) terrifying number of bugs.
|
3068
3577
|
* Add a GUI (any volunteer to do it?).
|
3069
3578
|
|