HDLRuby 3.7.0 → 3.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +93 -15
- data/lib/HDLRuby/hdr_samples/ruby_program/with_sw_hruby.rb +15 -5
- data/lib/HDLRuby/hdr_samples/ruby_program/with_sw_hruby_hruby_test.rb +6 -3
- data/lib/HDLRuby/hruby_high.rb +15 -0
- data/lib/HDLRuby/hruby_rcsim.rb +1 -1
- data/lib/HDLRuby/std/sequencer.rb +10 -0
- data/lib/HDLRuby/std/sequencer_sw.rb +965 -179
- data/lib/HDLRuby/version.rb +1 -1
- data/tuto/tutorial_sw.html +6 -0
- data/tuto/tutorial_sw.md +12 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c6e130a54f880c57186b29328c125d603604f4da1f25a5c4f890549a9847782
|
4
|
+
data.tar.gz: 19256d21296a7662f6241c27b41a969c0bb411c64084d8e48e740950c84867ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6069997b2fbeb1acf6a4202e72fa6558060283de21d2ed2ea6a8ef72032f7c54166788346e03b4b4ba79d3c8cf0565edd3bee945b4c736907210b35b97d8331
|
7
|
+
data.tar.gz: be5e84ac24bd4df32e1a6a83fe37a294c9f17475de7c12bb8a40e96d944fb63f2e94556ea9cb6b954d82e81ff03fa44e9550dcd6cba349dc7d7b263e3808dece
|
data/README.md
CHANGED
@@ -17,9 +17,19 @@ hdrcc --get-tuto
|
|
17
17
|
|
18
18
|
__What's new__
|
19
19
|
|
20
|
-
For HDLRuby version 3.7.
|
20
|
+
For HDLRuby version 3.7.2:
|
21
21
|
|
22
|
-
* Added the
|
22
|
+
* Added the `text` command for the sequencers in software.
|
23
|
+
|
24
|
+
* Added the `value_text` method to sequencers in software's signal for generatign Ruby/C code for accessing a value with correct typing.
|
25
|
+
|
26
|
+
* Added the `alive?` and `reset!` commands for HDLRuby sequencers.
|
27
|
+
|
28
|
+
* Added the `require_ruby` method for loading Ruby (i.e., non-HDLRuby) libraries.
|
29
|
+
|
30
|
+
For HDLRuby version 3.7.x:
|
31
|
+
|
32
|
+
* Added the possibility to run [sequencers in software](#sequencers-as-software-code). (WIP)
|
23
33
|
This allows both much faster simulation and the use of the same code for both hardware and software design.
|
24
34
|
|
25
35
|
|
@@ -255,7 +265,9 @@ hdr_vhdl
|
|
255
265
|
hdr_sim
|
256
266
|
```
|
257
267
|
|
268
|
+
## HDLRuby files.
|
258
269
|
|
270
|
+
HDLRuby being built on top of the Ruby language, we choose as convension to name the HDLRuby file with the `.rb` extension. For the same reason, including external HDLRuby files is done using the `require` or `require_relative` methods, that are identical to their Ruby counterpart. Those method however can only be used for including HDLRuby description file and not Ruby ones, for the later, the method `require_ruby` and `require_relative_ruby` must be used instead.
|
259
271
|
|
260
272
|
|
261
273
|
# HDLRuby programming guide
|
@@ -739,7 +751,7 @@ __Notes__:
|
|
739
751
|
- Since this is Ruby code, the body can also be delimited by the `do` and `end` Ruby keywords (in which case the parentheses can be omitted) as follows:
|
740
752
|
|
741
753
|
```ruby
|
742
|
-
system :box
|
754
|
+
system :box do
|
743
755
|
end
|
744
756
|
```
|
745
757
|
|
@@ -927,9 +939,17 @@ inner sig: 0
|
|
927
939
|
As another example, an 8-bit 8-word ROM could be declared and initialized as follows:
|
928
940
|
|
929
941
|
```ruby
|
930
|
-
bit[8][-8] rom: [
|
942
|
+
bit[8][-8] rom: [ _h00,_h01,_h02,_h03,_h04,_h05,_h06,_h07 ]
|
931
943
|
```
|
932
944
|
|
945
|
+
__Note__:
|
946
|
+
|
947
|
+
* The notation `_hXY` is used for indicating a 2-digit, `X` and `Y`, hexadecimal.
|
948
|
+
|
949
|
+
* By default, Ruby integers (not preceded by the `_` prefix) are typed as 64-bit HDLRuby values, whereas HDLRuby explicit values (preceded by the `_ ` prefix) have a bit-width corresponding to their representation.
|
950
|
+
|
951
|
+
* When declaring the contents of a ROM, the bit-width of the elements must match that of the declared type; otherwise, misalignments may occur.
|
952
|
+
|
933
953
|
|
934
954
|
### Scope in a system
|
935
955
|
|
@@ -3221,7 +3241,7 @@ end
|
|
3221
3241
|
## Sequencer (software-like hardware coding):: `std/sequencer.rb`
|
3222
3242
|
<a name="sequencer"></a>
|
3223
3243
|
|
3224
|
-
This library provides a new set of software-like control statements for describing the behavior of a circuit. Behind the curtain, these constructs build a finite state machine where states are deduced from the control points within the description. Eventhough sequencers are ment to describe hardware, they are software-compatible so that they can efficiently be executed as software programs as explain in section [software sequencers](#sequencers-as-software-code).
|
3244
|
+
This library provides a new set of software-like control statements for describing the behavior of a circuit. Behind the curtain, these constructs build a finite state machine where states are deduced from the control points within the description. Eventhough sequencers are ment to describe hardware, they are software-compatible so that they can efficiently be executed as software programs as explain in section about [software sequencers](#sequencers-as-software-code).
|
3225
3245
|
|
3226
3246
|
A sequencer can be declared anywhere in a system provided it is outside a behavior using the `sequencer` keyword as follows:
|
3227
3247
|
|
@@ -3255,6 +3275,29 @@ A sequence is a specific case of a `seq` block that includes the following softw
|
|
3255
3275
|
|
3256
3276
|
- `sterminate`: ends the execution of the sequence.
|
3257
3277
|
|
3278
|
+
- `alive?`: is 0 if the sequencer is still running and not 0 otherwise.
|
3279
|
+
Can be used outside the sequencer.
|
3280
|
+
|
3281
|
+
- `reset!`: resets the sequencer to its start.
|
3282
|
+
Can be used outside the sequencer.
|
3283
|
+
|
3284
|
+
The two last commands can be used to control a sequencer outside of it. For that purpose, a reference must be assigned to the sequencer as follows, where `ref_sequencer` is a Ruby variable that will refer to the sequencer:
|
3285
|
+
|
3286
|
+
```ruby
|
3287
|
+
ref_sequencer = sequencer(clk,start) do
|
3288
|
+
< some sequencer code >
|
3289
|
+
end
|
3290
|
+
|
3291
|
+
< somewhere else in the code. >
|
3292
|
+
|
3293
|
+
# Reset the sequencer if it ended its execution.
|
3294
|
+
hif(ref_sequencer.alive? == 0) do
|
3295
|
+
ref_sequencer.reset!
|
3296
|
+
end
|
3297
|
+
```
|
3298
|
+
|
3299
|
+
|
3300
|
+
|
3258
3301
|
It is also possible to use enumerators (iterators) similar to the Ruby `each` using the following methods within sequences:
|
3259
3302
|
|
3260
3303
|
- `<object>.seach`: `object` any enumerable Ruby object or any signal. If a block is given, it works like `sfor`, otherwise, it returns a HDLRuby enumerator (please see [enumerator](#hdlruby-enumerators-and-enumerable-objects-stdsequencerrb) for details about them).
|
@@ -3725,7 +3768,7 @@ __Notes__:
|
|
3725
3768
|
|
3726
3769
|
#### Introduction to Sequencer as Software Code
|
3727
3770
|
|
3728
|
-
Sequencers can be executed in software using a Ruby interpreter
|
3771
|
+
Sequencers can be executed in software using a Ruby interpreter while maintaining functional equivalence with the hardware implementation. To achieve this, the following headers must be added to your Ruby source code:
|
3729
3772
|
|
3730
3773
|
```ruby
|
3731
3774
|
require 'HDLRuby/std/sequencer_sw'
|
@@ -3733,7 +3776,7 @@ include RubyHDL::High
|
|
3733
3776
|
using RubyHDL::High
|
3734
3777
|
```
|
3735
3778
|
|
3736
|
-
After this, signals and sequencers can be described exactly like in HDLRuby. However, the resulting sequencer objects are not executed immediately and must be stored in a variable for further reference. For example the following Ruby code defines a sequencer,
|
3779
|
+
After this, signals and sequencers can be described exactly like in HDLRuby. However, the resulting sequencer objects are not executed immediately and must be stored in a variable for further reference. For example, the following Ruby code defines a sequencer, referred by the variable `my_seq`, which increments the signal `counter` up to 1000:
|
3737
3780
|
|
3738
3781
|
```ruby
|
3739
3782
|
require 'HDLRuby/std/sequencer_sw'
|
@@ -3752,7 +3795,7 @@ end
|
|
3752
3795
|
|
3753
3796
|
You may notice that no clock or start signal is provided to the sequencer. This is because, in software, execution is sequential, and no clock nor control signals are required. Instead, starting the execution of a sequencer is done using the call operators as follows: `my_seq.()`.
|
3754
3797
|
|
3755
|
-
To verify
|
3798
|
+
To verify whether the sequencer executed correctly, you can access signal values outside the sequencer using the `value` method. For example, the following code initializes `counter` to 0 and then displays the counter value after executing the sequencer.
|
3756
3799
|
|
3757
3800
|
```ruby
|
3758
3801
|
require 'HDLRuby/std/sequencer_sw'
|
@@ -3781,6 +3824,17 @@ __Note__: When displaying the value of a signal, the `.value` method can be omit
|
|
3781
3824
|
puts "counter=#{counter}"
|
3782
3825
|
```
|
3783
3826
|
|
3827
|
+
Internally, the HDLRuby code of a sequencer is converted to Ruby before execution. This code can be accessed through the `source` command. It can then be saved into a file for separate execution for example, as follows:
|
3828
|
+
|
3829
|
+
```ruby
|
3830
|
+
File.open("sequencer_in_ruby.rb","w") do |f|
|
3831
|
+
f << my_seq.source
|
3832
|
+
end
|
3833
|
+
```
|
3834
|
+
|
3835
|
+
__Note__: the ruby code for sequencers is compatible with mruby for execution on embedded systems.
|
3836
|
+
|
3837
|
+
|
3784
3838
|
#### Why Would I Want to Execute a Sequencer in Software, and What are the Limitations?
|
3785
3839
|
|
3786
3840
|
There are two main reasons for executing sequencers in software:
|
@@ -3793,12 +3847,12 @@ There are two main reasons for executing sequencers in software:
|
|
3793
3847
|
|
3794
3848
|
* Reduced design time (no need for recoding).
|
3795
3849
|
|
3796
|
-
While software sequencer are
|
3850
|
+
While software sequencer are functionally equivalent to their hardware implementations, their handling of time and parallelism is fundamentally different. In hardware, sequencers are implemented as finite state machines that transition according to a clock and that run in parallel with the remain of the circuits. By contrast, in software, sequencers are implemented as fibers executed sequentially. If parallel synchronization is important in your design (e.g., a communication protocol), software sequencers may not be useful. However, there are ways to add hardware timing and parallelism as described in the following sections.
|
3797
3851
|
|
3798
3852
|
|
3799
3853
|
#### Adding a Clock to a Software Sequencer.
|
3800
3854
|
|
3801
|
-
|
3855
|
+
As mentioned earlier, there is no clock in software. However, it is possible to simulate one while executing a sequencer to estimate its performance when implemented in hardware. This is done by passing as argument a signal that will be increase at each estimated clock cycle as follows:
|
3802
3856
|
|
3803
3857
|
```ruby
|
3804
3858
|
sequencer(<clock counting signal>) do
|
@@ -3806,8 +3860,7 @@ sequencer(<clock counting signal>) do
|
|
3806
3860
|
end
|
3807
3861
|
```
|
3808
3862
|
|
3809
|
-
After the
|
3810
|
-
|
3863
|
+
After the execution of a sequencer with a clock, the estimated number of clock cycles required for the hardware implementation is stored into the clock signal. For example, the following code will display `1000 clocks`, which is the estimated number of executed clocks if the sequencer were implemented in hardware:
|
3811
3864
|
|
3812
3865
|
```ruby
|
3813
3866
|
[32].inner :clk_count
|
@@ -3824,7 +3877,7 @@ __Note__: In the code above, the sequencer is not stored in a variable because i
|
|
3824
3877
|
|
3825
3878
|
#### Adding a Signal to Control the Execution of a Software Sequencer.
|
3826
3879
|
|
3827
|
-
In addition to a clock counter signal,
|
3880
|
+
In addition to a clock counter signal, you can add a signal that, when set to 1, starts the execution of a software sequencer, just like in the hardware implementation. To achieve this, pass the signal as second argument of the sequencer. For example the following code, starts the execution of the sequencer using signal `start`:
|
3828
3881
|
|
3829
3882
|
```ruby
|
3830
3883
|
[32].inner :clk_count
|
@@ -3840,7 +3893,7 @@ start.value = 1
|
|
3840
3893
|
puts "#{clk_count} clocks"
|
3841
3894
|
```
|
3842
3895
|
|
3843
|
-
With this
|
3896
|
+
With this alternative execution method, storing the sequencer in a Ruby variable is no longer necessary. The execution can be started exactly as in hardware, and also from another sequencer. For example, in the following code, the execution of the second sequencer is controlled by the first one.
|
3844
3897
|
|
3845
3898
|
```ruby
|
3846
3899
|
[1].inner :start0, :start1
|
@@ -3857,7 +3910,6 @@ sequencer(nil,start1) do
|
|
3857
3910
|
swhile(count1<100) { count1 <= count1 + 1 }
|
3858
3911
|
end
|
3859
3912
|
```
|
3860
|
-
|
3861
3913
|
|
3862
3914
|
#### Synchronizing Sequencers for Pseudo-Parallel Execution
|
3863
3915
|
|
@@ -3889,6 +3941,11 @@ puts "end at count=#{count}"
|
|
3889
3941
|
|
3890
3942
|
For full cycle-accurate synchronization, insert a sync command at each estimated cycle. However, sync has a significant performance cost, and depending on the Ruby interpreter and software configuration, excessive use may make execution slower than the HDLRuby hardware simulator. Hence it is recommended to use this command only when necessary, and use the HDLRuby hardware simulator for cycle-accurate synchronization.
|
3891
3943
|
|
3944
|
+
Finally, to determine whether a sequencer has completed execution or is paused at sync command, use the `alive?` method. For example, the following code will resume execution of sequencer `my_seq` until it completes:
|
3945
|
+
|
3946
|
+
```ruby
|
3947
|
+
my_seq.() while(my_seq.alive?)
|
3948
|
+
```
|
3892
3949
|
|
3893
3950
|
#### Executing ruby code within a software sequencer.
|
3894
3951
|
|
@@ -3903,6 +3960,27 @@ sequencer do
|
|
3903
3960
|
end.()
|
3904
3961
|
```
|
3905
3962
|
|
3963
|
+
Another possibility is to put the code into a string using the command `text` as follows:
|
3964
|
+
|
3965
|
+
```
|
3966
|
+
sequencer do
|
3967
|
+
stimes.10 do
|
3968
|
+
text('puts "Hello"')
|
3969
|
+
end
|
3970
|
+
end.()
|
3971
|
+
```
|
3972
|
+
|
3973
|
+
Both method are functionally equivalent. However, the first is safer as potential errors are detected at the compile stage, but is incompatible with separated code generation and is slow, while the second allows separate code generation, if fast, but is less safe since it is only at the execution stage that the code is checked.
|
3974
|
+
|
3975
|
+
__Note__: Since the string in text is grafted as is into the generated Ruby (or C) code, you cannot directly access the value of a signal. However, you can use to_ruby or to_c to access the underlying raw value, or use value_text to retrieve the value with proper type adjustment in case of overflow or underflow. For example, the following will display the raw value of signal sig0 and the hardware-accurate value of signal sig1:
|
3976
|
+
|
3977
|
+
```ruby
|
3978
|
+
sequencer do
|
3979
|
+
text("puts #{sig0.to_ruby}")
|
3980
|
+
text("puts #{sig1.value_text}")
|
3981
|
+
end
|
3982
|
+
```
|
3983
|
+
|
3906
3984
|
|
3907
3985
|
## Fixed-point (fixpoint): `std/fixpoint.rb`
|
3908
3986
|
<a name="fixpoint"></a>
|
@@ -19,15 +19,19 @@ some_ruby_value = 1
|
|
19
19
|
prog0 = sequencer(clk) do
|
20
20
|
a <= 1
|
21
21
|
b <= 2
|
22
|
-
c <= ruby { some_ruby_value }
|
22
|
+
# c <= ruby { some_ruby_value }
|
23
|
+
c <= ruby("some_ruby_value")
|
23
24
|
d <= 0
|
24
25
|
i <= 0
|
25
26
|
# swhile(c<10000000) do
|
26
|
-
10000000.stimes do
|
27
|
+
# 10000000.stimes do
|
28
|
+
sfor(0..10000000) do |u|
|
27
29
|
c <= a + b + d
|
28
30
|
d <= c + 1
|
29
|
-
ar[i%4] <= i
|
30
|
-
|
31
|
+
# ar[i%4] <= i
|
32
|
+
ar[u%4] <= i
|
33
|
+
sif(i<1000) { i <= i + 1 }
|
34
|
+
selse { i <= 0 }
|
31
35
|
sync
|
32
36
|
end
|
33
37
|
a[4] <= 1
|
@@ -35,7 +39,10 @@ prog0 = sequencer(clk) do
|
|
35
39
|
res0 <= ar[0]
|
36
40
|
end
|
37
41
|
|
38
|
-
puts "prog0 source code
|
42
|
+
puts "Generating file from prog0 source code..."
|
43
|
+
File.open("with_sw_hruby_mruby.rb","w") do |f|
|
44
|
+
f << prog0.source
|
45
|
+
end
|
39
46
|
|
40
47
|
prog1 = sequencer do
|
41
48
|
sloop do
|
@@ -44,6 +51,8 @@ prog1 = sequencer do
|
|
44
51
|
end
|
45
52
|
end
|
46
53
|
|
54
|
+
puts "Executing concurrently prog0 and prog1..."
|
55
|
+
|
47
56
|
while prog0.alive? do
|
48
57
|
prog0.call
|
49
58
|
prog1.call
|
@@ -54,6 +63,7 @@ puts "b=#{b}"
|
|
54
63
|
puts "c=#{c}"
|
55
64
|
puts "d=#{d}"
|
56
65
|
puts "ar=#{ar}"
|
66
|
+
puts "ar[1]=#{ar.value[1]}"
|
57
67
|
puts "res0=#{res0}"
|
58
68
|
puts "res1=#{res1}"
|
59
69
|
puts "clk=#{clk}"
|
@@ -23,11 +23,14 @@ system :test_with_sw_ruby do
|
|
23
23
|
i <= 0
|
24
24
|
e <= 0
|
25
25
|
# swhile(c<10000000) do
|
26
|
-
10000000.stimes do
|
26
|
+
# 10000000.stimes do
|
27
|
+
sfor(0..10000000) do |u|
|
27
28
|
c <= a + b + d
|
28
29
|
d <= c + 1
|
29
|
-
ar[i%4] <= i
|
30
|
-
|
30
|
+
# ar[i%4] <= i
|
31
|
+
ar[u%4] <= i
|
32
|
+
sif(i<1000) { i <= i + 1 }
|
33
|
+
selse { i <= 0 }
|
31
34
|
end
|
32
35
|
a[4] <= 1
|
33
36
|
b[7..5] <= 5
|
data/lib/HDLRuby/hruby_high.rb
CHANGED
@@ -1466,6 +1466,11 @@ module HDLRuby::High
|
|
1466
1466
|
sys.no_parent!
|
1467
1467
|
systemT.scope.add_systemI(sys)
|
1468
1468
|
end
|
1469
|
+
# Adds it programs.
|
1470
|
+
included.scope.each_program do |program|
|
1471
|
+
program.no_parent!
|
1472
|
+
systemT.scope.add_program(program)
|
1473
|
+
end
|
1469
1474
|
# Adds its code.
|
1470
1475
|
included.scope.each_code do |code|
|
1471
1476
|
code.no_parent!
|
@@ -5495,6 +5500,16 @@ module HDLRuby::High
|
|
5495
5500
|
|
5496
5501
|
end
|
5497
5502
|
|
5503
|
+
# Require a Ruby file.
|
5504
|
+
def self.require_ruby(str)
|
5505
|
+
require(str)
|
5506
|
+
end
|
5507
|
+
|
5508
|
+
# Require a Ruby file from current path.
|
5509
|
+
def self.require_relative_ruby(str)
|
5510
|
+
require_relative(str)
|
5511
|
+
end
|
5512
|
+
|
5498
5513
|
# Tell if already configured.
|
5499
5514
|
$HDLRuby_configure = false
|
5500
5515
|
|
data/lib/HDLRuby/hruby_rcsim.rb
CHANGED
@@ -315,6 +315,16 @@ module HDLRuby::High::Std
|
|
315
315
|
expr.seach.with_index(&ruby_block)
|
316
316
|
end
|
317
317
|
|
318
|
+
# Tell if the sequencer ends it execution.
|
319
|
+
def alive?
|
320
|
+
return @fsm.cur_state_sig != self.end_state_value
|
321
|
+
end
|
322
|
+
|
323
|
+
# Resets the sequencer.
|
324
|
+
def reset!
|
325
|
+
@fsm.next_state_sig <= self.start_state_value
|
326
|
+
end
|
327
|
+
|
318
328
|
|
319
329
|
# Fills the top user with the content of block +blk+.
|
320
330
|
def fill_top_user(blk)
|