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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '096e1c8ad3a0b6627819a744569d9ef159a40201ed13b36f11203f4ba751ee84'
4
- data.tar.gz: c0d06d1991d5e8f4f691b56402cd00230f8f46f87a1fbbce235ccd27b6921140
3
+ metadata.gz: 9c6e130a54f880c57186b29328c125d603604f4da1f25a5c4f890549a9847782
4
+ data.tar.gz: 19256d21296a7662f6241c27b41a969c0bb411c64084d8e48e740950c84867ac
5
5
  SHA512:
6
- metadata.gz: b13fed12851411946a251af87cb00053a1f65ef118b5ade71c017ed3642aec055c8c60943d9873422100e389a87dd404f98b16e1a55d90cb2529a2d137405b37
7
- data.tar.gz: fb7231f1cda8cef8afdfa13077952ecc3a71d281e12d83379df0bf64ffd1623266f2e59b218fd2e08be607225eaff0f901346cf892f1ad80f640401f45ff1f1b
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.0:
20
+ For HDLRuby version 3.7.2:
21
21
 
22
- * Added the possibility to run sequencers in software (WIP).
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 does
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: [ 0,1,2,3,4,5,6,7 ]
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 with functional equivalance to the hardware implementation. To achieve this, the following headers must be added to your Ruby source code:
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, refered by the variable `my_seq`, which increments signal `counter` up to 1000:
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 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.
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 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.
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
- 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:
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 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
-
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, 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`:
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 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.
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
- i <= i + 1
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: #{prog0.source}\n"
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
- i <= i + 1
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
@@ -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
 
@@ -559,7 +559,7 @@ module HDLRuby::High
559
559
  # Loads the code files.
560
560
  self.each_code do |code|
561
561
  if code.is_a?(Proc)
562
- TOPLEVEL_BINDING.eval(&code)
562
+ Object.instance_eval(&code)
563
563
  else
564
564
  Kernel.require("./"+code.to_s)
565
565
  end
@@ -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)