HDLRuby 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.html +2330 -2670
- data/README.md +391 -101
- data/ext/hruby_sim/hruby_rcsim_build.c +400 -3
- data/ext/hruby_sim/hruby_sim.h +2 -1
- data/ext/hruby_sim/hruby_sim_calc.c +1 -1
- data/ext/hruby_sim/hruby_sim_core.c +15 -5
- data/ext/hruby_sim/hruby_sim_tree_calc.c +1 -1
- data/lib/HDLRuby/hdr_samples/c_program/echo.c +33 -0
- data/lib/HDLRuby/hdr_samples/ruby_program/echo.rb +9 -0
- data/lib/HDLRuby/hdr_samples/ruby_program/stdrw.rb +6 -0
- data/lib/HDLRuby/hdr_samples/ruby_program/sw_cpu_terminal.rb +614 -0
- data/lib/HDLRuby/hdr_samples/ruby_program/sw_inc_mem.rb +32 -0
- data/lib/HDLRuby/hdr_samples/ruby_program/sw_log.rb +33 -0
- data/lib/HDLRuby/hdr_samples/with_board.rb +63 -0
- data/lib/HDLRuby/hdr_samples/with_clocks.rb +42 -0
- data/lib/HDLRuby/hdr_samples/with_of.rb +1 -1
- data/lib/HDLRuby/hdr_samples/with_program_c.rb +28 -0
- data/lib/HDLRuby/hdr_samples/with_program_ruby.rb +28 -0
- data/lib/HDLRuby/hdr_samples/with_program_ruby_cpu.rb +234 -0
- data/lib/HDLRuby/hdr_samples/with_program_ruby_io.rb +23 -0
- data/lib/HDLRuby/hdr_samples/with_program_ruby_mem.rb +58 -0
- data/lib/HDLRuby/hdr_samples/with_program_ruby_threads.rb +56 -0
- data/lib/HDLRuby/hdr_samples/with_sequencer_func.rb +2 -4
- data/lib/HDLRuby/hdrcc.rb +60 -21
- data/lib/HDLRuby/hruby_error.rb +13 -0
- data/lib/HDLRuby/hruby_high.rb +50 -7
- data/lib/HDLRuby/hruby_low.rb +74 -30
- data/lib/HDLRuby/hruby_rcsim.rb +89 -5
- data/lib/HDLRuby/std/clocks.rb +118 -50
- data/lib/HDLRuby/std/std.rb +5 -0
- data/lib/HDLRuby/ui/hruby_board.rb +1079 -0
- data/lib/HDLRuby/version.rb +1 -1
- data/lib/c/Rakefile +8 -0
- data/lib/c/cHDL.h +12 -0
- data/lib/c/extconf.rb +7 -0
- data/lib/rubyHDL.rb +33 -0
- data/tuto/gui_accum.png +0 -0
- data/tuto/gui_board.png +0 -0
- data/tuto/tutorial_sw.html +2263 -1890
- data/tuto/tutorial_sw.md +957 -62
- metadata +24 -5
- data/README.pdf +0 -0
- data/tuto/tutorial_sw.pdf +0 -0
data/README.md
CHANGED
@@ -7,14 +7,29 @@ __Note__:
|
|
7
7
|
|
8
8
|
If you are new to HDLRuby, it is recommended that you consult first the following tutorial (even if you are a hardware person):
|
9
9
|
|
10
|
-
* [HDLRuby tutorial for software people](tuto/tutorial_sw.md)
|
10
|
+
* [HDLRuby tutorial for software people](tuto/tutorial_sw.md) [md]
|
11
|
+
|
12
|
+
* [HDLRuby tutorial for software people](tuto/tutorial_sw.html) [html]
|
11
13
|
|
12
14
|
__What's new__
|
13
15
|
|
16
|
+
For HDLRuby version 3.3.0:
|
17
|
+
|
18
|
+
* Remade the description of software components using the program construct.
|
19
|
+
Now the Code objects are deprecated.
|
20
|
+
|
21
|
+
* Added HW-SW co-simulation capability for Ruby and compiled C-compatible
|
22
|
+
software programs.
|
23
|
+
|
24
|
+
* Added a browser-based graphical interface simulating simulates a development board that interacts with the HDLRuby simulator.
|
25
|
+
|
26
|
+
* Updated the documentation and tutorial accordingly and fixed several typos.
|
27
|
+
|
28
|
+
|
14
29
|
For HDLRuby version 3.2.0:
|
15
30
|
|
16
31
|
* Added component for declaring BRAM and BRAM-based stacks.
|
17
|
-
The goal is to
|
32
|
+
The goal is to allocate memories inside FPGAs efficiently.
|
18
33
|
|
19
34
|
* Inner code overhaul for preparing version 4.0.0
|
20
35
|
|
@@ -46,7 +61,7 @@ For HDLRuby version 3.0.0:
|
|
46
61
|
|
47
62
|
__Install__:
|
48
63
|
|
49
|
-
The recommended installation method is from
|
64
|
+
The recommended installation method is from rubygems as follows:
|
50
65
|
|
51
66
|
```
|
52
67
|
gem install HDLRuby
|
@@ -61,7 +76,7 @@ git clone HDLRuby
|
|
61
76
|
__Warning__:
|
62
77
|
|
63
78
|
- This is still preliminary work which may change before we release a stable version.
|
64
|
-
- It is highly recommended to have both basic
|
79
|
+
- It is highly recommended to have both basic knowledge of the Ruby language and hardware description languages before using HDLRuby.
|
65
80
|
|
66
81
|
|
67
82
|
# Compiling HDLRuby descriptions
|
@@ -108,7 +123,7 @@ __Notes__:
|
|
108
123
|
|
109
124
|
* If no top system is given, it is automatically looked for from the input file.
|
110
125
|
* If no option is given, it simply checks the input file.
|
111
|
-
* If you are new to HDLRuby, or if you want to see how new features work, we strongly encourage to get a local copy of the test HDLRuby sample using:
|
126
|
+
* If you are new to HDLRuby, or if you want to see how new features work, we strongly encourage you to get a local copy of the test HDLRuby sample using:
|
112
127
|
|
113
128
|
```bash
|
114
129
|
hdrcc --get-samples
|
@@ -165,7 +180,7 @@ hdrcc -I pry
|
|
165
180
|
|
166
181
|
## Using HDLRuby in interactive mode
|
167
182
|
|
168
|
-
When running in interactive mode, the HDLRuby framework starts a REPL prompt and creates a working directory called
|
183
|
+
When running in interactive mode, the HDLRuby framework starts a REPL prompt and creates a working directory called `HDLRubyWorkspace`. By default, the REPL is `irb`, but it can be set to `pry`. Within this prompt, HDLRuby code can be written like in an HDLRuby description file. However, to process this code the following commands are added:
|
169
184
|
|
170
185
|
* Compile an HDLRuby module:
|
171
186
|
|
@@ -384,7 +399,7 @@ system :reg do |typ|
|
|
384
399
|
end
|
385
400
|
```
|
386
401
|
|
387
|
-
Wait... I have just realized that D-FF without any inverted output does not look very serious. So, let us extend the existing `dff` to provide an inverted output. There are three ways
|
402
|
+
Wait... I have just realized that D-FF without any inverted output does not look very serious. So, let us extend the existing `dff` to provide an inverted output. There are three ways to do this. First, inheritance can be used: a new system is built inheriting from `dff` as it is done in the following code.
|
388
403
|
|
389
404
|
```ruby
|
390
405
|
system :dff_full, dff do
|
@@ -442,7 +457,7 @@ system :shifter do |n|
|
|
442
457
|
end
|
443
458
|
```
|
444
459
|
|
445
|
-
As can be seen in the above examples, in HDLRuby, any construct is an object and therefore
|
460
|
+
As can be seen in the above examples, in HDLRuby, any construct is an object and therefore includes methods. For instance, declaring a signal of a given `type` and direction (input, output, or inout) is done as follows so that `direction` is a method of the type, and the signal names are the arguments of this method (symbols or string are supported.)
|
446
461
|
|
447
462
|
```ruby
|
448
463
|
<type>.<direction> <list of symbols representing the signal>
|
@@ -493,7 +508,7 @@ end
|
|
493
508
|
In the code above, there are two generic parameters,
|
494
509
|
`typ`, which indicates the data type of the circuit, and `coefs`, which is assumed to be an array of coefficients. Since the number of inputs depends on the number of provided coefficients, it is declared as an array of `width` bit signed whose size is equal to the number of coefficients.
|
495
510
|
|
496
|
-
The description of the sum of products may be more difficult to understand for people not familiar with the Ruby language. The `each_with_index` method iterates over the coefficients adding their index as an iteration variable, the resulting operation (i.e., the iteration loop) is then modified by the `reduce` method that accumulates the code passed as arguments. This code, starting by `|sum,coef,i|`
|
511
|
+
The description of the sum of products may be more difficult to understand for people not familiar with the Ruby language. The `each_with_index` method iterates over the coefficients adding their index as an iteration variable, the resulting operation (i.e., the iteration loop) is then modified by the `reduce` method that accumulates the code passed as arguments. This code, starting by `|sum,coef,i|` performs the addition of the current accumulation result (`sum`) with the product of the current coefficient (`coef`) and input (`ins[i]`, where `i` is the index) in the iteration. The argument `_b0` initializes the sum to `0`.
|
497
512
|
|
498
513
|
While slightly longer than the previous description, this description allows declaring a circuit implementing a sum of products with any bit width and any number of coefficients. For instance, the following code describes a signed 32-bit sum of products with 16 coefficients (just random numbers here).
|
499
514
|
|
@@ -501,7 +516,7 @@ While slightly longer than the previous description, this description allows dec
|
|
501
516
|
sumprod(signed[32], [3,78,43,246, 3,67,1,8, 47,82,99,13, 5,77,2,4]).(:my_circuit)
|
502
517
|
```
|
503
518
|
|
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
|
519
|
+
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 to avoid confusion.
|
505
520
|
|
506
521
|
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:
|
507
522
|
|
@@ -547,7 +562,7 @@ hdef :add do |max, x, y|
|
|
547
562
|
end
|
548
563
|
```
|
549
564
|
|
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
|
565
|
+
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 kinds of addition operations, and hence, the code of `sumprod` has no general purpose.
|
551
566
|
|
552
567
|
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:
|
553
568
|
|
@@ -592,7 +607,7 @@ sat16_1000.define_operator(:+) do |x,y|
|
|
592
607
|
end
|
593
608
|
```
|
594
609
|
|
595
|
-
In the code above, the first line defines the new type `sat16_1000` to be 16-bit signed and the remaining overloads (redefines) the `+` operator for this type (the same should be done for the `*` operator).
|
610
|
+
In the code above, the first line defines the new type `sat16_1000` to be 16-bit signed, and the remaining overloads (redefines) the `+` operator for this type (the same should be done for the `*` operator).
|
596
611
|
Then, the initial version of `sumprod` can be used with this type to achieve saturated computations as follows:
|
597
612
|
|
598
613
|
```ruby
|
@@ -668,7 +683,7 @@ Several constructs in HDLRuby are referred to by name, e.g., systems and signals
|
|
668
683
|
After being declared, the construct can be referred to by using the name directly (i.e., without the `:` of Ruby symbols). For example, if a construct
|
669
684
|
has been declared with `:hello` as the name, it will be afterward referred to by `hello`.
|
670
685
|
|
671
|
-
## Systems and
|
686
|
+
## Systems and Signals
|
672
687
|
|
673
688
|
A system represents a digital system and corresponds to a Verilog HDL module. A system has an interface comprising input, output, and inout signals, and includes structural and behavioral descriptions.
|
674
689
|
|
@@ -676,7 +691,7 @@ A signal represents a state in a system. It has a data type and a value, the lat
|
|
676
691
|
|
677
692
|
### Declaring an empty system
|
678
693
|
|
679
|
-
A system is declared using the keyword `system`. It must be given a Ruby symbol for its name and a block that
|
694
|
+
A system is declared using the keyword `system`. It must be given a Ruby symbol for its name and a block that describes its content. For instance, the following code describes an empty system named `box`:
|
680
695
|
|
681
696
|
```ruby
|
682
697
|
system(:box) {}
|
@@ -684,7 +699,7 @@ system(:box) {}
|
|
684
699
|
|
685
700
|
__Notes__:
|
686
701
|
|
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)
|
702
|
+
- 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) as follows:
|
688
703
|
|
689
704
|
```ruby
|
690
705
|
system :box does
|
@@ -860,7 +875,7 @@ end
|
|
860
875
|
### Initialization of signals
|
861
876
|
<a name="initialization"></a>
|
862
877
|
|
863
|
-
Output, inner and constant signals of a system can be initialized when declared using the following syntax in place of the usual name of the signal:
|
878
|
+
Output, inner, and constant signals of a system can be initialized when declared using the following syntax in place of the usual name of the signal:
|
864
879
|
|
865
880
|
```ruby
|
866
881
|
<signal name>: <intial value>
|
@@ -899,7 +914,7 @@ system :div2 do
|
|
899
914
|
|
900
915
|
```
|
901
916
|
|
902
|
-
For robustness or, readability
|
917
|
+
For robustness or, readability purposes, it is possible to add an inner scope inside the existing scope using the `sub` keyword as follows:
|
903
918
|
|
904
919
|
```ruby
|
905
920
|
sub do
|
@@ -981,7 +996,7 @@ end
|
|
981
996
|
```
|
982
997
|
|
983
998
|
In addition, it is possible to declare inner signals within an execution block.
|
984
|
-
While such signals will be physically linked to the system, they are only accessible within the block they are declared into. This permits a tighter scope for signals, which improves the readability of the code and
|
999
|
+
While such signals will be physically linked to the system, they are only accessible within the block they are declared into. This permits a tighter scope for signals, which improves the readability of the code and makes it possible to declare several signals with identical names provided their respective scopes are different.
|
985
1000
|
|
986
1001
|
An event represents a specific change in the state of a signal.
|
987
1002
|
For example, a rising edge of a clock signal named `clk` will be represented by the event `clk.posedge`. In HDLRuby, events are obtained directly from
|
@@ -1024,7 +1039,7 @@ system :with_sequential_behavior do
|
|
1024
1039
|
end
|
1025
1040
|
```
|
1026
1041
|
|
1027
|
-
A sub-block can also have a different execution mode if it is declared using `seq`, which will force sequential execution mode and `par` which will force parallel execution mode. For example, in the following code a parallel sub-block is declared within a sequential one:
|
1042
|
+
A sub-block can also have a different execution mode if it is declared using `seq`, which will force sequential execution mode, and `par` which will force parallel execution mode. For example, in the following code a parallel sub-block is declared within a sequential one:
|
1028
1043
|
|
1029
1044
|
```ruby
|
1030
1045
|
system :with_sequential_behavior do
|
@@ -1133,7 +1148,7 @@ end
|
|
1133
1148
|
( a <= b+1 ).at(clk.posedge)
|
1134
1149
|
```
|
1135
1150
|
|
1136
|
-
For sake of consistency, this operator can also be applied to block statements as follows, but it is probably less readable than the standard declaration of behaviors:
|
1151
|
+
For the sake of consistency, this operator can also be applied to block statements as follows, but it is probably less readable than the standard declaration of behaviors:
|
1137
1152
|
|
1138
1153
|
```ruby
|
1139
1154
|
( seq do
|
@@ -1247,8 +1262,7 @@ __Note:__
|
|
1247
1262
|
## Statements
|
1248
1263
|
|
1249
1264
|
Statements are the basic elements of a behavioral description. They are regrouped in blocks that specify their execution mode (parallel or sequential).
|
1250
|
-
There are four kinds of statements: the transmit statement which computes expressions and sends the result to the target signals, the control statement
|
1251
|
-
that changes the execution flow of the behavior, the block statement (described earlier), and the inner signal declaration.
|
1265
|
+
There are four kinds of statements: the transmit statement which computes expressions and sends the result to the target signals, the control statement which changes the execution flow of the behavior, the block statement (described earlier), and the inner signal declaration.
|
1252
1266
|
|
1253
1267
|
__Note__:
|
1254
1268
|
|
@@ -1329,8 +1343,7 @@ end
|
|
1329
1343
|
|
1330
1344
|
#### About loops
|
1331
1345
|
|
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
|
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).
|
1346
|
+
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 that the current synthesis tools do not 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).
|
1334
1347
|
|
1335
1348
|
__Notes__:
|
1336
1349
|
|
@@ -1348,10 +1361,9 @@ __Notes__:
|
|
1348
1361
|
## Types
|
1349
1362
|
<a name="types"></a>
|
1350
1363
|
|
1351
|
-
Each signal and each expression is associated with a data type that describes the kind of value it can represent. In HDLRuby, the data types represent
|
1352
|
-
bit-vectors associated with the way they should be interpreted, i.e., as bit strings, unsigned values, signed values, or hierarchical contents.
|
1364
|
+
Each signal and each expression is associated with a data type that describes the kind of value it can represent. In HDLRuby, the data types represent bit-vectors associated with the way they should be interpreted, i.e., as bit strings, unsigned values, signed values, or hierarchical contents.
|
1353
1365
|
|
1354
|
-
### Type
|
1366
|
+
### Type Construction
|
1355
1367
|
|
1356
1368
|
There are five basic types, `bit`, `signed`, `unsigned`, `integer`, and `float` that represent respectively single bit logical values, single-bit unsigned values, single-bit signed values, Ruby integer values, and Ruby floating-point values (double precision). The first three types are HW and support four-valued logic, whereas the two last ones are SW (but are compatible with HW) and only support Boolean logic. Ruby integers can represent any element of **Z** (the mathematical integers) and have for that purpose a variable bit-width.
|
1357
1369
|
|
@@ -1454,9 +1466,9 @@ They include [immediate values](#immediate-values), [reference to signals](#refe
|
|
1454
1466
|
|
1455
1467
|
### Immediate values
|
1456
1468
|
|
1457
|
-
The immediate values of HDLRuby can represent vectors of `bit`, `unsigned`, and `signed`, and integer or floating-point numbers. They are prefixed by a `_` character and include a header that indicates the vector type and the base used for representing the value, followed by a numeral representing the value. The bit width of a value is obtained by default from the width of the numeral, but it is also possible to specify it in the header. In addition, the character `_` can be put anywhere in the number
|
1469
|
+
The immediate values of HDLRuby can represent vectors of `bit`, `unsigned`, and `signed`, and integer or floating-point numbers. They are prefixed by a `_` character and include a header that indicates the vector type and the base used for representing the value, followed by a numeral representing the value. The bit width of a value is obtained by default from the width of the numeral, but it is also possible to specify it in the header. In addition, the character `_` can be put anywhere in the number to increase the readability, but it will be ignored.
|
1458
1470
|
|
1459
|
-
The vector type specifiers are the
|
1471
|
+
The vector type specifiers are the following:
|
1460
1472
|
|
1461
1473
|
- `b`: `bit` type, can be omitted,
|
1462
1474
|
|
@@ -1464,7 +1476,7 @@ The vector type specifiers are the followings:
|
|
1464
1476
|
|
1465
1477
|
- `s`: `signed` type, the last figure is sign-extended if required by the binary, octal, and hexadecimal bases, but not for the decimal base.
|
1466
1478
|
|
1467
|
-
The base specifiers are the
|
1479
|
+
The base specifiers are the following:
|
1468
1480
|
|
1469
1481
|
- `b`: binary,
|
1470
1482
|
|
@@ -1510,8 +1522,7 @@ __Notes__:
|
|
1510
1522
|
|
1511
1523
|
References are expressions used to designate signals or a part of signals.
|
1512
1524
|
|
1513
|
-
The simplest reference is simply the name of a signal. It designates the signal corresponding to this name in the current scope. For instance, in the
|
1514
|
-
following code, inner signal `sig0` is declared, and therefore the name *sig0* becomes a reference to designate this signal.
|
1525
|
+
The simplest reference is simply the name of a signal. It designates the signal corresponding to this name in the current scope. For instance, in the following code, inner signal `sig0` is declared, and therefore the name *sig0* becomes a reference to designate this signal.
|
1515
1526
|
|
1516
1527
|
```ruby
|
1517
1528
|
# Declaration of signal sig0.
|
@@ -1603,7 +1614,7 @@ __Conversion operators:__
|
|
1603
1614
|
| :to\_bit | cast to bit vector |
|
1604
1615
|
| :to\_unsigned | cast to unsigned vector |
|
1605
1616
|
| :to\_signed | cast to signed vector |
|
1606
|
-
| :to\_big | cast to big
|
1617
|
+
| :to\_big | cast to big-endian |
|
1607
1618
|
| :to\_little | cast to little endian |
|
1608
1619
|
| :reverse | reverse the bit order |
|
1609
1620
|
| :ljust | increase width from the left, preserves the sign |
|
@@ -1615,7 +1626,7 @@ __Selection /concatenation operators:__
|
|
1615
1626
|
|
1616
1627
|
| symbol | description |
|
1617
1628
|
| :--- | :--- |
|
1618
|
-
| :[] | sub
|
1629
|
+
| :[] | sub-vector selection |
|
1619
1630
|
| :@[] | concatenation operator |
|
1620
1631
|
| :. | field selection |
|
1621
1632
|
|
@@ -1693,8 +1704,8 @@ The type puns include `to_bit`, `to_unsigned`, and `to_signed` that convert expr
|
|
1693
1704
|
sig.to_bit <= _b01010011
|
1694
1705
|
```
|
1695
1706
|
|
1696
|
-
The type casts change both the type and the value and are used to adjust the width of the types. They can only be applied to vectors of `bit`, `signed`, or `
|
1697
|
-
These operators comprise the bit width conversions: `ljust`, `rjust`, `zext
|
1707
|
+
The type casts change both the type and the value and are used to adjust the width of the types. They can only be applied to vectors of `bit`, `signed`, or `unsigned` and can only increase the bit width (bit width can be truncated using the selection operator, please refer to the next section).
|
1708
|
+
These operators comprise the bit width conversions: `ljust`, `rjust`, `zext`, and `sext`.
|
1698
1709
|
|
1699
1710
|
More precisely, the bit width conversions operate as follows:
|
1700
1711
|
|
@@ -1757,7 +1768,7 @@ Concatenation and selection are done using the `[]` operator as follows:
|
|
1757
1768
|
#### Implicit conversions
|
1758
1769
|
<a name="implicit"></a>
|
1759
1770
|
|
1760
|
-
When there is no ambiguity, HDLRuby will automatically insert conversion operators when two types are not compatible with one another. The cases where such implicit conversions are applied are summarized in the following tables where:
|
1771
|
+
When there is no ambiguity, HDLRuby will automatically insert conversion operators when two types are not compatible with one another. The cases where such implicit conversions are applied are summarized in the following tables, where:
|
1761
1772
|
|
1762
1773
|
- `operator` is the operator in use
|
1763
1774
|
- `result width` is the width of the result's type
|
@@ -1854,7 +1865,7 @@ __Notes__:
|
|
1854
1865
|
function :one { 1 }
|
1855
1866
|
```
|
1856
1867
|
|
1857
|
-
- Functions can accept any
|
1868
|
+
- Functions can accept any object as an argument, including variadic arguments or blocks of code as shown below with a function that applies the code passed as an argument to all the variadic arguments of `args`:
|
1858
1869
|
|
1859
1870
|
```ruby
|
1860
1871
|
function :apply do |*args, &code|
|
@@ -1862,7 +1873,7 @@ __Notes__:
|
|
1862
1873
|
end
|
1863
1874
|
```
|
1864
1875
|
|
1865
|
-
Such a function can be used for example for connecting a signal to a set of other signals as follows (where `sig` is connected to `x`, `y
|
1876
|
+
Such a function can be used for example for connecting a signal to a set of other signals as follows (where `sig` is connected to `x`, `y`, and `z`):
|
1866
1877
|
```ruby
|
1867
1878
|
apply(x,y,z) { |v| v <= sig }
|
1868
1879
|
```
|
@@ -1915,7 +1926,7 @@ As another example, the following function will add an alternative code that gen
|
|
1915
1926
|
|
1916
1927
|
```ruby
|
1917
1928
|
def too_bad
|
1918
|
-
helse {
|
1929
|
+
helse { rst <= 1 }
|
1919
1930
|
end
|
1920
1931
|
```
|
1921
1932
|
|
@@ -1936,6 +1947,308 @@ end
|
|
1936
1947
|
Ruby functions can be compared to the macros of the C languages: they are more flexible since they edit the code they are invoked in, but they are also dangerous to use. In general, it is not recommended to use them, unless when designing a library of generic code for HDLRuby.
|
1937
1948
|
|
1938
1949
|
|
1950
|
+
## Software code
|
1951
|
+
|
1952
|
+
It is possible to describe hardware-software components in HDLRuby using the `program` construct that encapsulates some software code a provides an interface for communicating with the hardware. This interface is composed of three kinds of components:
|
1953
|
+
|
1954
|
+
* The activation events: 1-bit signal that triggers the execution of a given software function when the switch from 0 to 1 (or for negative ones, from 1 to 0).
|
1955
|
+
|
1956
|
+
* The reading ports: bit-vector signals that can be read from a software function.
|
1957
|
+
|
1958
|
+
* The writing ports: bit-vector signals that can be written from a software function.
|
1959
|
+
|
1960
|
+
__Note:__
|
1961
|
+
|
1962
|
+
The same signal can be used at the same time by multiple ports for both reading and writing, however, from the software point of view, it will correspond to two different ports.
|
1963
|
+
|
1964
|
+
### Declaring a software component
|
1965
|
+
|
1966
|
+
A software component is declared like a hardware process within a system. The syntax is the following:
|
1967
|
+
|
1968
|
+
```ruby
|
1969
|
+
program(<programming language>, <function name>) do
|
1970
|
+
<location of the software files and description of its interface>
|
1971
|
+
end
|
1972
|
+
```
|
1973
|
+
|
1974
|
+
In the code above, `programming language` is a symbol representing the programming language used for the software. For now, only two languages are supported:
|
1975
|
+
|
1976
|
+
* `:ruby`: for programs in Ruby.
|
1977
|
+
|
1978
|
+
* `:c`: for programs in C. However, for this case, any language that can be compiled to a shared library linkable with C is supported.
|
1979
|
+
|
1980
|
+
The `function name` parameter indicates which function is to be executed when an activation event occurs. There can be only one such function per program, but any number of programs can be declared inside the same module.
|
1981
|
+
|
1982
|
+
The `location of the software files and description of its interface` part can include the following declaration statements:
|
1983
|
+
|
1984
|
+
* `actport <list of events>`: for declaring the list of events that activates the program, i.e., that will trigger the execution of the program's start function.
|
1985
|
+
|
1986
|
+
* `inport <list of port names associated with a signal>`: for declaring the list of ports that the software code of the program can read.
|
1987
|
+
|
1988
|
+
* `outport <list of port names associated with a signal>`: for declaring the list of ports that the software code of the program can write to.
|
1989
|
+
|
1990
|
+
* `code <list of filenames>`: for declaring the source code files.
|
1991
|
+
|
1992
|
+
For example the following declares a program in the Ruby language whose start function is `echo` that is activated on the positive edge of signal `req`, has a read port called `inP` that is connected to signal `count` and a write port called `outP` that is connected to signal `val`, finally the code of this program is given in a file named `echo.rb`:
|
1993
|
+
|
1994
|
+
```ruby
|
1995
|
+
system :my_system do
|
1996
|
+
inner :req
|
1997
|
+
[8].inner :count, :val
|
1998
|
+
|
1999
|
+
...
|
2000
|
+
|
2001
|
+
program(:ruby,'echo') do
|
2002
|
+
actport req.posedge
|
2003
|
+
inport inP: count
|
2004
|
+
outport outP: val
|
2005
|
+
code "echo.rb"
|
2006
|
+
end
|
2007
|
+
end
|
2008
|
+
```
|
2009
|
+
|
2010
|
+
|
2011
|
+
__Note:__
|
2012
|
+
|
2013
|
+
The size of the input and output ports is one of the signals they give access to. However, from the software side, their values are converted to `long long` types.
|
2014
|
+
|
2015
|
+
|
2016
|
+
|
2017
|
+
### About the software code used in HDLRuby programs
|
2018
|
+
|
2019
|
+
#### Location and format of the files
|
2020
|
+
|
2021
|
+
The file names indicating the software code to use must indicate the path to these files relative to where the HDLRuby tools are used. In the example above, that would mean that the `echo.rb` program must be in the same directory as the HDLRuby description. If this code were to be into a `ruby` directory under the current directory, the declaration would become: `code "ruby/echo.rb"`.
|
2022
|
+
For the Ruby language, any number of source files can be declared, and plain Ruby code can be used as is. However, for the C language, the software code must first be compiled, and it is the resulting file that must be declared in the code declaration. For example, if for the example above, C had to be used, then the program description would be the following:
|
2023
|
+
|
2024
|
+
```ruby
|
2025
|
+
program(:c, :echo) do
|
2026
|
+
actport req.posedge
|
2027
|
+
inport inP: count
|
2028
|
+
outport outP: val
|
2029
|
+
code "echo"
|
2030
|
+
end
|
2031
|
+
```
|
2032
|
+
|
2033
|
+
Then, for this program to work, the C code must be compiled to a file named `echo`. Please notice that, in the example, the extension is omitted, to allow the system to look for the valid file type (e.g., `.so` for a Linux shared library).
|
2034
|
+
|
2035
|
+
__Note:__
|
2036
|
+
|
2037
|
+
The same software file can be used for several different program constructs, however the functions it contains will be unique for the whole device.
|
2038
|
+
|
2039
|
+
|
2040
|
+
#### The hardware interface
|
2041
|
+
|
2042
|
+
From the software point of view, the hardware interface consists only of a list of ports that can either be read or written. However, the implementation of this interface depends on the language.
|
2043
|
+
|
2044
|
+
##### For Ruby
|
2045
|
+
|
2046
|
+
For ruby, the interface is accessed by requiring the `rubyHDL` library. It gives provides the `RubyHDL` module that provides accessors to ports of the program.
|
2047
|
+
For example, the following program reads on port `inP` and writes the results on port `outP`:
|
2048
|
+
|
2049
|
+
```ruby
|
2050
|
+
require 'rubyHDL'
|
2051
|
+
|
2052
|
+
def echo
|
2053
|
+
val = RubyHDL.inP
|
2054
|
+
RubyHDL.outP = val
|
2055
|
+
end
|
2056
|
+
```
|
2057
|
+
|
2058
|
+
__Note:__
|
2059
|
+
|
2060
|
+
As long as a port has been declared in the HDLRuby description of the program, it will be available to the software as a module accessor without the need for any additional declaration or configuration.
|
2061
|
+
|
2062
|
+
##### For C
|
2063
|
+
|
2064
|
+
For C (and C-compatible compiled languages), the interface is accessed by including the `cHDL.h` file. This file must be generated using the following command:
|
2065
|
+
|
2066
|
+
```bash
|
2067
|
+
hdrcc --ch <destination project>
|
2068
|
+
```
|
2069
|
+
|
2070
|
+
In the command above, `<destination project>` is the directory where the C code is meant to be.
|
2071
|
+
|
2072
|
+
|
2073
|
+
Once generated, this file provides the three following C functions:
|
2074
|
+
|
2075
|
+
* `void* c_get_port(const char* name)`: returns a pointer to the port whose name is passed as argument.
|
2076
|
+
|
2077
|
+
* `int c_read_port(void* port)`: reads the port whose pointer is passed as argument and returns its value.
|
2078
|
+
|
2079
|
+
* `int c_write_port(void* port, int val)`: write the value `val` to the port passed as argument.
|
2080
|
+
|
2081
|
+
For example, the following program reads on port `inP` and writes the results on port `outP`:
|
2082
|
+
|
2083
|
+
```c
|
2084
|
+
#include "cHDL.h"
|
2085
|
+
|
2086
|
+
void echo() {
|
2087
|
+
void* inP = c_get_port("inP");
|
2088
|
+
void* outP = c_get_port("outP");
|
2089
|
+
int val;
|
2090
|
+
|
2091
|
+
val = c_read_port(inP);
|
2092
|
+
c_write_port(outP,val);
|
2093
|
+
}
|
2094
|
+
```
|
2095
|
+
|
2096
|
+
__Note:__
|
2097
|
+
|
2098
|
+
* The command for generating the C header file for using the HDLRuby hardware interface also generates files for helping to compile the source code. Please see [compile for simulation](#compiling-the-c-code).
|
2099
|
+
|
2100
|
+
* **Important:** for windows, dynamically loaded functions must be declared with the following prefix: `__declspec(dllexport)`. If this prefix is not present before each function that is used as an HDLRuby program, the simulation will not work. For example, for Windows, the function echo *must* be written as follows:
|
2101
|
+
|
2102
|
+
```c
|
2103
|
+
#include "cHDL.h"
|
2104
|
+
|
2105
|
+
__declspec(dllexport) void echo() {
|
2106
|
+
void* inP = c_get_port("inP");
|
2107
|
+
void* outP = c_get_port("outP");
|
2108
|
+
int val;
|
2109
|
+
|
2110
|
+
val = c_read_port(inP);
|
2111
|
+
c_write_port(outP,val);
|
2112
|
+
}
|
2113
|
+
```
|
2114
|
+
|
2115
|
+
|
2116
|
+
|
2117
|
+
#### Hardware-software co-simulation
|
2118
|
+
|
2119
|
+
As long as your programs a correctly described and the software files provided (and compiled in the case of C), the hardware-software co-simulation will be automatically performed when executing the HDLRuby simulator.
|
2120
|
+
|
2121
|
+
##### Compiling the C code
|
2122
|
+
|
2123
|
+
While programs in Ruby can be used directly, programs in C must be compiled first. For that purpose, the required files, including the hardware interface `cHDL.h`, must be generated. This is done by using the following HDLRruby command:
|
2124
|
+
|
2125
|
+
```bash
|
2126
|
+
hdrcc --ch <destination project>
|
2127
|
+
```
|
2128
|
+
|
2129
|
+
In the command above, `<destination project>` is both the directory where the C code is and the name of the resulting shared library.
|
2130
|
+
|
2131
|
+
For example, if you want to compile the code located in the directory `echo` you need first to execute:
|
2132
|
+
|
2133
|
+
```bash
|
2134
|
+
hdrcc --ch echo
|
2135
|
+
```
|
2136
|
+
|
2137
|
+
Then, you will have to put your C files into the resulting directory and go inside it for compiling. If you have some specific needs for this compiling, or if you do not want to rely on the Ruby environment, you can compile your program there as a shared library like any other project. For example, if you are using GCC, you could type (after entering the `echo` directory):
|
2138
|
+
|
2139
|
+
```bash
|
2140
|
+
gcc -shared -fPIC -undefined dynamic_lookup -o c_program.so echo.c
|
2141
|
+
```
|
2142
|
+
|
2143
|
+
The command above is for compiling a single file project on a Linux system.
|
2144
|
+
|
2145
|
+
Otherwise, it may be easier to use the Ruby environment by first installing `rake-compiler` as follows:
|
2146
|
+
|
2147
|
+
```bash
|
2148
|
+
gem install rake-compiler
|
2149
|
+
```
|
2150
|
+
|
2151
|
+
And simply type the following command (after entering the `echo` directory):
|
2152
|
+
|
2153
|
+
```bash
|
2154
|
+
rake compile
|
2155
|
+
```
|
2156
|
+
|
2157
|
+
The rake tool will take care of everything for performing the compiling whatever your system may be.
|
2158
|
+
|
2159
|
+
|
2160
|
+
#### Hardware generation
|
2161
|
+
|
2162
|
+
In the current stage, HDLRuby only generates the hardware part of a description. E.g., when generating Verilog, the programs are simply being ignored. It is therefore up to the user to provide additional code for implementing the hardware-software interface. The reason is that such interfaces are target-dependent, and often comprise licensed software and IP components that cannot be integrated into HDLruby.
|
2163
|
+
|
2164
|
+
This is less a limitation than it seems since it is possible to write program constructs that wrap such accesses so that the software and HDLRuby code can be used as is in the target system. As an illustration, you can consult the example given in the tutorial: [7.6. Hardware-software co-synthesis](tuto/tutorial_sw.md#7-6-hardware-software-co-synthesis).
|
2165
|
+
|
2166
|
+
|
2167
|
+
### Extended co-simulation
|
2168
|
+
|
2169
|
+
Since HDLRuby programs can support any compiled software, these components can also be used for executing any kind of application that is not specifically meant to be executed on the target CPU. For instance, some peripheral circuits like a keyboard or a monitor can be modeled using an HDLRuby program, as illustrated in the HDLRuby sample `with_program_ruby_cpu.rb`.
|
2170
|
+
|
2171
|
+
|
2172
|
+
|
2173
|
+
### Development board simulation graphical interface
|
2174
|
+
|
2175
|
+
HDLRuby provides a web browser-based GUI for the simulator as an extension of the co-design platform presented in this section. This GUI is to be declared as follows within a module:
|
2176
|
+
|
2177
|
+
```ruby
|
2178
|
+
board(:<board name>,<server port>) do
|
2179
|
+
actport <event>
|
2180
|
+
<description of the GUI>
|
2181
|
+
end
|
2182
|
+
```
|
2183
|
+
|
2184
|
+
In the code above, `board name` is the name of the board, `server port` is the local port the browser has to connect to for accessing the GUI (by default it is set to 8000), and `event` is the event (e.g., the rising edge of a clock) that activates the synchronization of the GUI with the simulator.
|
2185
|
+
|
2186
|
+
Then the description of the GUI consists of a list of the following possible development board-oriented elements. Active elements are to be given a name and attached to a HDLRuby signal as follows:
|
2187
|
+
|
2188
|
+
```ruby
|
2189
|
+
<element> <element name>: <HDLRuby signal>
|
2190
|
+
```
|
2191
|
+
|
2192
|
+
The list of possible elements is as follows:
|
2193
|
+
|
2194
|
+
* `sw`: represents a set of slide switches, their number is set to match the bit-width of the attached signal.
|
2195
|
+
|
2196
|
+
* `bt`: represents a set of push buttons, their number is set to match the bit-width of the attached signal.
|
2197
|
+
|
2198
|
+
* `led`: represents a set of LEDs, their number is set to match the bit-width of the attached signal.
|
2199
|
+
|
2200
|
+
* `hexa`: represents a hexadecimal number display, its character width is set to match the width of the largest possible value of the attached signal.
|
2201
|
+
|
2202
|
+
* `digit`: represents a decimal number display, its character width is set to match the width of the largest possible positive or the smallest possible negative value of the attached signal.
|
2203
|
+
|
2204
|
+
* `scope`: represents an oscilloscope display, the vertical axis represents the value of the attached signal, its range is determined by its data type, and the horizontal axis represents the time is number of synchronization of the GUI.
|
2205
|
+
|
2206
|
+
* `row`: inserts a new line in the GUI.
|
2207
|
+
|
2208
|
+
|
2209
|
+
For example, for a GUI presenting an interface to an adder with input signals `x` and `y` and output signal `z` displayed as LEDs, a digit display, and an oscilloscope, the following description can be used:
|
2210
|
+
|
2211
|
+
```ruby
|
2212
|
+
system :adder_with_gui do
|
2213
|
+
[8].inner :x, :y, :z
|
2214
|
+
|
2215
|
+
z <= x + y
|
2216
|
+
|
2217
|
+
inner :gui_sync
|
2218
|
+
|
2219
|
+
board(:adder_gui) do
|
2220
|
+
actport gui_sync.posedge
|
2221
|
+
sw x: x
|
2222
|
+
sw y: y
|
2223
|
+
row
|
2224
|
+
led z_led: z
|
2225
|
+
digit z_digit: z
|
2226
|
+
row
|
2227
|
+
scope z_scope: z
|
2228
|
+
end
|
2229
|
+
|
2230
|
+
timed do
|
2231
|
+
clk <= 0
|
2232
|
+
repeat(10000) do
|
2233
|
+
!10.ns
|
2234
|
+
clk <= ~clk
|
2235
|
+
end
|
2236
|
+
end
|
2237
|
+
end
|
2238
|
+
```
|
2239
|
+
|
2240
|
+
With the code above, the GUI will show a row containing two sets of slide switches for input `x` and `y`, a row containing a set of LEDs and a digital display for showing `z`, and a row containing an oscilloscope for showing the evolution `z`.
|
2241
|
+
|
2242
|
+
This code is simulated exactly like any other HDLRuby description, e.g., the following command will start the simulation and generate a VCD wave file:
|
2243
|
+
|
2244
|
+
```bash
|
2245
|
+
hdrcc --sim --vcd my_adder.rb my_adder
|
2246
|
+
```
|
2247
|
+
|
2248
|
+
However, the simulator will wait until a browser connects to it. For that, you can open a web browser, and go to the local url: `http://localhost:8000`. The simulation will then start and you can interact with the GUI.
|
2249
|
+
|
2250
|
+
|
2251
|
+
|
1939
2252
|
## Time
|
1940
2253
|
|
1941
2254
|
### Time values
|
@@ -1978,7 +2291,7 @@ There are two kinds of such statements:
|
|
1978
2291
|
!10.ns
|
1979
2292
|
```
|
1980
2293
|
|
1981
|
-
- The `repeat` statements: such a statement takes as argument
|
2294
|
+
- The `repeat` statements: such a statement takes as argument the number of iterations and a block. The execution of the block is repeated the given number of times. For example, the following code executes 10 times the inversion of the `clk` signal every 10 nanoseconds:
|
1982
2295
|
|
1983
2296
|
```ruby
|
1984
2297
|
repeat(10) do
|
@@ -2002,7 +2315,7 @@ sequential blocks. The execution semantic is the following:
|
|
2002
2315
|
|
2003
2316
|
1. Statements are grouped in sequence until a time statement is met.
|
2004
2317
|
|
2005
|
-
2. The grouped
|
2318
|
+
2. The grouped sequences are executed in parallel.
|
2006
2319
|
|
2007
2320
|
3. The time statement is executed.
|
2008
2321
|
|
@@ -2146,8 +2459,7 @@ In the code above, `some_sig` is a signal available in the current context. This
|
|
2146
2459
|
|
2147
2460
|
#### Basics
|
2148
2461
|
|
2149
|
-
In HDLRuby, a system can inherit from the content of one or several other parent systems using the `include` command as follows: `include <list of
|
2150
|
-
systems>`. Such an include can be put anywhere in the body of a system, but the resulting content will be accessible only after this command.
|
2462
|
+
In HDLRuby, a system can inherit from the content of one or several other parent systems using the `include` command as follows: `include <list of systems>`. Such an include can be put anywhere in the body of a system, but the resulting content will be accessible only after this command.
|
2151
2463
|
|
2152
2464
|
For example, the following code describes first a simple D-FF, and then uses it to describe a FF with an additional reversed output (`qb`):
|
2153
2465
|
|
@@ -2193,8 +2505,7 @@ __Note__:
|
|
2193
2505
|
|
2194
2506
|
#### About inner signals and system instances
|
2195
2507
|
|
2196
|
-
By default, inner signals and instances of a parent system are not accessible by its child systems. They can be made accessible using the `export` keyword as follows: `export <symbol 0>, <symbol 1>,
|
2197
|
-
code exports signals `clk` and `rst` and instance `dff0` of system `exporter` so that they can be accessed in child system `importer`.
|
2508
|
+
By default, inner signals and instances of a parent system are not accessible by its child systems. They can be made accessible using the `export` keyword as follows: `export <symbol 0>, <symbol 1>, ...`. For example, the following code exports signals `clk` and `rst` and instance `dff0` of system `exporter` so that they can be accessed in child system `importer`.
|
2198
2509
|
|
2199
2510
|
```ruby
|
2200
2511
|
system :exporter do
|
@@ -2356,7 +2667,7 @@ end
|
|
2356
2667
|
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.
|
2357
2668
|
|
2358
2669
|
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.
|
2359
|
-
For instance, let us consider the following fixed-point type of variable width (
|
2670
|
+
For instance, let us consider the following fixed-point type of variable width (whose decimal point is set at half of its bit range):
|
2360
2671
|
|
2361
2672
|
```ruby
|
2362
2673
|
typedef(:fixed) do |width|
|
@@ -2402,22 +2713,6 @@ Several enumerators are also provided for accessing the internals of the current
|
|
2402
2713
|
| `each_statement` | statements of the current block |
|
2403
2714
|
| `each_inner` | inner signals of the current block (or system if not within a block) |
|
2404
2715
|
|
2405
|
-
### Global signals
|
2406
|
-
|
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.
|
2408
|
-
|
2409
|
-
To ease the design of standardized libraries, the following global signals are defined by default:
|
2410
|
-
|
2411
|
-
| signal name | signal type | signal function |
|
2412
|
-
| :--- | :--- | :--- |
|
2413
|
-
| `$reset` | bit | global reset |
|
2414
|
-
| `$resetb` | bit | global reset complement |
|
2415
|
-
| `$clk` | bit | global clock |
|
2416
|
-
|
2417
|
-
__Note__:
|
2418
|
-
|
2419
|
-
- When not used, the global signals are discarded.
|
2420
|
-
|
2421
2716
|
|
2422
2717
|
|
2423
2718
|
### Defining and executing Ruby methods within HDLRuby constructs
|
@@ -2497,7 +2792,7 @@ end
|
|
2497
2792
|
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:
|
2498
2793
|
|
2499
2794
|
```ruby
|
2500
|
-
def after(cycles,
|
2795
|
+
def after(cycles, rst, &code)
|
2501
2796
|
sub do
|
2502
2797
|
inner :count
|
2503
2798
|
hif rst == 1 do
|
@@ -2517,34 +2812,28 @@ end
|
|
2517
2812
|
|
2518
2813
|
In the code above:
|
2519
2814
|
|
2520
|
-
- the default initialization of `rst` to `$rst` allows resetting the counter even if no such signal is provided as an argument.
|
2521
|
-
|
2522
2815
|
- `sub` ensures that the `count` signal does not conflict with another signal with the same name.
|
2523
2816
|
|
2524
2817
|
- the `instance_eval` keyword is a standard Ruby method that executes the block passed as an argument in context.
|
2525
2818
|
|
2526
|
-
The following is an example that switches
|
2819
|
+
The following is an example that switches an LED on after 1000000 clock cycles using the previously defined `after` ruby method:
|
2527
2820
|
|
2528
2821
|
```ruby
|
2529
2822
|
system :led_after do
|
2530
2823
|
output :led
|
2531
|
-
input :clk
|
2824
|
+
input :clk, :rst
|
2532
2825
|
|
2533
2826
|
par(clk.posedge) do
|
2534
|
-
(led <= 0).hif(
|
2535
|
-
after(100000) { led <= 1 }
|
2827
|
+
(led <= 0).hif(rst)
|
2828
|
+
after(100000,rst) { led <= 1 }
|
2536
2829
|
end
|
2537
2830
|
end
|
2538
2831
|
```
|
2539
2832
|
|
2540
2833
|
__Note__:
|
2541
2834
|
|
2542
|
-
- Ruby's closure still applies in HDLRuby, hence, the block sent to `after` can use the signals and instances of the current block. Moreover, the
|
2543
|
-
|
2544
|
-
|
2545
|
-
### Dynamic description
|
2835
|
+
- Ruby's closure still applies in HDLRuby, hence, the block sent to `after` can use the signals and instances of the current block. Moreover, the signals declared in this method will not collide with them.
|
2546
2836
|
|
2547
|
-
When describing a system, it is possible to disconnect or completely undefine a signal or an instance.
|
2548
2837
|
|
2549
2838
|
|
2550
2839
|
## Extending HDLRuby
|
@@ -2582,11 +2871,12 @@ The following table gives the class of each construct of HDLRuby.
|
|
2582
2871
|
| transmit | Transmit |
|
2583
2872
|
| hif | If |
|
2584
2873
|
| hcase | Case |
|
2874
|
+
| program | Program |
|
2585
2875
|
|
2586
2876
|
|
2587
2877
|
### Extending HDLRuby constructs locally
|
2588
2878
|
|
2589
|
-
By local extension of a hardware construct, we mean that while the construct will be changed, all the other constructs will remain unchanged. This is achieved like
|
2879
|
+
By local extension of a hardware construct, we mean that while the construct will be changed, all the other constructs will remain unchanged. This is achieved like with Ruby by accessing the Eigen class using the `singleton_class` method and extending it using the `class_eval` method. For example, with the following code, only system `dff` will respond to method `interface_size`:
|
2590
2880
|
|
2591
2881
|
```ruby
|
2592
2882
|
dff.singleton_class.class_eval do
|
@@ -2691,7 +2981,7 @@ After the libraries are loaded, the module `Std` must be included as follows:
|
|
2691
2981
|
include HDLRuby::High::Std
|
2692
2982
|
```
|
2693
2983
|
|
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
|
2984
|
+
> 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 following:
|
2695
2985
|
|
2696
2986
|
- `std/clocks.rb`
|
2697
2987
|
|
@@ -2746,7 +3036,7 @@ Where:
|
|
2746
3036
|
* `<clock>` is the clock to use, this argument can be omitted.
|
2747
3037
|
* `<reset>` is the signal used to reset the counter used for waiting, this argument can be omitted.
|
2748
3038
|
|
2749
|
-
This statement can be used
|
3039
|
+
This statement can be used inside a clocked behavior where the clock event of the behavior is used for the counter unless specified otherwise.
|
2750
3040
|
|
2751
3041
|
The second construct is the `before` statement that activates a block until a given number of clock cycles is passed. Its syntax and usage are identical to the `after` statement.
|
2752
3042
|
|
@@ -2792,7 +3082,7 @@ A finite state machine can be declared anywhere in a system provided it is outsi
|
|
2792
3082
|
fsm(<event>,<reset>,<mode>) <block>
|
2793
3083
|
```
|
2794
3084
|
|
2795
|
-
Where `event` is the event (rising or falling edge of a signal) activating the state transitions, `rst` is the reset signal,
|
3085
|
+
Where `event` is the event (rising or falling edge of a signal) activating the state transitions, `rst` is the reset signal, `mode` is the default execution mode, and `block` is the execution block describing the states of the FSM. This last parameter can be either `:sync` for synchronous (Moore type) or `:async` for asynchronous (Mealy type).
|
2796
3086
|
|
2797
3087
|
The states of an FSM are described as follows:
|
2798
3088
|
|
@@ -2800,12 +3090,12 @@ The states of an FSM are described as follows:
|
|
2800
3090
|
<kind>(<name>) <block>
|
2801
3091
|
```
|
2802
3092
|
|
2803
|
-
Where `kind` is the kind of state, `name` is the name of the state, and `block` is the actions to execute for the corresponding state. The kinds of states are the
|
3093
|
+
Where `kind` is the kind of state, `name` is the name of the state, and `block` is the actions to execute for the corresponding state. The kinds of states are the following:
|
2804
3094
|
|
2805
3095
|
* reset: the state reached when resetting the FSM. This state can be forced to be asynchronous by setting the `name` argument to `:async` and forced to be synchronous by setting the `name` argument to `:sync`. By default, the `name` argument is to be omitted.
|
2806
|
-
* state: the default kind of state,
|
2807
|
-
* sync: the synchronous kind of state,
|
2808
|
-
* async: the asynchronous kind of state,
|
3096
|
+
* state: the default kind of state, will be synchronous if the FSM is synchronous or asynchronous otherwise.
|
3097
|
+
* sync: the synchronous kind of state, will be synchronous whatever the kind of FSM is used.
|
3098
|
+
* async: the asynchronous kind of state, will be asynchronous whatever the kind of FSM is used.
|
2809
3099
|
|
2810
3100
|
In addition, it is possible to define a default action that will be executed whatever the state is using the following statement:
|
2811
3101
|
|
@@ -2853,7 +3143,7 @@ fsm(clk.posedge,rst,:sync) do
|
|
2853
3143
|
end
|
2854
3144
|
```
|
2855
3145
|
|
2856
|
-
__Note__: the goto statements act globally, i.e., they are independent of the place where they are declared within the state. For example for both following statements, the next state will always be `st_a` whatever `cond`
|
3146
|
+
__Note__: the goto statements act globally, i.e., they are independent of the place where they are declared within the state. For example for both following statements, the next state will always be `st_a` whatever `cond` may be:
|
2857
3147
|
|
2858
3148
|
```ruby
|
2859
3149
|
state(:st_0) do
|
@@ -3010,11 +3300,11 @@ The enumerators can be controlled using the following methods:
|
|
3010
3300
|
|
3011
3301
|
- `type`: returns the type of the elements accessed by the enumerator.
|
3012
3302
|
|
3013
|
-
- `seach`: returns the current enumerator. If a block is given, performs the iteration instead of returning an enumerator.
|
3303
|
+
- `seach`: returns the current enumerator. If a block is given, it performs the iteration instead of returning an enumerator.
|
3014
3304
|
|
3015
|
-
- `seach_with_index`: returns an enumerator over the elements of the current enumerator associated with their index position. If a block is given, performs the iteration instead of returning an enumerator.
|
3305
|
+
- `seach_with_index`: returns an enumerator over the elements of the current enumerator associated with their index position. If a block is given, it performs the iteration instead of returning an enumerator.
|
3016
3306
|
|
3017
|
-
- `seach_with_object(<obj>)`: returns an enumerator over the elements of the current enumerator associated with object `obj` (any object, HDLRuby or not, can be used). If a block is given, performs the iteration instead of returning an enumerator.
|
3307
|
+
- `seach_with_object(<obj>)`: returns an enumerator over the elements of the current enumerator associated with object `obj` (any object, HDLRuby or not, can be used). If a block is given, it performs the iteration instead of returning an enumerator.
|
3018
3308
|
|
3019
3309
|
- `with_index`: identical to `seach_with_index`.
|
3020
3310
|
|
@@ -3036,7 +3326,7 @@ It is also possible to define a custom enumerator using the following command:
|
|
3036
3326
|
<enum> = senumerator(<typ>,<size>) <block>
|
3037
3327
|
```
|
3038
3328
|
|
3039
|
-
Where `enum` is a Ruby variable referring to the enumerator, `typ` is the data type
|
3329
|
+
Where `enum` is a Ruby variable referring to the enumerator, `typ` is the data type, `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:
|
3040
3330
|
|
3041
3331
|
```ruby
|
3042
3332
|
bit[8][-8].inner mem: [ _h01, _h02, _h03, _h04, _h30, _h30, _h30, _h30 ]
|
@@ -3135,7 +3425,7 @@ With this basis, several algorithms have been implemented using enumerators and
|
|
3135
3425
|
|
3136
3426
|
#### Shared signals
|
3137
3427
|
|
3138
|
-
Like any other process,
|
3428
|
+
Like any other process, several sequencers can't write to the same signal. This is because there would be race competition that may physically destroy 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.
|
3139
3429
|
|
3140
3430
|
The shared signals are declared like the other kind of signals from their type. The syntax is the following:
|
3141
3431
|
|
@@ -3149,14 +3439,14 @@ They can also have an initial (and default) value when declared as follows:
|
|
3149
3439
|
<type>.shared <list of names with initialization>
|
3150
3440
|
```
|
3151
3441
|
|
3152
|
-
For example, the following code declares two 8-bit shared signals `x` and `y
|
3442
|
+
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`:
|
3153
3443
|
|
3154
3444
|
```ruby
|
3155
3445
|
[8].shared :x, :y
|
3156
3446
|
signed[8].shared u: 0, v: 0
|
3157
3447
|
```
|
3158
3448
|
|
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
|
3449
|
+
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 outside a sequencer. For example, the following code is valid:
|
3160
3450
|
|
3161
3451
|
```ruby
|
3162
3452
|
input :clk, :start
|
@@ -3191,7 +3481,7 @@ This default behavior of shared signal avoids race competition but is not very u
|
|
3191
3481
|
<shared signal>.select
|
3192
3482
|
```
|
3193
3483
|
|
3194
|
-
The select value starts from 0 for the first sequencer writing to the shared signal
|
3484
|
+
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:
|
3195
3485
|
|
3196
3486
|
```ruby
|
3197
3487
|
x.select <= 1
|
@@ -3203,12 +3493,12 @@ This value can be changed at runtime too. For example, instead of setting the se
|
|
3203
3493
|
par(clk.posedge) { x.select <= x.select + 1 }
|
3204
3494
|
```
|
3205
3495
|
|
3206
|
-
__Note__: this select sub
|
3496
|
+
__Note__: this select sub-signal is a standard RTL signal that has the same properties and limitations as the other ones, i.e., this is not a shared signal itself.
|
3207
3497
|
|
3208
3498
|
|
3209
3499
|
#### Arbiters
|
3210
3500
|
|
3211
|
-
Usually, it is not the signals that we want to share, but the resources they drive. For example,
|
3501
|
+
Usually, it is not the signals that we want to share, but the resources they drive. For example, with a CPU, it is the ALU that is shared as a whole rather than each of its inputs separately. 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:
|
3212
3502
|
|
3213
3503
|
```ruby
|
3214
3504
|
arbiter(:<name>).(<list of shared signal>)
|
@@ -3247,7 +3537,7 @@ end
|
|
3247
3537
|
In the example, both sequencers require access to signals `x` and `y` before accessing them and then releasing the access.
|
3248
3538
|
|
3249
3539
|
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
|
3540
|
+
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 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:
|
3251
3541
|
|
3252
3542
|
```ruby
|
3253
3543
|
hif(ctrl_xy.acquired) { x <= x + 1 }
|
@@ -3259,7 +3549,7 @@ The policy of an arbiter can be changed using command policy. You can either pro
|
|
3259
3549
|
ctrl_xy.policy([1,0])
|
3260
3550
|
```
|
3261
3551
|
|
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
|
3552
|
+
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 performed. For example, in the previous code, a policy switch priorities at each access can be implemented as follows:
|
3263
3553
|
|
3264
3554
|
```ruby
|
3265
3555
|
inner priority_xy: 0
|
@@ -3282,7 +3572,7 @@ ctrl_xy.policy do |acq|
|
|
3282
3572
|
end
|
3283
3573
|
```
|
3284
3574
|
|
3285
|
-
As seen in the code above, each bit of the `acq` vector is one when the corresponding sequencer
|
3575
|
+
As seen in the code above, each bit of the `acq` vector is one when the corresponding sequencer requires access or 0 otherwise, bit 0 corresponds to sequencer 0, bit 1 to sequencer 1, and so on.
|
3286
3576
|
|
3287
3577
|
|
3288
3578
|
#### Monitors
|
@@ -3295,7 +3585,7 @@ The monitor component is instantiated like the arbiters as follows:
|
|
3295
3585
|
monitor(:<name>).(<list of shared signals>)
|
3296
3586
|
```
|
3297
3587
|
|
3298
|
-
Monitors are used
|
3588
|
+
Monitors are used 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:
|
3299
3589
|
|
3300
3590
|
```ruby
|
3301
3591
|
monitor(:ctrl_xy).(x,y)
|
@@ -3331,7 +3621,7 @@ end
|
|
3331
3621
|
|
3332
3622
|
### Sequencer-specific function: `std/sequencer_func.rb`
|
3333
3623
|
|
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.
|
3624
|
+
HDLRuby function defined by `hdef` can be used in a 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
3625
|
|
3336
3626
|
However, it is possible to define functions that do support the sequencer constructs using `sdef` instead of `hdef` as follows:
|
3337
3627
|
|
@@ -3354,7 +3644,7 @@ end
|
|
3354
3644
|
|
3355
3645
|
As seen in the code above, a new construct `sreturn` can be used for returning a value from anywhere inside the function.
|
3356
3646
|
|
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
|
3647
|
+
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 sets the size to 32 whatever the arguments are:
|
3358
3648
|
|
3359
3649
|
```ruby
|
3360
3650
|
sdef(:fact,32) do |n|
|
@@ -3633,7 +3923,7 @@ The naming convention of the samples is the following:
|
|
3633
3923
|
* `<name>.rb`: default type of sample.
|
3634
3924
|
* `<name>_gen.rb`: generic parameters are required for processing the sample.
|
3635
3925
|
* `<name>_bench.rb`: sample including a simulation benchmark, these are the only samples that can be simulated using `hdrcc -S`. Please notice that such a sample cannot be converted to VHDL or Verilog HDL yet.
|
3636
|
-
* `with_<name>.rb`: sample illustrating a single aspect of HDLRuby or one of its
|
3926
|
+
* `with_<name>.rb`: sample illustrating a single aspect of HDLRuby or one of its libraries, usually includes a benchmark.
|
3637
3927
|
|
3638
3928
|
|
3639
3929
|
# Contributing
|