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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70c4b4aec90e44d8229a4f13990112c3599b7104e3a867cf87741b2d1e4ff246
4
- data.tar.gz: 10c6957977c1df45cae0f270dd27db75824a92e51dc508b5ffa600ce4654f1a0
3
+ metadata.gz: '096e1c8ad3a0b6627819a744569d9ef159a40201ed13b36f11203f4ba751ee84'
4
+ data.tar.gz: c0d06d1991d5e8f4f691b56402cd00230f8f46f87a1fbbce235ccd27b6921140
5
5
  SHA512:
6
- metadata.gz: 56b18d61f3945edaaff50244ecb8ae69fa5c29058942c456ca377eb54da83bb64677004dae4047a8f2aae9b4dd2fa57972c8cf6f0fc497bfe9785c447466cfaa
7
- data.tar.gz: 81e58ca1a9ae1b6ba038c5a43c857ebff9e93dd2627befe89706c0200389a5ee18e1d77261e21d13f291b547dc4cf5be2e9024d75d524e2d54d26c37f7943679
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
 
@@ -3067,7 +3067,7 @@ module HDLRuby::High
3067
3067
  end
3068
3068
 
3069
3069
  # Adds the unary operations generation.
3070
- [:"-@",:"@+",:"~", :abs,
3070
+ [:"-@",:"+@",:"~", :abs,
3071
3071
  :boolean, :bit, :signed, :unsigned].each do |operator|
3072
3072
  meth = proc do
3073
3073
  expr = self.to_expr
@@ -3031,7 +3031,8 @@ module HDLRuby::Low
3031
3031
 
3032
3032
  # Add a new code file.
3033
3033
  def add_code(code)
3034
- @codes << code.to_s
3034
+ # @codes << code.to_s
3035
+ @codes << (code.is_a?(Proc) ? code : code.to_s)
3035
3036
  end
3036
3037
 
3037
3038
  # Add a new input port.
@@ -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|