HDLRuby 3.1.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/HDLRuby.gemspec +1 -0
  3. data/README.html +2330 -2670
  4. data/README.md +400 -100
  5. data/ext/hruby_sim/hruby_rcsim_build.c +402 -3
  6. data/ext/hruby_sim/hruby_sim.h +2 -1
  7. data/ext/hruby_sim/hruby_sim_calc.c +34 -7
  8. data/ext/hruby_sim/hruby_sim_core.c +15 -5
  9. data/ext/hruby_sim/hruby_sim_tree_calc.c +112 -23
  10. data/lib/HDLRuby/hdr_samples/c_program/echo.c +33 -0
  11. data/lib/HDLRuby/hdr_samples/comparison_bench.rb +2 -2
  12. data/lib/HDLRuby/hdr_samples/counter_bench.rb +1 -1
  13. data/lib/HDLRuby/hdr_samples/counter_dff_bench.rb +8 -7
  14. data/lib/HDLRuby/hdr_samples/dff_properties.rb +2 -0
  15. data/lib/HDLRuby/hdr_samples/enum_as_param.rb +52 -0
  16. data/lib/HDLRuby/hdr_samples/linear_test.rb +2 -0
  17. data/lib/HDLRuby/hdr_samples/logic_bench.rb +6 -0
  18. data/lib/HDLRuby/hdr_samples/mei8.rb +6 -6
  19. data/lib/HDLRuby/hdr_samples/mei8_bench.rb +6 -6
  20. data/lib/HDLRuby/hdr_samples/memory_test.rb +2 -0
  21. data/lib/HDLRuby/hdr_samples/named_sub.rb +9 -5
  22. data/lib/HDLRuby/hdr_samples/ram.rb +7 -6
  23. data/lib/HDLRuby/hdr_samples/ruby_fir_hw.rb +2 -0
  24. data/lib/HDLRuby/hdr_samples/ruby_program/echo.rb +9 -0
  25. data/lib/HDLRuby/hdr_samples/ruby_program/stdrw.rb +6 -0
  26. data/lib/HDLRuby/hdr_samples/ruby_program/sw_cpu_terminal.rb +614 -0
  27. data/lib/HDLRuby/hdr_samples/ruby_program/sw_inc_mem.rb +32 -0
  28. data/lib/HDLRuby/hdr_samples/ruby_program/sw_log.rb +33 -0
  29. data/lib/HDLRuby/hdr_samples/struct.rb +15 -3
  30. data/lib/HDLRuby/hdr_samples/with_board.rb +63 -0
  31. data/lib/HDLRuby/hdr_samples/with_bram.rb +1 -1
  32. data/lib/HDLRuby/hdr_samples/with_bram_frame_stack.rb +1 -1
  33. data/lib/HDLRuby/hdr_samples/with_bram_stack.rb +1 -1
  34. data/lib/HDLRuby/hdr_samples/with_channel.rb +2 -0
  35. data/lib/HDLRuby/hdr_samples/with_channel_other.rb +2 -0
  36. data/lib/HDLRuby/hdr_samples/with_class.rb +3 -1
  37. data/lib/HDLRuby/hdr_samples/with_clocks.rb +42 -0
  38. data/lib/HDLRuby/hdr_samples/with_connector.rb +2 -0
  39. data/lib/HDLRuby/hdr_samples/with_connector_memory.rb +2 -0
  40. data/lib/HDLRuby/hdr_samples/with_fixpoint.rb +6 -0
  41. data/lib/HDLRuby/hdr_samples/with_fixpoint_adv.rb +73 -0
  42. data/lib/HDLRuby/hdr_samples/with_leftright.rb +1 -1
  43. data/lib/HDLRuby/hdr_samples/with_of.rb +1 -1
  44. data/lib/HDLRuby/hdr_samples/with_program_c.rb +28 -0
  45. data/lib/HDLRuby/hdr_samples/with_program_ruby.rb +28 -0
  46. data/lib/HDLRuby/hdr_samples/with_program_ruby_cpu.rb +234 -0
  47. data/lib/HDLRuby/hdr_samples/with_program_ruby_io.rb +23 -0
  48. data/lib/HDLRuby/hdr_samples/with_program_ruby_mem.rb +58 -0
  49. data/lib/HDLRuby/hdr_samples/with_program_ruby_threads.rb +56 -0
  50. data/lib/HDLRuby/hdr_samples/with_sequencer.rb +17 -0
  51. data/lib/HDLRuby/hdr_samples/with_sequencer_channel.rb +58 -0
  52. data/lib/HDLRuby/hdr_samples/with_sequencer_enumerable.rb +10 -0
  53. data/lib/HDLRuby/hdr_samples/with_sequencer_enumerator.rb +18 -4
  54. data/lib/HDLRuby/hdr_samples/with_sequencer_func.rb +2 -4
  55. data/lib/HDLRuby/hdr_samples/with_sequencer_sync.rb +2 -1
  56. data/lib/HDLRuby/hdrcc.rb +72 -21
  57. data/lib/HDLRuby/hruby_error.rb +13 -0
  58. data/lib/HDLRuby/hruby_high.rb +125 -26
  59. data/lib/HDLRuby/hruby_low.rb +171 -3
  60. data/lib/HDLRuby/hruby_low2programs.rb +47 -0
  61. data/lib/HDLRuby/hruby_low_resolve.rb +3 -2
  62. data/lib/HDLRuby/hruby_low_without_namespace.rb +133 -5
  63. data/lib/HDLRuby/hruby_low_without_subsignals.rb +1 -1
  64. data/lib/HDLRuby/hruby_rcsim.rb +113 -6
  65. data/lib/HDLRuby/hruby_serializer.rb +2 -1
  66. data/lib/HDLRuby/hruby_verilog.rb +94 -20
  67. data/lib/HDLRuby/hruby_verilog_name.rb +3 -17
  68. data/lib/HDLRuby/std/clocks.rb +118 -50
  69. data/lib/HDLRuby/std/fixpoint.rb +2 -2
  70. data/lib/HDLRuby/std/function_generator.rb +1 -1
  71. data/lib/HDLRuby/std/linear.rb +7 -7
  72. data/lib/HDLRuby/std/sequencer.rb +263 -13
  73. data/lib/HDLRuby/std/sequencer_channel.rb +90 -0
  74. data/lib/HDLRuby/std/sequencer_func.rb +28 -15
  75. data/lib/HDLRuby/std/std.rb +6 -0
  76. data/lib/HDLRuby/ui/hruby_board.rb +1079 -0
  77. data/lib/HDLRuby/version.rb +1 -1
  78. data/lib/c/Rakefile +8 -0
  79. data/lib/c/cHDL.h +12 -0
  80. data/lib/c/extconf.rb +7 -0
  81. data/lib/rubyHDL.rb +33 -0
  82. data/tuto/gui_accum.png +0 -0
  83. data/tuto/gui_board.png +0 -0
  84. data/tuto/tutorial_sw.html +2263 -1890
  85. data/tuto/tutorial_sw.md +957 -62
  86. metadata +43 -5
  87. data/README.pdf +0 -0
  88. data/tuto/tutorial_sw.pdf +0 -0
