HDLRuby 2.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/HDLRuby.gemspec +36 -0
- data/LICENSE.txt +21 -0
- data/README.md +2774 -0
- data/README.pdf +0 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/hdrcc +3 -0
- data/lib/HDLRuby/alcc.rb +137 -0
- data/lib/HDLRuby/backend/hruby_allocator.rb +69 -0
- data/lib/HDLRuby/backend/hruby_c_allocator.rb +76 -0
- data/lib/HDLRuby/hdr_samples/adder.rb +7 -0
- data/lib/HDLRuby/hdr_samples/adder_assign_error.rb +11 -0
- data/lib/HDLRuby/hdr_samples/adder_bench.rb +27 -0
- data/lib/HDLRuby/hdr_samples/adder_gen.rb +7 -0
- data/lib/HDLRuby/hdr_samples/adder_nodef_error.rb +7 -0
- data/lib/HDLRuby/hdr_samples/addsub.rb +19 -0
- data/lib/HDLRuby/hdr_samples/addsubz.rb +22 -0
- data/lib/HDLRuby/hdr_samples/alu.rb +47 -0
- data/lib/HDLRuby/hdr_samples/calculator.rb +48 -0
- data/lib/HDLRuby/hdr_samples/counter_bench.rb +83 -0
- data/lib/HDLRuby/hdr_samples/dff.rb +9 -0
- data/lib/HDLRuby/hdr_samples/dff_bench.rb +66 -0
- data/lib/HDLRuby/hdr_samples/dff_counter.rb +20 -0
- data/lib/HDLRuby/hdr_samples/include.rb +14 -0
- data/lib/HDLRuby/hdr_samples/instance_open.rb +23 -0
- data/lib/HDLRuby/hdr_samples/mei8.rb +256 -0
- data/lib/HDLRuby/hdr_samples/mei8_bench.rb +309 -0
- data/lib/HDLRuby/hdr_samples/multer_gen.rb +8 -0
- data/lib/HDLRuby/hdr_samples/multer_seq.rb +29 -0
- data/lib/HDLRuby/hdr_samples/neural/a.rb +9 -0
- data/lib/HDLRuby/hdr_samples/neural/a_sub.rb +5 -0
- data/lib/HDLRuby/hdr_samples/neural/bw.rb +23 -0
- data/lib/HDLRuby/hdr_samples/neural/counter.rb +16 -0
- data/lib/HDLRuby/hdr_samples/neural/dadz.rb +9 -0
- data/lib/HDLRuby/hdr_samples/neural/dadz_sub.rb +4 -0
- data/lib/HDLRuby/hdr_samples/neural/forward.rb +153 -0
- data/lib/HDLRuby/hdr_samples/neural/forward_sub.rb +62 -0
- data/lib/HDLRuby/hdr_samples/neural/forward_sub_rand.rb +41 -0
- data/lib/HDLRuby/hdr_samples/neural/forward_sub_rand_typedef.rb +47 -0
- data/lib/HDLRuby/hdr_samples/neural/mem.rb +30 -0
- data/lib/HDLRuby/hdr_samples/neural/random.rb +23 -0
- data/lib/HDLRuby/hdr_samples/neural/selector.rb +29 -0
- data/lib/HDLRuby/hdr_samples/neural/sigmoid.rb +20 -0
- data/lib/HDLRuby/hdr_samples/neural/z.rb +33 -0
- data/lib/HDLRuby/hdr_samples/prog.obj +256 -0
- data/lib/HDLRuby/hdr_samples/ram.rb +18 -0
- data/lib/HDLRuby/hdr_samples/register_with_code_bench.rb +98 -0
- data/lib/HDLRuby/hdr_samples/rom.rb +10 -0
- data/lib/HDLRuby/hdr_samples/struct.rb +14 -0
- data/lib/HDLRuby/hdr_samples/sumprod.rb +29 -0
- data/lib/HDLRuby/hdr_samples/sw_encrypt_bench.rb +103 -0
- data/lib/HDLRuby/hdr_samples/sw_encrypt_cpu_bench.rb +261 -0
- data/lib/HDLRuby/hdr_samples/sw_encrypt_cpusim_bench.rb +302 -0
- data/lib/HDLRuby/hdr_samples/system_open.rb +11 -0
- data/lib/HDLRuby/hdr_samples/tuple.rb +16 -0
- data/lib/HDLRuby/hdr_samples/with_channel.rb +118 -0
- data/lib/HDLRuby/hdr_samples/with_class.rb +199 -0
- data/lib/HDLRuby/hdr_samples/with_decoder.rb +17 -0
- data/lib/HDLRuby/hdr_samples/with_fsm.rb +34 -0
- data/lib/HDLRuby/hdr_samples/with_reconf.rb +103 -0
- data/lib/HDLRuby/hdrcc.rb +623 -0
- data/lib/HDLRuby/high_samples/_adder_fault.rb +23 -0
- data/lib/HDLRuby/high_samples/_generic_transmission2.rb +146 -0
- data/lib/HDLRuby/high_samples/adder.rb +21 -0
- data/lib/HDLRuby/high_samples/adder_common_errors.rb +25 -0
- data/lib/HDLRuby/high_samples/addsub.rb +33 -0
- data/lib/HDLRuby/high_samples/addsubz.rb +37 -0
- data/lib/HDLRuby/high_samples/after.rb +28 -0
- data/lib/HDLRuby/high_samples/all_signals.rb +29 -0
- data/lib/HDLRuby/high_samples/alu.rb +61 -0
- data/lib/HDLRuby/high_samples/anonymous.rb +41 -0
- data/lib/HDLRuby/high_samples/before.rb +28 -0
- data/lib/HDLRuby/high_samples/blockblock.rb +26 -0
- data/lib/HDLRuby/high_samples/bugs/dadz.rb +22 -0
- data/lib/HDLRuby/high_samples/bugs/misample_instan.rb +20 -0
- data/lib/HDLRuby/high_samples/bugs/misample_updown.rb +22 -0
- data/lib/HDLRuby/high_samples/bugs/sample_add.rb +16 -0
- data/lib/HDLRuby/high_samples/bugs/sample_barrel.rb +13 -0
- data/lib/HDLRuby/high_samples/bugs/sample_daice.rb +57 -0
- data/lib/HDLRuby/high_samples/bugs/sample_kumiawase.rb +52 -0
- data/lib/HDLRuby/high_samples/bugs/sample_multi.rb +18 -0
- data/lib/HDLRuby/high_samples/bugs/sample_sub.rb +14 -0
- data/lib/HDLRuby/high_samples/bugs/z2.rb +32 -0
- data/lib/HDLRuby/high_samples/case.rb +32 -0
- data/lib/HDLRuby/high_samples/case2.rb +30 -0
- data/lib/HDLRuby/high_samples/change.rb +23 -0
- data/lib/HDLRuby/high_samples/clocks.rb +35 -0
- data/lib/HDLRuby/high_samples/comparer.rb +21 -0
- data/lib/HDLRuby/high_samples/conditionals.rb +29 -0
- data/lib/HDLRuby/high_samples/dff.rb +23 -0
- data/lib/HDLRuby/high_samples/each.rb +28 -0
- data/lib/HDLRuby/high_samples/exporter.rb +42 -0
- data/lib/HDLRuby/high_samples/functions.rb +60 -0
- data/lib/HDLRuby/high_samples/if_seq.rb +26 -0
- data/lib/HDLRuby/high_samples/inherit_as_dff.rb +32 -0
- data/lib/HDLRuby/high_samples/inherit_dff.rb +36 -0
- data/lib/HDLRuby/high_samples/instance.rb +37 -0
- data/lib/HDLRuby/high_samples/memory.rb +64 -0
- data/lib/HDLRuby/high_samples/multi_file.rb +27 -0
- data/lib/HDLRuby/high_samples/overload.rb +32 -0
- data/lib/HDLRuby/high_samples/paper_after.rb +49 -0
- data/lib/HDLRuby/high_samples/ram.rb +27 -0
- data/lib/HDLRuby/high_samples/registers.rb +139 -0
- data/lib/HDLRuby/high_samples/rom.rb +23 -0
- data/lib/HDLRuby/high_samples/scopeblockname.rb +37 -0
- data/lib/HDLRuby/high_samples/scopescope.rb +26 -0
- data/lib/HDLRuby/high_samples/shift.rb +31 -0
- data/lib/HDLRuby/high_samples/shift2.rb +40 -0
- data/lib/HDLRuby/high_samples/simple_instance.rb +31 -0
- data/lib/HDLRuby/high_samples/test_all.sh +10 -0
- data/lib/HDLRuby/high_samples/typedef.rb +24 -0
- data/lib/HDLRuby/high_samples/values.rb +70 -0
- data/lib/HDLRuby/high_samples/vector.rb +22 -0
- data/lib/HDLRuby/high_samples/with_decoder.rb +30 -0
- data/lib/HDLRuby/high_samples/with_fsm.rb +46 -0
- data/lib/HDLRuby/high_samples/with_pipe.rb +43 -0
- data/lib/HDLRuby/high_samples/with_seq.rb +25 -0
- data/lib/HDLRuby/hruby_bstr.rb +1085 -0
- data/lib/HDLRuby/hruby_check.rb +317 -0
- data/lib/HDLRuby/hruby_db.rb +432 -0
- data/lib/HDLRuby/hruby_error.rb +44 -0
- data/lib/HDLRuby/hruby_high.rb +4103 -0
- data/lib/HDLRuby/hruby_low.rb +4735 -0
- data/lib/HDLRuby/hruby_low2c.rb +1986 -0
- data/lib/HDLRuby/hruby_low2high.rb +738 -0
- data/lib/HDLRuby/hruby_low2seq.rb +248 -0
- data/lib/HDLRuby/hruby_low2sym.rb +126 -0
- data/lib/HDLRuby/hruby_low2vhd.rb +1437 -0
- data/lib/HDLRuby/hruby_low_bool2select.rb +295 -0
- data/lib/HDLRuby/hruby_low_cleanup.rb +193 -0
- data/lib/HDLRuby/hruby_low_fix_types.rb +437 -0
- data/lib/HDLRuby/hruby_low_mutable.rb +1803 -0
- data/lib/HDLRuby/hruby_low_resolve.rb +165 -0
- data/lib/HDLRuby/hruby_low_skeleton.rb +129 -0
- data/lib/HDLRuby/hruby_low_with_bool.rb +141 -0
- data/lib/HDLRuby/hruby_low_with_port.rb +167 -0
- data/lib/HDLRuby/hruby_low_with_var.rb +302 -0
- data/lib/HDLRuby/hruby_low_without_bit2vector.rb +88 -0
- data/lib/HDLRuby/hruby_low_without_concat.rb +162 -0
- data/lib/HDLRuby/hruby_low_without_connection.rb +113 -0
- data/lib/HDLRuby/hruby_low_without_namespace.rb +718 -0
- data/lib/HDLRuby/hruby_low_without_outread.rb +107 -0
- data/lib/HDLRuby/hruby_low_without_select.rb +206 -0
- data/lib/HDLRuby/hruby_serializer.rb +398 -0
- data/lib/HDLRuby/hruby_tools.rb +37 -0
- data/lib/HDLRuby/hruby_types.rb +239 -0
- data/lib/HDLRuby/hruby_values.rb +64 -0
- data/lib/HDLRuby/hruby_verilog.rb +1888 -0
- data/lib/HDLRuby/hruby_verilog_name.rb +52 -0
- data/lib/HDLRuby/low_samples/adder.yaml +97 -0
- data/lib/HDLRuby/low_samples/after.yaml +228 -0
- data/lib/HDLRuby/low_samples/before.yaml +223 -0
- data/lib/HDLRuby/low_samples/blockblock.yaml +48 -0
- data/lib/HDLRuby/low_samples/bugs/sample_add.yaml +97 -0
- data/lib/HDLRuby/low_samples/bugs/sample_daice.yaml +444 -0
- data/lib/HDLRuby/low_samples/bugs/sample_kumiawase.yaml +332 -0
- data/lib/HDLRuby/low_samples/bugs/sample_sub.yaml +97 -0
- data/lib/HDLRuby/low_samples/bugs/seqpar.yaml +184 -0
- data/lib/HDLRuby/low_samples/case.yaml +327 -0
- data/lib/HDLRuby/low_samples/change.yaml +135 -0
- data/lib/HDLRuby/low_samples/clocks.yaml +674 -0
- data/lib/HDLRuby/low_samples/cloner.rb +22 -0
- data/lib/HDLRuby/low_samples/comparer.yaml +85 -0
- data/lib/HDLRuby/low_samples/conditionals.yaml +133 -0
- data/lib/HDLRuby/low_samples/dff.yaml +107 -0
- data/lib/HDLRuby/low_samples/each.yaml +1328 -0
- data/lib/HDLRuby/low_samples/exporter.yaml +226 -0
- data/lib/HDLRuby/low_samples/functions.yaml +298 -0
- data/lib/HDLRuby/low_samples/generic_transmission.yaml +597 -0
- data/lib/HDLRuby/low_samples/inherit_as_dff.yaml +125 -0
- data/lib/HDLRuby/low_samples/inherit_dff.yaml +107 -0
- data/lib/HDLRuby/low_samples/load_yaml.rb +11 -0
- data/lib/HDLRuby/low_samples/memory.yaml +678 -0
- data/lib/HDLRuby/low_samples/namespace_extractor.rb +23 -0
- data/lib/HDLRuby/low_samples/overload.yaml +226 -0
- data/lib/HDLRuby/low_samples/paper_after.yaml +431 -0
- data/lib/HDLRuby/low_samples/port_maker.rb +14 -0
- data/lib/HDLRuby/low_samples/ram.yaml +207 -0
- data/lib/HDLRuby/low_samples/registers.yaml +228 -0
- data/lib/HDLRuby/low_samples/rom.yaml +2950 -0
- data/lib/HDLRuby/low_samples/shift.yaml +230 -0
- data/lib/HDLRuby/low_samples/shift2.yaml +2095 -0
- data/lib/HDLRuby/low_samples/simple_instance.yaml +102 -0
- data/lib/HDLRuby/low_samples/test_all.sh +43 -0
- data/lib/HDLRuby/low_samples/typedef.yaml +115 -0
- data/lib/HDLRuby/low_samples/values.yaml +577 -0
- data/lib/HDLRuby/low_samples/variable_maker.rb +14 -0
- data/lib/HDLRuby/low_samples/vector.yaml +56 -0
- data/lib/HDLRuby/low_samples/with_seq.yaml +188 -0
- data/lib/HDLRuby/low_samples/yaml2hdr.rb +10 -0
- data/lib/HDLRuby/low_samples/yaml2vhd.rb +19 -0
- data/lib/HDLRuby/sim/Makefile +19 -0
- data/lib/HDLRuby/sim/hruby_sim.h +590 -0
- data/lib/HDLRuby/sim/hruby_sim_calc.c +2362 -0
- data/lib/HDLRuby/sim/hruby_sim_core.c +589 -0
- data/lib/HDLRuby/sim/hruby_sim_list.c +93 -0
- data/lib/HDLRuby/sim/hruby_sim_vizualize.c +91 -0
- data/lib/HDLRuby/sim/hruby_value_pool.c +64 -0
- data/lib/HDLRuby/std/channel.rb +354 -0
- data/lib/HDLRuby/std/clocks.rb +165 -0
- data/lib/HDLRuby/std/counters.rb +82 -0
- data/lib/HDLRuby/std/decoder.rb +214 -0
- data/lib/HDLRuby/std/fsm.rb +516 -0
- data/lib/HDLRuby/std/pipeline.rb +220 -0
- data/lib/HDLRuby/std/reconf.rb +309 -0
- data/lib/HDLRuby/test_hruby_bstr.rb +2259 -0
- data/lib/HDLRuby/test_hruby_high.rb +594 -0
- data/lib/HDLRuby/test_hruby_high_low.rb +99 -0
- data/lib/HDLRuby/test_hruby_low.rb +934 -0
- data/lib/HDLRuby/v_samples/adder.v +10 -0
- data/lib/HDLRuby/v_samples/dff.v +12 -0
- data/lib/HDLRuby/v_samples/ram.v +20 -0
- data/lib/HDLRuby/v_samples/rom.v +270 -0
- data/lib/HDLRuby/version.rb +3 -0
- data/lib/HDLRuby.rb +11 -0
- data/makedoc +1 -0
- data/metadata.yaml +4 -0
- metadata +299 -0
data/README.md
ADDED
@@ -0,0 +1,2774 @@
|
|
1
|
+
# About HDLRuby
|
2
|
+
|
3
|
+
HDLRuby is a library for describing and simulating digital electronic
|
4
|
+
systems.
|
5
|
+
|
6
|
+
__Warning__:
|
7
|
+
|
8
|
+
- This is still preliminary work which may change a before we release a stable version.
|
9
|
+
- It is highly recommended to have both basic knowledge of the Ruby language and hardware description languages before using HDLRuby.
|
10
|
+
|
11
|
+
|
12
|
+
# Compiling HDLRuby descriptions
|
13
|
+
|
14
|
+
## Using the HDLRuby compiler
|
15
|
+
|
16
|
+
'hdrcc.rb' is the HDLRuby compiler. It takes as input a HDLRuby file, checks it, and can produce as output a Verilog HDL or a YAML low-level descriptions of a HW components but also simulate the input description.
|
17
|
+
|
18
|
+
|
19
|
+
__Usage__:
|
20
|
+
|
21
|
+
```
|
22
|
+
hdrcc.rb [options] <input file> <output directory>
|
23
|
+
```
|
24
|
+
|
25
|
+
Where:
|
26
|
+
|
27
|
+
* `options` is a list of options
|
28
|
+
* `<input file>` is the initial file to compile (mandatory)
|
29
|
+
* `<output directory>` is the directory where the generated files will be put
|
30
|
+
|
31
|
+
| Options | |
|
32
|
+
|:------------------|:-----------------------------------------------------|
|
33
|
+
| `-y, --yaml` | Output in YAML format |
|
34
|
+
| `-v, --verilog` | Output in Verlog HDL format |
|
35
|
+
| `-V, --vhdl` | Output in VHDL format |
|
36
|
+
| `-s, --syntax` | Output the Ruby syntax tree |
|
37
|
+
| `-C, --clang` | Output the C code of the simulator |
|
38
|
+
| `-S, --sim` | Output the executable simulator and execute it |
|
39
|
+
| `-d, --directory` | Specify the base directory for loading the HDLRuby files |
|
40
|
+
| `-D, --debug` | Set the HDLRuby debug mode |
|
41
|
+
| `-t, --top system`| Specify the top system describing the circuit to compile |
|
42
|
+
| `-p, --param x,y,z` | Specify the generic parameters |
|
43
|
+
| `-h, --help` | Show the help message |
|
44
|
+
|
45
|
+
__Notes__:
|
46
|
+
|
47
|
+
* If no top system is given, it is automatically looked for from the input file.
|
48
|
+
* If no option is given, simply checks the input file.
|
49
|
+
|
50
|
+
__Examples__:
|
51
|
+
|
52
|
+
* Compile system named `adder` from `adder.rb` input file and generate a low-level YAML description into directory `adder`:
|
53
|
+
|
54
|
+
```
|
55
|
+
hdrcc.rb --yaml --top adder adder.rb adder
|
56
|
+
```
|
57
|
+
|
58
|
+
* Compile `adder.rb` input file and generate a low-level Verilog HDL description into directory `adder`:
|
59
|
+
|
60
|
+
```
|
61
|
+
hdrcc.rb -v adder.rb adder
|
62
|
+
```
|
63
|
+
|
64
|
+
* Compile system `adder` whose bit width is generic from `adder_gen.rb` input file to a 16-bit circuit low-level VHDL description into directory `adder`:
|
65
|
+
|
66
|
+
```
|
67
|
+
hdrcc.rb -V -t adder --param 16 adder_gen.rb adder
|
68
|
+
```
|
69
|
+
|
70
|
+
* Compile system `multer` with inputs and output bit width is generic from `multer_gen.rb` input file to a 16x16->32 bit cicruit whose low-level YAML description into directory `multer`:
|
71
|
+
|
72
|
+
```
|
73
|
+
hdrcc.rb -y -t multer -p 16,16,32 multer_gen.rb multer
|
74
|
+
```
|
75
|
+
|
76
|
+
* Simulate the circuit described in file `counter_bench.rb` using directory `counter` for
|
77
|
+
storing the simulator's files:
|
78
|
+
|
79
|
+
```
|
80
|
+
hdrcc.rb -S counter_bench.rb counter
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
## Using HDLRuby within Ruby
|
85
|
+
|
86
|
+
You can also use HDLRuby in a Ruby program by loading `HDLRuby.rb` in your Ruby file:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
require 'HDLRuby'
|
90
|
+
```
|
91
|
+
|
92
|
+
Then, you can set up Ruby for supporting high-level description of hardware components. This is done by adding the following line of code:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
configure_high
|
96
|
+
```
|
97
|
+
|
98
|
+
After this statement, standard HDLRuby code can be written. In order to produce HW descriptions from this code a low-level hardware must then be generated
|
99
|
+
from an instance of an HW module (*system* in HDLRuby).
|
100
|
+
For example, assuming system 'circuitT' has been described in your Ruby program, an instance named 'circuitI' can be declared as follows:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
circuitT :circuitI
|
104
|
+
```
|
105
|
+
|
106
|
+
From there a low-level description of the circuit is generated using the `to_low` methods as follows (in the following code, this description is assigned to Ruby variable 'circuitL'):
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
circuitL = circuitI.to_low
|
110
|
+
```
|
111
|
+
|
112
|
+
This low-level description can then be converted to a YAML format using 'to_yaml' or to a VHDL format using 'to_vhd' as follows:
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
circuitY = circuitL.to_yaml
|
116
|
+
circuitV = circuitL.to_vhdl
|
117
|
+
```
|
118
|
+
|
119
|
+
In the above examples, 'cricuitY' and 'cricuitV' are Ruby variables referring to the strings containing the respective YAML and Verilog HDL code.
|
120
|
+
|
121
|
+
|
122
|
+
## Handling the low-level HDLRuby representation
|
123
|
+
|
124
|
+
You can include `HDLRuby::Low` for gaining access to the classes used for low-level description of hardware components.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
include HDLRuby::Low
|
128
|
+
```
|
129
|
+
|
130
|
+
It is then possible to load a low-level representation of hardware as follows, where `stream` is a stream containing the representation.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
hardwares = HDLRuby::from_yaml(stream)
|
134
|
+
```
|
135
|
+
|
136
|
+
For instance, you can load the sample description of an 8-bit adder as follows:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
adder = HDLRuby::from_yaml(File.read("#{$:[0]}/HDLRuby/low_samples/adder.yaml"))
|
140
|
+
```
|
141
|
+
|
142
|
+
__Note__:
|
143
|
+
|
144
|
+
- A `HDLRuby::Low` description of hardware can only be built through standard Ruby class constructors, and does not include any validity check of the resulting hardware.
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
# HDLRuby programming guide
|
149
|
+
|
150
|
+
HDLRuby has been designed to bring the high flexibility of the Ruby language to hardware descriptions while ensuring that they remain synthesizable. In this
|
151
|
+
context, all the abstractions provided by HDLRuby are in the way of describing hardware, but not in its execution model, this latter being RTL by construction.
|
152
|
+
|
153
|
+
The second specificity of HDLRuby is that it supports natively all the features of the Ruby language.
|
154
|
+
|
155
|
+
__Notes__:
|
156
|
+
|
157
|
+
- It is still possible to extend HDLRuby to support hardware descriptions of higher level than RTL, please refer to section [Extending HDLRuby](#extend) for more details.
|
158
|
+
- In this document, HDLRuby constructs will often be compared to their Verilog HDL or VHDL equivalents for simpler explanations.
|
159
|
+
|
160
|
+
## Introduction
|
161
|
+
|
162
|
+
This introduction gives a glimpse of the possibilities of the language.
|
163
|
+
However, we do recommend to consult the section about the [high-level programming features](#highfeat) to have a more complete view of the advanced possibilities of this language.
|
164
|
+
|
165
|
+
At first glance, HDLRuby is similar to any other HDL languages (like Verilog HDL or VHDL), for instance the following code describes a simple D-FF:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
system :dff do
|
169
|
+
bit.input :clk, :rst, :d
|
170
|
+
bit.output :q
|
171
|
+
|
172
|
+
par(clk.posedge) do
|
173
|
+
q <= d & ~rst
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
As it can be seen in the code above, `system` is the keyword used for describing a digital circuit. This keyword is an equivalent of the Verilog HDL `module`. In such a system, signals are declared using a `<type>.<direction>` construct where `type` is the data type of the signal (e.g., `bit` as in the code above) and `direction` indicates if the signal is an input, an output, an inout or an inner one; and executable blocks (similar to `always` block of Verilog HDL) are described using the `par` keyword when they are parallel and `seq` when they are sequential (i.e, with respectively non-blocking and blocking assignments).
|
179
|
+
|
180
|
+
After such a system has been defined, it can be instantiated. For axample a single instance of the `dff` system named `dff0` can be declared as follows:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
dff :dff0
|
184
|
+
```
|
185
|
+
|
186
|
+
The ports of this instance can then be accessed to be used like any other signals, e.g., `dff0.d` for access the `d` input of the FF.
|
187
|
+
|
188
|
+
Several instances can also be declared in a single statement. For example, a 2-bit counter based on the previous `dff` circuits can be described as follows:
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
system :counter2 do
|
192
|
+
input :clk,:rst
|
193
|
+
output :q
|
194
|
+
|
195
|
+
dff [ :dff0, :dff1 ]
|
196
|
+
|
197
|
+
dff0.clk <= clk
|
198
|
+
dff0.rst <= rst
|
199
|
+
dff0.d <= ~dff0.q
|
200
|
+
|
201
|
+
dff1.clk <= ~dff0.q
|
202
|
+
dff1.rst <= rst
|
203
|
+
dff1.d <= ~dff1.q
|
204
|
+
|
205
|
+
q <= dff1.q
|
206
|
+
end
|
207
|
+
|
208
|
+
The instances can also be connected while being declared. For example the code above can be rewritten as follows:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
system :counter2 do
|
212
|
+
input :clk, :rst
|
213
|
+
output :q
|
214
|
+
|
215
|
+
dff(:dff0).(clk: clk, rst: rst, d: ~dff0.q)
|
216
|
+
dff(:dff1).(~dff0.q, rst, ~dff1.q, q)
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
In the code above, two possible connection methods are shown: for `dff0` ports are connected by name, and for `dff1` ports are connected by declaration order. Please notice that it is also possible to connect only a subset of the ports while declaring and to reconnect already connected ports in further statements.
|
221
|
+
|
222
|
+
While a circuit can be generated from the code given above, a benchmark must
|
223
|
+
be provided to test it. Such benchmark as described by constructs called
|
224
|
+
timed behavior that give the evolution of signals depending of the time.
|
225
|
+
For example the following code simulates the previous D-FF for 4 cycles
|
226
|
+
of 20ns each, with reset on the first cycle, set of signal `d` to 1 for
|
227
|
+
the third cycle and set of this signal to 0 for the last.
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
system :dff_bench do
|
231
|
+
|
232
|
+
dff :dff0
|
233
|
+
|
234
|
+
timed do
|
235
|
+
dff0.clk <= 0
|
236
|
+
dff0.rst <= 1
|
237
|
+
!10.ns
|
238
|
+
dff0.clk <= 1
|
239
|
+
!10.ns
|
240
|
+
dff0.clk <= 0
|
241
|
+
dff0.rst <= 0
|
242
|
+
dff0.d <= 1
|
243
|
+
!10.ns
|
244
|
+
dff0.clk <= 1
|
245
|
+
!10.ns
|
246
|
+
dff0.clk <= 0
|
247
|
+
!10.ns
|
248
|
+
dff0.clk <= 1
|
249
|
+
!10.ns
|
250
|
+
dff0.clk <= 0
|
251
|
+
dff0.d <= 1
|
252
|
+
!10.ns
|
253
|
+
dff0.clk <= 1
|
254
|
+
!10.ns
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
258
|
+
|
259
|
+
---
|
260
|
+
|
261
|
+
The code describing a `dff` given above is not much different from its equivalent in other HDL. However, HDLRuby provides several features for achieving a higher productivity when describing hardware. We will now describe a few of them.
|
262
|
+
|
263
|
+
First, several syntactic sugars exist that allow shorter code, for instance the following code is strictly equivalent to the previous description of `dff`:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
system :dff do
|
267
|
+
input :clk, :rst, :d
|
268
|
+
output :q
|
269
|
+
|
270
|
+
(q <= d & ~rst).at(clk.posedge)
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
Then, it often happens that a system will end up with only one instance.
|
275
|
+
In such a case, the system declaration can be omitted and an instance
|
276
|
+
can be directly declared as follows:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
instance :dff_single do
|
280
|
+
input :clk, :rst, :d
|
281
|
+
output :q
|
282
|
+
|
283
|
+
(q <= d & ~rst).at(clk.posedge)
|
284
|
+
end
|
285
|
+
```
|
286
|
+
|
287
|
+
In the example above, `dff_single` is an instance describing, again, a
|
288
|
+
D-FF, but whose system is anonymous.
|
289
|
+
|
290
|
+
Furthermore, generic parameters can be used for anything in HDLRuby.
|
291
|
+
For instance, the following code describes an 8-bit register without any parameterization:
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
system :reg8 do
|
295
|
+
input :clk, :rst
|
296
|
+
[7..0].input :d
|
297
|
+
[7..0].output :q
|
298
|
+
|
299
|
+
(q <= d & [~rst]*8).at(clk.posedge)
|
300
|
+
end
|
301
|
+
```
|
302
|
+
|
303
|
+
But it is also possible to describe a register of arbitrary size as follows, where `n` is the parameter giving the number of bits of the register:
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
system :regn do |n|
|
307
|
+
input :clk, :rst
|
308
|
+
[n-1..0].input :d
|
309
|
+
[n-1..0].output :q
|
310
|
+
|
311
|
+
(q <= d & [~rst]*n).at(clk.posedge)
|
312
|
+
end
|
313
|
+
```
|
314
|
+
|
315
|
+
Or, even further, it is possible to describe a register of arbitrary type (not only bit vectors) as follows:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
system :reg do |typ|
|
319
|
+
input :clk, :rst
|
320
|
+
typ.input :d
|
321
|
+
typ.output :q
|
322
|
+
|
323
|
+
(q <= d & [~rst]*typ.width).at(clk.posedge)
|
324
|
+
end
|
325
|
+
```
|
326
|
+
|
327
|
+
Wait... I have just realized: a 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 basically three ways for doing this. First, inheritance can be used: a new system is built inheriting from `dff` as it is done in the following code.
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
system :dff_full, dff do
|
331
|
+
output :qb
|
332
|
+
qb <= ~q
|
333
|
+
end
|
334
|
+
```
|
335
|
+
|
336
|
+
The second possibility is to modify `dff` afterward. In HDLRuby, this achieved using the `open` method as it is done the following code:
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
dff.open do
|
340
|
+
output :qb
|
341
|
+
qb <= ~q
|
342
|
+
end
|
343
|
+
```
|
344
|
+
|
345
|
+
The third possibility is to modify directly a single instance of `dff` which require an inverted output, using again the `open` method, as in the following code:
|
346
|
+
|
347
|
+
```ruby
|
348
|
+
# Declare dff0 as an instance of dff
|
349
|
+
dff :dff0
|
350
|
+
|
351
|
+
# Modify it
|
352
|
+
dff0.open do
|
353
|
+
output :qb
|
354
|
+
qb <= ~q
|
355
|
+
end
|
356
|
+
```
|
357
|
+
|
358
|
+
In this later case, only `dff0` will have an inverted output, the other instances of `dff` will not change.
|
359
|
+
|
360
|
+
Now assuming we opted for the first solution, we have now `dff_full`, a highly advanced D-FF with such unique features as an inverted output. So we would like to use it in other designs, for example a shift register of `n` bits. Such a system will include a generic number of `dff_full` instances, and can be
|
361
|
+
described as follows making use of the native Ruby method `each_cons` for connecting them together:
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
system :shifter do |n|
|
365
|
+
input :clk, :rst
|
366
|
+
input :i0
|
367
|
+
output :o0, :o0b
|
368
|
+
|
369
|
+
# Instantiating n D-FF
|
370
|
+
[n].dff_full :dffIs
|
371
|
+
|
372
|
+
# Connect the clock and the reset.
|
373
|
+
dffIs.each { |ff| ff.clk <= clk ; ff.rst <= rst }
|
374
|
+
|
375
|
+
# Interconnect them as a shift register
|
376
|
+
dffIs[0..-1].each_cons(2) { |ff0,ff1| ff1.d <= ff0.q }
|
377
|
+
|
378
|
+
# Connects the input and output of the circuit
|
379
|
+
dffIs[0].d <= i0
|
380
|
+
o0 <= dffIs[-1].q
|
381
|
+
o0b <= dffIs[-1].qb
|
382
|
+
end
|
383
|
+
```
|
384
|
+
|
385
|
+
As it can be seen in the above examples, in HDLRuby, any construct is an object and therefore include methods. For instance, declaring a signal of a given `type` and direction (input, output or inout) is done as follows, so that `direction` is actually a method of the type, and the signal names are actually the arguments of this method (symbols or string are supported.)
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
<type>.<direction> <list of symbols representing the signal>
|
389
|
+
```
|
390
|
+
|
391
|
+
Of course, if you do not need to use the specific component `dff_full` you can describe a shift register more simply as follows:
|
392
|
+
|
393
|
+
```ruby
|
394
|
+
system :shifter do |n|
|
395
|
+
input :clk, :rst
|
396
|
+
input :i0
|
397
|
+
output :o0
|
398
|
+
[n].inner :sh
|
399
|
+
|
400
|
+
par (clk.posedge) do
|
401
|
+
hif(rst) { sh <= 0 }
|
402
|
+
helse { sh <= [sh[n-2..0], i0] }
|
403
|
+
end
|
404
|
+
|
405
|
+
o0 <= sh[n-1]
|
406
|
+
end
|
407
|
+
```
|
408
|
+
|
409
|
+
Now, let us assume you want to design a circuit that performs a sum of products of several inputs with constant coefficients. For the case of 4 16-bit signed inputs and given coefficient as 3, 4, 5 and 6. The corresponding basic code could be as follows:
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
system :sumprod_16_3456 do
|
413
|
+
signed[16].input :i0, :i1, :i2, :i3
|
414
|
+
signed[16].output :o
|
415
|
+
|
416
|
+
o <= i0*3 + i1*4 + i2*5 + i3*6
|
417
|
+
end
|
418
|
+
```
|
419
|
+
|
420
|
+
The description above is straight forward, but it would be necessary to rewrite it if another circuit with different bit width or coefficients is to be designed. Moreover, if the number of coefficient is large an error in the expression will be easy to make and hard to find. A better approach would be to use a generic description of such a circuit as follows:
|
421
|
+
|
422
|
+
```ruby
|
423
|
+
system :sumprod do |typ,coefs|
|
424
|
+
typ[coefs.size].input ins
|
425
|
+
typ.output :o
|
426
|
+
|
427
|
+
o <= coefs.each_with_index.reduce(_0) do |sum,(coef,i)|
|
428
|
+
sum + ins[i]*coef
|
429
|
+
end
|
430
|
+
end
|
431
|
+
```
|
432
|
+
|
433
|
+
In the code above, there are two generic parameters,
|
434
|
+
`typ` that indicates the data type of the circuit and `coefs` that 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.
|
435
|
+
|
436
|
+
The description of the sum of product maybe more difficult to understand for people not familiar with the Ruby language. The `each_with_index` method iterates over the coefficients adding their index as iteration variable, the resulting operation (i.e., the iteration loop) is then modified by the `reduce` method that accumulates the code passed as arguments. This code, starting by `|sum,coef,i|` simply performs the addition of the current accumulation result (`sum`) with the product of the current coefficient (`coef`) and input (`ins[i]`, where `i` is the index) in the iteration. The argument `_0` initializes the sum to `0`.
|
437
|
+
|
438
|
+
While slightly longer than the previous description, this description allows to declare a circuit implementing a sum of product with any bit width and any number of coefficients. For instance, the following code describes a signed 32-bit sum of product with 16 coefficients (actually just random numbers here).
|
439
|
+
|
440
|
+
```ruby
|
441
|
+
sumprod(signed[32], [3,78,43,246, 3,67,1,8, 47,82,99,13, 5,77,2,4]).(:my_circuit)
|
442
|
+
```
|
443
|
+
|
444
|
+
As seen in the code above, when passing generic argument for instantiating a generic system, the name of the instance is put between brackets for avoiding confusion.
|
445
|
+
|
446
|
+
While description `sumprod` is already usable in a wide range of cases, it still uses the 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:
|
447
|
+
|
448
|
+
```ruby
|
449
|
+
system :sumprod_func do |typ,coefs|
|
450
|
+
typ[coefs.size].input ins
|
451
|
+
typ.output :o
|
452
|
+
|
453
|
+
o <= coefs.each_with_index.reduce(_0) do
|
454
|
+
|sum,(coef,i)|
|
455
|
+
add(sum, mult(ins[i]*coef))
|
456
|
+
end
|
457
|
+
end
|
458
|
+
```
|
459
|
+
|
460
|
+
Where `add` and `mult` are functions implementing the required specific operations. HDLRuby functions are similar to Verilog HDL ones. In our example, an addition that saturates at 1000 could be described as follows:
|
461
|
+
|
462
|
+
```ruby
|
463
|
+
function :add do |x,y|
|
464
|
+
inner :res
|
465
|
+
seq do
|
466
|
+
res <= x + y
|
467
|
+
(res <= 1000).hif(res > 1000)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
```
|
471
|
+
|
472
|
+
With HDLRuby functions, the result of the last statement in the return value, in this case that will be the value of res. The code above is also an example of the usage of the postfixed if statement, it an equivalent of the following code:
|
473
|
+
|
474
|
+
```ruby
|
475
|
+
hif(res>1000) { res <= 1000 }
|
476
|
+
```
|
477
|
+
|
478
|
+
With functions, it is enough to change their content to obtained a new kind of circuit without change the main code. This approach suffers for two drawbacks though: first, the level of saturation is hard-coded in the function, 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:
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
function :add do |max, x, y|
|
482
|
+
inner :res
|
483
|
+
seq do
|
484
|
+
res <= x + y
|
485
|
+
(res <= max).hif(res > max)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
```
|
489
|
+
|
490
|
+
It would however be necessary to add this argument when invoking the function, e.g., `add(1000,sum,mult(...))`. While this argument is relevant for addition with saturation, it is not for the other kind of addition operations, and hence, the code of `sumprod` is not general any longer.
|
491
|
+
|
492
|
+
HDLRuby provides two ways to address such issues. First, it is possible to pass code as 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 bellow:
|
493
|
+
|
494
|
+
```ruby
|
495
|
+
system :sumprod_proc do |add,mult,typ,coefs|
|
496
|
+
typ[coefs.size].input ins
|
497
|
+
typ.output :o
|
498
|
+
|
499
|
+
o <= coefs.each_with_index.reduce(_0) do
|
500
|
+
|sum,(coef,i)|
|
501
|
+
add.(sum, mult.(ins[i]*coef))
|
502
|
+
end
|
503
|
+
end
|
504
|
+
```
|
505
|
+
|
506
|
+
__Note__:
|
507
|
+
|
508
|
+
- With HDLRuby, when some code is passed as argument, it is invoked using the `.()` operator, and not simple parenthesis like functions.
|
509
|
+
|
510
|
+
Assuming the addition with saturation is now implemented by a function named `add_sat` and a multiplication with saturation is implemented by a function named `mult_sat` (with similar arguments), a circuit implementing a signed 16-bit sum of product saturating at 1000 with 16 coefficients could be described as follows:
|
511
|
+
|
512
|
+
```ruby
|
513
|
+
sumprod_proc(
|
514
|
+
proc { |x,y| add_sat(1000,x,y) },
|
515
|
+
proc { |x,y| mult_sat(1000,x,y) },
|
516
|
+
signed[64],
|
517
|
+
[3,78,43,246, 3,67,1,8,
|
518
|
+
47,82,99,13, 5,77,2,4]).(:my_circuit)
|
519
|
+
```
|
520
|
+
|
521
|
+
As seen in the example above, a piece of code is passed as argument using the proc keyword.
|
522
|
+
|
523
|
+
A second possible approach provided by HDLRuby is to declare a new data type with redefined addition and multiplication operators. For the case of a 16-bit saturated addition and multiplication the following generic data type can be defined (for signed computations):
|
524
|
+
|
525
|
+
```
|
526
|
+
signed[16].typedef(:sat16_1000)
|
527
|
+
|
528
|
+
sat16_1000.define_operator(:+) do |x,y|
|
529
|
+
[16].inner :res
|
530
|
+
seq do
|
531
|
+
res <= x + y
|
532
|
+
( res <= 1000 ).hif(res > 1000)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
```
|
536
|
+
|
537
|
+
In the code above, the first line define 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).
|
538
|
+
Then, the initial version of `sumprod` can be used with this type to achieve saturated computations as follows:
|
539
|
+
|
540
|
+
```ruby
|
541
|
+
sumprod(sat16_1000,
|
542
|
+
[3,78,43,246, 3,67,1,8,
|
543
|
+
47,82,99,13, 5,77,2,4]).(:my_circuit)
|
544
|
+
```
|
545
|
+
|
546
|
+
It is also possible to declare a generic type. For instance a generic signed type with saturation can be declared as follows:
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
typedef :sat do |width, max|
|
550
|
+
signed[width]
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
sat.define_operator(:+) do |width,max, x,y|
|
555
|
+
[width].inner :res
|
556
|
+
seq do
|
557
|
+
res <= x + y
|
558
|
+
( res <= max ).hif(res > max)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
```
|
562
|
+
|
563
|
+
__Note:__
|
564
|
+
|
565
|
+
- The generic parameters have also to be declared for the operator redefinitions.
|
566
|
+
|
567
|
+
With this generic type, the circuit can be declared as follows:
|
568
|
+
|
569
|
+
```ruby
|
570
|
+
sumprod(sat(16,1000),
|
571
|
+
[3,78,43,246, 3,67,1,8,
|
572
|
+
47,82,99,13, 5,77,2,4]).(:my_circuit)
|
573
|
+
```
|
574
|
+
|
575
|
+
|
576
|
+
As final note, HDLRuby is also a language with supports reflection for
|
577
|
+
all its constructs. For example, the system of an instance can be accessed
|
578
|
+
using the `systemT` method, and this latter can be used to create
|
579
|
+
other instances. For example, previously, `dff_single` was declared with
|
580
|
+
an anonymous system (i.e., it cannot be accessed by name). This system
|
581
|
+
can however be used as follows to generate another instance:
|
582
|
+
|
583
|
+
```ruby
|
584
|
+
dff_single.systemT.instantiate(:dff_not_single)
|
585
|
+
```
|
586
|
+
|
587
|
+
In the above example, `dff_not_single` is declared to be an instance
|
588
|
+
of the same system as `dff_single`.
|
589
|
+
|
590
|
+
This reflection capability can also be used for instance for accessing the
|
591
|
+
data type of a signal (`sig.type`), but also the current basic block
|
592
|
+
(`cur_block`), the current process (`cur_behavior`) and so on.
|
593
|
+
The standard library of HDLRuby, that includes several hardware constructs
|
594
|
+
like final state machine descriptors, is mainly based on using these
|
595
|
+
reflection features.
|
596
|
+
|
597
|
+
|
598
|
+
|
599
|
+
## How does HDLRuby work
|
600
|
+
|
601
|
+
Contrary to descriptions in high-level HDL like SystemVerilog, VHDL or SystemC, HDLRuby descriptions are not software-like description of hardware, but are programs meant to produce hardware descriptions. In other words, while the execution of a common HDL code will result in some simulation of the described hardware, the execution of HDLRuby code will result in some low-level hardware description. This low-level description is synthesizable, and can also be simulated like any standard hardware description.
|
602
|
+
This decoupling of the representation of the hardware from the point of view of the user (HDLRuby), and the actual hardware description (HDLRuby::Low) makes it possible to provide the user with any advanced software features without jeopardizing the synthesizability of the actual hardware description.
|
603
|
+
|
604
|
+
For that purpose, each construct in HDLRuby is not a direct description of some hardware construct, but a program which generates the corresponding description. For example, let us consider the following line of code of HDLRuby describing the connection between signal `a` and signal `b`:
|
605
|
+
|
606
|
+
```ruby
|
607
|
+
a <= b
|
608
|
+
```
|
609
|
+
|
610
|
+
Its execution will produce the actual hardware description of this connection as an object of the HDLRuby::Low library — in this case an instance of the `HDLRuby::Low::Connection` class. Concretely, a HDLRuby system is described by a Ruby block, and the instantiation of this system is actually performed by executing this block. The actual synthesizable description of this hardware is the execution result of this instantiation.
|
611
|
+
|
612
|
+
|
613
|
+
|
614
|
+
From there, we will describe into more details each construct of HDLRuby.
|
615
|
+
|
616
|
+
## Naming rules
|
617
|
+
<a name="names"></a>
|
618
|
+
|
619
|
+
Several constructs in HDLRuby are referred to by name, e.g., systems and signals. When such constructs are declared, their names are to be specified by Ruby symbols starting with a lower case. For example, `:hello` is a valid name declaration, but `:Hello` is not.
|
620
|
+
|
621
|
+
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
|
622
|
+
has been declared with `:hello` as name, it will be afterward referred by `hello`.
|
623
|
+
|
624
|
+
## Systems and signals
|
625
|
+
|
626
|
+
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 of structural and behavioral descriptions.
|
627
|
+
|
628
|
+
A signal represents a state in a system. It has a data type and a value, the latter varying with time. Similarly to VHDL, HDLRuby signals can be viewed as abstractions of both wires and registers in a digital circuit. As a general rule, a signal whose value is explicitly set all the time models a wire, otherwise it models a register.
|
629
|
+
|
630
|
+
### Declaring an empty system
|
631
|
+
|
632
|
+
A system is declared using the keyword `system`. It must be given a Ruby symbol for name and a block that describe its content. For instance, the following code describes an empty system named `box`:
|
633
|
+
|
634
|
+
```ruby
|
635
|
+
system(:box) {}
|
636
|
+
```
|
637
|
+
|
638
|
+
__Notes__:
|
639
|
+
|
640
|
+
- Since this is Ruby code, the body can also be delimited by the `do` and `end`
|
641
|
+
Ruby keywords (in which case the parentheses can be omitted) as follows:
|
642
|
+
|
643
|
+
```ruby
|
644
|
+
system :box do
|
645
|
+
end
|
646
|
+
```
|
647
|
+
|
648
|
+
- Names in HDLRuby are natively stored as Ruby symbols, but strings can
|
649
|
+
also be used, e.g., `system("box") {}` is also valid.
|
650
|
+
|
651
|
+
### Declaring a system with an interface
|
652
|
+
|
653
|
+
The interface of a system can be described anywhere in its body, but it is recommended to do it at its beginning. This is done by declaring input, output or inout signals of given data types as follows:
|
654
|
+
|
655
|
+
```ruby
|
656
|
+
<data type>.<direction> <list of colon-preceded names>
|
657
|
+
```
|
658
|
+
|
659
|
+
For example, declaring a 1-bit input signal named `clk` can be declared as follows:
|
660
|
+
|
661
|
+
```ruby
|
662
|
+
bit.input :clk
|
663
|
+
```
|
664
|
+
|
665
|
+
Now, since `bit` is the default data type in HDLRuby, it can be omitted as follows:
|
666
|
+
|
667
|
+
```ruby
|
668
|
+
input :clk
|
669
|
+
```
|
670
|
+
|
671
|
+
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
|
672
|
+
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.
|
673
|
+
|
674
|
+
```ruby
|
675
|
+
system :mem8_16 do
|
676
|
+
input :clk, :rwb
|
677
|
+
[15..0].input :addr
|
678
|
+
[7..0].inout :data
|
679
|
+
|
680
|
+
bit[7..0][2**16].inner :content
|
681
|
+
|
682
|
+
par(clk.posedge) do
|
683
|
+
hif(rwb) { data <= content[addr] }
|
684
|
+
helse { content[addr] <= data }
|
685
|
+
end
|
686
|
+
end
|
687
|
+
```
|
688
|
+
|
689
|
+
### Structural description in a system
|
690
|
+
|
691
|
+
In a system, structural descriptions consist of subsystems and interconnections among them.
|
692
|
+
|
693
|
+
A subsystem is obtained by instantiating an existing system as follows, where `<system name>` is the name of the system to instantiate (without any colon):
|
694
|
+
|
695
|
+
```ruby
|
696
|
+
<system name> :<instance name>
|
697
|
+
```
|
698
|
+
|
699
|
+
For example, system `mem8_16` declared in the previous section can be instantiated as follows:
|
700
|
+
|
701
|
+
```ruby
|
702
|
+
mem8_16 :mem8_16I
|
703
|
+
```
|
704
|
+
|
705
|
+
It is also possible to declare multiple instances of a same system at time as follows:
|
706
|
+
|
707
|
+
```ruby
|
708
|
+
<system name> [list of colon-separated instance names]
|
709
|
+
```
|
710
|
+
|
711
|
+
For example, the following code declares two instances of system `mem8_16`:
|
712
|
+
|
713
|
+
```ruby
|
714
|
+
mem8_16 [ :mem8_16I0, :mem8_16I1 ]
|
715
|
+
```
|
716
|
+
|
717
|
+
Interconnecting instances may require internal signals in the system.
|
718
|
+
Such signals are declared using the `inner` direction.
|
719
|
+
For example, the following code declares a 1-bit inner signal named `w1` and a 2-bit inner signal named `w2`:
|
720
|
+
|
721
|
+
```ruby
|
722
|
+
inner :w1
|
723
|
+
[1..0].inner :w2
|
724
|
+
```
|
725
|
+
|
726
|
+
A connection between signals is done using the arrow operator `<=` as follows:
|
727
|
+
|
728
|
+
```ruby
|
729
|
+
<destination> <= <source>
|
730
|
+
```
|
731
|
+
|
732
|
+
The `<destination>` must be a reference to a signal, and the `<source>` can be any expression.
|
733
|
+
|
734
|
+
For example the following code, connects signal `w1` to signal `ready` and signal `clk` to the first bit of signal `w2`:
|
735
|
+
|
736
|
+
```ruby
|
737
|
+
ready <= w1
|
738
|
+
w2[0] <= clk
|
739
|
+
```
|
740
|
+
|
741
|
+
As another example, the following code connects to the second bit of `w2` the output of an AND operation between `clk` and `rst` as follows:
|
742
|
+
|
743
|
+
```ruby
|
744
|
+
w2[1] <= clk & rst
|
745
|
+
```
|
746
|
+
|
747
|
+
The signals of an instance can be connected through the arrow operator too, provided they are properly referred to. One way to refer them is to use the dot operator `.` on the instance as follows:
|
748
|
+
|
749
|
+
```ruby
|
750
|
+
<instance name>.<signal name>
|
751
|
+
```
|
752
|
+
|
753
|
+
For example, the following code connects signal `clk` of instance `mem8_16I` to signal `clk` of the current system:
|
754
|
+
|
755
|
+
```ruby
|
756
|
+
mem8_16I.clk <= clk
|
757
|
+
```
|
758
|
+
|
759
|
+
It is also possible to connect multiple signals of an instance using the call operator `.()` as follows, where each target can be any expression:
|
760
|
+
|
761
|
+
```ruby
|
762
|
+
<intance name>.(<signal name0>: <target0>, ...)
|
763
|
+
```
|
764
|
+
|
765
|
+
For example, the following code connects signals `clk` and `rst` of instance
|
766
|
+
`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.
|
767
|
+
|
768
|
+
```ruby
|
769
|
+
mem8_16I.(clk: clk, rst: rst)
|
770
|
+
```
|
771
|
+
|
772
|
+
This last connection method can be used directly while declaring an instance.
|
773
|
+
For example, `mem8_16I` could have been declared and connected to `clk` and `rst` as follows:
|
774
|
+
|
775
|
+
```ruby
|
776
|
+
mem8_16(:mem8_16I).(clk: clk, rst: rest)
|
777
|
+
```
|
778
|
+
|
779
|
+
To summarize this section, here is a structural description of a 16-bit memory made of two 8-bit memories (or equivalent) sharing the same address bus, and using respectively the lower and the higher 8-bits of the data bus:
|
780
|
+
|
781
|
+
```ruby
|
782
|
+
system :mem16_16 do
|
783
|
+
input :clk, :rwb
|
784
|
+
[15..0].input :addr
|
785
|
+
[15..0].inout :data
|
786
|
+
|
787
|
+
mem8_16(:memL).(clk: clk, rwb: rwb, addr: addr, data: data[7..0])
|
788
|
+
mem8_16(:memH).(clk: clk, rwb: rwb, addr: addr, data: data[15..8])
|
789
|
+
end
|
790
|
+
```
|
791
|
+
|
792
|
+
And here is an equivalent code using the arrow operator:
|
793
|
+
|
794
|
+
```ruby
|
795
|
+
system :mem16_16 do
|
796
|
+
input :clk, :rwb
|
797
|
+
[15..0].input :addr
|
798
|
+
[15..0].inout :data
|
799
|
+
|
800
|
+
mem8_16 [:memL, :memH]
|
801
|
+
|
802
|
+
memL.clk <= clk
|
803
|
+
memL.rwb <= rwb
|
804
|
+
memL.addr <= addr
|
805
|
+
memL.data <= data[7..0]
|
806
|
+
|
807
|
+
memH.clk <= clk
|
808
|
+
memH.rwb <= rwb
|
809
|
+
memH.addr <= addr
|
810
|
+
memH.data <= data[15..8]
|
811
|
+
end
|
812
|
+
```
|
813
|
+
|
814
|
+
### Scope in a system
|
815
|
+
|
816
|
+
#### General scopes
|
817
|
+
|
818
|
+
The signals of the interface of signals are accessible from anywhere in a HDLRuby description. This is not the case for inner signals and instances: they are accessible only within the scope they are declared in.
|
819
|
+
|
820
|
+
A scope is a region of the code where locally declared objects are accessible. Each system has its own scope that cannot be accessible from other part of an HDLRuby description. For example in the following code, signals `d` and `qb` as well as instance `dffI` cannot be accessed from outside system `div2`:
|
821
|
+
|
822
|
+
```ruby
|
823
|
+
system :div2 do
|
824
|
+
input :clk
|
825
|
+
output :q
|
826
|
+
|
827
|
+
inner :d, :qb
|
828
|
+
d <= qb
|
829
|
+
|
830
|
+
dff_full(:dffI).(clk: clk, d: d, q: q, qb: qb)
|
831
|
+
|
832
|
+
```
|
833
|
+
|
834
|
+
For robustness or, readability purpose, it is possible to add inner scope inside existing scope using the `sub` keyword as follows:
|
835
|
+
|
836
|
+
```ruby
|
837
|
+
sub do
|
838
|
+
<code>
|
839
|
+
end
|
840
|
+
```
|
841
|
+
|
842
|
+
For example, in the code bellow, signal `sig` is not accessible from outside the additional inner scope of system `sys`
|
843
|
+
|
844
|
+
```ruby
|
845
|
+
system :sys do
|
846
|
+
...
|
847
|
+
sub
|
848
|
+
inner :sig
|
849
|
+
<sig is accessible here>
|
850
|
+
end
|
851
|
+
<sig is not accessible from here>
|
852
|
+
end
|
853
|
+
```
|
854
|
+
|
855
|
+
It is also possible to add an inner scope within another inner scope as follows:
|
856
|
+
|
857
|
+
```ruby
|
858
|
+
system :sys do
|
859
|
+
...
|
860
|
+
sub
|
861
|
+
inner :sig0
|
862
|
+
<sig0 is accessible here>
|
863
|
+
sub
|
864
|
+
inner :sig1
|
865
|
+
<sig0 and sig1 are accessible here>
|
866
|
+
end
|
867
|
+
<sig1 is not accessible here>
|
868
|
+
end
|
869
|
+
<neither sig0 nor sig1 are accessible here>
|
870
|
+
end
|
871
|
+
```
|
872
|
+
|
873
|
+
Within a same scope it is not possible to declared multiple signals or instances with a same name. However, it is possible to declare a signal or an instance with a name identical to one previously declared outside the scope: the inner-most declaration will be used.
|
874
|
+
|
875
|
+
|
876
|
+
#### Named scopes
|
877
|
+
|
878
|
+
It is possible to declare a scope with a name as follows:
|
879
|
+
|
880
|
+
```ruby
|
881
|
+
sub :<name> do
|
882
|
+
<code>
|
883
|
+
end
|
884
|
+
```
|
885
|
+
|
886
|
+
Where:
|
887
|
+
|
888
|
+
- `<name>` is the name of the scope.
|
889
|
+
- `<code>` is the code within the scope.
|
890
|
+
|
891
|
+
Contrary to the case of scopes without name, signals and instances declared within a named scope can be accessed outside using this name as reference. For example in the code bellow signal `sig` declared within scope named `scop` is accessed outside it using `scop.sig`:
|
892
|
+
|
893
|
+
```ruby
|
894
|
+
sub :scop do
|
895
|
+
inner :sig
|
896
|
+
...
|
897
|
+
end
|
898
|
+
...
|
899
|
+
scop.sig <= ...
|
900
|
+
```
|
901
|
+
|
902
|
+
|
903
|
+
### Behavioral description in a system.
|
904
|
+
|
905
|
+
In a system, parallel behavioral descriptions are declared using the `par` keyword, and sequential behavioral descriptions are declared using the `seq` keyword. They are the equivalent of the Verilog HDL `always` blocks.
|
906
|
+
|
907
|
+
A behavior is made of a list of events (the sensitivity list) upon which it is activated, and a list of statements. A behavior is declared as follows:
|
908
|
+
|
909
|
+
```ruby
|
910
|
+
par <list of events> do
|
911
|
+
<list of statements>
|
912
|
+
end
|
913
|
+
```
|
914
|
+
|
915
|
+
In addition, it is possible to declare inner signals within an execution block.
|
916
|
+
While such signals will be physically linked to the system, they are only accessible within the block they are declared into. This permits a tighter scope for signals, which improves the readability of the code and make it possible to declare several signals with identical names provided their respective scopes are different.
|
917
|
+
|
918
|
+
An event represents a specific change of state of a signal.
|
919
|
+
For example, a rising edge of a clock signal named `clk` will be represented by event `clk.posedge`. In HDLRuby, events are obtained directly from
|
920
|
+
expressions using the following methods: `posedge` for rising edge, `negedge` for falling edge, and `edge` for any edge.
|
921
|
+
Events are described in more detail in section [Events](#events).
|
922
|
+
|
923
|
+
When one of the events of the sensitivity list of a behavior occurs, the behavior is executed, i.e., each of its statements is executed in sequence. A statement can represent a data transmission to a signal, a control flow, a nested execution block or the declaration of an inner signal (as stated
|
924
|
+
earlier). Statements are described in more detail in section [statements](#statements). In this section, we focus on the transmission statements and the block statements.
|
925
|
+
|
926
|
+
A transmission statement is declared using the arrow operator `<=` as follows:
|
927
|
+
|
928
|
+
```ruby
|
929
|
+
<destination> <= <source>
|
930
|
+
```
|
931
|
+
|
932
|
+
The `<destination>` must be a reference to a signal, and the `<source>` can be any expression. A transmission has therefore exactly the same structure as a connection. However, its execution model is different: whereas a connection is continuously executed, a transmission is only executed during the execution of its block.
|
933
|
+
|
934
|
+
A block comprises a list of statements. It is used for adding hierarchy within a behavior. Blocks can be either parallel or sequential, i.e., their transmission statements are respectively non-blocking or blocking.
|
935
|
+
By default, a top block is created when declaring a behavior, and it inherits from its execution mode. For example, with the following code, the top block of the behavior is sequential.
|
936
|
+
|
937
|
+
```ruby
|
938
|
+
system :with_sequential_behavior do
|
939
|
+
seq do
|
940
|
+
<list of statements>
|
941
|
+
end
|
942
|
+
end
|
943
|
+
```
|
944
|
+
|
945
|
+
It is possible to declare new blocks within an existing block.
|
946
|
+
For declaring a sub block with the same execution mode as the upper one, the keyword `sub` is used. For example, the following code declare a sub block within a sequential block, with the same execution mode:
|
947
|
+
|
948
|
+
```ruby
|
949
|
+
system :with_sequential_behavior do
|
950
|
+
seq do
|
951
|
+
<list of statements>
|
952
|
+
sub do
|
953
|
+
<list of statements>
|
954
|
+
end
|
955
|
+
end
|
956
|
+
end
|
957
|
+
```
|
958
|
+
|
959
|
+
A sub block can also have a different execution mode if it is declared using `seq`, that will force sequential execution mode, and `par` that will force parallel execution mode. For example in the following code, a parallel sub block is declared within a sequential one:
|
960
|
+
|
961
|
+
```ruby
|
962
|
+
system :with_sequential_behavior do
|
963
|
+
seq do
|
964
|
+
<list of statements>
|
965
|
+
par do
|
966
|
+
<list of statements>
|
967
|
+
end
|
968
|
+
end
|
969
|
+
end
|
970
|
+
```
|
971
|
+
|
972
|
+
Sub blocks have their own scope so that it is possible to declare signals without colliding with existing ones. For example it is possible to
|
973
|
+
declare three different inner signals all called `sig` as follows:
|
974
|
+
|
975
|
+
```ruby
|
976
|
+
...
|
977
|
+
par(<sensibility list>) do
|
978
|
+
inner :sig
|
979
|
+
...
|
980
|
+
sub do
|
981
|
+
inner :sig
|
982
|
+
...
|
983
|
+
sub do
|
984
|
+
inner :sig
|
985
|
+
...
|
986
|
+
end
|
987
|
+
end
|
988
|
+
...
|
989
|
+
end
|
990
|
+
```
|
991
|
+
|
992
|
+
To summarize this section, here is a behavioral description of a 16-bit shift register with asynchronous reset (`hif` and `helse` are keywords used for specifying hardware _if_ and _else_ control statements).
|
993
|
+
|
994
|
+
```ruby
|
995
|
+
system :shift16 do
|
996
|
+
input :clk, :rst, :din
|
997
|
+
output :dout
|
998
|
+
|
999
|
+
[15..0].inner :reg
|
1000
|
+
|
1001
|
+
dout <= reg[15] # The output is the last bit of the register.
|
1002
|
+
|
1003
|
+
par(clk.posedge) do
|
1004
|
+
hif(rst) { reg <= 0 }
|
1005
|
+
helse do
|
1006
|
+
reg[0] <= din
|
1007
|
+
reg[15..1] <= reg[14..0]
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
```
|
1012
|
+
|
1013
|
+
In the example above, the order of the transmission statements is of no consequence. This is not the case for the following example, that implements the same register using a sequential block. In this second example, putting statement `reg[0] <= din` in the last place would have lead to an invalid functionality for a shift register.
|
1014
|
+
|
1015
|
+
```ruby
|
1016
|
+
system :shift16 do
|
1017
|
+
input :clk, :rst, :din
|
1018
|
+
output :dout
|
1019
|
+
|
1020
|
+
[15..0].inner :reg
|
1021
|
+
|
1022
|
+
dout <= reg[15] # The output is the last bit of the register.
|
1023
|
+
|
1024
|
+
par(clk.posedge) do
|
1025
|
+
hif(rst) { reg <= 0 }
|
1026
|
+
helse seq do
|
1027
|
+
reg[0] <= din
|
1028
|
+
reg <= reg[14..0]
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
```
|
1033
|
+
|
1034
|
+
__Note__:
|
1035
|
+
|
1036
|
+
- `helse seq` ensures that the block of the hardware else is in sequential mode.
|
1037
|
+
- `hif(rst)` could also have been set to sequential mode as follows:
|
1038
|
+
|
1039
|
+
```ruby
|
1040
|
+
hif rst, seq do
|
1041
|
+
reg <= 0
|
1042
|
+
end
|
1043
|
+
```
|
1044
|
+
- Parallel mode can be set the same way using `par`.
|
1045
|
+
|
1046
|
+
Finally, it often happens that a behavior contains only one statement.
|
1047
|
+
In such a case, the description can be shortened using the `at` operator as follows:
|
1048
|
+
|
1049
|
+
```ruby
|
1050
|
+
( statement ).at(<list of events>)
|
1051
|
+
```
|
1052
|
+
|
1053
|
+
For example the following two code samples are equivalent:
|
1054
|
+
|
1055
|
+
```ruby
|
1056
|
+
par(clk.posedge) do
|
1057
|
+
a <= b+1
|
1058
|
+
end
|
1059
|
+
```
|
1060
|
+
|
1061
|
+
```ruby
|
1062
|
+
( a <= b+1 ).at(clk.posedge)
|
1063
|
+
```
|
1064
|
+
|
1065
|
+
For sake of consistency, this operator can also be applied on block statements as follows, but it is probably less readable than the standard declaration of behaviors:
|
1066
|
+
|
1067
|
+
```ruby
|
1068
|
+
( seq do
|
1069
|
+
a <= b+1
|
1070
|
+
c <= d+2
|
1071
|
+
end ).at(clk.posedge)
|
1072
|
+
```
|
1073
|
+
|
1074
|
+
|
1075
|
+
## Events
|
1076
|
+
<a name="events"></a>
|
1077
|
+
|
1078
|
+
Each behavior of a system is associated with a list of events, called sensibility list, that specifies when the behavior is to be executed. An event is associated with a signal and represents the instants when the signal reaches a given state.
|
1079
|
+
|
1080
|
+
There are three kinds of event: positive edge events represent the instants when their corresponding signals vary from 0 to 1, negative edge events
|
1081
|
+
represent the instants when their corresponding signals vary from 1 to 0 and the change events represent the instants when their corresponding signals vary.
|
1082
|
+
Events are declared directly from the signals, using the `posedge` operator for positive edge, the `negedge` operator for negative edge, and the `change` operator for change. For example the following code declares 3 behaviors activated respectively on the positive edge, the negative edge and any change of the `clk` signal.
|
1083
|
+
|
1084
|
+
```ruby
|
1085
|
+
inner :clk
|
1086
|
+
|
1087
|
+
par(clk.posedge) do
|
1088
|
+
...
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
par(clk.negedge) do
|
1092
|
+
...
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
par(clk.change) do
|
1096
|
+
...
|
1097
|
+
end
|
1098
|
+
```
|
1099
|
+
|
1100
|
+
__Note:__
|
1101
|
+
- The `change` keyword can be omitted.
|
1102
|
+
|
1103
|
+
## Statements
|
1104
|
+
<a name="statements"></a>
|
1105
|
+
|
1106
|
+
Statements are the basic elements of a behavioral description. They are regrouped in blocks that specify their execution mode (parallel or sequential).
|
1107
|
+
There are four kinds of statements: the transmit statement that computes expressions and send the result to the target signals, the control statement
|
1108
|
+
that changes the execution flow of the behavior, the block statement (described earlier) and the inner signal declaration.
|
1109
|
+
|
1110
|
+
__Note__:
|
1111
|
+
|
1112
|
+
- There is actually a fifth type of statement, the time statement. It will be discussed in section [Time](#time).
|
1113
|
+
|
1114
|
+
|
1115
|
+
### Transmit statement
|
1116
|
+
|
1117
|
+
A transmit statement is declared using the arrow operator `<=` within a behavior. Its right value is the expression to compute and its left value is a reference to the target signals (or parts of signals), i.e., the signals (or part of signals) that receive the computation result.
|
1118
|
+
|
1119
|
+
For example following code transmits the value `3` to signal `s0` and the sum of the values of signals `i0` and `i1` to the first four bits of signal `s1`:
|
1120
|
+
|
1121
|
+
```ruby
|
1122
|
+
s0 <= 3
|
1123
|
+
s1[3..0] <= i0 + i1
|
1124
|
+
```
|
1125
|
+
|
1126
|
+
The comportment of a transmit statement depends on the execution mode of the enclosing block:
|
1127
|
+
|
1128
|
+
- If the mode is parallel, the target signals are updated when all the statements of the current block are processed.
|
1129
|
+
- If the mode is sequential, the target signals are updated immediately after the right value of the statement is computed.
|
1130
|
+
|
1131
|
+
|
1132
|
+
### Control statements
|
1133
|
+
|
1134
|
+
There are only two possible control statements: the hardware if `hif` and the hardware case `hcase`.
|
1135
|
+
|
1136
|
+
#### hif
|
1137
|
+
|
1138
|
+
The `hif` construct is made of a condition and a block that is executed if and only if the condition is met. It is declared as follows, where the condition can be any expression:
|
1139
|
+
|
1140
|
+
```ruby
|
1141
|
+
hif <condition> do
|
1142
|
+
<block contents>
|
1143
|
+
end
|
1144
|
+
```
|
1145
|
+
|
1146
|
+
#### hcase
|
1147
|
+
|
1148
|
+
The `hcase` construct is made of an expression and a list of value-block pairs.
|
1149
|
+
A block is executed when the corresponding value is equal to the value of the expression of the `hcase`. This construct is declared as follows:
|
1150
|
+
|
1151
|
+
```ruby
|
1152
|
+
hcase <expression>
|
1153
|
+
hwhen <value 0> do
|
1154
|
+
<block contents 0>
|
1155
|
+
end
|
1156
|
+
hwhen <value 1> do
|
1157
|
+
<block contents 1>
|
1158
|
+
end
|
1159
|
+
...
|
1160
|
+
```
|
1161
|
+
|
1162
|
+
#### helse
|
1163
|
+
|
1164
|
+
It is possible to add a block that is executed when the condition of an `hif` is not met, or when no case matches the expression of a `hcase`, using the `helse` keyword as follows:
|
1165
|
+
|
1166
|
+
```ruby
|
1167
|
+
<hif or hcase construct>
|
1168
|
+
helse do
|
1169
|
+
<block contents>
|
1170
|
+
end
|
1171
|
+
```
|
1172
|
+
|
1173
|
+
### helsif
|
1174
|
+
|
1175
|
+
In addition to `helse` it is possible to set additional conditions to an `hif` using the `helsif` keyword as follows:
|
1176
|
+
|
1177
|
+
```ruby
|
1178
|
+
hif <condition 0> do
|
1179
|
+
<block contents 0>
|
1180
|
+
end
|
1181
|
+
helsif <condition 1> do
|
1182
|
+
<block contents 1>
|
1183
|
+
end
|
1184
|
+
...
|
1185
|
+
```
|
1186
|
+
|
1187
|
+
#### About loops
|
1188
|
+
|
1189
|
+
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
|
1190
|
+
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).
|
1191
|
+
|
1192
|
+
__Notes__:
|
1193
|
+
|
1194
|
+
- HDLRuby being based on Ruby, it is highly recommended to avoid `for` or `while` constructs and to use enumerators instead.
|
1195
|
+
- The Ruby `if` and `case` statements can also be used, but they do not represent nay hardware. Actually, they are executed when the corresponding system is instantiated. For example, the following code will display `Hello world!` when the described system is instantiated, provided the generic parameter `param` is not nil.
|
1196
|
+
|
1197
|
+
```ruby
|
1198
|
+
system :say_hello do |param = nil|
|
1199
|
+
if param != nil then
|
1200
|
+
puts "Hello world!"
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
```
|
1204
|
+
|
1205
|
+
## Types
|
1206
|
+
<a name="types"></a>
|
1207
|
+
|
1208
|
+
Each signal and expression is associated with a data type which describes the kind of value it can represent. In HDLRuby, the data types represent
|
1209
|
+
basically bit vectors associated with the way they should be interpreted, i.e., as bit strings, unsigned values, signed values, or hierarchical contents.
|
1210
|
+
|
1211
|
+
### Type construction
|
1212
|
+
|
1213
|
+
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.
|
1214
|
+
|
1215
|
+
|
1216
|
+
The other types are built from them using a combination of the two following
|
1217
|
+
type operators.
|
1218
|
+
|
1219
|
+
__The vector operator__ `[]` is used for building types representing vectors of single or multiple other types. A vector whose elements have all the same type are declared as follows:
|
1220
|
+
|
1221
|
+
```ruby
|
1222
|
+
<type>[<range>]
|
1223
|
+
```
|
1224
|
+
|
1225
|
+
The `<range>` of a vector type indicates the position of the starting and ending bits relatively to the radix point. If the position of the starting bit
|
1226
|
+
is on the left side of the range, the vector is big endian, otherwise it is little endian. Negative values in a range are also possible and indicate positions bellow the radix point. For example the following code describes a big endian fixed point type with 8 bits above the radix point and 4 bits
|
1227
|
+
bellow:
|
1228
|
+
|
1229
|
+
```ruby
|
1230
|
+
bit[7..-4]
|
1231
|
+
```
|
1232
|
+
|
1233
|
+
A `n..0` range can also be abbreviated to `n+1`. For instance the two following types are identical:
|
1234
|
+
|
1235
|
+
```ruby
|
1236
|
+
bit[7..0]
|
1237
|
+
bit[8]
|
1238
|
+
```
|
1239
|
+
|
1240
|
+
A vector of multiple types, also called tuple, is declared as follows:
|
1241
|
+
|
1242
|
+
```ruby
|
1243
|
+
[<type 0>, <type 1>, ... ]
|
1244
|
+
```
|
1245
|
+
|
1246
|
+
For example the following code declares the type of the vectors made of a 8-bit logical, a 16-bit signed and a 16-bit unsigned values:
|
1247
|
+
|
1248
|
+
```ruby
|
1249
|
+
[ bit[8], signed[16], unsigned[16] ]
|
1250
|
+
```
|
1251
|
+
|
1252
|
+
__The structure opertor__ `{}` is used for building hierarchical types made of named subtypes. This operator is used as follows:
|
1253
|
+
|
1254
|
+
```ruby
|
1255
|
+
{ <name 0>: <type 0>, <name 1>: <type 1>, ... }
|
1256
|
+
```
|
1257
|
+
|
1258
|
+
For instance, the following code declares a hierarchical type with an 8-bit sub type named `header` and a 24-bit sub type named `data`:
|
1259
|
+
|
1260
|
+
```ruby
|
1261
|
+
{ header: bit[7..0], data: bit[23..0] }
|
1262
|
+
```
|
1263
|
+
|
1264
|
+
|
1265
|
+
### Type definition
|
1266
|
+
|
1267
|
+
It is possible to give names to type constructs using the `typedef` keywords as follows:
|
1268
|
+
|
1269
|
+
```ruby
|
1270
|
+
<type construct>.typedef :<name>
|
1271
|
+
```
|
1272
|
+
|
1273
|
+
For example the followings gives the name `char` to a signed 8-bit vector:
|
1274
|
+
|
1275
|
+
```ruby
|
1276
|
+
signed[7..0].typedef :char
|
1277
|
+
```
|
1278
|
+
|
1279
|
+
After this statement, `char` can be used like any other type. For example, the following code sample declares a new input signal `sig` whose type is `char`:
|
1280
|
+
|
1281
|
+
```ruby
|
1282
|
+
char.input :sig
|
1283
|
+
```
|
1284
|
+
|
1285
|
+
Alternatively, a new type can also be defined using the following syntax:
|
1286
|
+
|
1287
|
+
```ruby
|
1288
|
+
typedef :<type name> do
|
1289
|
+
<code>
|
1290
|
+
end
|
1291
|
+
```
|
1292
|
+
|
1293
|
+
Where:
|
1294
|
+
|
1295
|
+
- `type name` is the name of the type
|
1296
|
+
|
1297
|
+
- `code` is a description of the content of the type
|
1298
|
+
|
1299
|
+
For example, the previous `char` could have been declared as follows:
|
1300
|
+
|
1301
|
+
```ruby
|
1302
|
+
typedef :char do
|
1303
|
+
signed[7..0]
|
1304
|
+
end
|
1305
|
+
```
|
1306
|
+
|
1307
|
+
### Type compatibility and conversion
|
1308
|
+
|
1309
|
+
The basis of all the types in HDLRuby is the vector of bits (bitvector) where each bit can have four values: 0, 1, Z and X (for undefined). Bit vectors are by default unsigned but can be set to be signed. When performing computations between signals of different bitvector type, the shorter signal is extended to the size of the larger one preserving its sign if it is signed.
|
1310
|
+
|
1311
|
+
While the underlying structure of any HDLRuby type is the bitvector, complex types can be be defined. When using such types in computational expressions and assignments they are first implicitly converted to an unsigned bit vector of the same size.
|
1312
|
+
|
1313
|
+
## Expressions
|
1314
|
+
<a name="expressions"></a>
|
1315
|
+
|
1316
|
+
Expressions are any construct that represents a value associated with a type.
|
1317
|
+
They include [immediate values](#values), [reference to signals](#references) and operations among other expressions using [expression operators](#operators).
|
1318
|
+
|
1319
|
+
|
1320
|
+
### Immediate values
|
1321
|
+
<a name="values"></a>
|
1322
|
+
|
1323
|
+
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 enforce it in the header.
|
1324
|
+
|
1325
|
+
The vector type specifiers are the followings:
|
1326
|
+
|
1327
|
+
- `b`: `bit` type, can be omitted,
|
1328
|
+
|
1329
|
+
- `u`: `unsigned` type, (equivalent to `b` and can be used for avoiding confusion with the binary specifier),
|
1330
|
+
|
1331
|
+
- `s`: `signed` type, the last figure is sign extended if required by the binary, octal and hexadecimal bases, but not for the decimal base.
|
1332
|
+
|
1333
|
+
The base specifiers are the followings:
|
1334
|
+
|
1335
|
+
- `b`: binary, can be omitted,
|
1336
|
+
|
1337
|
+
- `o`: octal,
|
1338
|
+
|
1339
|
+
- `d`: decimal,
|
1340
|
+
|
1341
|
+
- `h`: hexadecimal.
|
1342
|
+
|
1343
|
+
For example, all the following immediate values represent an 8-bit `100` (either in unsigned or signed representation):
|
1344
|
+
|
1345
|
+
```ruby
|
1346
|
+
_bb01100100
|
1347
|
+
_b8b1100100
|
1348
|
+
_b01100100
|
1349
|
+
_01100100
|
1350
|
+
_u8d100
|
1351
|
+
_s8d100
|
1352
|
+
_uh64
|
1353
|
+
_s8o144
|
1354
|
+
```
|
1355
|
+
|
1356
|
+
__Notes__:
|
1357
|
+
|
1358
|
+
- Ruby immediate values can also be used, their bit width is automatically adjusted to match the data type of the expression they are used in. Please notice this adjusting may change the value of the immediate, for example the following code will actually set `sig` to 4 instead of 100:
|
1359
|
+
|
1360
|
+
```ruby
|
1361
|
+
[3..0].inner :sig
|
1362
|
+
sig <= 100
|
1363
|
+
```
|
1364
|
+
|
1365
|
+
|
1366
|
+
### References
|
1367
|
+
<a name="references"></a>
|
1368
|
+
|
1369
|
+
References are expressions used to designate signals, or a part of signals.
|
1370
|
+
|
1371
|
+
The most simple reference is simply the name of a signal. It designates the signal corresponding to this name in the current scope. For instance, in the
|
1372
|
+
following code, inner signal `sig0` is declared, and therefore the name *sig0* becomes a reference to designate this signal.
|
1373
|
+
|
1374
|
+
```ruby
|
1375
|
+
# Declaration of signal sig0.
|
1376
|
+
inner :sig0
|
1377
|
+
|
1378
|
+
# Access to signal sig0 using a name reference.
|
1379
|
+
sig0 <= 0
|
1380
|
+
```
|
1381
|
+
|
1382
|
+
For designating a signal of another system, or a sub signal in a hierarchical signal, you can use the `.` operator as follows:
|
1383
|
+
|
1384
|
+
```ruby
|
1385
|
+
<parent name>.<signal name>
|
1386
|
+
```
|
1387
|
+
|
1388
|
+
For example, in the following code, input signal `d` of system instance `dff0` is connected to sub signal `sub0` of hierarchical signal `sig`.
|
1389
|
+
|
1390
|
+
```ruby
|
1391
|
+
system :dff do
|
1392
|
+
input :clk, :rst, :d
|
1393
|
+
output :q
|
1394
|
+
|
1395
|
+
par(clk.posedge) { q <= d & ~rst }
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
system :my_system do
|
1399
|
+
input :clk, :rst
|
1400
|
+
{ sub0: bit, sub1: bit}.inner :sig
|
1401
|
+
|
1402
|
+
dff(:dff0).(clk: clk, rst: rst)
|
1403
|
+
dff0.d <= sig.sub0
|
1404
|
+
...
|
1405
|
+
end
|
1406
|
+
```
|
1407
|
+
|
1408
|
+
### Expression operators
|
1409
|
+
<a name="operators"></a>
|
1410
|
+
|
1411
|
+
The following table gives a summary of the operators available in HDLRuby.
|
1412
|
+
More details are given for each group of operator in the subsequent sections.
|
1413
|
+
|
1414
|
+
__Assignment operators (left-most operator of a statement):__
|
1415
|
+
|
1416
|
+
| symbol | description |
|
1417
|
+
| :--- | :--- |
|
1418
|
+
| :<= | connection, if outside behavior |
|
1419
|
+
| :<= | transmission, if inside behavior |
|
1420
|
+
|
1421
|
+
__Arithmetic operators:__
|
1422
|
+
|
1423
|
+
| symbol | description |
|
1424
|
+
| :--- | :--- |
|
1425
|
+
| :+ | addition |
|
1426
|
+
| :- | subtraction |
|
1427
|
+
| :\* | multiplication |
|
1428
|
+
| :/ | division |
|
1429
|
+
| :% | modulo |
|
1430
|
+
| :\*\* | power |
|
1431
|
+
| :+@ | positive sign |
|
1432
|
+
| :-@ | negation |
|
1433
|
+
|
1434
|
+
__Comparison operators:__
|
1435
|
+
|
1436
|
+
| symbol | description |
|
1437
|
+
| :--- | :--- |
|
1438
|
+
| :== | equality |
|
1439
|
+
| :!= | difference |
|
1440
|
+
| :> | greater than |
|
1441
|
+
| :< | smaller than |
|
1442
|
+
| :>= | greater or equal |
|
1443
|
+
| :<= | smaller or equal |
|
1444
|
+
|
1445
|
+
__Logic and shift operators:__
|
1446
|
+
|
1447
|
+
| symbol | description |
|
1448
|
+
| :--- | :--- |
|
1449
|
+
| :& | bitwise / logical and |
|
1450
|
+
| :| | bitwise / logical or |
|
1451
|
+
| :~ | bitwise / logical not |
|
1452
|
+
| :mux | multiplex |
|
1453
|
+
| :<< / :ls | left shift |
|
1454
|
+
| :>> / :rs | right shift |
|
1455
|
+
| :lr | left rotate |
|
1456
|
+
| :rr | right rotate |
|
1457
|
+
|
1458
|
+
__Conversion operators:__
|
1459
|
+
|
1460
|
+
| symbol | description |
|
1461
|
+
| :--- | :--- |
|
1462
|
+
| :to\_bit | cast to bit vector |
|
1463
|
+
| :to\_unsigned | cast to unsigned vector |
|
1464
|
+
| :to\_signed | cast to signed vector |
|
1465
|
+
| :to\_big | cast to big endian |
|
1466
|
+
| :to\_little | cast to little endian |
|
1467
|
+
| :reverse | reverse the bit order |
|
1468
|
+
| :ljust | increase width from the left, preserves the sign |
|
1469
|
+
| :rjust | increase width from the right, preserves the sign |
|
1470
|
+
| :zext | zero extension, converts to unsigned if signed |
|
1471
|
+
| :sext | sign extension, converts to sign |
|
1472
|
+
|
1473
|
+
__Selection /concatenation operators:__
|
1474
|
+
|
1475
|
+
| symbol | description |
|
1476
|
+
| :--- | :--- |
|
1477
|
+
| :[] | sub vector selection |
|
1478
|
+
| :@[] | concatenation operator |
|
1479
|
+
| :. | field selection |
|
1480
|
+
|
1481
|
+
|
1482
|
+
__Notes__:
|
1483
|
+
|
1484
|
+
- The operator precedence is the one of Ruby.
|
1485
|
+
|
1486
|
+
- Ruby does not allow to override the `&&`, the `||` and the `?:` operators so that they are not present in HDLRuby. Instead of the `?:` operator, HDLRuby provides the more general multiplex operator `mux`. However, HDLRuby does not provides any replacement for the `&&` and the `||` operators, please refer to section [Logic operators](#logic) for a justification about this issue.
|
1487
|
+
|
1488
|
+
#### Assignment operators
|
1489
|
+
<a name="assignment"></a>
|
1490
|
+
|
1491
|
+
The assignment operators can be used with any type. They are actually the connection and the transmission operators, both being represented by `<=`.
|
1492
|
+
|
1493
|
+
__Note__:
|
1494
|
+
|
1495
|
+
- The first operator of a statement is necessarily an assignment operator, while the other occurrences of `<=` represent the usual `less than or equal to` operators.
|
1496
|
+
|
1497
|
+
#### Arithmetic operators
|
1498
|
+
<a name="arithmetic"></a>
|
1499
|
+
|
1500
|
+
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.
|
1501
|
+
|
1502
|
+
#### Comparison operators
|
1503
|
+
<a name="comparison"></a>
|
1504
|
+
|
1505
|
+
Comparison operators are the operators whose result is either true or false.
|
1506
|
+
In HDLRuby, true and false are represented by respectively `bit` value 1 and `bit` value 0. This operators are `==`, `!=`, `<`, `>`, `<=`, `>=` . They
|
1507
|
+
have the same meaning as their Ruby equivalents.
|
1508
|
+
|
1509
|
+
__Notes__:
|
1510
|
+
|
1511
|
+
- The `<`, `>`, `<=` and `>=` operators can only be used on vectors of `bit`, `unsigned` or `signed` values, `integer` or `float` values.
|
1512
|
+
|
1513
|
+
- When compared, values of type different from vector of `signed` and from `float` are considered as vectors of `unsigned`.
|
1514
|
+
|
1515
|
+
|
1516
|
+
#### Logic and shift operators
|
1517
|
+
<a name="logic"></a>
|
1518
|
+
|
1519
|
+
In HDLRuby, the logic operators are all bitwise. For performing boolean computations it is necessary to use single bit values. The bitwise logic binary operators are `&`, `|`, and `^`, and the unary one is `~`. They have the same meaning as their Ruby equivalents.
|
1520
|
+
|
1521
|
+
__Note__: there is two reasons why there is no boolean operators
|
1522
|
+
|
1523
|
+
1. Ruby language does not support redefinition of the boolean operators
|
1524
|
+
|
1525
|
+
2. In Ruby, each value which is not `false` nor `nil` is considered to be true. This is perfectly relevant for software, but not for hardware where the basic data types are bit vectors. Hence, it seemed preferable to support boolean computation for one-bit values only, which can be done through bitwise operations.
|
1526
|
+
|
1527
|
+
The shift operators are `<<` and `>>` and have the same meaning as their Ruby equivalent. They do not change the bit width, and preserve the sign for `signed` values.
|
1528
|
+
|
1529
|
+
The rotation operators are `rl` and `rr` for respectively left and right bit rotations. Like the shifts, they do not change the bit width and preserve the sign for the `signed` values. However, since such operators do not exist in Ruby, they are actually used like methods as follows:
|
1530
|
+
|
1531
|
+
```ruby
|
1532
|
+
<expression>.rl(<other expression>)
|
1533
|
+
<expression>.rr(<other expression>)
|
1534
|
+
```
|
1535
|
+
|
1536
|
+
For example, for rotating left signal `sig` 3 times, the following code can be used:
|
1537
|
+
|
1538
|
+
```ruby
|
1539
|
+
sig.rl(3)
|
1540
|
+
```
|
1541
|
+
|
1542
|
+
It is possible to perform other kinds of shifts or rotations using the selection and the concatenation operators. Please refer to section [Concatenation and
|
1543
|
+
selection operators](#concat) for more details about these operators.
|
1544
|
+
|
1545
|
+
|
1546
|
+
#### Conversion operators
|
1547
|
+
<a name="conversion"></a>
|
1548
|
+
|
1549
|
+
The conversion operators are used to change the type of an expression.
|
1550
|
+
There are two kinds of such operators: the type pun that do not change the raw value of the expression and the type cast that changes the raw value.
|
1551
|
+
|
1552
|
+
The type puns include `to_bit`, `to_unsigned` and `to_signed` that convert expressions of any type 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:
|
1553
|
+
|
1554
|
+
```ruby
|
1555
|
+
[ up: signed[3..0], down: unsigned[3..0] ].inner :sig
|
1556
|
+
sig.to_bit <= b01010011
|
1557
|
+
```
|
1558
|
+
|
1559
|
+
The type casts change both the type and the value and are used to adjust the width of the types. They can only be applied to vectors of `bit`, `signed` or `unsinged` and can only increase the bit width (bit width can be truncated using the selection operator, please refer to the [next section](#concat)).
|
1560
|
+
These operators comprise the bit width conversions: `ljust`, `rjust`, `zext` and `sext`; they also comprise the bit endianness conversions: `to_big`, `to_little` and `reverse`.
|
1561
|
+
|
1562
|
+
More precisely, the bit width conversions operate as follows:
|
1563
|
+
|
1564
|
+
- `ljust` and `rjust` increase the size from respectively the left or the right side of the bit vector. They take as argument the width of the new type and the value (0 or 1) of the bits to add. For example the following code increases the size of `sig0` to 12 bits by adding 1 on the right:
|
1565
|
+
|
1566
|
+
```ruby
|
1567
|
+
[7..0].inner :sig0
|
1568
|
+
[11..0].inner :sig1
|
1569
|
+
sig0 <= 25
|
1570
|
+
sig1 <= sig0.ljust(12,1)
|
1571
|
+
```
|
1572
|
+
|
1573
|
+
- `zext` increases the size by adding several 0 bits on the most significant bit side, this side depending on the endianness of the expression. This conversion takes as argument the width of the resulting type. For example, the following code increases the size of `sig0` to 12 bits by adding 0 on the left:
|
1574
|
+
|
1575
|
+
```ruby
|
1576
|
+
signed[7..0].inner :sig0
|
1577
|
+
[11..0].inner :sig1
|
1578
|
+
sig0 <= -120
|
1579
|
+
sig1 <= sig0.zext(12)
|
1580
|
+
```
|
1581
|
+
|
1582
|
+
- `sext` increases the size by duplicating the most significant bit, the side of the extension depending on the endianness of the expression. This conversion takes as argument the width of the resulting type. For example, the following code increases the size of `sig0` to 12 bits by adding 1 on the right:
|
1583
|
+
|
1584
|
+
```ruby
|
1585
|
+
signed[0..7].inner :sig0
|
1586
|
+
[0..11].inner :sig1
|
1587
|
+
sig0 <= -120
|
1588
|
+
sig1 <= sig0.sext(12)
|
1589
|
+
```
|
1590
|
+
|
1591
|
+
Finally, the bit endianness conversions operate as follows:
|
1592
|
+
|
1593
|
+
- `to_big` ensures the type of the converted expression is big endian. If the initial expression is already big endian, it is left as is, otherwise its bits are reversed.
|
1594
|
+
|
1595
|
+
- `to_little` ensures the type of the converted expression is little endian. If the initial expression is already little endian, it is left as is, otherwise its bits are reversed.
|
1596
|
+
|
1597
|
+
- `reverse` always reverses the bit order of the expression.
|
1598
|
+
|
1599
|
+
|
1600
|
+
#### Concatenation and selection operators
|
1601
|
+
<a name="concat"></a>
|
1602
|
+
|
1603
|
+
Concatenation and selection are done using the `[]` operator as follows:
|
1604
|
+
|
1605
|
+
- when this operator takes as arguments several expressions, it concatenates them. For example, the following code concatenates `sig0` to `sig1`:
|
1606
|
+
|
1607
|
+
```ruby
|
1608
|
+
[3..0].inner :sig0
|
1609
|
+
[7..0].inner :sig1
|
1610
|
+
[11..0].inner :sig2
|
1611
|
+
sig0 <= 5
|
1612
|
+
sig1 <= 6
|
1613
|
+
sig2 <= [sig0, sig1]
|
1614
|
+
```
|
1615
|
+
|
1616
|
+
- 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 select, 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`:
|
1617
|
+
|
1618
|
+
```ruby
|
1619
|
+
[7..0].inner :sig0
|
1620
|
+
[7..0].inner :sig1
|
1621
|
+
[3..0].inner :sig2
|
1622
|
+
bit.inner :sig3
|
1623
|
+
sig0 <= 5
|
1624
|
+
sig1 <= 6
|
1625
|
+
sig2 <= sig0[3..1]
|
1626
|
+
sig3 <= sig1[4]
|
1627
|
+
```
|
1628
|
+
|
1629
|
+
#### Implicit conversions
|
1630
|
+
<a name="implicit"></a>
|
1631
|
+
|
1632
|
+
When there is no ambiguity with bit vector types of same endianness, 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:
|
1633
|
+
|
1634
|
+
- `operator` is the operator in use
|
1635
|
+
- `result width` is the width of the result's type
|
1636
|
+
- `result base` is the base type of the result's type
|
1637
|
+
- `S` is the shortest operand
|
1638
|
+
- `L` is the longest operand
|
1639
|
+
- `S operand type` is the base type of the shortest operand
|
1640
|
+
- `L operand type` is the base type of the longest operand
|
1641
|
+
- `operand conversion` is the conversions added to make the operands
|
1642
|
+
compatible.
|
1643
|
+
- `w` is the width of the operands after conversion
|
1644
|
+
- `lw` is the width of the left operand's type before conversion
|
1645
|
+
- `rw` is the width of the right operand's type before conversion
|
1646
|
+
|
1647
|
+
|
1648
|
+
|
1649
|
+
__Additive and logical operators:__
|
1650
|
+
|
1651
|
+
| operator | result's width |
|
1652
|
+
| :--- | :--- |
|
1653
|
+
| <= (assign) | w (error is raised if L.width < R.width) |
|
1654
|
+
| +, - | w+1 |
|
1655
|
+
| &, \|, ^ | w |
|
1656
|
+
| == | 1 |
|
1657
|
+
| < | 1 |
|
1658
|
+
| > | 1 |
|
1659
|
+
| <= (comp.) | 1 |
|
1660
|
+
| >= | 1 |
|
1661
|
+
|
1662
|
+
| S operand base | L operand base | result base | operand conversion |
|
1663
|
+
| :--- | :--- | :--- | :--- |
|
1664
|
+
| bit | bit | bit | S.zext(L.width) |
|
1665
|
+
| bit | unsigned | unsigned | S.zext(L.width).to_unsigned |
|
1666
|
+
| bit | signed | signed | S.zext(max(S.width+1,L.width).to_signed |
|
1667
|
+
| unsigned | bit | unsigned | S.zext(L.width), L.to_unsigned |
|
1668
|
+
| unsigned | unsigned | unsigned | S.zext(L.width) |
|
1669
|
+
| unsigned | signed | signed | S.zext(max(S.width+1,L.width).to_signed |
|
1670
|
+
| signed | bit | signed | S.sext(L.width+1), L.zext(L.width+1).to_signed |
|
1671
|
+
| signed | unsigned | signed | S.sext(L.width+1), L.zext(L.width+1).to_signed |
|
1672
|
+
| signed | signed | signed | S.sext(L.width) |
|
1673
|
+
|
1674
|
+
|
1675
|
+
__Multiplicative operators:__
|
1676
|
+
|
1677
|
+
| operator | result width |
|
1678
|
+
| :--- | :--- |
|
1679
|
+
| * | lw * rw |
|
1680
|
+
| / | lw |
|
1681
|
+
| % | rw |
|
1682
|
+
| ** | rw |
|
1683
|
+
| << / ls | lw |
|
1684
|
+
| >> / rs | lw |
|
1685
|
+
| lr | lw |
|
1686
|
+
| rr | lw |
|
1687
|
+
|
1688
|
+
| S operand base | L operand base | result base | operand conversion |
|
1689
|
+
| :--- | :--- | :--- | :--- |
|
1690
|
+
| bit | bit | bit | |
|
1691
|
+
| bit | unsigned | unsigned | S.to_unsigned |
|
1692
|
+
| bit | signed | signed | S.zext(S.width+1).to_signed |
|
1693
|
+
| unsigned | bit | unsigned | L.to_unsigned |
|
1694
|
+
| unsigned | unsigned | unsigned | |
|
1695
|
+
| unsigned | signed | signed | S.zext(S.width).to_signed |
|
1696
|
+
| signed | bit | signed | L.zext(L.width+1).to_signed |
|
1697
|
+
| signed | unsigned | signed | L.zext(L.width+1).to_signed |
|
1698
|
+
| signed | signed | signed | |
|
1699
|
+
|
1700
|
+
|
1701
|
+
## Functions
|
1702
|
+
|
1703
|
+
### HDLRuby functions
|
1704
|
+
|
1705
|
+
Similarly to Verilog HDL, HDLRuby provides function constructs for reusing code. HDLRuby functions are declared as follows:
|
1706
|
+
|
1707
|
+
```ruby
|
1708
|
+
function :<function name> do |<arguments>|
|
1709
|
+
<code>
|
1710
|
+
end
|
1711
|
+
```
|
1712
|
+
|
1713
|
+
Where:
|
1714
|
+
|
1715
|
+
- `function name` is the name of the function.
|
1716
|
+
- `arguments` is the list of arguments of the function.
|
1717
|
+
- `code` is the code of the function.
|
1718
|
+
|
1719
|
+
__Notes__:
|
1720
|
+
|
1721
|
+
- Functions have their own scope, so that any declaration within a function is local. It is also forbidden to declare interface signals (input, output or inout) within a function.
|
1722
|
+
|
1723
|
+
- Similarly to Ruby proc objects, the last statement of a function's code serves as return value. For instance the following function returns `1` (in this example the function does not have any argument):
|
1724
|
+
|
1725
|
+
```ruby
|
1726
|
+
function :one { 1 }
|
1727
|
+
```
|
1728
|
+
|
1729
|
+
- Functions can accept any kind of object as argument, including variadic arguments or blocks of code as shown bellow with a function which apply the code passed as argument to all the variadic arguments of `args`:
|
1730
|
+
|
1731
|
+
```ruby
|
1732
|
+
function :apply do |*args, &code|
|
1733
|
+
args.each { |arg| code.call(args) }
|
1734
|
+
end
|
1735
|
+
```
|
1736
|
+
|
1737
|
+
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`):
|
1738
|
+
```ruby
|
1739
|
+
apply(x,y,z) { |v| v <= sig }
|
1740
|
+
```
|
1741
|
+
|
1742
|
+
A function can be invoked anywhere in the code using its name and passing its argument between parentheses as follows:
|
1743
|
+
|
1744
|
+
```ruby
|
1745
|
+
<function name>(<list of values>)
|
1746
|
+
```
|
1747
|
+
|
1748
|
+
|
1749
|
+
|
1750
|
+
### Ruby functions
|
1751
|
+
|
1752
|
+
HDLRuby functions are useful for reusing code, but they cannot interact with the code they are called in. For example, it is not possible to add interface signals through a function nor to modify a control statement (e.g., `hif`) with them. These high-level generic operations can however be performed using the functions of the Ruby language declared as follows:
|
1753
|
+
|
1754
|
+
```ruby
|
1755
|
+
def <function name>(<arguments>)
|
1756
|
+
<code>
|
1757
|
+
end
|
1758
|
+
```
|
1759
|
+
Where:
|
1760
|
+
|
1761
|
+
- `function name` is the name of the function.
|
1762
|
+
- `arguments` is the list of arguments of the function.
|
1763
|
+
- `code` is the code of the function.
|
1764
|
+
|
1765
|
+
These functions are called the same way HDLRuby functions are called, but this operation actually pastes the code of the function as is within the code.
|
1766
|
+
Moreover, these function do not have any scope so that any inner signal or instance declared within them will actually added to the object they are invoked in.
|
1767
|
+
|
1768
|
+
For example, the following function will add input `in0` to any system where it is invoked:
|
1769
|
+
|
1770
|
+
```ruby
|
1771
|
+
def add_in0
|
1772
|
+
input :in0
|
1773
|
+
end
|
1774
|
+
```
|
1775
|
+
|
1776
|
+
This function can be used as follows:
|
1777
|
+
|
1778
|
+
```ruby
|
1779
|
+
system :sys do
|
1780
|
+
...
|
1781
|
+
add_in0
|
1782
|
+
...
|
1783
|
+
end
|
1784
|
+
```
|
1785
|
+
|
1786
|
+
As another example, following function will add an alternative code that generates a reset to a condition statement (`hif` or `hcase`):
|
1787
|
+
|
1788
|
+
```ruby
|
1789
|
+
def too_bad
|
1790
|
+
helse { $rst <= 1 }
|
1791
|
+
end
|
1792
|
+
```
|
1793
|
+
|
1794
|
+
This function can be used as follows:
|
1795
|
+
|
1796
|
+
```ruby
|
1797
|
+
system :sys do
|
1798
|
+
...
|
1799
|
+
par do
|
1800
|
+
hif(sig == 1) do
|
1801
|
+
...
|
1802
|
+
end
|
1803
|
+
too_bad
|
1804
|
+
end
|
1805
|
+
end
|
1806
|
+
```
|
1807
|
+
|
1808
|
+
Ruby functions can be compared to the macros of the C languages: they have more flexible since they actually edit the code they are invoked in, but are also dangerous to use. In general, it is not recommended to use them, unless when designing a library of generic code for HDLRuby.
|
1809
|
+
|
1810
|
+
|
1811
|
+
## Time
|
1812
|
+
<a name="time"></a>
|
1813
|
+
|
1814
|
+
### Time values
|
1815
|
+
<a name="time_val"></a>
|
1816
|
+
|
1817
|
+
In HDLRuby, time values can be created using the time operators: `s` for seconds, `ms` for millisecond, `us` for microsecond, `ns` for nano second, `ps` for pico second and `fs` for femto second. For example, the followings are all indicating one second of time:
|
1818
|
+
|
1819
|
+
```ruby
|
1820
|
+
1.s
|
1821
|
+
1000.ms
|
1822
|
+
1000000.us
|
1823
|
+
1000000000.ns
|
1824
|
+
1000000000000.ps
|
1825
|
+
1000000000000000.fs
|
1826
|
+
```
|
1827
|
+
|
1828
|
+
|
1829
|
+
### Time behaviors and time statements
|
1830
|
+
<a name="time_beh"></a>
|
1831
|
+
|
1832
|
+
Similarly to the other HDL, HDLRuby provides specific statements that models 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.
|
1833
|
+
|
1834
|
+
```ruby
|
1835
|
+
timed do
|
1836
|
+
<statements>
|
1837
|
+
end
|
1838
|
+
```
|
1839
|
+
|
1840
|
+
A time behavior do not have any sensitivity list but it can include any statement supported by a standard behavior in addition to the time statements.
|
1841
|
+
There are two kinds of such statements:
|
1842
|
+
|
1843
|
+
- The `wait` statements: such a statement blocks the execution of the behavior
|
1844
|
+
for the amount of time given in argument. For example the following code
|
1845
|
+
waits 10ns before proceeding:
|
1846
|
+
|
1847
|
+
```ruby
|
1848
|
+
wait(10.ns)
|
1849
|
+
```
|
1850
|
+
|
1851
|
+
This statement can also be abbreviated using the `!` operator as follows:
|
1852
|
+
|
1853
|
+
```ruby
|
1854
|
+
!10.ns
|
1855
|
+
```
|
1856
|
+
|
1857
|
+
- The `repeat` statements: such a statement takes as argument a time value and a block. The execution of the block is repeated until the delay given by the time value argument expires. For example, the following code executes repeatedly the inversion of the `clk` signal every 10 nanoseconds for 10 seconds (i.e., it simulates a clock signal for 10 seconds):
|
1858
|
+
|
1859
|
+
```ruby
|
1860
|
+
repeat(10.s) do
|
1861
|
+
!10.ns
|
1862
|
+
clk <= ~clk
|
1863
|
+
end
|
1864
|
+
```
|
1865
|
+
|
1866
|
+
### Parallel and sequential execution
|
1867
|
+
|
1868
|
+
Time behaviors are by default sequential but they can include both parallel and
|
1869
|
+
sequential blocks. The execution semantic is the following:
|
1870
|
+
|
1871
|
+
- A sequential block in a time behavior is executed sequentially.
|
1872
|
+
|
1873
|
+
- A parallel block in a time behavior is executed in semi-parallel fashion as follows:
|
1874
|
+
|
1875
|
+
1. Statements are grouped in sequence until a time statement is met.
|
1876
|
+
|
1877
|
+
2. The grouped sequence are executed in parallel.
|
1878
|
+
|
1879
|
+
3. The time statement is executed.
|
1880
|
+
|
1881
|
+
4. The subsequent statements are processed the same way.
|
1882
|
+
|
1883
|
+
|
1884
|
+
|
1885
|
+
## High-level programming features
|
1886
|
+
<a name="highfeat"></a>
|
1887
|
+
|
1888
|
+
### Using Ruby in HDLRuby
|
1889
|
+
|
1890
|
+
Since HDLRuby is pure Ruby code, the constructs of Ruby can be freely used without any compatibility issue. Moreover, this Ruby code will not interfere with the synthesizability of the design. It is then possible to define Ruby classes, methods or modules whose execution generates constructs of
|
1891
|
+
HDLRuby.
|
1892
|
+
|
1893
|
+
|
1894
|
+
### Generic programming
|
1895
|
+
|
1896
|
+
#### Declaring
|
1897
|
+
|
1898
|
+
##### Declaring generic systems
|
1899
|
+
|
1900
|
+
Systems can be declared with generic parameters as follows:
|
1901
|
+
|
1902
|
+
```ruby
|
1903
|
+
system :<system name> do |<list of generic parameters>|
|
1904
|
+
...
|
1905
|
+
end
|
1906
|
+
```
|
1907
|
+
|
1908
|
+
For example, the following code describes an empty system with two generic parameters named respectively `a` and `b`:
|
1909
|
+
|
1910
|
+
```ruby
|
1911
|
+
system(:nothing) { |a,b| }
|
1912
|
+
```
|
1913
|
+
|
1914
|
+
The generic parameters can be anything: values, data types, systems, Ruby variables, and so on. For example, the following system uses generic argument
|
1915
|
+
`t` as a type for an input signal, generic argument `w` as a bit range for an
|
1916
|
+
output signal and generic argument `s` as a system used for creating instance
|
1917
|
+
`sI` whose input and output signals `i` and `o` are connected respectively to
|
1918
|
+
signals `isig` and `osig`.
|
1919
|
+
|
1920
|
+
```ruby
|
1921
|
+
system :something do |t,w,s|
|
1922
|
+
t.input isig
|
1923
|
+
[w].output osig
|
1924
|
+
|
1925
|
+
s :sI.(i: isig, o: osig)
|
1926
|
+
end
|
1927
|
+
```
|
1928
|
+
|
1929
|
+
It is also possible to use a variable number of generic parameters using the variadic operator `*` like in the following example. In this examples, `args` is an array containing an indefinite number of parameters.
|
1930
|
+
|
1931
|
+
```ruby
|
1932
|
+
system(:variadic) { |*args| }
|
1933
|
+
```
|
1934
|
+
|
1935
|
+
##### Declaring generic types
|
1936
|
+
|
1937
|
+
Data types can be declared with generic parameters as follows:
|
1938
|
+
|
1939
|
+
```ruby
|
1940
|
+
typedef :<type name> do |<list of generic parameters>|
|
1941
|
+
...
|
1942
|
+
end
|
1943
|
+
```
|
1944
|
+
|
1945
|
+
For example, the following code describes a bit-vector type with generic number of bits `width`:
|
1946
|
+
|
1947
|
+
```ruby
|
1948
|
+
type(:bitvec) { |width| bit[width] }
|
1949
|
+
```
|
1950
|
+
|
1951
|
+
Like with the systems, the generic parameters of types can be any kind of objects, and it is also possible to use variadic arguments.
|
1952
|
+
|
1953
|
+
|
1954
|
+
|
1955
|
+
#### Specializing
|
1956
|
+
|
1957
|
+
##### Specializing generic systems
|
1958
|
+
|
1959
|
+
A generic system is specialized by invoking its name and passing as argument the values corresponding to the generic arguments as follows:
|
1960
|
+
|
1961
|
+
```ruby
|
1962
|
+
<system name>(<generic argument value's list>)
|
1963
|
+
```
|
1964
|
+
|
1965
|
+
If less values are provided than the number of generic arguments, the system is partially specialized. However, only a fully specialized system can be instantiated.
|
1966
|
+
|
1967
|
+
A specialized system can also be used for inheritance. For example, assuming system `sys` has 2 generic arguments, it can be specialized and used for building system `subsys` as follows:
|
1968
|
+
|
1969
|
+
```ruby
|
1970
|
+
system :subsys, sys(1,2) do
|
1971
|
+
...
|
1972
|
+
end
|
1973
|
+
```
|
1974
|
+
|
1975
|
+
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:
|
1976
|
+
|
1977
|
+
```ruby
|
1978
|
+
system :subsys_gen do |param|
|
1979
|
+
include sys(1,param)
|
1980
|
+
...
|
1981
|
+
end
|
1982
|
+
```
|
1983
|
+
|
1984
|
+
__Note:__
|
1985
|
+
|
1986
|
+
- In the example above, generic parameter `param` of `sybsys_gen` is used for specializing system `sys`.
|
1987
|
+
|
1988
|
+
|
1989
|
+
##### Specializing generic types
|
1990
|
+
|
1991
|
+
A generic type is specialized by invoking its name and passing as argument the values corresponding to the generic arguments as follows:
|
1992
|
+
|
1993
|
+
```ruby
|
1994
|
+
<type name>(<generic argument value's list>)
|
1995
|
+
```
|
1996
|
+
|
1997
|
+
If less values are provided than the number of generic arguments, the type is partially specialized. However, only a fully specialized type can be used for declaring signals.
|
1998
|
+
|
1999
|
+
|
2000
|
+
### Inheritance
|
2001
|
+
<a name="inherit"></a>
|
2002
|
+
|
2003
|
+
#### Basics
|
2004
|
+
|
2005
|
+
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
|
2006
|
+
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.
|
2007
|
+
|
2008
|
+
For example, the following code describes first a simple D-FF, and then use it to described a FF with an additional reversed output (`qb`):
|
2009
|
+
|
2010
|
+
```ruby
|
2011
|
+
system :dff do
|
2012
|
+
input :clk, :rst, :d
|
2013
|
+
output :q
|
2014
|
+
|
2015
|
+
par(clk.posedge) { q <= d & ~rst }
|
2016
|
+
end
|
2017
|
+
|
2018
|
+
system :dff_full do
|
2019
|
+
output :qb
|
2020
|
+
|
2021
|
+
include dff
|
2022
|
+
|
2023
|
+
qb <= ~q
|
2024
|
+
end
|
2025
|
+
```
|
2026
|
+
|
2027
|
+
It is also possible to declare inheritance in a more object oriented fashion by listing the parents of a system just after declaring its name as follows:
|
2028
|
+
|
2029
|
+
```ruby
|
2030
|
+
system :<new system name>, <list of parent systems> do
|
2031
|
+
<additional system code>
|
2032
|
+
end
|
2033
|
+
```
|
2034
|
+
|
2035
|
+
For example, the following code is another to describe `dff_full`:
|
2036
|
+
|
2037
|
+
```ruby
|
2038
|
+
system :dff_full, dff do
|
2039
|
+
output :qb
|
2040
|
+
|
2041
|
+
qb <= ~q
|
2042
|
+
end
|
2043
|
+
```
|
2044
|
+
|
2045
|
+
__Note__:
|
2046
|
+
|
2047
|
+
- As a matter of implementation, HDLRuby systems can be seen as set of methods used for accessing various constructs (signals, instances). Hence inheritance in HDLRuby is actually closer the Ruby mixin mechanism than to a true software inheritance.
|
2048
|
+
|
2049
|
+
|
2050
|
+
#### About inner signals and system instances
|
2051
|
+
|
2052
|
+
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
|
2053
|
+
code exports signals `clk` and `rst` and instance `dff0` of system `exporter` so that they can be accessed in child system `importer`.
|
2054
|
+
|
2055
|
+
```ruby
|
2056
|
+
system :exporter do
|
2057
|
+
input :d
|
2058
|
+
inner :clk, :rst
|
2059
|
+
|
2060
|
+
dff(:dff0).(clk: clk, rst: rst, d: d)
|
2061
|
+
|
2062
|
+
export :clk, :rst, :dff0
|
2063
|
+
end
|
2064
|
+
|
2065
|
+
system :importer, exporter do
|
2066
|
+
input :clk0, :rst0
|
2067
|
+
output :q
|
2068
|
+
|
2069
|
+
clk <= clk0
|
2070
|
+
rst <= rst0
|
2071
|
+
dff0.q <= q
|
2072
|
+
end
|
2073
|
+
```
|
2074
|
+
|
2075
|
+
__Note__:
|
2076
|
+
- export takes as arguments the symbols (or the strings) representing the name of the components to export *and not* a reference to them. For instance, the following code is invalid:
|
2077
|
+
|
2078
|
+
```ruby
|
2079
|
+
system :exporter do
|
2080
|
+
input :d
|
2081
|
+
inner :clk, :rst
|
2082
|
+
|
2083
|
+
dff(:dff0).(clk: clk, rst: rst, d: d)
|
2084
|
+
|
2085
|
+
export clk, rst, dff0
|
2086
|
+
end
|
2087
|
+
```
|
2088
|
+
|
2089
|
+
#### Conflicts when inheriting
|
2090
|
+
|
2091
|
+
Signals and instances cannot be overridden, this is also the case for signals and instances accessible through inheritance. For example the following code is invalid since `rst` has already been defined in `dff`:
|
2092
|
+
|
2093
|
+
```ruby
|
2094
|
+
system :dff_bad, dff do
|
2095
|
+
input :rst
|
2096
|
+
end
|
2097
|
+
```
|
2098
|
+
|
2099
|
+
Conflicts among several inherited systems can be avoided by renaming the signals and instances that collide with one another as shown in the next
|
2100
|
+
section.
|
2101
|
+
|
2102
|
+
|
2103
|
+
#### Shadowed signals and instances
|
2104
|
+
|
2105
|
+
It is possible in HDLRuby to declare a signal or an instance whose name is identical to one used in one of the included systems. In such a case, the corresponding construct of the included system is still present, but is not directly accessible even if exported, they are said to be shadowed.
|
2106
|
+
|
2107
|
+
In order to access to the shadowed signals or instances, a system must be reinterpreted as the relevant parent system using the `as` operator as follows: `as(system)`.
|
2108
|
+
|
2109
|
+
For example, in the following code signal `db` of system `dff_db` is shadowed by signal `db` of system `dff_shadow`, but is accessed using the `as` operator.
|
2110
|
+
|
2111
|
+
```ruby
|
2112
|
+
system :dff_db do
|
2113
|
+
input :clk,:rst,:d
|
2114
|
+
inner :db
|
2115
|
+
output :q
|
2116
|
+
|
2117
|
+
db <= ~d
|
2118
|
+
(q <= d & ~rst).at(clk.posedge)
|
2119
|
+
end
|
2120
|
+
|
2121
|
+
system :dff_shadow, dff_db do
|
2122
|
+
output :qb, :db
|
2123
|
+
|
2124
|
+
db <= ~d
|
2125
|
+
qb <= as(dff_db).db
|
2126
|
+
end
|
2127
|
+
```
|
2128
|
+
|
2129
|
+
|
2130
|
+
|
2131
|
+
### Opening a system
|
2132
|
+
<a name="system_open"></a>
|
2133
|
+
|
2134
|
+
It is possible to pursue the definition of a system after it has been declared using the `open` methods as follows:
|
2135
|
+
|
2136
|
+
```ruby
|
2137
|
+
<system>.open do
|
2138
|
+
<additional system description>
|
2139
|
+
end
|
2140
|
+
```
|
2141
|
+
|
2142
|
+
For example `dff`, a system describing a D-FF, can be modified to have an inverted output as follows:
|
2143
|
+
|
2144
|
+
```ruby
|
2145
|
+
dff.open do
|
2146
|
+
output :qb
|
2147
|
+
|
2148
|
+
qb <= ~q
|
2149
|
+
end
|
2150
|
+
```
|
2151
|
+
|
2152
|
+
|
2153
|
+
### Opening an instance
|
2154
|
+
<a name="instance_open"></a>
|
2155
|
+
|
2156
|
+
When there is a modification to apply to an instance, it is sometimes preferable to modify this sole instance rather than declaring a all new system to derivate the instance from. For that purpose it is possible to open an instance for modification as follows:
|
2157
|
+
|
2158
|
+
```ruby
|
2159
|
+
<instance name>.open do
|
2160
|
+
<additional description for the instance>
|
2161
|
+
end
|
2162
|
+
```
|
2163
|
+
|
2164
|
+
For example, an instance of the previous `dff` system can be extended with an inverted output as follows:
|
2165
|
+
```ruby
|
2166
|
+
system :some_system do
|
2167
|
+
...
|
2168
|
+
dff :dff0
|
2169
|
+
dff0.open do
|
2170
|
+
output :qb
|
2171
|
+
qb <= ~q
|
2172
|
+
end
|
2173
|
+
...
|
2174
|
+
end
|
2175
|
+
```
|
2176
|
+
|
2177
|
+
|
2178
|
+
|
2179
|
+
### Overloading of operators
|
2180
|
+
|
2181
|
+
Operators can be overloaded for specific types. This allows for instance to support seamlessly fixed-point computations without requiring explicit readjustment of the position of the decimal point.
|
2182
|
+
|
2183
|
+
An operator is redefined as follows:
|
2184
|
+
|
2185
|
+
```ruby
|
2186
|
+
<type>.define_operaot(:<op>) do |<args>|
|
2187
|
+
<operation description>
|
2188
|
+
end
|
2189
|
+
```
|
2190
|
+
|
2191
|
+
Where:
|
2192
|
+
|
2193
|
+
- `type` is the type from which the operation is overloaded.
|
2194
|
+
- `op` is the operator that is overloaded (e.g., `+`)
|
2195
|
+
- `args` are the arguments of the operation.
|
2196
|
+
- `operation description` is an HDLRuby description of the new operation.
|
2197
|
+
|
2198
|
+
For example, for `fix32` a 32-bit (decimal point at 16-bit) fixed point type defined as follows:
|
2199
|
+
|
2200
|
+
```ruby
|
2201
|
+
signed[31..0].typedef(:fix32)
|
2202
|
+
```
|
2203
|
+
|
2204
|
+
The multiplication operator can be overloaded as follows to ensure the decimal point have always the right position:
|
2205
|
+
|
2206
|
+
```ruby
|
2207
|
+
fix32.define_operator(:*) do |left,right|
|
2208
|
+
(left.as(signed[31..0]) * right) >> 16
|
2209
|
+
end
|
2210
|
+
```
|
2211
|
+
|
2212
|
+
Please notice, that in the code above, the left value has been casted to a plain bit-vector in order to avoid infinite recursive call of the `*` operator.
|
2213
|
+
|
2214
|
+
Operator can also be overloaded for generic types. However, is such a case, the generic argument must also be present in the list of arguments of the overloaded operators.
|
2215
|
+
For instance, let us consider the following fixed point type of variable width (and whose decimal point is set at the half of its bit range):
|
2216
|
+
|
2217
|
+
```ruby
|
2218
|
+
typedef(:fixed) do |width|
|
2219
|
+
signed[(width-1)..0]
|
2220
|
+
end
|
2221
|
+
```
|
2222
|
+
|
2223
|
+
The multiplication operator would be overloaded as follows:
|
2224
|
+
|
2225
|
+
```ruby
|
2226
|
+
fixed.define_operator do |width,left,right|
|
2227
|
+
(left.as(signed[(width-1)..0]) * right) >> width/2
|
2228
|
+
end
|
2229
|
+
```
|
2230
|
+
|
2231
|
+
### Predicate and access methods
|
2232
|
+
|
2233
|
+
In order to get information about the current state of the hardware description HDLRuby provides the following predicates:
|
2234
|
+
|
2235
|
+
| predicate name | predicate type | predicate meaning |
|
2236
|
+
| :--- | :--- | :--- |
|
2237
|
+
| `is_block?` | bit | tells if in execution block |
|
2238
|
+
| `is_par?` | bit | tells if current parallel block is parallel|
|
2239
|
+
| `is_seq?` | bit | tells if current parallel block is sequential|
|
2240
|
+
| `is_clocked?` | bit | tells if current behavior is clocked (activated on a sole rising or falling edge of a signal) |
|
2241
|
+
| `cur_block` | block | gets the current block |
|
2242
|
+
| `cur_behavior` | behavior | gets the current behavior |
|
2243
|
+
| `cur_systemT` | system | gets the current system |
|
2244
|
+
| `one_up` | block/system | gets the upper construct (block or system) |
|
2245
|
+
| `last_one` | any | last declared construct |
|
2246
|
+
|
2247
|
+
Several enumerators are also provided for accessing the internals of the current construct (in the current state):
|
2248
|
+
|
2249
|
+
| enumerator name | accessed elements |
|
2250
|
+
| :--- | :--- |
|
2251
|
+
| `each_input` | input signals of the current system |
|
2252
|
+
| `each_output` | output signals of the current system |
|
2253
|
+
| `each_inout` | inout signals of the current system |
|
2254
|
+
| `each_behavior` | behaviors of the current system |
|
2255
|
+
| `each_event` | events of the current behavior |
|
2256
|
+
| `each_block` | blocks of the current behavior |
|
2257
|
+
| `each_statement` | statements of the current block |
|
2258
|
+
| `each_inner` | inner signals of the current block (or system if not within a block) |
|
2259
|
+
|
2260
|
+
### Global signals
|
2261
|
+
|
2262
|
+
HDLRuby allows to declare 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.
|
2263
|
+
|
2264
|
+
In order to ease the design of standardized libraries, the following global signals are defined by default:
|
2265
|
+
|
2266
|
+
| signal name | signal type | signal function |
|
2267
|
+
| :--- | :--- | :--- |
|
2268
|
+
| `$reset` | bit | global reset |
|
2269
|
+
| `$resetb` | bit | global reset complement |
|
2270
|
+
| `$clk` | bit | global clock |
|
2271
|
+
|
2272
|
+
__Note__:
|
2273
|
+
|
2274
|
+
- When not used, the global signals are discarded.
|
2275
|
+
|
2276
|
+
|
2277
|
+
|
2278
|
+
### Defining and executing Ruby methods within HDLRuby constructs
|
2279
|
+
<a name="method"></a>
|
2280
|
+
|
2281
|
+
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 will be usable within this system, while when defining a method outside any construct, it will be usable everywhere in the HDLRuby description.
|
2282
|
+
|
2283
|
+
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`.
|
2284
|
+
|
2285
|
+
```ruby
|
2286
|
+
def some_arrow
|
2287
|
+
sig1 <= sig0
|
2288
|
+
end
|
2289
|
+
|
2290
|
+
system :sys0 do
|
2291
|
+
input :sig0
|
2292
|
+
output :sig1
|
2293
|
+
|
2294
|
+
some_arrow
|
2295
|
+
end
|
2296
|
+
|
2297
|
+
system :sys1 do
|
2298
|
+
input :sig0, :clk
|
2299
|
+
output :sig1
|
2300
|
+
|
2301
|
+
par(clk.posedge) do
|
2302
|
+
some_arrow
|
2303
|
+
end
|
2304
|
+
end
|
2305
|
+
```
|
2306
|
+
|
2307
|
+
__Warning__:
|
2308
|
+
|
2309
|
+
- In the above example, the semantic of `some_arrow` changes depending on where it is invoked from: within a system, it is a connection, within a behavior it is a transmission.
|
2310
|
+
|
2311
|
+
- Using Ruby methods for describing hardware might lead to weak code, for example the in following code, the method declares `in0` as input signal. Hence, while used in `sys0` no problems happens, an exception will be raised for `sys1` because a signal `in0` is already declare, and will also be raised for `sys2` because it is not possible to declare an input from within a behavior.
|
2312
|
+
|
2313
|
+
```ruby
|
2314
|
+
def in_decl
|
2315
|
+
input :in0
|
2316
|
+
end
|
2317
|
+
|
2318
|
+
system :sys0 do
|
2319
|
+
in_decl
|
2320
|
+
end
|
2321
|
+
|
2322
|
+
system :sys1 do
|
2323
|
+
input :in0
|
2324
|
+
in_decl
|
2325
|
+
end
|
2326
|
+
|
2327
|
+
system :sys2 do
|
2328
|
+
par do
|
2329
|
+
in_decl
|
2330
|
+
end
|
2331
|
+
end
|
2332
|
+
```
|
2333
|
+
|
2334
|
+
Like any other Ruby method, methods defined in HDLRuby support variadic arguments, named arguments and block arguments. For example, the following method can be used to connects a driver to multiple signals:
|
2335
|
+
|
2336
|
+
```ruby
|
2337
|
+
def mconnect(driver, *signals)
|
2338
|
+
signals.each do |signal|
|
2339
|
+
signal <= driver
|
2340
|
+
end
|
2341
|
+
end
|
2342
|
+
|
2343
|
+
system :sys0 do
|
2344
|
+
input :i0
|
2345
|
+
input :o0, :o1, :o2, :o3
|
2346
|
+
|
2347
|
+
mconnect(i0,o0,o1,o2,o3)
|
2348
|
+
end
|
2349
|
+
```
|
2350
|
+
|
2351
|
+
|
2352
|
+
While requiring care, 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:
|
2353
|
+
|
2354
|
+
```ruby
|
2355
|
+
def after(cycles,rst = $rst, &code)
|
2356
|
+
sub do
|
2357
|
+
inner :count
|
2358
|
+
hif rst == 1 do
|
2359
|
+
count <= 0
|
2360
|
+
end
|
2361
|
+
helse do
|
2362
|
+
hif count < cycles do
|
2363
|
+
count <= count + 1
|
2364
|
+
end
|
2365
|
+
helse do
|
2366
|
+
instance_eval(&code)
|
2367
|
+
end
|
2368
|
+
end
|
2369
|
+
end
|
2370
|
+
end
|
2371
|
+
```
|
2372
|
+
|
2373
|
+
In the code above:
|
2374
|
+
|
2375
|
+
- the default initialization of `rst` to `$rst` allows to reset the counter even if no such signal it provided as argument.
|
2376
|
+
|
2377
|
+
- `sub` ensures that the `count` signal do not conflict with another signal with the same name.
|
2378
|
+
|
2379
|
+
- the `instance_eval` keyword is a standard Ruby method that executes the block passed as argument in context.
|
2380
|
+
|
2381
|
+
The following is an example that switches a LED on after 1000000 clock cycles using the previously defined `after` ruby method:
|
2382
|
+
|
2383
|
+
```ruby
|
2384
|
+
system :led_after do
|
2385
|
+
output :led
|
2386
|
+
input :clk
|
2387
|
+
|
2388
|
+
par(clk.posedge) do
|
2389
|
+
(led <= 0).hif($rst)
|
2390
|
+
after(100000) { led <= 1 }
|
2391
|
+
end
|
2392
|
+
end
|
2393
|
+
```
|
2394
|
+
|
2395
|
+
__Note__:
|
2396
|
+
|
2397
|
+
- 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 signal declared in this method will not collide with them.
|
2398
|
+
|
2399
|
+
|
2400
|
+
### Dynamic description
|
2401
|
+
|
2402
|
+
When describing a system, it is possible to disconnect or to completely undefine a signal or an instance.
|
2403
|
+
|
2404
|
+
|
2405
|
+
## Extending HDLRuby
|
2406
|
+
<a name="extend"></a>
|
2407
|
+
|
2408
|
+
Like any Ruby classes, the constructs of HDLRuby can be dynamically extended. If it is not recommended to change their internal structure, it is possible to add methods to them for extension.
|
2409
|
+
|
2410
|
+
### Extending HDLRuby constructs globally
|
2411
|
+
|
2412
|
+
By gobal extension of hardware constructs we actually mean the classical extension of Ruby classes by monkey patching the corresponding class. For example, it is possible to add a methods giving the number of signals in the interface of a system instance as follows:
|
2413
|
+
|
2414
|
+
```ruby
|
2415
|
+
class SystemI
|
2416
|
+
def interface_size
|
2417
|
+
return each_input.size + each_output.size + each_inout.size
|
2418
|
+
end
|
2419
|
+
end
|
2420
|
+
```
|
2421
|
+
|
2422
|
+
From there, the method `interface_size` can be used on any system instance as follows: `<system instance>.interface_size`.
|
2423
|
+
|
2424
|
+
The following table gives the class of each construct of HDLRuby.
|
2425
|
+
|
2426
|
+
| construct | class |
|
2427
|
+
| :--- | :--- |
|
2428
|
+
| data type | Type |
|
2429
|
+
| system | SystemT |
|
2430
|
+
| scope | Scope |
|
2431
|
+
| system instance | SystemI |
|
2432
|
+
| signal | Signal |
|
2433
|
+
| connection | Connection |
|
2434
|
+
| par/seq | Behavior |
|
2435
|
+
| timed | TimeBehavior |
|
2436
|
+
| event | Event |
|
2437
|
+
| par/seq/sub | Block |
|
2438
|
+
| transmit | Transmit |
|
2439
|
+
| hif | If |
|
2440
|
+
| hcase | Case |
|
2441
|
+
|
2442
|
+
|
2443
|
+
### Extending HDLRuby constructs locally
|
2444
|
+
|
2445
|
+
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 in 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`:
|
2446
|
+
|
2447
|
+
```ruby
|
2448
|
+
dff.singleton_class.class_eval do
|
2449
|
+
def interface_size
|
2450
|
+
return each_input.size + each_output.size + each_inout.size
|
2451
|
+
end
|
2452
|
+
end
|
2453
|
+
```
|
2454
|
+
|
2455
|
+
It is also possible to extend locally an instance using the same methods.
|
2456
|
+
For example, with the following code, only instance `dff0` will respond to method `interface_size`:
|
2457
|
+
|
2458
|
+
```ruby
|
2459
|
+
dff :dff0
|
2460
|
+
|
2461
|
+
dff0.singleton_class.class_eval do
|
2462
|
+
def interface_size
|
2463
|
+
return each_input.size + each_output.size + each_inout.size
|
2464
|
+
end
|
2465
|
+
end
|
2466
|
+
```
|
2467
|
+
|
2468
|
+
Finally, it is possible to extend locally all the instances of a system using method `singleton_instance` in place of method `singleton_class`.
|
2469
|
+
For example, with the following code, all the instances of system `dff` will respond to method `interface_size`:
|
2470
|
+
|
2471
|
+
```ruby
|
2472
|
+
dff.singleton_instance.class_eval do
|
2473
|
+
def interface_size
|
2474
|
+
return each_input.size + each_output.size + each_inout.size
|
2475
|
+
end
|
2476
|
+
end
|
2477
|
+
```
|
2478
|
+
|
2479
|
+
### Modifying the generation behavior
|
2480
|
+
|
2481
|
+
The main purpose of allowing global and local extensions for hardware constructs is to give the user the possibility implements its own synthesis methods. For example, one may want to implement some algorithm for a given kind of system. For that purpose, the user can define an abstract system (without any hardware content), that holds the specific algorithm as follows:
|
2482
|
+
|
2483
|
+
```ruby
|
2484
|
+
system(:my_base) {}
|
2485
|
+
|
2486
|
+
my_base.singleton_instance.class_eval do
|
2487
|
+
def my_generation
|
2488
|
+
<some code>
|
2489
|
+
end
|
2490
|
+
end
|
2491
|
+
```
|
2492
|
+
|
2493
|
+
Then, when this system named `my_base` is included into another system, this latter will inherit from the algorithms implemented inside method `my_generation` as shown in the following code:
|
2494
|
+
|
2495
|
+
```ruby
|
2496
|
+
system :some_system, my_base do
|
2497
|
+
<some system description>
|
2498
|
+
end
|
2499
|
+
```
|
2500
|
+
|
2501
|
+
However, when generation the low-level description of this system, code similar to the following will have to be written for applying `my_generation`:
|
2502
|
+
|
2503
|
+
```ruby
|
2504
|
+
some_system :instance0
|
2505
|
+
instance0.my_generation
|
2506
|
+
low = instance0.to_low
|
2507
|
+
```
|
2508
|
+
|
2509
|
+
This can be avoided by redefining the `to_low` method as follows:
|
2510
|
+
|
2511
|
+
```ruby
|
2512
|
+
system(:my_base) {}
|
2513
|
+
|
2514
|
+
my_base.singleton_instance.class_eval do
|
2515
|
+
def my_generation
|
2516
|
+
<some code>
|
2517
|
+
end
|
2518
|
+
|
2519
|
+
alias :_to_low :to_low
|
2520
|
+
def to_low
|
2521
|
+
my_generation
|
2522
|
+
_to_low
|
2523
|
+
end
|
2524
|
+
end
|
2525
|
+
```
|
2526
|
+
|
2527
|
+
This way, calling directly `to_low` will automatically use `my_generation`.
|
2528
|
+
|
2529
|
+
|
2530
|
+
|
2531
|
+
|
2532
|
+
|
2533
|
+
|
2534
|
+
# Standard library
|
2535
|
+
<a name="library"></a>
|
2536
|
+
|
2537
|
+
The standard libraries are included into the module `Std`.
|
2538
|
+
They can be loaded as follows, where `<library name>` is the name of the
|
2539
|
+
library:
|
2540
|
+
|
2541
|
+
```ruby
|
2542
|
+
require 'std/<library name>'
|
2543
|
+
```
|
2544
|
+
|
2545
|
+
After the libraries are loaded, the module `Std` must be included as follows:
|
2546
|
+
|
2547
|
+
```ruby
|
2548
|
+
include HDLRuby::High::Std
|
2549
|
+
```
|
2550
|
+
|
2551
|
+
|
2552
|
+
|
2553
|
+
## Clocks
|
2554
|
+
<a name="clocks"></a>
|
2555
|
+
|
2556
|
+
The `clocks` library provides utilities for an easier handling of clock synchronizations.
|
2557
|
+
|
2558
|
+
It adds the possibility to multiply events by integer. The result is a new event whose frequency is divided by the integer multiplicand. For example the following code describes a D-FF that memorizes each three clock cycle.
|
2559
|
+
|
2560
|
+
```ruby
|
2561
|
+
require 'std/clocks'
|
2562
|
+
include HDLRuby::High::Std
|
2563
|
+
|
2564
|
+
system :dff_slow do
|
2565
|
+
input :clk, :rst
|
2566
|
+
input :d
|
2567
|
+
output :q
|
2568
|
+
|
2569
|
+
( q <= d & ~rst ).at(clk.posedge * 3)
|
2570
|
+
end
|
2571
|
+
```
|
2572
|
+
|
2573
|
+
__Note__: this library does generate all the RTL code for the circuit handling the division of the frequency.
|
2574
|
+
|
2575
|
+
## Counters
|
2576
|
+
<a name="counters"></a>
|
2577
|
+
|
2578
|
+
This library provides two new constructs for implementing synthesizable wait statements.
|
2579
|
+
|
2580
|
+
The first construct is the `after` statement that activates a block after a given number of clocks cycles is passed. Its syntax is the following:
|
2581
|
+
|
2582
|
+
```ruby
|
2583
|
+
after(<number>,<clock>,<reset>)
|
2584
|
+
```
|
2585
|
+
|
2586
|
+
Where:
|
2587
|
+
* `<number>` is the number of cycles to wait.
|
2588
|
+
* `<clock>` is the clock to use, this argument can be omitted.
|
2589
|
+
* `<reset>` is the signal used to reset the counter used for waiting, this argument can be omitted.
|
2590
|
+
|
2591
|
+
This statement can be used both inside or outside a clocked behavior. When used within a clocked behavior, the clock event of the behavior is used for the counter unless specified otherwise. When used outside such a behavior, the clock is the global default clock `$clk`. In both cases, the reset is the global reset `$rst` unless specified otherwise.
|
2592
|
+
|
2593
|
+
The second construct is the `before` statement that activates a block until a given number of clocks cycles is passed. Its syntax and usage is identical to the `after` statement.
|
2594
|
+
|
2595
|
+
|
2596
|
+
## Decoder
|
2597
|
+
<a name="decoder"></a>
|
2598
|
+
|
2599
|
+
This library provides a new set of control statements for easily describing an instruction decoder.
|
2600
|
+
|
2601
|
+
A decoder can be declared anywhere in the code describing a system using the `decoder` keyword as follows:
|
2602
|
+
|
2603
|
+
```ruby
|
2604
|
+
decoder(<signal>) <block>
|
2605
|
+
```
|
2606
|
+
|
2607
|
+
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:
|
2608
|
+
|
2609
|
+
```ruby
|
2610
|
+
entry(<pattern>) <block>
|
2611
|
+
```
|
2612
|
+
|
2613
|
+
Where `pattern` is a string describing the pattern to match for the entry, and `block` is a procedure block describing the actions (some HDLRuby code) that are performed when the entry matches. The string describing the pattern can include `0` and `1` characters for specifying a specific value for the corresponding bit, or any alphabetical character for specifying a field in the pattern. The fields in the pattern can then be used by name in the block describing the action. When a letter is used several times within a pattern, the corresponding bits are concatenated, and are used as a signal multi-bit signal in the block.
|
2614
|
+
|
2615
|
+
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`:
|
2616
|
+
|
2617
|
+
```ruby
|
2618
|
+
decoder(ir) do
|
2619
|
+
entry("000xx0yy") { s <= x + y }
|
2620
|
+
entry("10zxxzyy") { s <= x + y + z }
|
2621
|
+
end
|
2622
|
+
```
|
2623
|
+
|
2624
|
+
In can be noticed for field `z` in the example above that the bits of are not required to be contiguous.
|
2625
|
+
|
2626
|
+
## FSM
|
2627
|
+
<a name="fsm"></a>
|
2628
|
+
|
2629
|
+
This library provides a new set of control statements for easily describing a finite state machine (FSM).
|
2630
|
+
|
2631
|
+
A finite state machine can be declared anywhere provided it is outside a behavior using the `fsm` keyword as follows:
|
2632
|
+
|
2633
|
+
```ruby
|
2634
|
+
fsm(<event>,<reset>,<mode>) <block>
|
2635
|
+
```
|
2636
|
+
|
2637
|
+
Where `event` is the event (rising falling edge of a signal) activating the state transitions, `rst` is the reset signal, and `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).
|
2638
|
+
|
2639
|
+
The states of a FSM are described follows:
|
2640
|
+
|
2641
|
+
```ruby
|
2642
|
+
<kind>(<name>) <block>
|
2643
|
+
```
|
2644
|
+
|
2645
|
+
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 followings:
|
2646
|
+
|
2647
|
+
* 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.
|
2648
|
+
* state: the default kind of state, it will be synchronous if the FSM is synchronous or asynchronous otherwise.
|
2649
|
+
* sync: the synchronous kind of state, it will be synchronous whatever the kind of FSM is used.
|
2650
|
+
* async: the asynchronous kind of state, it will be asynchronous whatever the kind of FSM is used.
|
2651
|
+
|
2652
|
+
In addition, it is possible to define a default action that will be executed whatever the state is using the following statement:
|
2653
|
+
|
2654
|
+
```ruby
|
2655
|
+
default <block>
|
2656
|
+
```
|
2657
|
+
|
2658
|
+
Where `block` is the action to execute.
|
2659
|
+
|
2660
|
+
State transitions are by default set to be from one state to the following in the description order. If no more transition is declared the next one is the first declared transition. A specific transition is defined using the `goto` statement as last statement of the action block as follows:
|
2661
|
+
|
2662
|
+
```ruby
|
2663
|
+
goto(<condition>,<names>)
|
2664
|
+
```
|
2665
|
+
|
2666
|
+
Where `condition` is a signal whose value is used as index for selection the target state among the ones specified in the `names` list. For example the following statement indicate to go to state named `st_a` if the `cond` is 0, `st_b` if condition is 1 and `st_c` if condition is 2, otherwise this specific transition is ignored:
|
2667
|
+
|
2668
|
+
```ruby
|
2669
|
+
goto(cond,:st_a,:st_b,:st_c)
|
2670
|
+
```
|
2671
|
+
|
2672
|
+
Several goto statements can be used, the last one having priority provided it is taken (i.e., its condition correspond to one of the target state). If no goto is taken, the next transition is the next declared one.
|
2673
|
+
|
2674
|
+
For example the following code describes a FSM describing a circuit that checks if two buttons (`but_a` and `but_b`) are pressed and released in sequence for activating an output signal (`ok`):
|
2675
|
+
|
2676
|
+
```ruby
|
2677
|
+
fsm(clk.posedge,rst,:sync) do
|
2678
|
+
default { ok <= 0 }
|
2679
|
+
reset do
|
2680
|
+
goto(but_a, :reset, but_a_on)
|
2681
|
+
end
|
2682
|
+
state(:but_a_on) do
|
2683
|
+
goto(but_a, :but_a_off, :but_a_on)
|
2684
|
+
end
|
2685
|
+
state(:but_a_off) do
|
2686
|
+
goto(but_b, :but_a_off, :but_b_on)
|
2687
|
+
end
|
2688
|
+
state(:but_b_on) do
|
2689
|
+
goto(but_b, :but_b_off, :but_b_on)
|
2690
|
+
end
|
2691
|
+
state(:but_b_off) do
|
2692
|
+
ok <= 1
|
2693
|
+
goto(:but_b_off)
|
2694
|
+
end
|
2695
|
+
end
|
2696
|
+
```
|
2697
|
+
|
2698
|
+
## Channel
|
2699
|
+
<a name="channel"></a>
|
2700
|
+
|
2701
|
+
This library provides a unified interface to complex communication protocols. It provides a new kind of component called the channel that abstracts the details of a communication protocol. The channels an be used similarly to the ports of a system and are used through a unified interface so that changing the kind of channel, i.e., the communication protocol, does not require any modification of the code.
|
2702
|
+
|
2703
|
+
A channel is used similarly to a pipe: it has an input where data can be written and an output where data can be read. The ordering of the data and the synchronisation depend on the internals of the channel, .e.g., a channel can be FIFO or LIFO. The interaction with the channel is done using the following methods:
|
2704
|
+
|
2705
|
+
* `writer_ports`: generate ports in the system for writing to the channel. This method is used without any argument.
|
2706
|
+
|
2707
|
+
* `reader_ports`: generate ports in the system for reading from the channel. This method is used without any argument.
|
2708
|
+
|
2709
|
+
* `write(<value>) <block>`: write `value` to the channel and execute `block` when `write` completes. Both `value` and `block` may be omitted depending on the kind of channel.
|
2710
|
+
|
2711
|
+
* `read(<target>) <block>`: read the channel, assign the result to signal `target` and execute `block` when the read completes. Both `target` and `block` may be omitted depending on the kind of channel.
|
2712
|
+
|
2713
|
+
For example a system sending successive 8 bit values through a channel can be described as follows:
|
2714
|
+
|
2715
|
+
```ruby
|
2716
|
+
system :producer8 do |channel|
|
2717
|
+
# Inputs of the producer: clock and reset.
|
2718
|
+
input :clk, :rst
|
2719
|
+
# Instantiate the channel ports
|
2720
|
+
channel.writer_ports
|
2721
|
+
# Inner 8-bit counter for generating values.
|
2722
|
+
[8].inner :counter
|
2723
|
+
|
2724
|
+
# The value production process
|
2725
|
+
par(clk.posedge) do
|
2726
|
+
hif(rst) { counter <= 0 }
|
2727
|
+
helse do
|
2728
|
+
channel.write(counter) { counter <= counter + 1 }
|
2729
|
+
end
|
2730
|
+
end
|
2731
|
+
end
|
2732
|
+
```
|
2733
|
+
|
2734
|
+
__Note__: In the code above, the channel is passed as generic argument of the system.
|
2735
|
+
|
2736
|
+
A new channel is declared like using the keyword `channel` as follows:
|
2737
|
+
|
2738
|
+
```ruby
|
2739
|
+
channel <name> <block>
|
2740
|
+
```
|
2741
|
+
|
2742
|
+
Where `name` is the name of the channel and `block` is a procedure block describing the channel. This block can contain any HDLRuby code, and is actually similar to the content of a block describing a system with the difference that it does not have standard input, output and inout ports are declared differently and that it supports following additional keywords:
|
2743
|
+
|
2744
|
+
* srgg
|
2745
|
+
|
2746
|
+
## Reconf
|
2747
|
+
<a name="reconf"></a>
|
2748
|
+
|
2749
|
+
This library provides a unified interface to partially (or dynamically)
|
2750
|
+
reconfigurable devices.
|
2751
|
+
|
2752
|
+
## Pipeline
|
2753
|
+
<a name="pipeline"></a>
|
2754
|
+
|
2755
|
+
This library provides a construct for an easy description of pipeline architectures.
|
2756
|
+
|
2757
|
+
|
2758
|
+
|
2759
|
+
|
2760
|
+
# Development
|
2761
|
+
|
2762
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
2763
|
+
|
2764
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
2765
|
+
|
2766
|
+
# Contributing
|
2767
|
+
|
2768
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Lovic Gauthier/HDLRuby.
|
2769
|
+
|
2770
|
+
|
2771
|
+
# License
|
2772
|
+
|
2773
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
2774
|
+
|