HDLRuby 3.6.2 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +189 -1
- data/lib/HDLRuby/hdr_samples/ruby_program/with_sw_hruby.rb +59 -0
- data/lib/HDLRuby/hdr_samples/ruby_program/with_sw_hruby_hruby_test.rb +74 -0
- data/lib/HDLRuby/hdr_samples/with_program_ruby.rb +8 -1
- data/lib/HDLRuby/hruby_high.rb +1 -1
- data/lib/HDLRuby/hruby_low.rb +2 -1
- data/lib/HDLRuby/hruby_rcsim.rb +4 -0
- data/lib/HDLRuby/std/sequencer_sw.rb +2825 -0
- data/lib/HDLRuby/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '096e1c8ad3a0b6627819a744569d9ef159a40201ed13b36f11203f4ba751ee84'
|
4
|
+
data.tar.gz: c0d06d1991d5e8f4f691b56402cd00230f8f46f87a1fbbce235ccd27b6921140
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b13fed12851411946a251af87cb00053a1f65ef118b5ade71c017ed3642aec055c8c60943d9873422100e389a87dd404f98b16e1a55d90cb2529a2d137405b37
|
7
|
+
data.tar.gz: fb7231f1cda8cef8afdfa13077952ecc3a71d281e12d83379df0bf64ffd1623266f2e59b218fd2e08be607225eaff0f901346cf892f1ad80f640401f45ff1f1b
|
data/README.md
CHANGED
@@ -17,6 +17,12 @@ hdrcc --get-tuto
|
|
17
17
|
|
18
18
|
__What's new__
|
19
19
|
|
20
|
+
For HDLRuby version 3.7.0:
|
21
|
+
|
22
|
+
* Added the possibility to run sequencers in software (WIP).
|
23
|
+
This allows both much faster simulation and the use of the same code for both hardware and software design.
|
24
|
+
|
25
|
+
|
20
26
|
For HDLRuby version 3.6.x:
|
21
27
|
|
22
28
|
* Added a new element for the GUI board that allows to assign an expression to a signal on the fly while simulating.
|
@@ -3215,7 +3221,7 @@ end
|
|
3215
3221
|
## Sequencer (software-like hardware coding):: `std/sequencer.rb`
|
3216
3222
|
<a name="sequencer"></a>
|
3217
3223
|
|
3218
|
-
This library provides a new set of 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.
|
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).
|
3219
3225
|
|
3220
3226
|
A sequencer can be declared anywhere in a system provided it is outside a behavior using the `sequencer` keyword as follows:
|
3221
3227
|
|
@@ -3715,6 +3721,188 @@ __Notes__:
|
|
3715
3721
|
|
3716
3722
|
With the code above, the only restriction is that the signal `stack_overflow` is declared before the function `fact` is called.
|
3717
3723
|
|
3724
|
+
### Sequencers as Software Code
|
3725
|
+
|
3726
|
+
#### Introduction to Sequencer as Software Code
|
3727
|
+
|
3728
|
+
Sequencers can be executed in software using a Ruby interpreter with functional equivalance to the hardware implementation. To achieve this, the following headers must be added to your Ruby source code:
|
3729
|
+
|
3730
|
+
```ruby
|
3731
|
+
require 'HDLRuby/std/sequencer_sw'
|
3732
|
+
include RubyHDL::High
|
3733
|
+
using RubyHDL::High
|
3734
|
+
```
|
3735
|
+
|
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, refered by the variable `my_seq`, which increments signal `counter` up to 1000:
|
3737
|
+
|
3738
|
+
```ruby
|
3739
|
+
require 'HDLRuby/std/sequencer_sw'
|
3740
|
+
include RubyHDL::High
|
3741
|
+
using RubyHDL::High
|
3742
|
+
|
3743
|
+
[32].inner :counter
|
3744
|
+
|
3745
|
+
my_seq = sequencer do
|
3746
|
+
counter <= 0
|
3747
|
+
1000.stimes do
|
3748
|
+
counter <= counter + 1
|
3749
|
+
end
|
3750
|
+
end
|
3751
|
+
```
|
3752
|
+
|
3753
|
+
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
|
+
|
3755
|
+
To verify wether the sequencer executed correctly, you can access the values of signals outside the sequencer using the `value` method. For example, the following code, initialize `counter` to 0, and then display the counter value after executing the sequencer.
|
3756
|
+
|
3757
|
+
```ruby
|
3758
|
+
require 'HDLRuby/std/sequencer_sw'
|
3759
|
+
include RubyHDL::High
|
3760
|
+
using RubyHDL::High
|
3761
|
+
|
3762
|
+
[32].inner :counter
|
3763
|
+
|
3764
|
+
counter.value = 0
|
3765
|
+
|
3766
|
+
my_seq = sequencer do
|
3767
|
+
counter <= 0
|
3768
|
+
1000.stimes do
|
3769
|
+
counter <= counter + 1
|
3770
|
+
end
|
3771
|
+
end
|
3772
|
+
|
3773
|
+
my_seq.()
|
3774
|
+
|
3775
|
+
puts "counter=#{counter.value}"
|
3776
|
+
```
|
3777
|
+
|
3778
|
+
__Note__: When displaying the value of a signal, the `.value` method can be omitted. For example, in the code above, the final statement can also be written as:
|
3779
|
+
|
3780
|
+
```ruby
|
3781
|
+
puts "counter=#{counter}"
|
3782
|
+
```
|
3783
|
+
|
3784
|
+
#### Why Would I Want to Execute a Sequencer in Software, and What are the Limitations?
|
3785
|
+
|
3786
|
+
There are two main reasons for executing sequencers in software:
|
3787
|
+
|
3788
|
+
1. High-speed simulation: Sequencers executed in software run approximately 10 times faster than in the HDLRuby simulator.
|
3789
|
+
|
3790
|
+
2. Seamless transition from software to hardware: During the early design stages, it may not be clear whether a given part will be implemented in software and hardware. Using the same code for both ensures:
|
3791
|
+
|
3792
|
+
* Reliability (guaranteed functional equivalence).
|
3793
|
+
|
3794
|
+
* Reduced design time (no need for recoding).
|
3795
|
+
|
3796
|
+
While software sequencer are functionaly equivalent to their hardware implementation, 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 functions executed in sequence. 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 in the following sections.
|
3797
|
+
|
3798
|
+
|
3799
|
+
#### Adding a Clock to a Software Sequencer.
|
3800
|
+
|
3801
|
+
While there is no clock in software, 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
|
+
|
3803
|
+
```ruby
|
3804
|
+
sequencer(<clock counting signal>) do
|
3805
|
+
...
|
3806
|
+
end
|
3807
|
+
```
|
3808
|
+
|
3809
|
+
After the executing of a sequencer with a clock, the estimate number of clock cycles required by 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 is implemented in hardware:
|
3810
|
+
|
3811
|
+
|
3812
|
+
```ruby
|
3813
|
+
[32].inner :clk_count
|
3814
|
+
clk_count.value = 0
|
3815
|
+
|
3816
|
+
sequencer(clk_count) do
|
3817
|
+
1000.stimes
|
3818
|
+
end.()
|
3819
|
+
|
3820
|
+
puts "#{clk_count} clocks"
|
3821
|
+
```
|
3822
|
+
|
3823
|
+
__Note__: In the code above, the sequencer is not stored in a variable because it is executed immediately upon declaration.
|
3824
|
+
|
3825
|
+
#### Adding a Signal to Control the Execution of a Software Sequencer.
|
3826
|
+
|
3827
|
+
In addition to a clock counter signal, it is possible to add a signal that when written one will start the execution of a software sequencer, like it is the case for the hardware implementation. For that purpose this signal is to be passed as second argument of the sequencer. For example the following code, starts the execution of the sequencer using signal `start`:
|
3828
|
+
|
3829
|
+
```ruby
|
3830
|
+
[32].inner :clk_count
|
3831
|
+
[1].inner :start
|
3832
|
+
clk_count.value = 0
|
3833
|
+
|
3834
|
+
sequencer(clk_count,start) do
|
3835
|
+
1000.stimes
|
3836
|
+
end
|
3837
|
+
|
3838
|
+
start.value = 1
|
3839
|
+
|
3840
|
+
puts "#{clk_count} clocks"
|
3841
|
+
```
|
3842
|
+
|
3843
|
+
With this alternate way for executing a software sequencer, it is not necesary any longer to store it into a ruby variable, and it is possible to start the execution of a sequencer exactly like in hardware, and also from anther sequencer. For example, in the following code, the sequencer sequencer start is controlled by the first one.
|
3844
|
+
|
3845
|
+
```ruby
|
3846
|
+
[1].inner :start0, :start1
|
3847
|
+
[8].inner :count0, :count1
|
3848
|
+
|
3849
|
+
sequencer(nil,start0) do
|
3850
|
+
count0 <= 0
|
3851
|
+
swhile(count0<100) { count0 <= count0 + 1 }
|
3852
|
+
start1 <= 1
|
3853
|
+
end
|
3854
|
+
|
3855
|
+
sequencer(nil,start1) do
|
3856
|
+
count1 <= 0
|
3857
|
+
swhile(count1<100) { count1 <= count1 + 1 }
|
3858
|
+
end
|
3859
|
+
```
|
3860
|
+
|
3861
|
+
|
3862
|
+
#### Synchronizing Sequencers for Pseudo-Parallel Execution
|
3863
|
+
|
3864
|
+
While software sequencers normally execute to completion before any other code runs, they can be interrupted using the `sync` command. This command does not correspond to anything in hardware but can be used to simulate parallel execution of multiple sequencers.
|
3865
|
+
|
3866
|
+
When a `sync` command is encountered during execution, the sequencer is paused, and execution resumes with the code that follows the sequencer’s start. The paused sequencer can then be resumed using the call operator or by setting its start signal to 1.
|
3867
|
+
|
3868
|
+
For example, in the following code, the sequencer starts execution, then prints "stop at count=20" before resuming execution, and finally prints "end at count=40":
|
3869
|
+
|
3870
|
+
```ruby
|
3871
|
+
|
3872
|
+
[32].inner :count
|
3873
|
+
|
3874
|
+
my_seq = sequencer do
|
3875
|
+
count <= 0
|
3876
|
+
20.stimes
|
3877
|
+
count <= count + 1
|
3878
|
+
sync
|
3879
|
+
20.stimes
|
3880
|
+
count <= count + 1
|
3881
|
+
end
|
3882
|
+
end
|
3883
|
+
|
3884
|
+
my_seq.()
|
3885
|
+
puts "stop at count=#{count}"
|
3886
|
+
my_seq.()
|
3887
|
+
puts "end at count=#{count}"
|
3888
|
+
```
|
3889
|
+
|
3890
|
+
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
|
+
|
3892
|
+
|
3893
|
+
#### Executing ruby code within a software sequencer.
|
3894
|
+
|
3895
|
+
When executing a sequencer in software, an additional command, `ruby`, is available for running plain Ruby code. For example, the following displays "Hello" ten times using the puts method:
|
3896
|
+
|
3897
|
+
|
3898
|
+
```ruby
|
3899
|
+
sequencer do
|
3900
|
+
stimes.10 do
|
3901
|
+
ruby { puts "Hello" }
|
3902
|
+
end
|
3903
|
+
end.()
|
3904
|
+
```
|
3905
|
+
|
3718
3906
|
|
3719
3907
|
## Fixed-point (fixpoint): `std/fixpoint.rb`
|
3720
3908
|
<a name="fixpoint"></a>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Ruby program for testing SW HDLRuby.
|
2
|
+
|
3
|
+
require 'HDLRuby/std/sequencer_sw'
|
4
|
+
|
5
|
+
include RubyHDL::High
|
6
|
+
using RubyHDL::High
|
7
|
+
|
8
|
+
[32].inner :clk
|
9
|
+
clk.value = 0
|
10
|
+
|
11
|
+
[32].inner :a,:b,:c,:i
|
12
|
+
[32].inner :d
|
13
|
+
bit[32][-4].inner :ar
|
14
|
+
[32].inner :res0, :res1
|
15
|
+
|
16
|
+
some_ruby_value = 1
|
17
|
+
|
18
|
+
|
19
|
+
prog0 = sequencer(clk) do
|
20
|
+
a <= 1
|
21
|
+
b <= 2
|
22
|
+
c <= ruby { some_ruby_value }
|
23
|
+
d <= 0
|
24
|
+
i <= 0
|
25
|
+
# swhile(c<10000000) do
|
26
|
+
10000000.stimes do
|
27
|
+
c <= a + b + d
|
28
|
+
d <= c + 1
|
29
|
+
ar[i%4] <= i
|
30
|
+
i <= i + 1
|
31
|
+
sync
|
32
|
+
end
|
33
|
+
a[4] <= 1
|
34
|
+
b[7..5] <= 5
|
35
|
+
res0 <= ar[0]
|
36
|
+
end
|
37
|
+
|
38
|
+
puts "prog0 source code: #{prog0.source}\n"
|
39
|
+
|
40
|
+
prog1 = sequencer do
|
41
|
+
sloop do
|
42
|
+
res1 <= ar[1]
|
43
|
+
sync
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
while prog0.alive? do
|
48
|
+
prog0.call
|
49
|
+
prog1.call
|
50
|
+
end
|
51
|
+
|
52
|
+
puts "a=#{a}"
|
53
|
+
puts "b=#{b}"
|
54
|
+
puts "c=#{c}"
|
55
|
+
puts "d=#{d}"
|
56
|
+
puts "ar=#{ar}"
|
57
|
+
puts "res0=#{res0}"
|
58
|
+
puts "res1=#{res1}"
|
59
|
+
puts "clk=#{clk}"
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Ruby program for testing SW HDLRuby.
|
2
|
+
|
3
|
+
# require 'HDLRuby/std/sequencer_sw'
|
4
|
+
|
5
|
+
# include RubyHDL::High
|
6
|
+
# using RubyHDL::High
|
7
|
+
|
8
|
+
system :test_with_sw_ruby do
|
9
|
+
|
10
|
+
[32].inner :a,:b,:c,:i
|
11
|
+
[32].inner :d, :e
|
12
|
+
bit[32][-4].inner :ar
|
13
|
+
[32].inner :res0, :res1
|
14
|
+
|
15
|
+
inner :clk,:start
|
16
|
+
|
17
|
+
|
18
|
+
sequencer(clk,start) do
|
19
|
+
a <= 1
|
20
|
+
b <= 2
|
21
|
+
c <= 0
|
22
|
+
d <= 0
|
23
|
+
i <= 0
|
24
|
+
e <= 0
|
25
|
+
# swhile(c<10000000) do
|
26
|
+
10000000.stimes do
|
27
|
+
c <= a + b + d
|
28
|
+
d <= c + 1
|
29
|
+
ar[i%4] <= i
|
30
|
+
i <= i + 1
|
31
|
+
end
|
32
|
+
a[4] <= 1
|
33
|
+
b[7..5] <= 5
|
34
|
+
res0 <= ar[0]
|
35
|
+
e <= 1
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
sequencer(clk,start) do
|
40
|
+
sloop do
|
41
|
+
res1 <= ar[1]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
timed do
|
47
|
+
clk <= 0
|
48
|
+
start <= 0
|
49
|
+
!10.ns
|
50
|
+
clk <= 1
|
51
|
+
!10.ns
|
52
|
+
clk <= 0
|
53
|
+
start <= 1
|
54
|
+
!10.ns
|
55
|
+
clk <= 1
|
56
|
+
!10.ns
|
57
|
+
clk <= 0
|
58
|
+
start <= 0
|
59
|
+
!10.ns
|
60
|
+
clk <= 1
|
61
|
+
!10.ns
|
62
|
+
repeat(100000000) do
|
63
|
+
clk <= ~clk
|
64
|
+
!10.ns
|
65
|
+
hif(e == 1) do
|
66
|
+
hprint("c=",c,"\n")
|
67
|
+
hprint("res0=",res0,"\n")
|
68
|
+
hprint("res1=",res1,"\n")
|
69
|
+
terminate
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -8,7 +8,14 @@ system :with_ruby_prog do
|
|
8
8
|
actport clk.posedge
|
9
9
|
inport inP: count
|
10
10
|
outport outP: echo
|
11
|
-
code "ruby_program/echo.rb"
|
11
|
+
# code "ruby_program/echo.rb"
|
12
|
+
code(proc do
|
13
|
+
def echo
|
14
|
+
val = RubyHDL.inP
|
15
|
+
puts "Echoing: #{val}"
|
16
|
+
RubyHDL.outP = val
|
17
|
+
end
|
18
|
+
end)
|
12
19
|
end
|
13
20
|
|
14
21
|
|
data/lib/HDLRuby/hruby_high.rb
CHANGED
@@ -3067,7 +3067,7 @@ module HDLRuby::High
|
|
3067
3067
|
end
|
3068
3068
|
|
3069
3069
|
# Adds the unary operations generation.
|
3070
|
-
[:"-@",:"
|
3070
|
+
[:"-@",:"+@",:"~", :abs,
|
3071
3071
|
:boolean, :bit, :signed, :unsigned].each do |operator|
|
3072
3072
|
meth = proc do
|
3073
3073
|
expr = self.to_expr
|
data/lib/HDLRuby/hruby_low.rb
CHANGED
data/lib/HDLRuby/hruby_rcsim.rb
CHANGED
@@ -558,7 +558,11 @@ module HDLRuby::High
|
|
558
558
|
if self.language == :ruby then
|
559
559
|
# Loads the code files.
|
560
560
|
self.each_code do |code|
|
561
|
+
if code.is_a?(Proc)
|
562
|
+
TOPLEVEL_BINDING.eval(&code)
|
563
|
+
else
|
561
564
|
Kernel.require("./"+code.to_s)
|
565
|
+
end
|
562
566
|
end
|
563
567
|
# Add the input ports.
|
564
568
|
self.each_inport do |sym, sig|
|