@@ -0,0 +1,1079 @@
1
+ require 'socket'
2
+
3
+ require 'rubyHDL'
4
+
5
+ # PCB Colors RGB: 42 100 36, 34 83 20, 106 155 108,
6
+
7
+
8
+ module HDLRuby::High::Std
9
+
10
+ ##
11
+ # Standard HDLRuby::High library:
12
+ # The idea is to be able to write sw-like sequential code.
13
+ #
14
+ ########################################################################
15
+
16
+
17
+ ## Class describing a board.
18
+ class Board
19
+
20
+ include Hmissing
21
+
22
+ attr_reader :namespace
23
+
24
+ ## Class describing a row of slide switches.
25
+ SW = Struct.new(:id, :size, :hwrite) do
26
+ def to_html
27
+ return "<div class=\"swset\" id=\"#{self.id}\" data-value=\"0\">\\n" +
28
+ '<span class="name">' + self.hwrite.to_s.chop + '</span>' +
29
+ '<span>&nbsp;&nbsp;</span>' +
30
+ self.size.times.map do |i|
31
+ '<label class="sw"><input type="checkbox" data-bit="' +
32
+ (self.size-i-1).to_s + '" ' +
33
+ 'onchange="sw_change(this)">' +
34
+ '<span class="slider"></span></label>\n'
35
+ end.join + "</div>\\n"
36
+ end
37
+ end
38
+
39
+ ## Class describing a row of buttons.
40
+ BT = Struct.new(:id, :size, :hwrite) do
41
+ def to_html
42
+ return "<div class=\"btset\" id=\"#{self.id}\" data-value=\"0\">\\n" +
43
+ '<span class="name">' + self.hwrite.to_s.chop + '</span>' +
44
+ '<span>&nbsp;&nbsp;</span>' +
45
+ self.size.times.map do |i|
46
+ '<button class="bt" data-bit="' +
47
+ (self.size-i-1).to_s + '" ' +
48
+ 'onmousedown="bt_click(this)" onmouseup="bt_release(this)" onmouseleave="bt_release(this)"><i class="bt_off"></i></button>\n'
49
+ end.join + "</div>\\n"
50
+ end
51
+ end
52
+
53
+ ## Class describing a row of LEDs.
54
+ LED = Struct.new(:id, :size, :hread) do
55
+ def to_html
56
+ return "<div class=\"ledset\" id=\"#{self.id}\" data-value=\"0\">\\n" +
57
+ '<span class="name">' + self.hread.to_s + '</span>' +
58
+ '<span>&nbsp;&nbsp;</span>' +
59
+ self.size.times.map do |i|
60
+ '<i class="led" class="led_off"></i>\n'
61
+ end.join + "</div>\\n"
62
+ end
63
+ end
64
+
65
+ ## Class describing a digit display.
66
+ DIGIT = Struct.new(:id, :size, :hread) do
67
+ def to_html
68
+ return '<div class="digitset" id=' + self.id.to_s +
69
+ ' data-width="' + self.size.to_s + '" data-value="0" >' +
70
+ '<span class="name">' + self.hread.to_s + '</span>' +
71
+ '<span>&nbsp;&nbsp;</span>' +
72
+ '<span class="matrix">' + "&nbsp;"*self.size + '</span>' +
73
+ "</div>\\n"
74
+ end
75
+ end
76
+
77
+ ## Class describing an hexadecimal display.
78
+ HEXA = Struct.new(:id, :size, :hread) do
79
+ def to_html
80
+ return "<div class=\"hexaset\" id=\"#{self.id}\" " +
81
+ "data-width=\"#{self.size}\" data-value=\"0\">" +
82
+ '<span class="name">' + self.hread.to_s + '</span>' +
83
+ '<span>&nbsp;&nbsp;</span>' +
84
+ "<span class=\"matrix\">#{"0"*self.size}</span>" + "</div>\\n"
85
+ end
86
+ end
87
+
88
+ ## Class describing an oscilloscope.
89
+ SCOPE = Struct.new(:id, :min, :max, :hread) do
90
+ def to_html
91
+ # Prepare the min, max and blank strings.
92
+ min = self.min.to_s
93
+ max = self.max.to_s
94
+ blank = (min.size > max.size) ? min : max;
95
+ # Generate the html.
96
+ return '<div>' +
97
+ '<div class="hdiv"><div class="r-blank">' + blank + '</div>' + # just for adjusting the position with the scope.
98
+ '<div class="scopetitle">' +self.hread.to_s + '</div>\\n' +
99
+ "</div>\\n" +
100
+ '<div class="scopepane">' +
101
+ '<div class="hdiv">\\n' +
102
+ '<div class="y-range">' +
103
+ '<div class="u-value">' + max + '</div>' +
104
+ '<div class="d-value">' + min + '</div></div>' +
105
+ '<div class="scope" id=' + self.id.to_s +
106
+ ' data-min="' + min + '" data-max="' + max + '"' +
107
+ ' data-pos="0" data-previous="0" data-value="0">' +
108
+ '<canvas class="screen"></canvas>' +
109
+ "</div>\\n" +
110
+ "</div>\\n" +
111
+ '<div class="hdiv"><div class="r-blank">' + blank + '</div>' + # just for adjusting the position with the scope.
112
+ '<div class="x-range">' +
113
+ '<div class="l-value">' + "&nbsp;0&nbsp;" + '</div>' +
114
+ '<div class="r-value">' + "100" + '</div></div>' +
115
+ "</div>\\n" +
116
+ "</div>\\n" +
117
+ "</div>\\n" +
118
+ "</div>\\n"
119
+ end
120
+ end
121
+
122
+
123
+ ## Class describing an ascii display.
124
+ ASCII = Struct.new(:id, :size, :hread)
125
+
126
+ ## Class describing a bitmap display with keyboard input supported
127
+ BITMAP = Struct.new(:id, :width, :height, :hread)
128
+
129
+ ## Class describing a new panel row.
130
+ PROW = Struct.new(:id) do
131
+ def to_html
132
+ return '<div class="prow" id="' + self.id.to_s + '"></div>';
133
+ end
134
+ end
135
+
136
+ ## The base HTML/Javascript/Ajax code of the FPGA UI:
137
+ # header
138
+ UI_header = <<-HTMLHEADER
139
+ HTTP/1.1 200
140
+ Content-Type: text/html
141
+
142
+ <!DOCTYPE html>
143
+ <html>
144
+ <head>
145
+ <meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
146
+ <style>
147
+
148
+ html {
149
+ background-color: #2a6424;
150
+ }
151
+
152
+ #header {
153
+ width: 100%;
154
+ /* border: solid 2px white; */
155
+ text-align: center;
156
+ }
157
+
158
+ .panel {
159
+ width: 100%;
160
+ height: 100%;
161
+ display: flex;
162
+ flex-direction: column;
163
+ justify-content: center;
164
+ align-content: flex-start;
165
+ gap: 20px;
166
+ }
167
+
168
+ .prow {
169
+ width: 100%;
170
+ display: flex;
171
+ flex-direction: row;
172
+ justify-content: center;
173
+ gap: 15px;
174
+ }
175
+
176
+ .title {
177
+ font-size: 36px;
178
+ color: #d0d0d0;
179
+ /* color: #2a6424; */
180
+ /* color: white; */
181
+ /* text-shadow: -1px -1px 1px white, 1px 1px 1px #101010; */
182
+ text-shadow: -1px -1px 1px #63c359, 1px 1px 1px #153212;
183
+ background-color: #265a20;
184
+ box-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
185
+ -moz-box-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
186
+ -webkit-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
187
+ padding: 4px;
188
+ padding-left: 16px;
189
+ padding-right: 16px;
190
+ display: inline-block;
191
+ }
192
+
193
+ .name {
194
+ font-size: 20px;
195
+ font-family: "Lucida Console", "Courier New", monospace;
196
+ color: white;
197
+ background-color: #265a20;
198
+ box-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
199
+ -moz-box-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
200
+ -webkit-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
201
+ padding-left: 8px;
202
+ padding-right: 8px;
203
+ /* display: table-cell;
204
+ vertical-align: middle; */
205
+ height: 28px;
206
+ line-height: 28px;
207
+ }
208
+
209
+ .vl {
210
+ /* border-left: 10px solid #2a6424; */
211
+ color: #2a6424;
212
+ box-shadow: -1px -1px 1px #63c359, 1px 1px 1px #153212;
213
+ -moz-box-shadow: -1px -1px 1px #63c359, 1px 1px 1px #153212;
214
+ -webkit-shadow: -1px -1px 1px #63c359, 1px 1px 1px #153212;
215
+ width: 10px;
216
+ }
217
+
218
+ .hdiv {
219
+ display: flex;
220
+ flex-direction: row;
221
+ align-item: stretch;
222
+ }
223
+
224
+ .vdiv {
225
+ display: flex;
226
+ flex-direction: column;
227
+ justify-content: stretch;
228
+ }
229
+
230
+ .swset {
231
+ display: flex;
232
+ flex-direction: row;
233
+ justify-content: center;
234
+ align-items: center;
235
+ margin-left: 8px;
236
+ margin-right: 8px;
237
+ height: 40px;
238
+ }
239
+
240
+ .btset {
241
+ display: flex;
242
+ flex-direction: row;
243
+ justify-content: center;
244
+ align-items: center;
245
+ margin-left: 8px;
246
+ margin-right: 8px;
247
+ height: 40px;
248
+ }
249
+
250
+ .ledset {
251
+ display: flex;
252
+ flex-direction: row;
253
+ justify-content: center;
254
+ align-items: center;
255
+ margin-left: 8px;
256
+ margin-right: 8px;
257
+ height: 40px;
258
+ }
259
+
260
+ .digitset {
261
+ display: flex;
262
+ flex-direction: row;
263
+ justify-content: center;
264
+ align-items: center;
265
+ margin-left: 8px;
266
+ margin-right: 8px;
267
+ height: 40px;
268
+ }
269
+
270
+ .hexaset {
271
+ display: flex;
272
+ flex-direction: row;
273
+ justify-content: center;
274
+ align-items: center;
275
+ margin-left: 8px;
276
+ margin-right: 8px;
277
+ height: 40px;
278
+ }
279
+
280
+ .scopetitle {
281
+ font-size: 20px;
282
+ font-family: "Lucida Console", "Courier New", monospace;
283
+ color: white;
284
+ background-color: #265a20;
285
+ box-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
286
+ -moz-box-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
287
+ -webkit-shadow: -1px -1px 1px #153212, 1px 1px 1px #63c359;
288
+ display: inline-block;
289
+ width: 30vw;
290
+ height: 28px;
291
+ line-height: 28px;
292
+ margin-right: auto;
293
+ margin-bottom: 4px;
294
+ text-align:center;
295
+ }
296
+
297
+ .scopepane {
298
+ background-color: #ccc;
299
+ border: solid 2px #505050;
300
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
301
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
302
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
303
+ padding: 10px;
304
+ width: max-content;
305
+ margin-left: auto;
306
+ margin-right: auto;
307
+ }
308
+
309
+ .y-range {
310
+ margin-left: auto;
311
+ /* border: solid 2px white; */
312
+ display: flex;
313
+ flex-direction: column;
314
+ }
315
+
316
+ .x-range {
317
+ width: 30vw;
318
+ /* border: solid 2px white; */
319
+ display: flex;
320
+ flex-direction: row;
321
+ margin-right: auto;
322
+ margin-left: 4px;
323
+ margin-top: 4px;
324
+ }
325
+
326
+ .u-value {
327
+ font-size: 16px;
328
+ font-family: "Lucida Console", "Courier New", monospace;
329
+ font-weight: bold;
330
+ color: black;
331
+ background-color: #ddd;
332
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
333
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
334
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
335
+ padding-left: 8px;
336
+ padding-right: 8px;
337
+ text-align:center;
338
+ }
339
+
340
+ .d-value {
341
+ font-size: 16px;
342
+ font-family: "Lucida Console", "Courier New", monospace;
343
+ font-weight: bold;
344
+ color: black;
345
+ background-color: #ddd;
346
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
347
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
348
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
349
+ padding-left: 8px;
350
+ padding-right: 8px;
351
+ margin-top: auto;
352
+ text-align:center;
353
+ }
354
+
355
+ .l-value {
356
+ font-size: 16px;
357
+ font-family: "Lucida Console", "Courier New", monospace;
358
+ font-weight: bold;
359
+ color: black;
360
+ background-color: #ddd;
361
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
362
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
363
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
364
+ padding-left: 8px;
365
+ padding-right: 8px;
366
+ height: fit-content;
367
+ }
368
+
369
+ .r-value {
370
+ font-size: 16px;
371
+ font-family: "Lucida Console", "Courier New", monospace;
372
+ font-weight: bold;
373
+ color: black;
374
+ background-color: #ddd;
375
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
376
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
377
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
378
+ padding-left: 8px;
379
+ padding-right: 8px;
380
+ margin-left: auto;
381
+ height: fit-content;
382
+ }
383
+
384
+ .r-blank {
385
+ font-size: 20px;
386
+ color: rgba(0,0,0,0);
387
+ border: solid 1px rgba(0,0,0,0);
388
+ padding-left: 8px;
389
+ padding-right: 8px;
390
+ margin-left: auto;
391
+ height: fit-content;
392
+ }
393
+
394
+ .scope {
395
+ width: 30vw;
396
+ height: 30vw;
397
+ margin-right: auto;
398
+ border: solid 2px #505050;
399
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
400
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
401
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
402
+ }
403
+
404
+
405
+ .screen {
406
+ object-fit:contain;
407
+ color: yellow;
408
+ background-color: black;
409
+ /* border: solid 2px #505050;
410
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
411
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
412
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010; */
413
+ }
414
+
415
+ .sw {
416
+ position: relative;
417
+ display: inline-block;
418
+ width: 24px;
419
+ height: 40px;
420
+ margin: 2px;
421
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
422
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
423
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
424
+ }
425
+
426
+ .sw input {
427
+ opacity: 0;
428
+ width: 0;
429
+ height: 0;
430
+ }
431
+
432
+ .slider {
433
+ position: absolute;
434
+ cursor: pointer;
435
+ top: 0;
436
+ left: 0;
437
+ right: 0;
438
+ bottom: 0;
439
+ background-color: #ccc;
440
+ -webkit-transition: .2s;
441
+ transition: .2s;
442
+ border: solid 2px #505050;
443
+ }
444
+
445
+ .slider:before {
446
+ position: absolute;
447
+ content: "";
448
+ height: 16px;
449
+ width: 16px;
450
+ left: 2px;
451
+ bottom: 2px;
452
+ background-color: black;
453
+ -webkit-transition: .2s;
454
+ transition: .2s;
455
+ }
456
+
457
+ input:checked + .slider {
458
+ background-color: yellow;
459
+ }
460
+
461
+ input:checked + .slider:before {
462
+ -webkit-transform: translateY(-16px);
463
+ -ms-transform: translateY(-16px);
464
+ transform: translateY(-16px);
465
+ }
466
+
467
+ .matrix {
468
+ font-size: 26px;
469
+ font-family: "Lucida Console", "Courier New", monospace;
470
+ color: yellow;
471
+ background-color: black;
472
+ border: solid 2px #505050;
473
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
474
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
475
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
476
+ }
477
+
478
+ .bt {
479
+ background-color: #ccc;
480
+ border: solid 2px #505050;
481
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
482
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
483
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
484
+ }
485
+
486
+ .bt:hover {
487
+ background-color: #aaa;
488
+ }
489
+
490
+ .bt_off {
491
+ height: 20px;
492
+ width: 20px;
493
+ border: solid 1px #505050;
494
+ border-radius: 50%;
495
+ background: linear-gradient(to bottom right, #A0A0A0, black 60%);
496
+ display: inline-block;
497
+ margin-top: 1px;
498
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
499
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
500
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
501
+ }
502
+
503
+ .bt_on {
504
+ height: 20px;
505
+ width: 20px;
506
+ border: solid 1px #505050;
507
+ border-radius: 50%;
508
+ background: linear-gradient(to top left, #A0A0A0, black 80%);
509
+ display: inline-block;
510
+ margin-top: 3px;
511
+ margin-bottom: -2px;
512
+ box-shadow: -1px -1px 1px #101010, 1px 1px 1px white;
513
+ -moz-box-shadow: -1px -1px 1px #101010, 1px 1px 1px white;
514
+ -webkit-shadow: -1px -1px 1px #101010, 1px 1px 1px white;
515
+ }
516
+
517
+ .led_off {
518
+ height: 20px;
519
+ width: 20px;
520
+ border: solid 2px #505050;
521
+ border-radius: 50%;
522
+ background: radial-gradient(circle at 30% 30%, red 20%, #8B0000);
523
+ display: inline-block;
524
+ margin: 2px;
525
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
526
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
527
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
528
+ }
529
+
530
+ .led_on {
531
+ height: 20px;
532
+ width: 20px;
533
+ border: solid 2px #505050;
534
+ border-radius: 50%;
535
+ background: radial-gradient(circle, yellow 15%, orange 50%, red);
536
+ display: inline-block;
537
+ margin: 2px;
538
+ box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
539
+ -moz-box-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
540
+ -webkit-shadow: -1px -1px 1px white, 1px 1px 1px #101010;
541
+ }
542
+
543
+ </style>
544
+ </head>
545
+
546
+ <body>
547
+
548
+ <div id="header">
549
+ <div id="cartouche" class="title">
550
+ Name of the FPGA board
551
+ </div>
552
+ </div>
553
+ <br>
554
+
555
+ <div id="panel" class="panel"><div class="prow"></div></div>
556
+
557
+ <script>
558
+ // Access to the components of the UI.
559
+ const cartouche = document.getElementById("cartouche");
560
+ const panel = document.getElementById("panel");
561
+
562
+ // The input and output elements' ids.
563
+ const input_ids = [];
564
+ const output_ids = [];
565
+
566
+ // The control functions.
567
+
568
+ // Set the cartouche of the board.
569
+ function set_cartouche(txt) {
570
+ cartouche.innerHTML = txt;
571
+ }
572
+
573
+ // Add an element.
574
+ function add_element(txt) {
575
+ if (txt.includes('prow')) {
576
+ // New panel row.
577
+ panel.innerHTML += txt;
578
+ return;
579
+ }
580
+ const prow = panel.lastElementChild;
581
+ prow.innerHTML += txt;
582
+ const element = prow.lastElementChild;
583
+ // Depending of the kind of element.
584
+ if (element.classList.contains('swset') ||
585
+ element.classList.contains('btset')) {
586
+ // Input element.
587
+ input_ids.push(element.id);
588
+ } else {
589
+ // Output element.
590
+ output_ids.push(element.id);
591
+ }
592
+ }
593
+
594
+ // Set the size of a canvas.
595
+ function setCanvasSize(canvas) {
596
+ // Get the DPR and size of the canvas
597
+ const dpr = window.devicePixelRatio;
598
+ const parentRect = canvas.parentElement.getBoundingClientRect();
599
+ // console.log("parentRect=[" + parentRect.width + "," + parentRect.height + "]");
600
+
601
+ // Set the "actual" size of the canvas
602
+ canvas.width = parentRect.width * dpr;
603
+ canvas.height = parentRect.height * dpr;
604
+
605
+ // Scale the context to ensure correct drawing operations
606
+ canvas.getContext("2d").scale(dpr, dpr);
607
+
608
+ // Set the "drawn" size of the canvas
609
+ canvas.style.width = `${parentRect.width}px`;
610
+ canvas.style.height = `${parentRect.height}px`;
611
+ }
612
+
613
+ // Handler of sw change.
614
+ function sw_change(sw) {
615
+ // Get the set holding sw.
616
+ const swset = sw.parentElement.parentElement;
617
+ const bit = sw.dataset.bit;
618
+ if (sw.checked) {
619
+ swset.dataset.value = swset.dataset.value | (1 << bit);
620
+ }
621
+ else {
622
+ swset.dataset.value = swset.dataset.value & ~(1 << bit);
623
+ }
624
+ // console.log("sw value=" + swset.dataset.value);
625
+ }
626
+
627
+ // Set the aspect of a button to clicked.
628
+ function bt_on(bt) {
629
+ bt.innerHTML = '<i class="bt_on"></i>';
630
+ }
631
+
632
+ // Set the aspect of a button to not clicked.
633
+ function bt_off(bt) {
634
+ bt.innerHTML = '<i class="bt_off"></i>';
635
+ }
636
+
637
+ // Handler of button clicked.
638
+ function bt_click(bt) {
639
+ // Change the aspect of the button.
640
+ bt_on(bt);
641
+ // Get the set holding bt.
642
+ const btset = bt.parentElement;
643
+ const bit = bt.dataset.bit;
644
+ // Update its value.
645
+ btset.dataset.value = btset.dataset.value | (1 << bit);
646
+ }
647
+
648
+ // Handler of button released.
649
+ function bt_release(bt) {
650
+ // Change the aspect of the button.
651
+ bt_off(bt);
652
+ // Get the set holding bt.
653
+ const btset = bt.parentElement;
654
+ const bit = bt.dataset.bit;
655
+ // Update its value.
656
+ btset.dataset.value = btset.dataset.value & ~(1 << bit);
657
+ }
658
+
659
+
660
+ // Switch a led on.
661
+ function led_on(led) {
662
+ led.classList.remove('led_off');
663
+ led.classList.add('led_on');
664
+ }
665
+
666
+ // Switch a led off.
667
+ function led_off(led) {
668
+ led.classList.remove('led_on');
669
+ led.classList.add('led_off');
670
+ }
671
+
672
+ // Update a led set.
673
+ function ledset_update(ledset,value) {
674
+ // Update the ledset value.
675
+ ledset.dataset.value = value;
676
+ // Update each led.
677
+ // Get the individual leds.
678
+ const leds = ledset.getElementsByTagName("i");
679
+ const num = leds.length;
680
+ // Set each led of the set.
681
+ for(let i=0; i < num; ++i) {
682
+ const val = (value >> i) & 1;
683
+ if (val == 1) { led_on(leds[num-i-1]); }
684
+ else { led_off(leds[num-i-1]); }
685
+ }
686
+ }
687
+
688
+ // Update a digit set.
689
+ function digitset_update(digitset,value) {
690
+ // Update the digiset value.
691
+ digitset.dataset.value = value;
692
+ // Unsigned case.
693
+ const num = digitset.dataset.width;
694
+ digitset.lastElementChild.innerHTML = String(value).padStart(num,"\u00A0");
695
+ }
696
+
697
+ // Update a hexadecimal set.
698
+ function hexaset_update(hexaset,value) {
699
+ // Update the digiset value.
700
+ hexaset.dataset.value = value;
701
+ // Update its display.
702
+ const num = hexaset.dataset.width;
703
+ hexaset.lastElementChild.innerHTML = Number(value).toString(16).padStart(num,'0');
704
+ }
705
+
706
+ // Update an oscilloscope.
707
+ function scope_update(scope,value) {
708
+ // Get the canvas.
709
+ const canvas = scope.lastElementChild;
710
+ // Shall we set up its size?
711
+ let first = 0;
712
+ if (scope.dataset.configured != 1) {
713
+ // First time, so yes.
714
+ setCanvasSize(canvas);
715
+ scope.dataset.configured = 1;
716
+ first = 1;
717
+ }
718
+ // Its size.
719
+ const { width, height } = canvas.getBoundingClientRect();
720
+ // Its context.
721
+ const cxt = canvas.getContext("2d");
722
+ // Get the properties of the scope.
723
+ const min = Number(scope.dataset.min);
724
+ const max = Number(scope.dataset.max);
725
+ const pos = Number(scope.dataset.pos);
726
+ const previous = Number(scope.dataset.previous);
727
+ // console.log("min=" + min + " max=" + max);
728
+ const toPx = function(val) { // Convert a percentage to x position.
729
+ return (val*width)/100;
730
+ }
731
+ const toPy = function(val) { // Convert an input value to y position.
732
+ return height - ((val-min) * height) / (max-min);
733
+ }
734
+ // Shall we restart the drawing?
735
+ if (pos >= 100 || first == 1) {
736
+ // Yes, clears the canvas.
737
+ cxt.clearRect(0, 0, width, height);
738
+ /* Draw the grid. */
739
+ cxt.strokeStyle = "#b3b3b3";
740
+ cxt.setLineDash([2,1]);
741
+ cxt.lineWidth = 1;
742
+ cxt.beginPath();
743
+ for(let i=0; i<100; i += 10) {
744
+ cxt.moveTo(toPx(0), toPy((i*(max-min))/100+min));
745
+ cxt.lineTo(toPx(100),toPy((i*(max-min))/100+min));
746
+ cxt.moveTo(toPx(i), toPy(min));
747
+ cxt.lineTo(toPx(i), toPy(max));
748
+ }
749
+ cxt.stroke();
750
+ cxt.setLineDash([]);
751
+ cxt.beginPath();
752
+ for(let i=0; i<100; i+= 2) {
753
+ cxt.moveTo(toPx(50-0.7), toPy((i*(max-min))/100+min));
754
+ cxt.lineTo(toPx(50+0.7), toPy((i*(max-min))/100+min));
755
+ cxt.moveTo(toPx(i), toPy((max-min)/2 + min - 0.7*(max-min)/100));
756
+ cxt.lineTo(toPx(i), toPy((max-min)/2 + min + 0.7*(max-min)/100));
757
+ }
758
+ cxt.stroke();
759
+ // Set the pen color for drawing the signal.
760
+ cxt.strokeStyle = "yellow";
761
+ cxt.lineWidth = 2;
762
+ // Draw a single pixel.
763
+ cxt.beginPath();
764
+ cxt.moveTo(toPx(0), toPy(value));
765
+ cxt.lineTo(toPx(0), toPy(value));
766
+ cxt.stroke();
767
+ /* Update the values. */
768
+ scope.dataset.previous = value;
769
+ scope.dataset.pos = 0;
770
+ } else {
771
+ // Go on, draw a line to the new position.
772
+ // Set the pen color for drawing the signal.
773
+ cxt.strokeStyle = "yellow";
774
+ cxt.lineWidth = 2;
775
+ // Draw a line to the new position.
776
+ cxt.beginPath();
777
+ cxt.moveTo(toPx(pos), toPy(previous));
778
+ cxt.lineTo(toPx(pos+1), toPy(value));
779
+ cxt.stroke();
780
+ /* Update the values. */
781
+ scope.dataset.previous = value;
782
+ scope.dataset.pos = pos + 1;
783
+ }
784
+ }
785
+
786
+ // Update a general display element.
787
+ function element_update(element,value) {
788
+ if(element.classList.contains('ledset')) { ledset_update(element,value); }
789
+ if(element.classList.contains('digitset')){ digitset_update(element,value); }
790
+ if(element.classList.contains('signedset')){signedset_update(element,value);}
791
+ if(element.classList.contains('hexaset')) { hexaset_update(element,value); }
792
+ if(element.classList.contains('scope')) { scope_update(element,value); }
793
+ }
794
+
795
+
796
+ // Synchronize with the HDLRuby simulator.
797
+ function hruby_sync() {
798
+ let xhttp = new XMLHttpRequest();
799
+ xhttp.onreadystatechange = function() {
800
+ // console.log("response=" + this.responseText);
801
+ if (this.readyState == 4 && this.status == 200) {
802
+ if (/[0-9]+:[0-9]/.test(this.responseText)) {
803
+ // There is a real response.
804
+ // Update the interface with the answer.
805
+ const commands = this.responseText.split(';');
806
+ for(command of commands) {
807
+ const toks = command.split(':');
808
+ element_update(document.getElementById(toks[0]),toks[1]);
809
+ }
810
+ }
811
+ }
812
+ };
813
+ // Builds the action from the state of the input elements.
814
+ act = '';
815
+ for(id of input_ids) {
816
+ act += id + ':' + document.getElementById(id).dataset.value + ';';
817
+ }
818
+ // console.log("act=" + act);
819
+ xhttp.open("GET", act, true);
820
+ xhttp.overrideMimeType("text/plain; charset=x-user-defined");
821
+ xhttp.send();
822
+ }
823
+
824
+ // First call of synchronisation.
825
+ hruby_sync();
826
+
827
+ // Then periodic synchronize.
828
+ setInterval(function() { hruby_sync(); }, 100);
829
+
830
+ </script>
831
+
832
+ HTMLHEADER
833
+
834
+ # Footer
835
+ UI_footer = <<-HTMLFOOTER
836
+ </body>
837
+ </html>
838
+ HTMLFOOTER
839
+
840
+ UI_response = <<-HTMLRESPONSE
841
+ HTTP/1.1 200
842
+ Content-Type: text/plain
843
+
844
+ HTMLRESPONSE
845
+
846
+
847
+ # The already used ports.
848
+ @@http_ports = []
849
+
850
+
851
+ # Create a new board named +name+ accessible on HTTP port +http_port+
852
+ # and whose content is describe in +hdlruby_block+.
853
+ def initialize(name, http_port = 8000, &hdlruby_block)
854
+ # Set the name.
855
+ @name = name.to_s
856
+ # Check and set the port.
857
+ http_port = http_port.to_i
858
+ if (@@http_ports.include?(http_port)) then
859
+ # Port already used, error.
860
+ raise UIError.new("UI (http) port #{http_port} already in use.")
861
+ end
862
+ @http_port = http_port
863
+ @@http_ports << @http_port
864
+ # Create the server
865
+ @server = TCPServer.new(@http_port)
866
+ # Create the running function.
867
+ this = self
868
+ Kernel.define_method(@name.to_sym) { this.run }
869
+ # Initialize the list of board elements to empty.
870
+ @elements = []
871
+ @out_elements = []
872
+ # And build the board.
873
+ # Create the namespace for the program.
874
+ @namespace = Namespace.new(self)
875
+ # Build the program object.
876
+ High.space_push(@namespace)
877
+ pr = nil
878
+ High.top_user.instance_eval { pr = program(:ruby, @name.to_sym) {} }
879
+ @program = pr
880
+ # Fill it.
881
+ High.top_user.instance_eval(&hdlruby_block)
882
+ High.space_pop
883
+ end
884
+
885
+ # Adds new activation ports.
886
+ def actport(*evs)
887
+ @program.actport(*evs)
888
+ end
889
+
890
+ # Add a new slide switch element attached to HDLRuby port +hport+.
891
+ def sw(hport)
892
+ if !hport.is_a?(Hash) or hport.size != 1 then
893
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
894
+ end
895
+ # Create the HDLRuby program port.
896
+ @program.outport(hport)
897
+ # Create the ui component.
898
+ hport = hport.first
899
+ @elements << SW.new(@elements.size,hport[1].type.width,:"#{hport[0]}=")
900
+ end
901
+
902
+ # Add a new button element attached to HDLRuby port +hport+.
903
+ def bt(hport)
904
+ if !hport.is_a?(Hash) or hport.size != 1 then
905
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
906
+ end
907
+ # Create the HDLRuby program port.
908
+ @program.outport(hport)
909
+ hport = hport.first
910
+ # Create the ui component.
911
+ @elements << BT.new(@elements.size,hport[1].type.width,:"#{hport[0]}=")
912
+ end
913
+
914
+ # Add a new LED element attached to HDLRuby port +hport+.
915
+ def led(hport)
916
+ if !hport.is_a?(Hash) or hport.size != 1 then
917
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
918
+ end
919
+ # Create the HDLRuby program port.
920
+ @program.inport(hport)
921
+ hport = hport.first
922
+ # Createthe ui component.
923
+ @elements << LED.new(@elements.size,hport[1].type.width,hport[0])
924
+ @out_elements << @elements[-1]
925
+ end
926
+
927
+ # Add a new digit element attached to HDLRuby port +hport+.
928
+ def digit(hport)
929
+ if !hport.is_a?(Hash) or hport.size != 1 then
930
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
931
+ end
932
+ # Create the HDLRuby program port.
933
+ @program.inport(hport)
934
+ hport = hport.first
935
+ sign = hport[1].type.signed?
936
+ # Createthe ui component.
937
+ @elements << DIGIT.new(@elements.size,
938
+ Math.log10(2**hport[1].type.width - 1).to_i + (sign ? 2 : 1),
939
+ hport[0])
940
+ @out_elements << @elements[-1]
941
+ end
942
+
943
+ # Add a new hexadecimal element attached to HDLRuby port +hport+.
944
+ def hexa(hport)
945
+ if !hport.is_a?(Hash) or hport.size != 1 then
946
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
947
+ end
948
+ # Create the HDLRuby program port.
949
+ @program.inport(hport)
950
+ hport = hport.first
951
+ # Createthe ui component.
952
+ @elements << HEXA.new(@elements.size,
953
+ (hport[1].type.width-1)/4+1, hport[0])
954
+ @out_elements << @elements[-1]
955
+ end
956
+
957
+ # Add a new scope element attached to HDLRuby port +hport+.
958
+ def scope(hport)
959
+ if !hport.is_a?(Hash) or hport.size != 1 then
960
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
961
+ end
962
+ # Create the HDLRuby program port.
963
+ @program.inport(hport)
964
+ hport = hport.first
965
+ width = hport[1].type.width
966
+ if hport[1].type.signed? then
967
+ min = -2**(width-1)
968
+ max = 2**(width-1) - 1
969
+ else
970
+ min = 0
971
+ max = 2**width - 1
972
+ end
973
+ # Createthe ui component.
974
+ @elements << SCOPE.new(@elements.size, min, max, hport[0])
975
+ @out_elements << @elements[-1]
976
+ end
977
+
978
+ # Add a new ASCII element of +n+ columns attached to HDLRuby port +hport+.
979
+ def ascii(n, hport)
980
+ if !hport.is_a?(Hash) or hport.size != 1 then
981
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
982
+ end
983
+ # Create the HDLRuby program port.
984
+ @program.inport(hport)
985
+ hport = hport.first
986
+ # Createthe ui component.
987
+ @elements << ASCII.new(@elements.size,hport[1].type.width,hport[0])
988
+ @out_elements << @elements[-1]
989
+ end
990
+
991
+ # Add a new bitmap element of width +w+ and height +h+ attached to HDLRuby
992
+ # port +hport+.
993
+ def bitmap(w,h, hport)
994
+ if !hport.is_a?(Hash) or hport.size != 1 then
995
+ raise UIError.new("Malformed HDLRuby port declaration: #{hport}")
996
+ end
997
+ # Create the HDLRuby program port.
998
+ @program.inport(hport)
999
+ hport = hport.first
1000
+ # Createthe ui component.
1001
+ @elements << BITMAP.new(@elements.size,w.to_i,h.to_i,hport[0])
1002
+ @out_elements << @elements[-1]
1003
+ end
1004
+
1005
+ # Add a new panel row.
1006
+ def row()
1007
+ # Createthe ui component.
1008
+ @elements << PROW.new(@elements.size)
1009
+ end
1010
+
1011
+
1012
+
1013
+ # Update port number +id+ with value +val+.
1014
+ def update_port(id,val)
1015
+ RubyHDL.send(@elements[id].hwrite,val)
1016
+ end
1017
+
1018
+ # Generate a response to a request to the server.
1019
+ def make_response(request)
1020
+ # puts "request=#{request}"
1021
+ if (request.empty?) then
1022
+ # First or re-connection, generate the UI.
1023
+ return UI_header + "\n<script>\n" +
1024
+ "set_cartouche('#{@name}');\n" +
1025
+ @elements.map do |elem|
1026
+ "add_element('#{elem.to_html}');"
1027
+ end.join("\n") +
1028
+ "\n</script>\n" + UI_footer
1029
+ else
1030
+ # This should be an AJAX request, process it.
1031
+ commands = request.split(";")
1032
+ commands.each do |command|
1033
+ id, val = command.split(":").map {|t| t.to_i}
1034
+ self.update_port(id,val)
1035
+ end
1036
+ # And generate the response: an update of each board output element.
1037
+ return UI_response + @out_elements.each.map do |e|
1038
+ # puts "resp=" + "#{e.id}:#{RubyHDL.send(e.hread)}"
1039
+ "#{e.id}:#{RubyHDL.send(e.hread)}"
1040
+ end.join(";")
1041
+ end
1042
+ end
1043
+
1044
+ # Start the ui.
1045
+ def run
1046
+ # At first the u is not running.
1047
+ @connected = false
1048
+ # Create the ui thread.
1049
+ @thread = Thread.new do
1050
+ while session = @server.accept
1051
+ # A request came, process it.
1052
+ request = session.gets
1053
+ verb,path,protocol = request.split(' ')
1054
+ # puts "verb=#{verb} path=#{path} protocol=#{protocol}"
1055
+ if protocol === "HTTP/1.1"
1056
+ # The request is valid, generate the response.
1057
+ session.print self.make_response(path[1..-1])
1058
+ # And tell the ui has been connected.
1059
+ @connected = true
1060
+ else
1061
+ session.print 'Connection Refuse'
1062
+ end
1063
+
1064
+ session.close
1065
+ end
1066
+ end
1067
+ # Wait for a first connection.
1068
+ sleep(0.1) while(!@connected)
1069
+ end
1070
+ end
1071
+
1072
+
1073
+ # Create a new board named +name+ accessible on HTTP port +http_port+
1074
+ # and whose content is describe in +block+.
1075
+ def board(name, http_port = 8000, &block)
1076
+ return Board.new(name,http_port,&block)
1077
+ end
1078
+
1079
+ end