HDLRuby 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +139 -79
- data/lib/HDLRuby/hdr_samples/constant_in_function.rb +2 -1
- data/lib/HDLRuby/hdr_samples/with_ref_expr.rb +30 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer.rb +32 -37
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb +103 -69
- data/lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb +10 -10
- data/lib/HDLRuby/hdr_samples/with_sequencer_func.rb +63 -0
- data/lib/HDLRuby/hdrcc.rb +1 -1
- data/lib/HDLRuby/hruby_high.rb +23 -5
- data/lib/HDLRuby/hruby_low_without_subsignals.rb +50 -11
- data/lib/HDLRuby/hruby_types.rb +5 -5
- data/lib/HDLRuby/hruby_values.rb +7 -7
- data/lib/HDLRuby/hruby_verilog.rb +101 -17
- data/lib/HDLRuby/hruby_verilog_name.rb +49 -42
- data/lib/HDLRuby/std/fsm.rb +10 -1
- data/lib/HDLRuby/std/sequencer.rb +281 -53
- data/lib/HDLRuby/std/sequencer_func.rb +533 -0
- data/lib/HDLRuby/std/std.rb +1 -0
- data/lib/HDLRuby/version.rb +1 -1
- data/tuto/tutorial_sw.md +267 -61
- metadata +5 -3
- data/lib/HDLRuby/hdr_samples/with_register_stack.rb +0 -150
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f491222adce0600f9d4ba58e748d4a72fd913adb50952fff71e5e67937f6b54
|
4
|
+
data.tar.gz: ffdb34990f8dabcb0e38c3a7189e7f99c07c351cd86de6411f7eaa64314147f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 854a95c8b73d27c9673997cb138ebdceba87546bbd6d585d201110a9b8f0ae0be67f2c197a13a6e864c235fefb4a59ca99a4dc6f7311758becea639aadd3c0e6
|
7
|
+
data.tar.gz: 60d260809614010f31e484c85e907b575518670770e4069da9216ba00e3b0a8502ef173dd260cf773e47d5ebd20d343ef00b815a840efbf7346c86b7b5b8c0cd
|
data/README.md
CHANGED
@@ -11,6 +11,18 @@ 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.1.0:
|
15
|
+
|
16
|
+
* [Functions for sequencers](#sequencer-specific-function-std-sequencer_func-rb) supporting recursion;
|
17
|
+
|
18
|
+
* The `function` keyword was replaced with `hdef` for better consistency with the new functions for sequencers;
|
19
|
+
|
20
|
+
* The `steps` command was added for waiting several steps in a sequencer;
|
21
|
+
|
22
|
+
* Verilog HDL code generation was improved to preserve as much as possible the original names of the signals;
|
23
|
+
|
24
|
+
* Several bug fixes for the sequencers.
|
25
|
+
|
14
26
|
For HDLRuby version 3.0.0:
|
15
27
|
|
16
28
|
* This section;
|
@@ -39,7 +51,7 @@ git clone HDLRuby
|
|
39
51
|
__Warning__:
|
40
52
|
|
41
53
|
- This is still preliminary work which may change before we release a stable version.
|
42
|
-
- It is highly recommended to have both basic
|
54
|
+
- It is highly recommended to have both basic knowledges of the Ruby language and hardware description languages before using HDLRuby.
|
43
55
|
|
44
56
|
|
45
57
|
# Compiling HDLRuby descriptions
|
@@ -72,7 +84,7 @@ Where:
|
|
72
84
|
| `-S, --sim` | Perform the simulation with the default engine |
|
73
85
|
| `--csim` | Perform the simulation with the standalone engine |
|
74
86
|
| `--rsim` | Perform the simulation with the Ruby engine |
|
75
|
-
| `--rcsim` | Perform the simulation with the
|
87
|
+
| `--rcsim` | Perform the simulation with the Hybrid engine |
|
76
88
|
| `--vcd` | Make the simulator generate a VCD file |
|
77
89
|
| `-d, --directory` | Specify the base directory for loading the HDLRuby files |
|
78
90
|
| `-D, --debug` | Set the HDLRuby debug mode |
|
@@ -92,7 +104,7 @@ __Notes__:
|
|
92
104
|
hdrcc --get-samples
|
93
105
|
```
|
94
106
|
|
95
|
-
Then in your current directory (folder) the `hdr_samples` subdirectory will appear that contains several HDLRuby example files.
|
107
|
+
Then in your current directory (folder) the `hdr_samples` subdirectory will appear that contains several HDLRuby example files. Details about the samples can be found here: [samples](#sample-hdlruby-descriptions).
|
96
108
|
|
97
109
|
|
98
110
|
__Examples__:
|
@@ -103,7 +115,7 @@ __Examples__:
|
|
103
115
|
hdrcc --yaml --top adder adder.rb adder
|
104
116
|
```
|
105
117
|
|
106
|
-
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into directory `adder`:
|
118
|
+
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into the directory `adder`:
|
107
119
|
|
108
120
|
```bash
|
109
121
|
hdrcc -v adder.rb adder
|
@@ -121,7 +133,7 @@ hdrcc -V -t adder --param 16 adder_gen.rb adder
|
|
121
133
|
hdrcc -y -t multer -p 16,16,32 multer_gen.rb multer
|
122
134
|
```
|
123
135
|
|
124
|
-
* Simulate the circuit described in file `counter_bench.rb` using the default simulation engine and putting the simulator's files in directory `counter`:
|
136
|
+
* Simulate the circuit described in file `counter_bench.rb` using the default simulation engine and putting the simulator's files in the directory `counter`:
|
125
137
|
|
126
138
|
```bash
|
127
139
|
hdrcc -S counter_bench.rb counter
|
@@ -257,7 +269,7 @@ system :counter2 do
|
|
257
269
|
end
|
258
270
|
```
|
259
271
|
|
260
|
-
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected
|
272
|
+
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected in declaration order. Please notice that it is also possible to connect only a subset of the ports while declaring, as well as to reconnect already connected ports in further statements.
|
261
273
|
|
262
274
|
While a circuit can be generated from the code given above, a benchmark must
|
263
275
|
be provided to test it. Such a benchmark is described by constructs called
|
@@ -323,8 +335,7 @@ instance :dff_single do
|
|
323
335
|
end
|
324
336
|
```
|
325
337
|
|
326
|
-
In the example above, `dff_single` is an instance describing, again, a
|
327
|
-
D-FF, but whose system is anonymous.
|
338
|
+
In the example above, `dff_single` is an instance describing, again, a D-FF, but whose system is anonymous.
|
328
339
|
|
329
340
|
Furthermore, generic parameters can be used for anything in HDLRuby.
|
330
341
|
For instance, the following code describes an 8-bit register without any parameterization:
|
@@ -482,7 +493,7 @@ sumprod(signed[32], [3,78,43,246, 3,67,1,8, 47,82,99,13, 5,77,2,4]).(:my_circuit
|
|
482
493
|
|
483
494
|
As seen in the code above, when passing a generic argument for instantiating a generic system, the name of the instance is put between brackets for avoiding confusion.
|
484
495
|
|
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:
|
496
|
+
While the description `sumprod` is already usable in a wide range of cases, it still uses standard addition and multiplication. However, there are cases where specific components are to be used for these operations, either for the sake of performance, compliance with constraints, or because functionally different operations are required (e.g., saturated computations). This can be solved by using functions implementing such computation in place of operators, for example, as follows:
|
486
497
|
|
487
498
|
```ruby
|
488
499
|
system :sumprod_func do |typ,coefs|
|
@@ -499,7 +510,7 @@ end
|
|
499
510
|
Where `add` and `mult` are functions implementing the required specific operations. HDLRuby functions are equivalent to the Verilog HDL ones. In our example, an addition that saturates at 1000 could be described as follows:
|
500
511
|
|
501
512
|
```ruby
|
502
|
-
|
513
|
+
hdef :add do |x,y|
|
503
514
|
inner :res
|
504
515
|
seq do
|
505
516
|
res <= x + y
|
@@ -517,7 +528,7 @@ With HDLRuby functions, the result of the last statement in the return value, in
|
|
517
528
|
With functions, it is enough to change their content to obtain a new kind of circuit without changing the main code. This approach suffers from two drawbacks though: first, the level of saturation is hard coded in the function, and second, it would be preferable to be able to select the function to execute instead of modifying its code. For the first problem, a simple approach is to add an argument to the function given the saturation level. Such an add function would therefore be as follows:
|
518
529
|
|
519
530
|
```ruby
|
520
|
-
|
531
|
+
hdef :add do |max, x, y|
|
521
532
|
inner :res
|
522
533
|
seq do
|
523
534
|
res <= x + y
|
@@ -526,7 +537,7 @@ function :add do |max, x, y|
|
|
526
537
|
end
|
527
538
|
```
|
528
539
|
|
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
|
540
|
+
It would however be necessary to add this argument when invoking the function, e.g., `add(1000,sum,mult(...))`. While this argument is relevant for addition with saturation, it is not for the other kind of addition operations, and hence, the code of `sumprod` is no general purpose.
|
530
541
|
|
531
542
|
HDLRuby provides two ways to address such issues. First, it is possible to pass code as an argument. In the case of `sumprod`, it would then be enough to add two arguments that perform the required addition and multiplication. The example is below:
|
532
543
|
|
@@ -608,12 +619,7 @@ sumprod(sat(16,1000),
|
|
608
619
|
```
|
609
620
|
|
610
621
|
|
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:
|
622
|
+
Lastly note, HDLRuby is also a language with supports reflection for all its constructs. For example, the system of an instance can be accessed using the `systemT` method, and this latter can be used to create other instances. For example, previously, `dff_single` was declared with an anonymous system (i.e., it cannot be accessed by name). This system can however be used as follows to generate another instance:
|
617
623
|
|
618
624
|
```ruby
|
619
625
|
dff_single.systemT.instantiate(:dff_not_single)
|
@@ -622,12 +628,8 @@ dff_single.systemT.instantiate(:dff_not_single)
|
|
622
628
|
In the above example, `dff_not_single` is declared to be an instance
|
623
629
|
of the same system as `dff_single`.
|
624
630
|
|
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.
|
631
|
+
This reflection capability can also be used for instance, for accessing the data type of a signal (`sig.type`), but also the current basic block (`cur_block`), the current process (`cur_behavior`), and so on.
|
632
|
+
The standard library of HDLRuby includes several hardware constructs like finite state machine descriptors and is mainly based on using these reflection features.
|
631
633
|
|
632
634
|
|
633
635
|
|
@@ -672,8 +674,7 @@ system(:box) {}
|
|
672
674
|
|
673
675
|
__Notes__:
|
674
676
|
|
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:
|
677
|
+
- Since this is Ruby code, the body can also be delimited by the `do` and `end` Ruby keywords (in which case the parentheses can be omitted) are as follows:
|
677
678
|
|
678
679
|
```ruby
|
679
680
|
system :box does
|
@@ -703,8 +704,7 @@ Now, since `bit` is the default data type in HDLRuby, it can be omitted as follo
|
|
703
704
|
input :clk
|
704
705
|
```
|
705
706
|
|
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.
|
707
|
+
The following is a more complete example: it is the code of a system describing an 8-bit data, 16-bit address memory whose interface includes a 1-bit input clock (`clk`), a 1-bit signal for selecting reading or writing access (`rwb`), a 16-bit address input (`addr`), and an 8-bit data inout — the remaining of the code describes the content and the behavior of the memory.
|
708
708
|
|
709
709
|
```ruby
|
710
710
|
system :mem8_16 do
|
@@ -799,8 +799,7 @@ It is also possible to connect multiple signals of an instance using the call op
|
|
799
799
|
<instance name>.(<signal name0>: <target0>, ...)
|
800
800
|
```
|
801
801
|
|
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.
|
802
|
+
For example, the following code connects signals `clk` and `rst` of instance `mem8_16I` to signals `clk` and `rst` of the current system. As seen in this example, this method allows partial connection since the address and the data buses are not connected yet.
|
804
803
|
|
805
804
|
```ruby
|
806
805
|
mem8_16I.(clk: clk, rst: rst)
|
@@ -1321,7 +1320,7 @@ end
|
|
1321
1320
|
#### About loops
|
1322
1321
|
|
1323
1322
|
HDLRuby does not include any hardware construct for describing loops. This might look poor compared to the other HDL, but it is important to understand
|
1324
|
-
that the current synthesis tools do not really synthesize hardware from such loops but instead preprocess them (e.g., unroll them) to synthesizable
|
1323
|
+
that the current synthesis tools do not really synthesize hardware from such loops but instead preprocess them (e.g., unroll them) to synthesizable loopless hardware. In HDLRuby, such features are natively supported by the Ruby loop constructs (`for`, `while`, and so on), but also by advanced Ruby constructs like the enumerators (`each`, `times`, and so on).
|
1325
1324
|
|
1326
1325
|
__Notes__:
|
1327
1326
|
|
@@ -1487,9 +1486,9 @@ _o144
|
|
1487
1486
|
|
1488
1487
|
__Notes__:
|
1489
1488
|
|
1490
|
-
- `_01100100` used to be considered
|
1489
|
+
- `_01100100` used to be considered equivalent to `_b01100100`, however, due to compatibility troubles with a recent version of Ruby it is considered deprecated.
|
1491
1490
|
|
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
|
1491
|
+
- Ruby immediate values can also be used, their bit width is automatically adjusted to match the data type of the expression they are used in. Please notice this adjustment may change the value of the immediate, for example, the following code will set `sig` to 4 instead of 100:
|
1493
1492
|
|
1494
1493
|
```ruby
|
1495
1494
|
[3..0].inner :sig
|
@@ -1615,7 +1614,7 @@ __Notes__:
|
|
1615
1614
|
|
1616
1615
|
- The operator precedence is the one of Ruby.
|
1617
1616
|
|
1618
|
-
- Ruby does not allow to override the `&&`, the
|
1617
|
+
- Ruby does not allow to override the `&&`, the `||`, and the `?:` operators so they are not present in HDLRuby. Instead of the `?:` operator, HDLRuby provides the more general multiplex operator `mux`. However, HDLRuby does not provide any replacement for the `&&` and the `||` operators, please refer to section [Logic operators](#logic-and-shift-operators) for a justification for this issue.
|
1619
1618
|
|
1620
1619
|
#### Assignment operators
|
1621
1620
|
|
@@ -1628,7 +1627,7 @@ __Note__:
|
|
1628
1627
|
#### Arithmetic operators
|
1629
1628
|
<a name="arithmetic"></a>
|
1630
1629
|
|
1631
|
-
The arithmetic operators can only be used on vectors of `bit`, `unsigned` or `signed` values, `integer
|
1630
|
+
The arithmetic operators can only be used on vectors of `bit`, `unsigned` or `signed` values, `integer`, or `float` values. These operators are `+`, `-`, `*`, `%` and the unary arithmetic operators are `-` and `+`. They have the same meaning as their Ruby equivalents.
|
1632
1631
|
|
1633
1632
|
#### Comparison operators
|
1634
1633
|
<a name="comparison"></a>
|
@@ -1669,8 +1668,7 @@ For example, for rotating the left signal `sig` 3 times, the following code can
|
|
1669
1668
|
sig.rl(3)
|
1670
1669
|
```
|
1671
1670
|
|
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.
|
1671
|
+
It is possible to perform other kinds of shifts or rotations using the selection and concatenation operators. Please refer to section [Concatenation and selection operators](#concatenation-and-selection-operators) for more details about these operators.
|
1674
1672
|
|
1675
1673
|
|
1676
1674
|
#### Conversion operators
|
@@ -1678,7 +1676,7 @@ selection operators](#concatenation-and-selection-operators) for more details ab
|
|
1678
1676
|
The conversion operators are used to change the type of an expression.
|
1679
1677
|
There are two kinds of such operators: the type pun that does not change the raw value of the expression and the type cast that changes the raw value.
|
1680
1678
|
|
1681
|
-
The type puns include `to_bit`, `to_unsigned
|
1679
|
+
The type puns include `to_bit`, `to_unsigned`, and `to_signed` that convert expressions of any type to vectors of respectively `bit`, `unsigned`, and `signed` elements. For example, the following code converts an expression of hierarchical type to an 8-bit signed vector:
|
1682
1680
|
|
1683
1681
|
```ruby
|
1684
1682
|
[ up: signed[3..0], down: unsigned[3..0] ].inner :sig
|
@@ -1733,7 +1731,7 @@ Concatenation and selection are done using the `[]` operator as follows:
|
|
1733
1731
|
sig2 <= [sig0, sig1]
|
1734
1732
|
```
|
1735
1733
|
|
1736
|
-
- when this operator is applied to an expression of `bit`, `unsigned
|
1734
|
+
- when this operator is applied to an expression of `bit`, `unsigned`, or `signed` vector type while taking as argument a range, it selects the bits corresponding to this range. If only one bit is to be selected, the offset of this bit can be used instead. For example, the following code selects bits from 3 to 1 of `sig0` and bit 4 of `sig1`:
|
1737
1735
|
|
1738
1736
|
```ruby
|
1739
1737
|
[7..0].inner :sig0
|
@@ -1825,7 +1823,7 @@ __Multiplicative operators:__
|
|
1825
1823
|
Like Verilog HDL, HDLRuby provides function constructs for reusing code. HDLRuby functions are declared as follows:
|
1826
1824
|
|
1827
1825
|
```ruby
|
1828
|
-
|
1826
|
+
hdef :<function name> do |<arguments>|
|
1829
1827
|
<code>
|
1830
1828
|
end
|
1831
1829
|
```
|
@@ -1903,7 +1901,7 @@ system :sys do
|
|
1903
1901
|
end
|
1904
1902
|
```
|
1905
1903
|
|
1906
|
-
As another example, following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
1904
|
+
As another example, the following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
1907
1905
|
|
1908
1906
|
```ruby
|
1909
1907
|
def too_bad
|
@@ -1933,7 +1931,7 @@ Ruby functions can be compared to the macros of the C languages: they are more f
|
|
1933
1931
|
### Time values
|
1934
1932
|
<a name="time_val"></a>
|
1935
1933
|
|
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
|
1934
|
+
In HDLRuby, time values can be created using the time operators: `s` for seconds, `ms` for a millisecond, `us` for microseconds, `ns` for nanoseconds, `ps` for picoseconds. For example, the following are all indicating one second:
|
1937
1935
|
|
1938
1936
|
```ruby
|
1939
1937
|
1.s
|
@@ -1947,7 +1945,7 @@ In HDLRuby, time values can be created using the time operators: `s` for seconds
|
|
1947
1945
|
### Time behaviors and time statements
|
1948
1946
|
<a name="time_beh"></a>
|
1949
1947
|
|
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.
|
1948
|
+
Like the other HDL, HDLRuby provides specific statements that model the advance of time. These statements are not synthesizable and are used for simulating the environment of a hardware component. For the sake of clarity, such statements are only allowed in explicitly non-synthesizable behavior declared using the `timed` keyword as follows.
|
1951
1949
|
|
1952
1950
|
```ruby
|
1953
1951
|
timed do
|
@@ -2057,7 +2055,7 @@ typedef :<type name> do |<list of generic parameters>|
|
|
2057
2055
|
end
|
2058
2056
|
```
|
2059
2057
|
|
2060
|
-
For example, the following code describes a bit-vector type with generic number of bits `width`:
|
2058
|
+
For example, the following code describes a bit-vector type with a generic number of bits `width`:
|
2061
2059
|
|
2062
2060
|
```ruby
|
2063
2061
|
type(:bitvec) { |width| bit[width] }
|
@@ -2087,7 +2085,7 @@ system :subsys, sys(1,2) do
|
|
2087
2085
|
end
|
2088
2086
|
```
|
2089
2087
|
|
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:
|
2088
|
+
This way of inheriting can only be done with fully specialized systems though. For partially specialized systems, `include` must be used instead. For example, if `sys` is specialized with only one value, can be used in generic `subsys_gen` as follows:
|
2091
2089
|
|
2092
2090
|
```ruby
|
2093
2091
|
system :subsys_gen do |param|
|
@@ -2348,7 +2346,7 @@ end
|
|
2348
2346
|
Please notice, that in the code above, the left value has been cast to a plain bit-vector to avoid the infinite recursive call of the `*` operator.
|
2349
2347
|
|
2350
2348
|
Operators can also be overloaded with generic types. However, in such a case, the generic argument must also be present in the list of arguments of the overloaded operators.
|
2351
|
-
For instance, let us consider the following fixed-point type of variable width (and whose decimal point is set at
|
2349
|
+
For instance, let us consider the following fixed-point type of variable width (and whose decimal point is set at half of its bit range):
|
2352
2350
|
|
2353
2351
|
```ruby
|
2354
2352
|
typedef(:fixed) do |width|
|
@@ -2396,7 +2394,7 @@ Several enumerators are also provided for accessing the internals of the current
|
|
2396
2394
|
|
2397
2395
|
### Global signals
|
2398
2396
|
|
2399
|
-
HDLRuby allows
|
2397
|
+
HDLRuby allows the declaration of global signals the same way system's signals are declared but outside the scope of any system. After being declared, these signals are accessible directly from within any hardware construct.
|
2400
2398
|
|
2401
2399
|
To ease the design of standardized libraries, the following global signals are defined by default:
|
2402
2400
|
|
@@ -2415,9 +2413,9 @@ __Note__:
|
|
2415
2413
|
### Defining and executing Ruby methods within HDLRuby constructs
|
2416
2414
|
<a name="method"></a>
|
2417
2415
|
|
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.
|
2416
|
+
Like with any Ruby program, it is possible to define and execute methods anywhere in HDLRuby using the standard Ruby syntax. When defined, a method is attached to the enclosing HDLRuby construct. For instance, when defining a method when declaring a system, it can be used within this system only, while when defining a method outside any construct, it can be used everywhere in the HDLRuby description.
|
2419
2417
|
|
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`.
|
2418
|
+
A method can include HDLRuby code in which case the resulting hardware is appended to the current construct. For example, the following code adds a connection between `sig0` and `sig1` in the system `sys0`, and transmission between `sig0` and `sig1` in the behavior of `sys1`.
|
2421
2419
|
|
2422
2420
|
```ruby
|
2423
2421
|
def some_arrow
|
@@ -2486,7 +2484,7 @@ end
|
|
2486
2484
|
```
|
2487
2485
|
|
2488
2486
|
|
2489
|
-
While requiring
|
2487
|
+
While requiring caution, a properly designed method can be very useful for clean code reuse. For example, the following method allows to start the execution of a block after a given number of cycles:
|
2490
2488
|
|
2491
2489
|
```ruby
|
2492
2490
|
def after(cycles,rst = $rst, &code)
|
@@ -2541,7 +2539,7 @@ When describing a system, it is possible to disconnect or completely undefine a
|
|
2541
2539
|
|
2542
2540
|
## Extending HDLRuby
|
2543
2541
|
|
2544
|
-
Like any Ruby
|
2542
|
+
Like any Ruby class, the constructs of HDLRuby can be dynamically extended. If it is not recommended to change their internal structure, it is possible to add methods to them for an extension.
|
2545
2543
|
|
2546
2544
|
### Extending HDLRuby constructs globally
|
2547
2545
|
|
@@ -2683,7 +2681,7 @@ After the libraries are loaded, the module `Std` must be included as follows:
|
|
2683
2681
|
include HDLRuby::High::Std
|
2684
2682
|
```
|
2685
2683
|
|
2686
|
-
> However, `hdrcc`
|
2684
|
+
> However, `hdrcc` loads the stable components of the standard library by default, so you do not need to require nor include anything more to use them. In the current version, the stable components are the followings:
|
2687
2685
|
|
2688
2686
|
- `std/clocks.rb`
|
2689
2687
|
|
@@ -2719,7 +2717,7 @@ system :dff_slow do
|
|
2719
2717
|
end
|
2720
2718
|
```
|
2721
2719
|
|
2722
|
-
__Note__: this library
|
2720
|
+
__Note__: this library generates all the RTL code for the circuit handling the frequency division.
|
2723
2721
|
|
2724
2722
|
## Counters: `std/counters.rb`
|
2725
2723
|
<a name="counters"></a>
|
@@ -2754,13 +2752,13 @@ A decoder can be declared anywhere in the code describing a system using the `de
|
|
2754
2752
|
decoder(<signal>) <block>
|
2755
2753
|
```
|
2756
2754
|
|
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
|
2755
|
+
Where `signal` is the signal to decode and `block` is a procedure block (i.e., Ruby `proc`) describing the decoding procedure. This procedure block can contain any code supported by a standard behavior but also supports the `entry` statement that describes a pattern of a bit vector to decode and the corresponding action to perform when the signal matches this pattern. The syntax of the `entry` statement is the following:
|
2758
2756
|
|
2759
2757
|
```ruby
|
2760
2758
|
entry(<pattern>) <block>
|
2761
2759
|
```
|
2762
2760
|
|
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
|
2761
|
+
Where `pattern` is a string describing the pattern to match the entry, and `block` is a procedure block describing the actions (some HDLRuby code) that are performed when the entry matches. The string describing the pattern can include `0` and `1` characters for specifying a specific value for the corresponding bit, or any alphabetical character for specifying a field in the pattern. The fields in the pattern can then be used by name in the block describing the action. When a letter is used several times within a pattern, the corresponding bits are concatenated and used as a multi-bit signal in the block.
|
2764
2762
|
|
2765
2763
|
For example, the following code describes a decoder for signal `ir` with two entries, the first one computing the sum of fields `x` and `y` and assigning the result to signal `s` and the second one computing the sum of fields `x` `y` and `z` and assigning the result to signal `s`:
|
2766
2764
|
|
@@ -2864,7 +2862,7 @@ That is to say, for a conditional `goto` for `st_1` the code should have been wr
|
|
2864
2862
|
end
|
2865
2863
|
```
|
2866
2864
|
|
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
|
2865
|
+
The use of `goto` makes the design of FSM shorter for a majority of the cases, be sometimes, a finer control is required. For that purpose, it is also possible to configure the FSM in `static` mode where the `next_state` statement indicates implicitly the next state. Putting in static mode is done by passing `:static` as an argument when declaring the FSM. For example, the following FSM uses `next_state` to specify explicitly the next states depending on some condition signals `cond0` and `cond1`:
|
2868
2866
|
|
2869
2867
|
```ruby
|
2870
2868
|
fsm(clk.posedge,rst,:static)
|
@@ -2894,9 +2892,11 @@ A sequence is a specific case of a `seq` block that includes the following softw
|
|
2894
2892
|
|
2895
2893
|
- `step`: wait until the next event (given argument `event` of the sequencer).
|
2896
2894
|
|
2895
|
+
- `steps(<num>)`: perform `num` times `step` (`num` can be any expression).
|
2896
|
+
|
2897
2897
|
- `sif(<condition>) <block>`: executes `block` if `condition` is met.
|
2898
2898
|
|
2899
|
-
- `selsif(<condition>) <block>`: executes `block` if previous `sif` or `selsif` condition is not met and if current `condition` is met.
|
2899
|
+
- `selsif(<condition>) <block>`: executes `block` if the previous `sif` or `selsif` condition is not met and if the current `condition` is met.
|
2900
2900
|
|
2901
2901
|
- `selse <block>`: executes `block` if the condition of the previous `sif` statement is not met.
|
2902
2902
|
|
@@ -3026,7 +3026,7 @@ It is also possible to define a custom enumerator using the following command:
|
|
3026
3026
|
<enum> = senumerator(<typ>,<size>) <block>
|
3027
3027
|
```
|
3028
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:
|
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
3030
|
|
3031
3031
|
```ruby
|
3032
3032
|
bit[8][-8].inner mem: [ _h01, _h02, _h03, _h04, _h30, _h30, _h30, _h30 ]
|
@@ -3042,7 +3042,7 @@ Where `enum` is a Ruby variable referring to the enumerator, `typ` is the data t
|
|
3042
3042
|
end
|
3043
3043
|
```
|
3044
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
|
3045
|
+
In the code above, `mem_enum` is the variable referring to the resulting enumerator built for accessing memory `mem`. For the access, it is assumed that one cycle must be waited for after the address is set, and therefore a `step` command is added in the access procedure before `data` can be returned.
|
3046
3046
|
|
3047
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
3048
|
|
@@ -3120,12 +3120,12 @@ With this basis, several algorithms have been implemented using enumerators and
|
|
3120
3120
|
|
3121
3121
|
|
3122
3122
|
|
3123
|
-
### Shared signals, arbiters and monitors: `std/sequencer_sync.rb`
|
3123
|
+
### Shared signals, arbiters, and monitors: `std/sequencer_sync.rb`
|
3124
3124
|
<a name="shared"></a>
|
3125
3125
|
|
3126
3126
|
#### Shared signals
|
3127
3127
|
|
3128
|
-
Like any other
|
3128
|
+
Like any other process, It is not possible for several sequencers to write to the same signal. This is because there would be race competition that may destroy physically the device if such operations were authorized. In standard RTL design, this limitation is overcome by implementing three-state buses, multiplexers, and arbiters. However, HDLRuby sequencers support another kind of signal called the *shared signals* that abstract the implementation details for avoiding race competition.
|
3129
3129
|
|
3130
3130
|
The shared signals are declared like the other kind of signals from their type. The syntax is the following:
|
3131
3131
|
|
@@ -3139,14 +3139,14 @@ They can also have an initial (and default) value when declared as follows:
|
|
3139
3139
|
<type>.shared <list of names with initialization>
|
3140
3140
|
```
|
3141
3141
|
|
3142
|
-
For example the following code
|
3142
|
+
For example, the following code declares two 8-bit shared signals `x` and `y` and two signed 16-bit shared signals initialized to 0 `u` and `v`:
|
3143
3143
|
|
3144
3144
|
```ruby
|
3145
3145
|
[8].shared :x, :y
|
3146
3146
|
signed[8].shared u: 0, v: 0
|
3147
3147
|
```
|
3148
3148
|
|
3149
|
-
A shared
|
3149
|
+
A shared signal can then be read and written to by any sequencer from anywhere in the subsequent code of the current scope. However, they cannot be written to outside a sequencer. For example, the following code is valid:
|
3150
3150
|
|
3151
3151
|
```ruby
|
3152
3152
|
input :clk, :start
|
@@ -3173,21 +3173,21 @@ But the following code is not valid:
|
|
3173
3173
|
par(clk.posedge) { w <= w + 1 }
|
3174
3174
|
```
|
3175
3175
|
|
3176
|
-
By default, a shared signal
|
3176
|
+
By default, a shared signal acknowledges writing from the first sequencer that accesses it in order of declaration, the others are ignored. In the first example given above, that means for signal `x` the value is always the one written by the first sequencer, i.e., from 0 to 9 changing once per clock cycle. However, the value of signal `y` is set by the second sequencer since it is this one only that writes to this signal.
|
3177
3177
|
|
3178
|
-
This default behavior of shared signal avoids race competition
|
3178
|
+
This default behavior of shared signal avoids race competition but is not very useful in practice. For better control, it is possible to select which sequencer is to be acknowledged for writing. This is done by setting the number of the sequencer which can write the signal that controls the sharing accessed as follows:
|
3179
3179
|
|
3180
3180
|
```ruby
|
3181
3181
|
<shared signal>.select
|
3182
3182
|
```
|
3183
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
|
3184
|
+
The select value starts from 0 for the first sequencer writing to the shared signal, and is increased by one per writing sequencer. For example, in the first example, for selecting the second sequencer for writing to `x` the following code can be added after this signal is declared:
|
3185
3185
|
|
3186
3186
|
```ruby
|
3187
3187
|
x.select <= 1
|
3188
3188
|
```
|
3189
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:
|
3190
|
+
This value can be changed at runtime too. For example, instead of setting the second sequencer, it is possible to switch the sequencer every clock cycle as follows:
|
3191
3191
|
|
3192
3192
|
```ruby
|
3193
3193
|
par(clk.posedge) { x.select <= x.select + 1 }
|
@@ -3198,15 +3198,15 @@ __Note__: this select sub signal is a standard RTL signal that has the same prop
|
|
3198
3198
|
|
3199
3199
|
#### Arbiters
|
3200
3200
|
|
3201
|
-
Usually, it is not the signals that we want to share, but the resources they
|
3201
|
+
Usually, it is not the signals that we want to share, but the resources they drive. For example, in a CPU, it is an ALU that is shared as a whole rather than each of its inputs separately. In order to support such cases and ease the handling of shared signals, the library also provides the *arbiter* components. This component is instantiated like a standard module as follows, where `name` is the name of the arbiter instance:
|
3202
3202
|
|
3203
3203
|
```ruby
|
3204
3204
|
arbiter(:<name>).(<list of shared signal>)
|
3205
3205
|
```
|
3206
3206
|
|
3207
|
-
When instantiated, an arbiter will take control of the select sub
|
3207
|
+
When instantiated, an arbiter will take control of the select sub-signals of the shared signals (hence, you cannot control the selection yourself for them any longer). In return, it provides the possibility of requiring or releasing access to the shared signals. Requiring access is done by sending the value 1 to the arbiter, and releasing is done by sending the value 0. If a sequencer writes to a shared signal under arbitration without requiring access first, the write will simply be ignored.
|
3208
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:
|
3209
|
+
The following is an example of an arbiter that controls access to shared signals `x` and `y` and two sequencers acquiring and releasing accesses to them:
|
3210
3210
|
|
3211
3211
|
```ruby
|
3212
3212
|
input :clk, :start
|
@@ -3236,20 +3236,20 @@ end
|
|
3236
3236
|
|
3237
3237
|
In the example, both sequencers require access to signals `x` and `y` before accessing them and then releasing the access.
|
3238
3238
|
|
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
|
3239
|
+
Requiring access does not guarantee that the access will be granted by the arbiter though. In the access is not granted, the write access will be ignored.
|
3240
|
+
The default access granting policy of an arbiter is the priority in the order of sequencer declaration. I.e., if several sequencers are requiring one at the same time, then the one declared the earliest in the code gains write access. For example, with the code given above, the first sequencer has to write access to `x` and `y`, and since after five write cycles it releases access, the second sequencer can then write to these signals. However, not obtaining write access does not block the execution of the sequencer, simply, its write access to the corresponding shared signals is simply ignored. In the example, the second sequencer will do its first five loop cycles without any effect and have only its five last ones that change the shared signals. To avoid such a behavior, it is possible to check if the write access is granted using arbiter sub signal `acquired`: if this signal is one in the current sequencer, that means the access is granted, otherwise it is 0. For example the following will increase signal `x` only if write access is granted:
|
3241
3241
|
|
3242
3242
|
```ruby
|
3243
3243
|
hif(ctrl_xy.acquired) { x <= x + 1 }
|
3244
3244
|
```
|
3245
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
|
3246
|
+
The policy of an arbiter can be changed using command policy. You can either provide a new priority table, containing the number of the sequencers in order of priority (the first one having higher priority. The number of a sequencer is assigned in order of declaration provided it uses the arbiter. For example, in the previous code, the second sequencer can be given higher priority by adding the following code after having declared the arbiter:
|
3247
3247
|
|
3248
3248
|
```ruby
|
3249
3249
|
ctrl_xy.policy([1,0])
|
3250
3250
|
```
|
3251
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
|
3252
|
+
It is also possible to set a more complex policy by providing to the policy method a block of code whose argument is a vector indicating which sequencer is currently requiring write access to the shared signals and whose result will be the number of the sequencer to grant the access. This code will be executed each time write access is actually performed. For example, in the previous code, a policy switch priorities at each access can be implemented as follows:
|
3253
3253
|
|
3254
3254
|
```ruby
|
3255
3255
|
inner priority_xy: 0
|
@@ -3272,26 +3272,26 @@ ctrl_xy.policy do |acq|
|
|
3272
3272
|
end
|
3273
3273
|
```
|
3274
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
|
3275
|
+
As seen in the code above, each bit of the `acq` vector is one when the corresponding sequencer required an access or 0 otherwise, bit 0 corresponds to sequencer 0, bit 1 to sequencer 1 and so on.
|
3276
3276
|
|
3277
3277
|
|
3278
3278
|
#### Monitors
|
3279
3279
|
|
3280
|
-
Arbiters
|
3280
|
+
Arbiters are especially useful when we can ensure that the sequencers accessing the same resource do not overlap or when they do not need to synchronize with each other. If such synchronizations are required, instead of arbiters, it is possible to use the *monitor* components.
|
3281
3281
|
|
3282
|
-
The monitor component
|
3282
|
+
The monitor component is instantiated like the arbiters as follows:
|
3283
3283
|
|
3284
3284
|
```ruby
|
3285
3285
|
monitor(:<name>).(<list of shared signals>)
|
3286
3286
|
```
|
3287
3287
|
|
3288
|
-
Monitors are used exactly the same ways as arbiters (including the write access granting policies)
|
3288
|
+
Monitors are used exactly the same ways as arbiters (including the write access granting policies) but block the execution of the sequencers that require write access until the access is granted. If we take the example of code with two sequencers given as an illustration of arbiter usage, replacing the arbiter with a monitor as follows will lock the second sequencer until it can write to shared variables `x` and `y` ensuring that all its loop cycles have the specified result:
|
3289
3289
|
|
3290
3290
|
```ruby
|
3291
3291
|
monitor(:ctrl_xy).(x,y)
|
3292
3292
|
```
|
3293
3293
|
|
3294
|
-
Since monitors
|
3294
|
+
Since monitors lock processes, they automatically insert a step. Hence to avoid confusion, acquiring access to a monitor is done by using the method `lock` instead of assigning 1, and releasing is done by using the method `unlock` instead of assigning 0. Hence, when using a monitor, the previous arbiter-based code should be rewritten as follows:
|
3295
3295
|
|
3296
3296
|
```ruby
|
3297
3297
|
input :clk, :start
|
@@ -3319,6 +3319,66 @@ sequencer(clk.posedge,start) do
|
|
3319
3319
|
end
|
3320
3320
|
```
|
3321
3321
|
|
3322
|
+
### Sequencer-specific function: `std/sequencer_func.rb`
|
3323
|
+
|
3324
|
+
HDLRuby function defined by `hdef` can be used in sequencer like any other HDLRuyby construct. But like the process constructs `hif` and so on, the body of these functions cannot include any sequencer-specific constructs.
|
3325
|
+
|
3326
|
+
However, it is possible to define functions that do support the sequencer constructs using `sdef` instead of `hdef` as follows:
|
3327
|
+
|
3328
|
+
```ruby
|
3329
|
+
sdef :<function name> do |<arguments>|
|
3330
|
+
<sequencer code>
|
3331
|
+
end
|
3332
|
+
```
|
3333
|
+
|
3334
|
+
Such functions can be defined anywhere in a HDLRuby description, but can only be called within a sequencer.
|
3335
|
+
|
3336
|
+
As additional features, since the `sdef` function is made to support the software-like code description of the sequencers, it also supports recursion. For example, a function describing a factorial can be described as follows:
|
3337
|
+
|
3338
|
+
```ruby
|
3339
|
+
sdef(:fact) do |n|
|
3340
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
3341
|
+
selse { sreturn(1) }
|
3342
|
+
end
|
3343
|
+
```
|
3344
|
+
|
3345
|
+
As seen in the code above, a new construct `sreturn` can be used for returning a value from anywhere inside the function.
|
3346
|
+
|
3347
|
+
When a recursion is present, HDLRuby automatically defines a stack for storing the return state and the arguments of the function. The size of the stack is heuristically set to the maximum number of bits of the arguments of the function when it is recursively called. For example, for the previous `fact` function, if when called, `n` is 16-bit, the stack will be able to hold 16 recursions. If this heuristic does not match the circuit's needs, the size can be forced as a second argument when defining the function. For example, the following code set the size to 32 whatever the arguments are:
|
3348
|
+
|
3349
|
+
```ruby
|
3350
|
+
sdef(:fact,32) do |n|
|
3351
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
3352
|
+
selse { sreturn(1) }
|
3353
|
+
end
|
3354
|
+
```
|
3355
|
+
|
3356
|
+
__Notes__:
|
3357
|
+
|
3358
|
+
* A call to such a function and a return take respectively one and two cycles of the sequencer.
|
3359
|
+
|
3360
|
+
* For now, there is no tail call optimization.
|
3361
|
+
|
3362
|
+
* In case of stack overflow (the number of recursive calls exceeds the size of the sack), the current recursion is terminated and the sequencer goes on its execution. It is possible to add a process that is to be executed in such a case as follows:
|
3363
|
+
|
3364
|
+
```ruby
|
3365
|
+
sdef(:<name>,<depth>, proc <block>) do
|
3366
|
+
<function code>
|
3367
|
+
end
|
3368
|
+
```
|
3369
|
+
|
3370
|
+
Where `block` contains the code of the stack overflow process. For now, this process cannot contain a sequencer construct. For example, the previous factorial function can be modified as follows so that signal `stack_overflow` is set to 1 in case of overflow:
|
3371
|
+
|
3372
|
+
```ruby
|
3373
|
+
sdef(:fact,32, proc { stack_overflow <= 1 }) do |n|
|
3374
|
+
sif(n > 1) { sreturn(n*fact(n-1)) }
|
3375
|
+
selse { sreturn(1) }
|
3376
|
+
end
|
3377
|
+
```
|
3378
|
+
|
3379
|
+
With the code above, the only restriction is that the signal `stack_overflow` is declared before the function `fact` is called.
|
3380
|
+
|
3381
|
+
|
3322
3382
|
## Fixed-point (fixpoint): `std/fixpoint.rb`
|
3323
3383
|
<a name="fixpoint"></a>
|
3324
3384
|
|