HDLRuby 3.0.0 → 3.2.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/HDLRuby.gemspec +1 -0
- data/README.md +149 -79
- data/ext/hruby_sim/hruby_rcsim_build.c +2 -0
- data/ext/hruby_sim/hruby_sim_calc.c +33 -6
- data/ext/hruby_sim/hruby_sim_tree_calc.c +111 -22
- data/lib/HDLRuby/hdr_samples/comparison_bench.rb +2 -2
- data/lib/HDLRuby/hdr_samples/constant_in_function.rb +2 -1
- data/lib/HDLRuby/hdr_samples/counter_bench.rb +1 -1
- data/lib/HDLRuby/hdr_samples/counter_dff_bench.rb +8 -7
- data/lib/HDLRuby/hdr_samples/dff_properties.rb +2 -0
- data/lib/HDLRuby/hdr_samples/enum_as_param.rb +52 -0
- data/lib/HDLRuby/hdr_samples/linear_test.rb +2 -0
- data/lib/HDLRuby/hdr_samples/logic_bench.rb +6 -0
- data/lib/HDLRuby/hdr_samples/mei8.rb +6 -6
- data/lib/HDLRuby/hdr_samples/mei8_bench.rb +6 -6
- data/lib/HDLRuby/hdr_samples/memory_test.rb +2 -0
- data/lib/HDLRuby/hdr_samples/named_sub.rb +9 -5
- data/lib/HDLRuby/hdr_samples/ram.rb +7 -6
- data/lib/HDLRuby/hdr_samples/ruby_fir_hw.rb +2 -0
- data/lib/HDLRuby/hdr_samples/struct.rb +15 -3
- data/lib/HDLRuby/hdr_samples/with_bram.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_bram_frame_stack.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_bram_stack.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_channel.rb +2 -0
- data/lib/HDLRuby/hdr_samples/with_channel_other.rb +2 -0
- data/lib/HDLRuby/hdr_samples/with_class.rb +3 -1
- data/lib/HDLRuby/hdr_samples/with_connector.rb +2 -0
- data/lib/HDLRuby/hdr_samples/with_connector_memory.rb +2 -0
- data/lib/HDLRuby/hdr_samples/with_fixpoint.rb +6 -0
- data/lib/HDLRuby/hdr_samples/with_fixpoint_adv.rb +73 -0
- data/lib/HDLRuby/hdr_samples/with_leftright.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_ref_expr.rb +30 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer.rb +49 -37
- data/lib/HDLRuby/hdr_samples/with_sequencer_channel.rb +58 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb +113 -69
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb +28 -14
- data/lib/HDLRuby/hdr_samples/with_sequencer_func.rb +63 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_sync.rb +2 -1
- data/lib/HDLRuby/hdrcc.rb +13 -1
- data/lib/HDLRuby/hruby_high.rb +105 -31
- data/lib/HDLRuby/hruby_low.rb +127 -3
- data/lib/HDLRuby/hruby_low2programs.rb +47 -0
- data/lib/HDLRuby/hruby_low_resolve.rb +3 -2
- data/lib/HDLRuby/hruby_low_without_namespace.rb +133 -5
- data/lib/HDLRuby/hruby_low_without_subsignals.rb +51 -12
- data/lib/HDLRuby/hruby_rcsim.rb +24 -1
- data/lib/HDLRuby/hruby_serializer.rb +2 -1
- data/lib/HDLRuby/hruby_types.rb +5 -5
- data/lib/HDLRuby/hruby_values.rb +7 -7
- data/lib/HDLRuby/hruby_verilog.rb +193 -35
- data/lib/HDLRuby/hruby_verilog_name.rb +35 -42
- data/lib/HDLRuby/std/fixpoint.rb +2 -2
- data/lib/HDLRuby/std/fsm.rb +10 -1
- data/lib/HDLRuby/std/function_generator.rb +1 -1
- data/lib/HDLRuby/std/linear.rb +7 -7
- data/lib/HDLRuby/std/sequencer.rb +538 -60
- data/lib/HDLRuby/std/sequencer_channel.rb +90 -0
- data/lib/HDLRuby/std/sequencer_func.rb +546 -0
- data/lib/HDLRuby/std/std.rb +2 -0
- data/lib/HDLRuby/version.rb +1 -1
- data/tuto/tutorial_sw.md +267 -61
- metadata +25 -4
- data/lib/HDLRuby/hdr_samples/with_register_stack.rb +0 -150
data/README.md
CHANGED
|
@@ -11,6 +11,28 @@ If you are new to HDLRuby, it is recommended that you consult first the followin
|
|
|
11
11
|
|
|
12
12
|
__What's new__
|
|
13
13
|
|
|
14
|
+
For HDLRuby version 3.2.0:
|
|
15
|
+
|
|
16
|
+
* Added component for declaring BRAM and BRAM-based stacks.
|
|
17
|
+
The goal is to efficiently allocate memories inside FPGAs.
|
|
18
|
+
|
|
19
|
+
* Inner code overhaul for preparing version 4.0.0
|
|
20
|
+
|
|
21
|
+
* Multiple bug fixes
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
For HDLRuby version 3.1.0:
|
|
25
|
+
|
|
26
|
+
* [Functions for sequencers](#sequencer-specific-function-std-sequencer_func-rb) supporting recursion;
|
|
27
|
+
|
|
28
|
+
* The `function` keyword was replaced with `hdef` for better consistency with the new functions for sequencers;
|
|
29
|
+
|
|
30
|
+
* The `steps` command was added for waiting several steps in a sequencer;
|
|
31
|
+
|
|
32
|
+
* Verilog HDL code generation was improved to preserve as much as possible the original names of the signals;
|
|
33
|
+
|
|
34
|
+
* Several bug fixes for the sequencers.
|
|
35
|
+
|
|
14
36
|
For HDLRuby version 3.0.0:
|
|
15
37
|
|
|
16
38
|
* This section;
|
|
@@ -39,7 +61,7 @@ git clone HDLRuby
|
|
|
39
61
|
__Warning__:
|
|
40
62
|
|
|
41
63
|
- This is still preliminary work which may change before we release a stable version.
|
|
42
|
-
- It is highly recommended to have both basic
|
|
64
|
+
- It is highly recommended to have both basic knowledges of the Ruby language and hardware description languages before using HDLRuby.
|
|
43
65
|
|
|
44
66
|
|
|
45
67
|
# Compiling HDLRuby descriptions
|
|
@@ -72,7 +94,7 @@ Where:
|
|
|
72
94
|
| `-S, --sim` | Perform the simulation with the default engine |
|
|
73
95
|
| `--csim` | Perform the simulation with the standalone engine |
|
|
74
96
|
| `--rsim` | Perform the simulation with the Ruby engine |
|
|
75
|
-
| `--rcsim` | Perform the simulation with the
|
|
97
|
+
| `--rcsim` | Perform the simulation with the Hybrid engine |
|
|
76
98
|
| `--vcd` | Make the simulator generate a VCD file |
|
|
77
99
|
| `-d, --directory` | Specify the base directory for loading the HDLRuby files |
|
|
78
100
|
| `-D, --debug` | Set the HDLRuby debug mode |
|
|
@@ -92,7 +114,7 @@ __Notes__:
|
|
|
92
114
|
hdrcc --get-samples
|
|
93
115
|
```
|
|
94
116
|
|
|
95
|
-
Then in your current directory (folder) the `hdr_samples` subdirectory will appear that contains several HDLRuby example files.
|
|
117
|
+
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).
|
|
96
118
|
|
|
97
119
|
|
|
98
120
|
__Examples__:
|
|
@@ -103,7 +125,7 @@ __Examples__:
|
|
|
103
125
|
hdrcc --yaml --top adder adder.rb adder
|
|
104
126
|
```
|
|
105
127
|
|
|
106
|
-
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into directory `adder`:
|
|
128
|
+
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into the directory `adder`:
|
|
107
129
|
|
|
108
130
|
```bash
|
|
109
131
|
hdrcc -v adder.rb adder
|
|
@@ -121,7 +143,7 @@ hdrcc -V -t adder --param 16 adder_gen.rb adder
|
|
|
121
143
|
hdrcc -y -t multer -p 16,16,32 multer_gen.rb multer
|
|
122
144
|
```
|
|
123
145
|
|
|
124
|
-
* Simulate the circuit described in file `counter_bench.rb` using the default simulation engine and putting the simulator's files in directory `counter`:
|
|
146
|
+
* Simulate the circuit described in file `counter_bench.rb` using the default simulation engine and putting the simulator's files in the directory `counter`:
|
|
125
147
|
|
|
126
148
|
```bash
|
|
127
149
|
hdrcc -S counter_bench.rb counter
|
|
@@ -257,7 +279,7 @@ system :counter2 do
|
|
|
257
279
|
end
|
|
258
280
|
```
|
|
259
281
|
|
|
260
|
-
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected
|
|
282
|
+
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.
|
|
261
283
|
|
|
262
284
|
While a circuit can be generated from the code given above, a benchmark must
|
|
263
285
|
be provided to test it. Such a benchmark is described by constructs called
|
|
@@ -323,8 +345,7 @@ instance :dff_single do
|
|
|
323
345
|
end
|
|
324
346
|
```
|
|
325
347
|
|
|
326
|
-
In the example above, `dff_single` is an instance describing, again, a
|
|
327
|
-
D-FF, but whose system is anonymous.
|
|
348
|
+
In the example above, `dff_single` is an instance describing, again, a D-FF, but whose system is anonymous.
|
|
328
349
|
|
|
329
350
|
Furthermore, generic parameters can be used for anything in HDLRuby.
|
|
330
351
|
For instance, the following code describes an 8-bit register without any parameterization:
|
|
@@ -482,7 +503,7 @@ sumprod(signed[32], [3,78,43,246, 3,67,1,8, 47,82,99,13, 5,77,2,4]).(:my_circuit
|
|
|
482
503
|
|
|
483
504
|
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.
|
|
484
505
|
|
|
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:
|
|
506
|
+
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:
|
|
486
507
|
|
|
487
508
|
```ruby
|
|
488
509
|
system :sumprod_func do |typ,coefs|
|
|
@@ -499,7 +520,7 @@ end
|
|
|
499
520
|
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:
|
|
500
521
|
|
|
501
522
|
```ruby
|
|
502
|
-
|
|
523
|
+
hdef :add do |x,y|
|
|
503
524
|
inner :res
|
|
504
525
|
seq do
|
|
505
526
|
res <= x + y
|
|
@@ -517,7 +538,7 @@ With HDLRuby functions, the result of the last statement in the return value, in
|
|
|
517
538
|
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:
|
|
518
539
|
|
|
519
540
|
```ruby
|
|
520
|
-
|
|
541
|
+
hdef :add do |max, x, y|
|
|
521
542
|
inner :res
|
|
522
543
|
seq do
|
|
523
544
|
res <= x + y
|
|
@@ -526,7 +547,7 @@ function :add do |max, x, y|
|
|
|
526
547
|
end
|
|
527
548
|
```
|
|
528
549
|
|
|
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
|
|
550
|
+
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.
|
|
530
551
|
|
|
531
552
|
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:
|
|
532
553
|
|
|
@@ -608,12 +629,7 @@ sumprod(sat(16,1000),
|
|
|
608
629
|
```
|
|
609
630
|
|
|
610
631
|
|
|
611
|
-
Lastly note, HDLRuby is also a language with supports reflection for
|
|
612
|
-
all its constructs. For example, the system of an instance can be accessed
|
|
613
|
-
using the `systemT` method, and this latter can be used to create
|
|
614
|
-
other instances. For example, previously, `dff_single` was declared with
|
|
615
|
-
an anonymous system (i.e., it cannot be accessed by name). This system
|
|
616
|
-
can however be used as follows to generate another instance:
|
|
632
|
+
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:
|
|
617
633
|
|
|
618
634
|
```ruby
|
|
619
635
|
dff_single.systemT.instantiate(:dff_not_single)
|
|
@@ -622,12 +638,8 @@ dff_single.systemT.instantiate(:dff_not_single)
|
|
|
622
638
|
In the above example, `dff_not_single` is declared to be an instance
|
|
623
639
|
of the same system as `dff_single`.
|
|
624
640
|
|
|
625
|
-
This reflection capability can also be used for instance, for accessing the
|
|
626
|
-
|
|
627
|
-
(`cur_block`), the current process (`cur_behavior`) and so on.
|
|
628
|
-
The standard library of HDLRuby includes several hardware constructs
|
|
629
|
-
like finite state machine descriptors and is mainly based on using these
|
|
630
|
-
reflection features.
|
|
641
|
+
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.
|
|
642
|
+
The standard library of HDLRuby includes several hardware constructs like finite state machine descriptors and is mainly based on using these reflection features.
|
|
631
643
|
|
|
632
644
|
|
|
633
645
|
|
|
@@ -672,8 +684,7 @@ system(:box) {}
|
|
|
672
684
|
|
|
673
685
|
__Notes__:
|
|
674
686
|
|
|
675
|
-
- Since this is Ruby code, the body can also be delimited by the `do` and `end`
|
|
676
|
-
Ruby keywords (in which case the parentheses can be omitted) are as follows:
|
|
687
|
+
- 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:
|
|
677
688
|
|
|
678
689
|
```ruby
|
|
679
690
|
system :box does
|
|
@@ -703,8 +714,7 @@ Now, since `bit` is the default data type in HDLRuby, it can be omitted as follo
|
|
|
703
714
|
input :clk
|
|
704
715
|
```
|
|
705
716
|
|
|
706
|
-
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
|
|
707
|
-
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.
|
|
717
|
+
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.
|
|
708
718
|
|
|
709
719
|
```ruby
|
|
710
720
|
system :mem8_16 do
|
|
@@ -799,8 +809,7 @@ It is also possible to connect multiple signals of an instance using the call op
|
|
|
799
809
|
<instance name>.(<signal name0>: <target0>, ...)
|
|
800
810
|
```
|
|
801
811
|
|
|
802
|
-
For example, the following code connects signals `clk` and `rst` of instance
|
|
803
|
-
`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.
|
|
812
|
+
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.
|
|
804
813
|
|
|
805
814
|
```ruby
|
|
806
815
|
mem8_16I.(clk: clk, rst: rst)
|
|
@@ -1321,7 +1330,7 @@ end
|
|
|
1321
1330
|
#### About loops
|
|
1322
1331
|
|
|
1323
1332
|
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
|
|
1324
|
-
that the current synthesis tools do not really synthesize hardware from such loops but instead preprocess them (e.g., unroll them) to synthesizable
|
|
1333
|
+
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).
|
|
1325
1334
|
|
|
1326
1335
|
__Notes__:
|
|
1327
1336
|
|
|
@@ -1487,9 +1496,9 @@ _o144
|
|
|
1487
1496
|
|
|
1488
1497
|
__Notes__:
|
|
1489
1498
|
|
|
1490
|
-
- `_01100100` used to be considered
|
|
1499
|
+
- `_01100100` used to be considered equivalent to `_b01100100`, however, due to compatibility troubles with a recent version of Ruby it is considered deprecated.
|
|
1491
1500
|
|
|
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
|
|
1501
|
+
- 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:
|
|
1493
1502
|
|
|
1494
1503
|
```ruby
|
|
1495
1504
|
[3..0].inner :sig
|
|
@@ -1615,7 +1624,7 @@ __Notes__:
|
|
|
1615
1624
|
|
|
1616
1625
|
- The operator precedence is the one of Ruby.
|
|
1617
1626
|
|
|
1618
|
-
- Ruby does not allow to override the `&&`, the
|
|
1627
|
+
- 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.
|
|
1619
1628
|
|
|
1620
1629
|
#### Assignment operators
|
|
1621
1630
|
|
|
@@ -1628,7 +1637,7 @@ __Note__:
|
|
|
1628
1637
|
#### Arithmetic operators
|
|
1629
1638
|
<a name="arithmetic"></a>
|
|
1630
1639
|
|
|
1631
|
-
The arithmetic operators can only be used on vectors of `bit`, `unsigned` or `signed` values, `integer
|
|
1640
|
+
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.
|
|
1632
1641
|
|
|
1633
1642
|
#### Comparison operators
|
|
1634
1643
|
<a name="comparison"></a>
|
|
@@ -1669,8 +1678,7 @@ For example, for rotating the left signal `sig` 3 times, the following code can
|
|
|
1669
1678
|
sig.rl(3)
|
|
1670
1679
|
```
|
|
1671
1680
|
|
|
1672
|
-
It is possible to perform other kinds of shifts or rotations using the selection and
|
|
1673
|
-
selection operators](#concatenation-and-selection-operators) for more details about these operators.
|
|
1681
|
+
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.
|
|
1674
1682
|
|
|
1675
1683
|
|
|
1676
1684
|
#### Conversion operators
|
|
@@ -1678,7 +1686,7 @@ selection operators](#concatenation-and-selection-operators) for more details ab
|
|
|
1678
1686
|
The conversion operators are used to change the type of an expression.
|
|
1679
1687
|
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.
|
|
1680
1688
|
|
|
1681
|
-
The type puns include `to_bit`, `to_unsigned
|
|
1689
|
+
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:
|
|
1682
1690
|
|
|
1683
1691
|
```ruby
|
|
1684
1692
|
[ up: signed[3..0], down: unsigned[3..0] ].inner :sig
|
|
@@ -1733,7 +1741,7 @@ Concatenation and selection are done using the `[]` operator as follows:
|
|
|
1733
1741
|
sig2 <= [sig0, sig1]
|
|
1734
1742
|
```
|
|
1735
1743
|
|
|
1736
|
-
- when this operator is applied to an expression of `bit`, `unsigned
|
|
1744
|
+
- 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`:
|
|
1737
1745
|
|
|
1738
1746
|
```ruby
|
|
1739
1747
|
[7..0].inner :sig0
|
|
@@ -1825,7 +1833,7 @@ __Multiplicative operators:__
|
|
|
1825
1833
|
Like Verilog HDL, HDLRuby provides function constructs for reusing code. HDLRuby functions are declared as follows:
|
|
1826
1834
|
|
|
1827
1835
|
```ruby
|
|
1828
|
-
|
|
1836
|
+
hdef :<function name> do |<arguments>|
|
|
1829
1837
|
<code>
|
|
1830
1838
|
end
|
|
1831
1839
|
```
|
|
@@ -1903,7 +1911,7 @@ system :sys do
|
|
|
1903
1911
|
end
|
|
1904
1912
|
```
|
|
1905
1913
|
|
|
1906
|
-
As another example, following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
|
1914
|
+
As another example, the following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
|
1907
1915
|
|
|
1908
1916
|
```ruby
|
|
1909
1917
|
def too_bad
|
|
@@ -1933,7 +1941,7 @@ Ruby functions can be compared to the macros of the C languages: they are more f
|
|
|
1933
1941
|
### Time values
|
|
1934
1942
|
<a name="time_val"></a>
|
|
1935
1943
|
|
|
1936
|
-
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
|
|
1944
|
+
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:
|
|
1937
1945
|
|
|
1938
1946
|
```ruby
|
|
1939
1947
|
1.s
|
|
@@ -1947,7 +1955,7 @@ In HDLRuby, time values can be created using the time operators: `s` for seconds
|
|
|
1947
1955
|
### Time behaviors and time statements
|
|
1948
1956
|
<a name="time_beh"></a>
|
|
1949
1957
|
|
|
1950
|
-
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.
|
|
1958
|
+
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.
|
|
1951
1959
|
|
|
1952
1960
|
```ruby
|
|
1953
1961
|
timed do
|
|
@@ -2057,7 +2065,7 @@ typedef :<type name> do |<list of generic parameters>|
|
|
|
2057
2065
|
end
|
|
2058
2066
|
```
|
|
2059
2067
|
|
|
2060
|
-
For example, the following code describes a bit-vector type with generic number of bits `width`:
|
|
2068
|
+
For example, the following code describes a bit-vector type with a generic number of bits `width`:
|
|
2061
2069
|
|
|
2062
2070
|
```ruby
|
|
2063
2071
|
type(:bitvec) { |width| bit[width] }
|
|
@@ -2087,7 +2095,7 @@ system :subsys, sys(1,2) do
|
|
|
2087
2095
|
end
|
|
2088
2096
|
```
|
|
2089
2097
|
|
|
2090
|
-
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:
|
|
2098
|
+
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:
|
|
2091
2099
|
|
|
2092
2100
|
```ruby
|
|
2093
2101
|
system :subsys_gen do |param|
|
|
@@ -2348,7 +2356,7 @@ end
|
|
|
2348
2356
|
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.
|
|
2349
2357
|
|
|
2350
2358
|
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.
|
|
2351
|
-
For instance, let us consider the following fixed-point type of variable width (and whose decimal point is set at
|
|
2359
|
+
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):
|
|
2352
2360
|
|
|
2353
2361
|
```ruby
|
|
2354
2362
|
typedef(:fixed) do |width|
|
|
@@ -2396,7 +2404,7 @@ Several enumerators are also provided for accessing the internals of the current
|
|
|
2396
2404
|
|
|
2397
2405
|
### Global signals
|
|
2398
2406
|
|
|
2399
|
-
HDLRuby allows
|
|
2407
|
+
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.
|
|
2400
2408
|
|
|
2401
2409
|
To ease the design of standardized libraries, the following global signals are defined by default:
|
|
2402
2410
|
|
|
@@ -2415,9 +2423,9 @@ __Note__:
|
|
|
2415
2423
|
### Defining and executing Ruby methods within HDLRuby constructs
|
|
2416
2424
|
<a name="method"></a>
|
|
2417
2425
|
|
|
2418
|
-
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.
|
|
2426
|
+
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.
|
|
2419
2427
|
|
|
2420
|
-
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`.
|
|
2428
|
+
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`.
|
|
2421
2429
|
|
|
2422
2430
|
```ruby
|
|
2423
2431
|
def some_arrow
|
|
@@ -2486,7 +2494,7 @@ end
|
|
|
2486
2494
|
```
|
|
2487
2495
|
|
|
2488
2496
|
|
|
2489
|
-
While requiring
|
|
2497
|
+
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:
|
|
2490
2498
|
|
|
2491
2499
|
```ruby
|
|
2492
2500
|
def after(cycles,rst = $rst, &code)
|
|
@@ -2541,7 +2549,7 @@ When describing a system, it is possible to disconnect or completely undefine a
|
|
|
2541
2549
|
|
|
2542
2550
|
## Extending HDLRuby
|
|
2543
2551
|
|
|
2544
|
-
Like any Ruby
|
|
2552
|
+
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.
|
|
2545
2553
|
|
|
2546
2554
|
### Extending HDLRuby constructs globally
|
|
2547
2555
|
|
|
@@ -2683,7 +2691,7 @@ After the libraries are loaded, the module `Std` must be included as follows:
|
|
|
2683
2691
|
include HDLRuby::High::Std
|
|
2684
2692
|
```
|
|
2685
2693
|
|
|
2686
|
-
> However, `hdrcc`
|
|
2694
|
+
> 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:
|
|
2687
2695
|
|
|
2688
2696
|
- `std/clocks.rb`
|
|
2689
2697
|
|
|
@@ -2719,7 +2727,7 @@ system :dff_slow do
|
|
|
2719
2727
|
end
|
|
2720
2728
|
```
|
|
2721
2729
|
|
|
2722
|
-
__Note__: this library
|
|
2730
|
+
__Note__: this library generates all the RTL code for the circuit handling the frequency division.
|
|
2723
2731
|
|
|
2724
2732
|
## Counters: `std/counters.rb`
|
|
2725
2733
|
<a name="counters"></a>
|
|
@@ -2754,13 +2762,13 @@ A decoder can be declared anywhere in the code describing a system using the `de
|
|
|
2754
2762
|
decoder(<signal>) <block>
|
|
2755
2763
|
```
|
|
2756
2764
|
|
|
2757
|
-
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
|
|
2765
|
+
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:
|
|
2758
2766
|
|
|
2759
2767
|
```ruby
|
|
2760
2768
|
entry(<pattern>) <block>
|
|
2761
2769
|
```
|
|
2762
2770
|
|
|
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
|
|
2771
|
+
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.
|
|
2764
2772
|
|
|
2765
2773
|
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`:
|
|
2766
2774
|
|
|
@@ -2864,7 +2872,7 @@ That is to say, for a conditional `goto` for `st_1` the code should have been wr
|
|
|
2864
2872
|
end
|
|
2865
2873
|
```
|
|
2866
2874
|
|
|
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
|
|
2875
|
+
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`:
|
|
2868
2876
|
|
|
2869
2877
|
```ruby
|
|
2870
2878
|
fsm(clk.posedge,rst,:static)
|
|
@@ -2894,9 +2902,11 @@ A sequence is a specific case of a `seq` block that includes the following softw
|
|
|
2894
2902
|
|
|
2895
2903
|
- `step`: wait until the next event (given argument `event` of the sequencer).
|
|
2896
2904
|
|
|
2905
|
+
- `steps(<num>)`: perform `num` times `step` (`num` can be any expression).
|
|
2906
|
+
|
|
2897
2907
|
- `sif(<condition>) <block>`: executes `block` if `condition` is met.
|
|
2898
2908
|
|
|
2899
|
-
- `selsif(<condition>) <block>`: executes `block` if previous `sif` or `selsif` condition is not met and if current `condition` is met.
|
|
2909
|
+
- `selsif(<condition>) <block>`: executes `block` if the previous `sif` or `selsif` condition is not met and if the current `condition` is met.
|
|
2900
2910
|
|
|
2901
2911
|
- `selse <block>`: executes `block` if the condition of the previous `sif` statement is not met.
|
|
2902
2912
|
|
|
@@ -3026,7 +3036,7 @@ It is also possible to define a custom enumerator using the following command:
|
|
|
3026
3036
|
<enum> = senumerator(<typ>,<size>) <block>
|
|
3027
3037
|
```
|
|
3028
3038
|
|
|
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:
|
|
3039
|
+
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
3040
|
|
|
3031
3041
|
```ruby
|
|
3032
3042
|
bit[8][-8].inner mem: [ _h01, _h02, _h03, _h04, _h30, _h30, _h30, _h30 ]
|
|
@@ -3042,7 +3052,7 @@ Where `enum` is a Ruby variable referring to the enumerator, `typ` is the data t
|
|
|
3042
3052
|
end
|
|
3043
3053
|
```
|
|
3044
3054
|
|
|
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
|
|
3055
|
+
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
3056
|
|
|
3047
3057
|
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
3058
|
|
|
@@ -3120,12 +3130,12 @@ With this basis, several algorithms have been implemented using enumerators and
|
|
|
3120
3130
|
|
|
3121
3131
|
|
|
3122
3132
|
|
|
3123
|
-
### Shared signals, arbiters and monitors: `std/sequencer_sync.rb`
|
|
3133
|
+
### Shared signals, arbiters, and monitors: `std/sequencer_sync.rb`
|
|
3124
3134
|
<a name="shared"></a>
|
|
3125
3135
|
|
|
3126
3136
|
#### Shared signals
|
|
3127
3137
|
|
|
3128
|
-
Like any other
|
|
3138
|
+
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
3139
|
|
|
3130
3140
|
The shared signals are declared like the other kind of signals from their type. The syntax is the following:
|
|
3131
3141
|
|
|
@@ -3139,14 +3149,14 @@ They can also have an initial (and default) value when declared as follows:
|
|
|
3139
3149
|
<type>.shared <list of names with initialization>
|
|
3140
3150
|
```
|
|
3141
3151
|
|
|
3142
|
-
For example the following code
|
|
3152
|
+
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
3153
|
|
|
3144
3154
|
```ruby
|
|
3145
3155
|
[8].shared :x, :y
|
|
3146
3156
|
signed[8].shared u: 0, v: 0
|
|
3147
3157
|
```
|
|
3148
3158
|
|
|
3149
|
-
A shared
|
|
3159
|
+
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
3160
|
|
|
3151
3161
|
```ruby
|
|
3152
3162
|
input :clk, :start
|
|
@@ -3173,21 +3183,21 @@ But the following code is not valid:
|
|
|
3173
3183
|
par(clk.posedge) { w <= w + 1 }
|
|
3174
3184
|
```
|
|
3175
3185
|
|
|
3176
|
-
By default, a shared signal
|
|
3186
|
+
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
3187
|
|
|
3178
|
-
This default behavior of shared signal avoids race competition
|
|
3188
|
+
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
3189
|
|
|
3180
3190
|
```ruby
|
|
3181
3191
|
<shared signal>.select
|
|
3182
3192
|
```
|
|
3183
3193
|
|
|
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
|
|
3194
|
+
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
3195
|
|
|
3186
3196
|
```ruby
|
|
3187
3197
|
x.select <= 1
|
|
3188
3198
|
```
|
|
3189
3199
|
|
|
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:
|
|
3200
|
+
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
3201
|
|
|
3192
3202
|
```ruby
|
|
3193
3203
|
par(clk.posedge) { x.select <= x.select + 1 }
|
|
@@ -3198,15 +3208,15 @@ __Note__: this select sub signal is a standard RTL signal that has the same prop
|
|
|
3198
3208
|
|
|
3199
3209
|
#### Arbiters
|
|
3200
3210
|
|
|
3201
|
-
Usually, it is not the signals that we want to share, but the resources they
|
|
3211
|
+
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
3212
|
|
|
3203
3213
|
```ruby
|
|
3204
3214
|
arbiter(:<name>).(<list of shared signal>)
|
|
3205
3215
|
```
|
|
3206
3216
|
|
|
3207
|
-
When instantiated, an arbiter will take control of the select sub
|
|
3217
|
+
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
3218
|
|
|
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:
|
|
3219
|
+
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
3220
|
|
|
3211
3221
|
```ruby
|
|
3212
3222
|
input :clk, :start
|
|
@@ -3236,20 +3246,20 @@ end
|
|
|
3236
3246
|
|
|
3237
3247
|
In the example, both sequencers require access to signals `x` and `y` before accessing them and then releasing the access.
|
|
3238
3248
|
|
|
3239
|
-
Requiring access does not
|
|
3240
|
-
The default access granting policy of an arbiter is the priority in order of sequencer declaration. I.e., if several
|
|
3249
|
+
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.
|
|
3250
|
+
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
3251
|
|
|
3242
3252
|
```ruby
|
|
3243
3253
|
hif(ctrl_xy.acquired) { x <= x + 1 }
|
|
3244
3254
|
```
|
|
3245
3255
|
|
|
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
|
|
3256
|
+
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
3257
|
|
|
3248
3258
|
```ruby
|
|
3249
3259
|
ctrl_xy.policy([1,0])
|
|
3250
3260
|
```
|
|
3251
3261
|
|
|
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
|
|
3262
|
+
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
3263
|
|
|
3254
3264
|
```ruby
|
|
3255
3265
|
inner priority_xy: 0
|
|
@@ -3272,26 +3282,26 @@ ctrl_xy.policy do |acq|
|
|
|
3272
3282
|
end
|
|
3273
3283
|
```
|
|
3274
3284
|
|
|
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
|
|
3285
|
+
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
3286
|
|
|
3277
3287
|
|
|
3278
3288
|
#### Monitors
|
|
3279
3289
|
|
|
3280
|
-
Arbiters
|
|
3290
|
+
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
3291
|
|
|
3282
|
-
The monitor component
|
|
3292
|
+
The monitor component is instantiated like the arbiters as follows:
|
|
3283
3293
|
|
|
3284
3294
|
```ruby
|
|
3285
3295
|
monitor(:<name>).(<list of shared signals>)
|
|
3286
3296
|
```
|
|
3287
3297
|
|
|
3288
|
-
Monitors are used exactly the same ways as arbiters (including the write access granting policies)
|
|
3298
|
+
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
3299
|
|
|
3290
3300
|
```ruby
|
|
3291
3301
|
monitor(:ctrl_xy).(x,y)
|
|
3292
3302
|
```
|
|
3293
3303
|
|
|
3294
|
-
Since monitors
|
|
3304
|
+
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
3305
|
|
|
3296
3306
|
```ruby
|
|
3297
3307
|
input :clk, :start
|
|
@@ -3319,6 +3329,66 @@ sequencer(clk.posedge,start) do
|
|
|
3319
3329
|
end
|
|
3320
3330
|
```
|
|
3321
3331
|
|
|
3332
|
+
### Sequencer-specific function: `std/sequencer_func.rb`
|
|
3333
|
+
|
|
3334
|
+
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.
|
|
3335
|
+
|
|
3336
|
+
However, it is possible to define functions that do support the sequencer constructs using `sdef` instead of `hdef` as follows:
|
|
3337
|
+
|
|
3338
|
+
```ruby
|
|
3339
|
+
sdef :<function name> do |<arguments>|
|
|
3340
|
+
<sequencer code>
|
|
3341
|
+
end
|
|
3342
|
+
```
|
|
3343
|
+
|
|
3344
|
+
Such functions can be defined anywhere in a HDLRuby description, but can only be called within a sequencer.
|
|
3345
|
+
|
|
3346
|
+
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:
|
|
3347
|
+
|
|
3348
|
+
```ruby
|
|
3349
|
+
sdef(:fact) do |n|
|
|
3350
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
|
3351
|
+
selse { sreturn(1) }
|
|
3352
|
+
end
|
|
3353
|
+
```
|
|
3354
|
+
|
|
3355
|
+
As seen in the code above, a new construct `sreturn` can be used for returning a value from anywhere inside the function.
|
|
3356
|
+
|
|
3357
|
+
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:
|
|
3358
|
+
|
|
3359
|
+
```ruby
|
|
3360
|
+
sdef(:fact,32) do |n|
|
|
3361
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
|
3362
|
+
selse { sreturn(1) }
|
|
3363
|
+
end
|
|
3364
|
+
```
|
|
3365
|
+
|
|
3366
|
+
__Notes__:
|
|
3367
|
+
|
|
3368
|
+
* A call to such a function and a return take respectively one and two cycles of the sequencer.
|
|
3369
|
+
|
|
3370
|
+
* For now, there is no tail call optimization.
|
|
3371
|
+
|
|
3372
|
+
* 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:
|
|
3373
|
+
|
|
3374
|
+
```ruby
|
|
3375
|
+
sdef(:<name>,<depth>, proc <block>) do
|
|
3376
|
+
<function code>
|
|
3377
|
+
end
|
|
3378
|
+
```
|
|
3379
|
+
|
|
3380
|
+
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:
|
|
3381
|
+
|
|
3382
|
+
```ruby
|
|
3383
|
+
sdef(:fact,32, proc { stack_overflow <= 1 }) do |n|
|
|
3384
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
|
3385
|
+
selse { sreturn(1) }
|
|
3386
|
+
end
|
|
3387
|
+
```
|
|
3388
|
+
|
|
3389
|
+
With the code above, the only restriction is that the signal `stack_overflow` is declared before the function `fact` is called.
|
|
3390
|
+
|
|
3391
|
+
|
|
3322
3392
|
## Fixed-point (fixpoint): `std/fixpoint.rb`
|
|
3323
3393
|
<a name="fixpoint"></a>
|
|
3324
3394
|
|