reggae_eda 0.0.6

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.
@@ -0,0 +1,107 @@
1
+ require_relative 'code'
2
+ require_relative 'visitor'
3
+ module Reggae
4
+
5
+ class PrettyPrinter < Visitor
6
+
7
+ def initialize
8
+ @indent=-2
9
+ end
10
+
11
+ def inc str=nil
12
+ say(str) if str
13
+ @indent+=2
14
+ end
15
+
16
+ def dec
17
+ @indent-=2
18
+ end
19
+
20
+ def say str
21
+ puts " "*@indent+str.to_s
22
+ end
23
+
24
+ def visit mm
25
+ inc
26
+ mm.accept(self,nil)
27
+ dec
28
+ end
29
+
30
+ def visitMemoryMap mm,args=nil
31
+ inc "MemoryMap"
32
+ say mm.name
33
+ mm.parameters.accept(self,nil)
34
+ mm.zones.each{|zone| zone.accept(self,nil)}
35
+ dec
36
+ end
37
+
38
+ def visitParameters params,args=nil
39
+ inc "Parameters"
40
+ params.bus.accept(self,nil)
41
+ params.range.accept(self,nil)
42
+ dec
43
+ end
44
+
45
+ def visitBus bus,args=nil
46
+ inc "Bus"
47
+ say bus.frequency
48
+ say bus.address_size
49
+ say bus.data_size
50
+ dec
51
+ end
52
+
53
+ def visitRange range,args=nil
54
+ inc "Range"
55
+ say range.from
56
+ say range.to
57
+ dec
58
+ end
59
+
60
+ def visitZone zone,args=nil
61
+ inc "Zone"
62
+ say zone.name
63
+ zone.range.accept(self)
64
+ zone.registers.each{|reg| reg.accept(self)}
65
+ zone.subzones.each{|subzone| subzone.accept(self)}
66
+ dec
67
+ end
68
+
69
+ def visitSubzone zone,args=nil
70
+ inc "Subzone"
71
+ say zone.name
72
+ zone.range.accept(self)
73
+ zone.registers.each{|reg| reg.accept(self)}
74
+ dec
75
+ end
76
+
77
+ def visitRegister reg,args=nil
78
+ inc "Register"
79
+ say reg.name
80
+ say reg.address
81
+ say reg.init
82
+ reg.bits.each{|bit| bit.accept(self)}
83
+ reg.bitfields.each{|bitfield| bitfield.accept(self)}
84
+
85
+ dec
86
+ end
87
+
88
+ def visitBit bit,args=nil
89
+ inc "Bit"
90
+ say bit.position
91
+ say bit.name
92
+ say bit.purpose
93
+ say bit.toggle
94
+ dec
95
+ end
96
+
97
+ def visitBitfield bitfield,args=nil
98
+ inc "Bitfield"
99
+ say bitfield.position
100
+ say bitfield.name
101
+ say bitfield.purpose
102
+ say bitfield.toggle
103
+ dec
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ module Reggae
2
+ VERSION="0.0.6"
3
+ end
@@ -0,0 +1,973 @@
1
+ require 'fileutils'
2
+
3
+ require_relative 'code'
4
+
5
+ module Reggae
6
+
7
+ class VHDLGenerator < Visitor
8
+
9
+ def initialize options
10
+ super()
11
+ @options=options
12
+ @verbose=options[:verbose]
13
+ @vhdl_entity_arch=nil
14
+ @vhdl_pkg=nil
15
+ @vhdl_files=[]
16
+ end
17
+
18
+ #.......................VHDL.......................
19
+ def generate_from model
20
+ @dest_dir=$working_dir #+"/src"
21
+ if !Dir.exists?(@dest_dir)
22
+ FileUtils.mkdir(@dest_dir)
23
+ end
24
+ begin
25
+ inc
26
+ model.accept(self,nil)
27
+ #gen_testbench
28
+ gen_compile_script
29
+ gen_synthesis_script
30
+ gen_ruby_sw_if(model) if @options[:gen_ruby]
31
+ dec
32
+ rescue Exception => e
33
+ puts e.backtrace
34
+ puts e
35
+ end
36
+ end
37
+
38
+ def now
39
+ time = Time.new
40
+ time.ctime
41
+ end
42
+
43
+ def header
44
+ code=Code.new
45
+ code << "-"*80
46
+ code << "-- Generated automatically by Reggae compiler "
47
+ code << "-- (c) Jean-Christophe Le Lann - 2011"
48
+ code << "-- date : #{now}"
49
+ code << "-"*80
50
+ #code.newline
51
+ code << "library ieee,std;"
52
+ code << "use ieee.std_logic_1164.all;"
53
+ code << "use ieee.numeric_std.all;"
54
+ code.newline
55
+ code
56
+ end
57
+
58
+ def clock_and_reset
59
+ code=Code.new
60
+ code << "reset_n : in std_logic;"
61
+ code << "clk : in std_logic;"
62
+ code << "sreset : in std_logic;"
63
+ code
64
+ end
65
+
66
+ def visitMemoryMap mm,args=nil
67
+ inc "MemoryMap"
68
+ @model_name=mm.name
69
+ @addr_size=mm.parameters.bus.address_size
70
+ @data_size=mm.parameters.bus.data_size
71
+
72
+ mm.parameters.accept(self,nil)
73
+ mm.zones.each{|zone| zone.accept(self,nil)}
74
+ if @options[:gen_system]
75
+ gen_system(mm)
76
+ unless @options[:include_uart]
77
+ puts "WARNING : no uart included. -u to add it if wanted."
78
+ end
79
+ end
80
+ dec
81
+ end
82
+
83
+ def visitZone zone,args=nil
84
+ inc "Zone"
85
+ gen_ip_pkg(zone)
86
+ gen_ip_regif(zone)
87
+ gen_ip_entity_arch(zone)
88
+ if @options[:gen_xdc]
89
+ gen_xdc(zone)
90
+ end
91
+ dec
92
+ end
93
+
94
+ def gen_ip_pkg zone
95
+ filename=zone.name.to_s+"_pkg.vhd"
96
+ puts " - code for IP package"+(" "+filename).rjust(38,'.')
97
+ code=Code.new
98
+ code << header
99
+ code << "package #{zone.name}_regif_pkg is"
100
+ code.indent=2
101
+ zone.registers.each do |reg|
102
+ code.newline
103
+ code << "type #{reg.name}_reg is record"
104
+ code.indent=4
105
+ tmp_h={}
106
+ reg.bits.each do |bit|
107
+ tmp_h[bit.name]="std_logic;"
108
+ end
109
+ reg.bitfields.each do |bitf|
110
+ min,max=bitf.position.minmax
111
+ nb_bits=(max-min)
112
+ tmp_h[bitf.name]="std_logic_vector(#{nb_bits} downto 0);"
113
+ end
114
+ max_justify=tmp_h.keys.map(&:size).max
115
+ tmp_h.each do |name,type|
116
+ code << "#{name.to_s.ljust(max_justify,' ')} : #{type}"
117
+ end
118
+ code.indent=2
119
+ code << "end record;"
120
+ code.newline
121
+ code << "constant #{reg.name.upcase}_INIT: #{reg.name}_reg :=("
122
+ code.indent=4
123
+ tmp_h={}
124
+ init_reg=reg.init.to_i(16).to_s(2).rjust(@data_size,'0')
125
+
126
+ reg.bits.each do |bit|
127
+ pos=bit.position
128
+ init_value=init_reg[@data_size-pos-1]
129
+ tmp_h[bit.name]="'#{init_value}'"
130
+ end
131
+
132
+ reg.bitfields.each do |bitf|
133
+ min,max=bitf.position.minmax
134
+ size=(max-min)+1
135
+ bitf.position
136
+ range=(@data_size-max-1)..(@data_size-min-1)
137
+ init_value=init_reg[range]
138
+ init_value=init_value.rjust(size,'0')
139
+ tmp_h[bitf.name]="\"#{init_value}\""
140
+ end
141
+
142
+ max_justify=tmp_h.keys.map(&:size).max
143
+ tmp_h.each do |name,value|
144
+ code << "#{name.to_s.ljust(max_justify,' ')} => #{value},"
145
+ end
146
+ code.indent=2
147
+
148
+ code << ");"
149
+ end
150
+
151
+ code.newline
152
+ code << "type registers_type is record"
153
+ code.indent=4
154
+ max_just=zone.registers.map{|reg| reg.name.size}.max
155
+ zone.registers.each do |reg|
156
+ code << "#{reg.name.to_s.ljust(max_just,' ')} : #{reg.name}_reg; -- #{reg.address}"
157
+ end
158
+ code.indent=2
159
+ code << "end record;"
160
+
161
+ code.newline
162
+ code << "constant REGS_INIT : registers_type :=("
163
+ code.indent=4
164
+ max_just=zone.registers.map{|reg| reg.name.size}.max
165
+ zone.registers.each do |reg|
166
+ code << "#{reg.name.to_s.ljust(max_just,' ')} => #{reg.name.upcase}_INIT,"
167
+ end
168
+ code.indent=2
169
+ code << ");"
170
+ code.newline
171
+
172
+ code << "--sampling values from IPs"
173
+ code << "type sampling_type is record"
174
+ code.indent=4
175
+ zone.registers.each do |reg|
176
+ if reg.sampling
177
+ reg.bits.each do |bit|
178
+ type="std_logic"
179
+ code << "#{reg.name.to_s}_#{bit.name} : #{type};"
180
+ end
181
+ reg.bitfields.each do |bitf|
182
+ min,max=bitf.position.minmax
183
+ nb_bits=(max-min)
184
+ type="std_logic_vector(#{nb_bits} downto 0)"
185
+ code << "#{reg.name.to_s}_#{bitf.name} : #{type};"
186
+ end
187
+
188
+ end
189
+ end
190
+ code.indent=2
191
+ code << "end record;"
192
+
193
+ code.indent=0
194
+ code.newline
195
+ code << "end package;"
196
+ @vhdl_files << vhdl="#{@dest_dir}/#{zone.name}_regif_pkg.vhd"
197
+ code.save_as(vhdl,verbose=false)
198
+ end
199
+
200
+ def gen_ip_regif zone
201
+ filename=zone.name.to_s+"_regif.vhd"
202
+ puts " - code for IP register interface "+(" "+filename).rjust(26,'.')
203
+ code=Code.new
204
+ code << header
205
+ code << "use work.#{zone.name}_pkg.all;"
206
+ code.newline
207
+ code << "entity #{zone.name}_reg is"
208
+ code.indent=2
209
+ code << "port("
210
+ code.indent=4
211
+ code << clock_and_reset
212
+ code << "ce : in std_logic;"
213
+ code << "we : in std_logic;"
214
+ code << "address : in unsigned(#{@addr_size-1} downto 0);"
215
+ code << "datain : in std_logic_vector(#{@data_size-1} downto 0);"
216
+ code << "dataout : out std_logic_vector(#{@data_size-1} downto 0);"
217
+ code << "registers : out registers_type;"
218
+ code << "sampling : in sampling_type;"
219
+ code.indent=2
220
+ code << ");"
221
+ code.indent=0
222
+ code << "end #{zone.name}_reg;"
223
+ code.newline
224
+ code << "architecture RTL of #{zone.name}_reg is"
225
+ code.newline
226
+ code.indent=2
227
+ code << "--interface"
228
+ code << "signal regs : registers_type;"
229
+ code.newline
230
+ code << "--addresses are declared here to avoid VHDL93 error /locally static/"
231
+ tmp_h={}
232
+ zone.registers.each do |reg|
233
+ addr_vhdl=reg.address.to_i(16)
234
+ addr_vhdl_b2=addr_vhdl.to_s(2).rjust(@addr_size,'0')
235
+ nb_digits_hex=(@addr_size/4.0).ceil
236
+ addr_vhdl=addr_vhdl.to_s.rjust(nb_digits_hex,'0')
237
+ addr_name="ADDR_#{reg.name.upcase}"
238
+ tmp_code=": unsigned(#{@addr_size-1} downto 0) := \"#{addr_vhdl_b2}\";-- 0x#{addr_vhdl};"
239
+ tmp_h[addr_name]=tmp_code
240
+ end
241
+ max_length_addr_name=(tmp_h.keys.max_by{|e|e.size}||[]).size
242
+ #pp tmp_h
243
+
244
+ tmp_h.each do |addr,kode|
245
+ code << "constant #{addr.ljust(max_length_addr_name,' ')} #{kode}"
246
+ end
247
+ code.newline
248
+ code << "--application signals"
249
+ code << declare_application_sampling_signals(zone)
250
+ code.indent=0
251
+ code.newline
252
+ code << "begin"
253
+ code.newline
254
+ code.indent=2
255
+ #---- write process
256
+ code << "write_reg_p : process(reset_n,clk)"
257
+ code << "begin"
258
+ code.indent=4
259
+ code << "if reset_n='0' then"
260
+ code.indent=6
261
+ code << "regs <= REGS_INIT;"
262
+ code.indent=4
263
+ code << "elsif rising_edge(clk) then"
264
+ code.indent=6
265
+ code << "if ce='1' then"
266
+ code.indent=8
267
+ code << "if we='1' then"
268
+ code.indent=10
269
+ code << "case address is"
270
+ code.indent=12
271
+ zone.registers.each do |reg|
272
+ if reg.writable
273
+ code << "when ADDR_#{reg.name.upcase} =>"
274
+ code.indent=14
275
+ code << gen_reg_write(reg)
276
+ code.indent=12
277
+ end
278
+ end
279
+ code << "when others =>"
280
+ code.indent=14
281
+ code << "null;"
282
+ code.indent=12
283
+ code.indent=10
284
+ code << "end case;"
285
+ code.indent=8
286
+ code << "end if;";
287
+ code.indent=6
288
+ code << "else --no bus preemption => sampling or toggle"
289
+ code << "--sampling"
290
+ code.indent=8
291
+ no_sampling=zone.registers.collect{|r| r.sampling}
292
+ no_sampling=zone.registers.collect{|r| r.sampling}.compact.empty?
293
+ if no_sampling
294
+ code << "null; --no_sampling"
295
+ else
296
+ zone.registers.each do |reg|
297
+ code.indent=8
298
+ if reg.sampling
299
+ code << gen_reg_sampling(reg)
300
+ end
301
+ code.indent=6
302
+ end
303
+ end
304
+ code << "--toggling"
305
+ zone.registers.each do |reg|
306
+ code.indent=8
307
+
308
+ toggling_bits=reg.bits.collect{|bit| bit.toggle}.compact
309
+ if toggling_bits
310
+ code << gen_reg_toggle(reg)
311
+ end
312
+ code.indent=6
313
+ end
314
+ code.indent=6
315
+ code << "end if;"
316
+ code.indent=4
317
+ code << "end if;"
318
+ code.indent=2
319
+ code << "end process;"
320
+ #-----END write process
321
+ code.newline
322
+ code << "read_reg_p: process(reset_n,clk)"
323
+ code << "begin"
324
+ code.indent=4
325
+ code << "if reset_n='0' then"
326
+ code.indent=6
327
+ code << "dataout <= (others=>'0');"
328
+ code.indent=4
329
+ code << "elsif rising_edge(clk) then"
330
+ code.indent=6
331
+ # code << "dataout <= (others=>'0');"
332
+ code << "if ce='1' then"
333
+ code.indent=8
334
+ code << "if we='0' then"
335
+ code.indent=10
336
+ code << "dataout <= (others=>'0');"
337
+ code << "case address is"
338
+ code.indent=12
339
+ zone.registers.each do |reg|
340
+ code << gen_reg_read(reg)
341
+ end
342
+ code << "when others=>"
343
+ code.indent=14
344
+ code << "dataout <= (others=>'0');"
345
+ code.indent=12
346
+ code.indent=10
347
+ code << "end case;"
348
+ code.indent=8
349
+ code << "end if;"
350
+ code.indent=6
351
+ code << "end if;"
352
+ code.indent=4
353
+ code << "end if;"
354
+ code.indent=2
355
+ code << "end process;"
356
+ #-----
357
+ code << "registers <= regs;"
358
+ code.indent=0
359
+ code.newline
360
+ #code << gen_vivadohls_instances(zone)
361
+ code << "end RTL;"
362
+ filename="#{@dest_dir}/#{zone.name}_regif.vhd"
363
+ code.save_as filename,verbose=false
364
+ @vhdl_files << filename
365
+ code
366
+ end
367
+
368
+ def gen_ip_entity_arch zone
369
+ filename=zone.name.to_s+".vhd"
370
+ puts " - code for IP "+(" "+filename).rjust(45,'.')
371
+ code=Code.new
372
+ code << header
373
+ code << "use work.#{zone.name}_pkg.all;"
374
+ code.newline
375
+ code << "entity #{zone.name} is"
376
+ code.indent=2
377
+ code << "port("
378
+ code.indent=4
379
+ code << clock_and_reset
380
+ code << "ce : in std_logic;"
381
+ code << "we : in std_logic;"
382
+ code << "address : in unsigned(#{@addr_size-1} downto 0);"
383
+ code << "datain : in std_logic_vector(#{@data_size-1} downto 0);"
384
+ code << "dataout : out std_logic_vector(#{@data_size-1} downto 0);"
385
+ code.indent=2
386
+ code << ");"
387
+ code.indent=0
388
+ code << "end #{zone.name};"
389
+ code.newline
390
+ code << "architecture RTL of #{zone.name} is"
391
+ code.newline
392
+ code.indent=2
393
+ code << "--interface"
394
+ code << "signal regs : registers_type;"
395
+ code << "signal sampling : sampling_type;"
396
+ code.newline
397
+ code.indent=0
398
+ code << "begin"
399
+ code.newline
400
+ code.indent=2
401
+ code << gen_regif_instance(zone)
402
+ code.newline
403
+ code << gen_vivadohls_instances(zone)
404
+ code.newline
405
+ code.indent=0
406
+ code << "end RTL;"
407
+ filename="#{@dest_dir}/#{zone.name}.vhd"
408
+ code.save_as filename,verbose=false
409
+ if @options[:show_code]
410
+ puts code.finalize
411
+ end
412
+ @vhdl_files << filename
413
+ code
414
+ end
415
+
416
+ def writing_process mm
417
+ code=Code.new
418
+ code << "-- writing process"
419
+ code << "write_proc: process(clk)"
420
+ code << "begin"
421
+ code.indent=2
422
+ code << "if reset_n='0' then"
423
+ code.indent=4
424
+ code << "regs <= REGS_INIT;"
425
+ code.indent=2
426
+ code << "elsif rising_edge(clk) then"
427
+ code.indent=4
428
+ code << "if sreset='1' then"
429
+ code.indent=6
430
+ code << "regs <= REGS_INIT;"
431
+ code.indent=4
432
+ code << "else"
433
+ code.indent=6
434
+ code << "if ce='1' and we='1' then "
435
+
436
+ code.indent=8
437
+ code << "case address is "
438
+ code.indent=10
439
+ for zone in mm.zones
440
+ code << "--zone #{zone.name.to_s.upcase.center(40,'-')}"
441
+ for reg in zone.registers
442
+ name="#{zone.name.upcase}_#{reg.name.upcase}"
443
+ code << "when ADDR_#{zone.name.upcase}_#{reg.name.upcase} =>"
444
+ code.indent=12
445
+ code << "reg(#{name}) <= datain;"
446
+ code.indent=10
447
+ end
448
+ end
449
+ code << "when others => null;"
450
+ code.indent=8
451
+ code << "end case;"
452
+ code.indent=6
453
+ code << "end if;"
454
+ code.indent=4
455
+ code << "end if;"
456
+ code.indent=2
457
+ code << "end if;"
458
+ code.indent=0
459
+ code << "end process;"
460
+ code
461
+ end
462
+
463
+ def gen_regif_instance zone
464
+ code=Code.new
465
+ code << "regif_inst : entity work.#{zone.name}_reg"
466
+ code.indent=2
467
+ code << "port map("
468
+ code.indent=4
469
+ code << "reset_n => reset_n,"
470
+ code << "clk => clk,"
471
+ code << "sreset => sreset,"
472
+ code << "ce => ce,"
473
+ code << "we => we,"
474
+ code << "address => address,"
475
+ code << "datain => datain,"
476
+ code << "dataout => dataout,"
477
+ code << "registers => regs,"
478
+ code << "sampling => sampling"
479
+ code.indent=2
480
+ code << ");"
481
+ code.indent=0
482
+ code
483
+ end
484
+
485
+ # in sexp, we can instanciate components
486
+ # WARNING : default clk and reset are hardcoded here !!!!
487
+ def gen_vivadohls_instances zone
488
+ code=Code.new
489
+ if zone.instances.any?
490
+ zone.instances.each do |inst|
491
+ @vhdl_files << inst.name.to_s+".vhd"
492
+ code << "inst_#{inst.name} : entity work.#{inst.name}"
493
+ code.indent=2
494
+ code << "port map("
495
+ code.indent=4
496
+ if @options[:from_vivado_hls]
497
+ code << "ap_clk => clk,"
498
+ else #standard mode
499
+ code << "reset_n => reset_n,"
500
+ code << "clk => clk,"
501
+ end
502
+ inst.mapping.each do |cnx|
503
+ if cnx.formal.is_a? Input
504
+ sig="regs."+cnx.actual.name.to_s+"."+cnx.actual.field.to_s
505
+ else
506
+ sig="sampling.#{cnx.actual.name}_#{cnx.actual.field}"
507
+ end
508
+ code << "#{cnx.formal.name} => #{sig},"
509
+ end
510
+ code.indent=2
511
+ code << ");"
512
+ code.indent=0
513
+ end
514
+ code.newline
515
+ end
516
+
517
+ code
518
+ end
519
+
520
+ def declare_application_sampling_signals zone
521
+ code=Code.new
522
+ tmp_h={}
523
+ zone.registers.each do |reg|
524
+ if reg.sampling
525
+ reg.bits.each do |bit|
526
+ sig_name="#{reg.name}_#{bit.name}"
527
+ tmp_h[sig_name]="std_logic;"
528
+ end
529
+ reg.bitfields.each do |bitfield|
530
+ min,max=bitfield.position.minmax
531
+ nb_bits=(max-min)
532
+ sig_name="#{reg.name}_#{bitfield.name}"
533
+ tmp_h[sig_name]="std_logic_vector(#{nb_bits} downto 0);"
534
+ end
535
+ end
536
+ end
537
+ max_size=(tmp_h.keys.max_by{|e|e.size} || []).size
538
+ tmp_h.each do |signame,decl|
539
+ code << "signal #{signame.ljust(max_size,' ')} : #{decl}"
540
+ end
541
+ code
542
+ end
543
+
544
+ def gen_reg_write reg
545
+ code=Code.new
546
+ reg.bits.each do |bit|
547
+ code << "regs.#{reg.name}.#{bit.name} <= datain(#{bit.position});"
548
+ end
549
+ reg.bitfields.each do |bitfield|
550
+ min,max=bitfield.position.minmax
551
+ code << "regs.#{reg.name}.#{bitfield.name} <= datain(#{max} downto #{min});"
552
+ end
553
+ if code.empty?
554
+ code << "regs.#{reg.name} <= datain;"
555
+ end
556
+ code
557
+ end
558
+
559
+ def gen_reg_read reg
560
+ code=Code.new
561
+ code << "when ADDR_#{reg.name.upcase} =>"
562
+ code.indent=2
563
+ reg.bits.each do |bit|
564
+ code << "dataout(#{bit.position}) <= regs.#{reg.name}.#{bit.name};"
565
+ end
566
+ reg.bitfields.each do |bitfield|
567
+ min,max=bitfield.position.minmax
568
+ code << "dataout(#{max} downto #{min}) <= regs.#{reg.name}.#{bitfield.name};"
569
+ end
570
+ code.indent=0
571
+ code
572
+ end
573
+
574
+ def gen_reg_sampling reg
575
+ code=Code.new
576
+ reg.bits.each do |bit|
577
+ sig="sampling.#{reg.name}_#{bit.name}"
578
+ code << "regs.#{reg.name}.#{bit.name} <= #{sig};"
579
+ end
580
+ reg.bitfields.each do |bitfield|
581
+ sig="sampling.#{reg.name}_#{bitfield.name}"
582
+ code << "regs.#{reg.name}.#{bitfield.name} <= #{sig};"
583
+ end
584
+ code
585
+ end
586
+
587
+ def gen_reg_toggle reg
588
+ code=Code.new
589
+ reg.bits.each do |bit|
590
+ if bit.toggle
591
+ bit_init="'0'"
592
+ code << "regs.#{reg.name}.#{bit.name} <= #{bit_init};"
593
+ end
594
+ end
595
+ reg.bitfields.each do |bitfield|
596
+ if bitfield.toggle
597
+ min,max=bitfield.position.minmax
598
+ bitfield_init="0"*((max-min)+1)
599
+ code << "regs.#{reg.name}.#{bitfield.name} <= \"#{bitfield_init}\";"
600
+ end
601
+ end
602
+ code
603
+ end
604
+
605
+ #================= System stuff ================
606
+ def gen_system mm
607
+ filename="#{@dest_dir}/#{mm.name}.vhd"
608
+ generate_assets
609
+ puts " - code for complete system "+(" "+filename).rjust(32,".")
610
+ code=Code.new
611
+ code << header
612
+ code << "entity #{mm.name} is"
613
+ code.indent=2
614
+ code << "port("
615
+ code.indent=4
616
+ code << "reset_n : in std_logic;"
617
+ code << "clk : in std_logic;"
618
+ code << "rx : in std_logic;"
619
+ code << "tx : out std_logic;"
620
+ code << "leds : out std_logic_vector(15 downto 0)"
621
+ code.indent=2
622
+ code << ");"
623
+ code.indent=0
624
+ code << "end entity;"
625
+ code.newline
626
+
627
+ code << gen_system_architecture(mm)
628
+ puts code.finalize if @options[:show_code]
629
+ code.save_as(filename,verbose=false)
630
+ @vhdl_files << filename
631
+ end
632
+
633
+ def generate_assets
634
+ puts " - code for assets"+"<8 vhdl files>".rjust(42,'.')
635
+ dir=__dir__
636
+ assets_dir=dir+"/../../assets/"
637
+ #vhdl_assets=Dir["#{assets_dir}/*.vhd"]
638
+ vhdl_assets=[
639
+ "mod_m_counter.vhd",
640
+ "fifo.vhd",
641
+ "flag_buf.vhd",
642
+ "uart_rx.vhd",
643
+ "uart_tx.vhd",
644
+ "uart.vhd",
645
+ "slow_ticker.vhd",
646
+ "uart_bus_master.vhd",
647
+ "bram_xilinx.vhd",
648
+ ]
649
+ dest_dir=$working_dir+"/assets"
650
+ if !Dir.exists?(dest_dir)
651
+ FileUtils.mkdir(dest_dir)
652
+ end
653
+ vhdl_assets.each do |file|
654
+ FileUtils.cp(assets_dir+file,dest_dir)
655
+ @vhdl_files << "../assets/"+File.basename(file)
656
+ end
657
+
658
+ end
659
+
660
+ def gen_synthesis_script
661
+ dest_dir=$working_dir #+"/synthesis"
662
+ filename="#{dest_dir}/synthesis.tcl"
663
+ if !Dir.exists?(dest_dir)
664
+ FileUtils.mkdir(dest_dir)
665
+ end
666
+ code=Code.new
667
+ code << "# =====FPGA device : Artix7 in Nexys4DDR"
668
+ code << "set partname \"xc7a100tcsg324-1\""
669
+ code << "set xdc_constraints \"./Nexys4DDR_Master.xdc\""
670
+ code.newline
671
+ code << "# =====Define output directory"
672
+ code << "set outputDir ./SYNTH_OUTPUTS"
673
+ code << "file mkdir $outputDir"
674
+ code.newline
675
+ code << "# =====Setup design sources and constraints"
676
+ code << "read_vhdl [ glob ../assets/*.vhd]"
677
+ code << "read_vhdl [ glob ../src/*.vhd]"
678
+ code << "read_xdc $xdc_constraints"
679
+ code.newline
680
+ code << "synth_design -top #{@model_name} -part $partname"
681
+ code << "write_checkpoint -force $outputDir/post_synth.dcp"
682
+ code << "report_timing_summary -file $outputDir/post_synth_timing_summary.rpt"
683
+ code << "report_utilization -file $outputDir/post_synth_util.rpt"
684
+ code << "opt_design"
685
+ code << "# reportCriticalPaths $outputDir/post_opt_critpath_report.csv"
686
+ code << "place_design"
687
+ code << "# report_clock_utilization -file $outputDir/clock_util.rpt"
688
+ code << "#"
689
+ code << "write_checkpoint -force $outputDir/post_place.dcp"
690
+ code << "report_utilization -file $outputDir/post_place_util.rpt"
691
+ code << "report_timing_summary -file $outputDir/post_place_timing_summary.rpt"
692
+ code.newline
693
+ code << "# ====== run the router, write the post-route design checkpoint, report the routing"
694
+ code << "# status, report timing, power, and DRC, and finally save the Verilog netlist."
695
+ code << "#"
696
+ code << "route_design"
697
+ code << "write_checkpoint -force $outputDir/post_route.dcp"
698
+ code << "report_route_status -file $outputDir/post_route_status.rpt"
699
+ code << "report_timing_summary -file $outputDir/post_route_timing_summary.rpt"
700
+ code << "report_power -file $outputDir/post_route_power.rpt"
701
+ code << "report_drc -file $outputDir/post_imp_drc.rpt"
702
+ code << "# write_verilog -force $outputDir/cpu_impl_netlist.v -mode timesim -sdf_anno true"
703
+ code.newline
704
+ code << "# ====== generate a bitstream"
705
+ code << "write_bitstream -force $outputDir/top.bit"
706
+ code << "exit"
707
+ code.save_as(filename,verbose=false)
708
+
709
+ dir=__dir__
710
+ assets_dir=dir+"/../../assets/"
711
+ file="Nexys4DDR_Master.xdc"
712
+ FileUtils.cp(assets_dir+file,dest_dir)
713
+ end
714
+
715
+ def gen_compile_script
716
+ dest_dir=$working_dir+"/src"
717
+ if !Dir.exists?(dest_dir)
718
+ FileUtils.mkdir(dest_dir)
719
+ end
720
+ File.open("#{dest_dir}/compile.x",'w') do |f|
721
+ f.puts "echo \"=> cleaning\""
722
+ f.puts "rm -rf *.o"
723
+ f.puts
724
+ f.puts "echo \"=> analyzing VHDL files\""
725
+ @vhdl_files.each do |vhdl|
726
+ f.puts "echo \"=> analyzing #{vhdl}\""
727
+ f.puts "ghdl -a #{vhdl}"
728
+ end
729
+ f.puts "echo \"=> elaboration\""
730
+ top=File.basename(@vhdl_files.last,".vhd")
731
+ f.puts "ghdl -e #{top}"
732
+ #f.puts "echo \"=> running simulation
733
+ #f.puts "ghdl -r #{top} --wave=#{top}.ghw"
734
+ #f.puts "echo \"=> viewing waveforms\""
735
+ #f.puts "gtkwave #{top}.ghw #{top}.sav"
736
+ puts "=> compile script"+" compile.x".rjust(45,'.')
737
+ end
738
+ end
739
+
740
+ def gen_system_architecture mm
741
+ code=Code.new
742
+ code << "architecture rtl of #{mm.name} is"
743
+ code.indent=2
744
+ code << "-- bus"
745
+ code << "signal ce : std_logic;"
746
+ code << "signal we : std_logic;"
747
+ code << "signal address : unsigned(#{@addr_size-1} downto 0);"
748
+ code << "signal datain : std_logic_vector(#{@data_size-1} downto 0);"
749
+ code << "signal dataout : std_logic_vector(#{@data_size-1} downto 0);"
750
+ code << "--"
751
+ code << "signal sreset : std_logic;"
752
+ code << "-- debug"
753
+ code << "signal slow_clk,slow_tick : std_logic;"
754
+ code.newline
755
+ code.indent=0
756
+ code << "begin"
757
+ code.indent=2
758
+ code.newline
759
+ code << instance_of_uart_bus_master if @options[:include_uart]
760
+ code << ip_instanciations(mm)
761
+ code << debug_stuff
762
+ code.newline
763
+ code.indent=0
764
+ code << "end;"
765
+ code
766
+ end
767
+
768
+ def instance_of_uart_bus_master
769
+ code=Code.new
770
+ code << "-- ============== UART as Master of bus !========="
771
+ code << "uart_master : entity work.uart_bus_master"
772
+ code << " generic map (DATA_WIDTH => #{@data_size})"
773
+ code << " port map("
774
+ code << " reset_n => reset_n,"
775
+ code << " clk => clk,"
776
+ code << " -- UART --"
777
+ code << " rx => rx,"
778
+ code << " tx => tx,"
779
+ code << " -- Bus --"
780
+ code << " ce => ce,"
781
+ code << " we => we,"
782
+ code << " address => address,"
783
+ code << " datain => datain,"
784
+ code << " dataout => dataout"
785
+ code << " );"
786
+ code
787
+ end
788
+
789
+ def ip_instanciations mm
790
+ code=Code.new
791
+ mm.zones.each do |zone|
792
+ code.newline
793
+ code << "-- "+zone.name.to_s.center(46,"=")
794
+ code << "inst_#{zone.name} : entity work.#{zone.name}"
795
+ code.indent=2
796
+ code << "port map ("
797
+ code.indent=4
798
+ code << "reset_n => reset_n,"
799
+ code << "clk => clk,"
800
+ code << "sreset => sreset,"
801
+ code << "ce => ce,"
802
+ code << "we => we,"
803
+ code << "address => address,"
804
+ code << "datain => datain,"
805
+ code << "dataout => dataout"
806
+ code.indent=2
807
+ code << ");"
808
+ code.indent=0
809
+ end
810
+ code
811
+ end
812
+
813
+ def debug_stuff
814
+ code=Code.new
815
+ code.newline
816
+ code << "-- =================== DEBUG ===================="
817
+ code << "ticker : entity work.slow_ticker(rtl)"
818
+ code << " port map("
819
+ code << " reset_n => reset_n,"
820
+ code << " fast_clk => clk,"
821
+ code << " slow_clk => slow_clk,"
822
+ code << " slow_tick => slow_tick"
823
+ code << " );"
824
+ code << "leds <= \"000000000000000\" & slow_clk;"
825
+ code
826
+ end
827
+
828
+
829
+
830
+ def gen_xdc zone
831
+ dest_dir=$working_dir #+"/synthesis"
832
+ if !Dir.exists?(dest_dir)
833
+ FileUtils.mkdir(dest_dir)
834
+ end
835
+ puts "==> generating XDC constraints file for Artix7"
836
+ code=Code.new
837
+ code
838
+ code << "## clock signal"
839
+ code << "set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_35"
840
+ code << "create_clock -add -name clk -period 10.00 -waveform {0 5} [get_ports {clk}];"
841
+ code.newline
842
+ pinout_str=%Q(
843
+ J15,L16,M13,R15,R17,T18,U18,R13,T8,U8,R16,T13,H6,U12,U11,V10,H17,K15,J13,N14,R18,V17,U17,U16,\
844
+ V16,T15,U14,T16,V15,V14,V12,V11,R12,M16,N15,G14,R11,N16,T10,R10,K16,K13,P15,T11,L18,H15,J17,J18,\
845
+ T9,J14,P14,T14,K2,U13,C12,N17,M18,P17,M17,P18,C17,D18,E18,G17,D17,E17,F18,G18,D14,F16,G16,H14,E16,\
846
+ F13,G13,H16,K1,F6,J2,G6,E7,J3,J4,E6,H4,H1,G1,G3,H2,G4,G2,F3,A14,A13,A16,A15,B17,B16,A18,B18,A3,B4,\
847
+ C5,A4,C6,A5,B6,A6,B7,C7,D7,D8,B11,B12,E2,A1,B1,C1,C2,E1,F1,D2,E15,F14,F15,D15,B13,C16,C14,C15,D13,\
848
+ B14,J5,H5,F5,A11,D12,C4,D4,D3,E5,F4,B2,C9,A9,B3,D9,C10,C11,D10,B9,A10,A8,D5,B8,K17,K18,L14,M14,L13\
849
+ )
850
+ pinout=pinout_str.strip.split(',').map(&:strip)
851
+ ports=[
852
+ {:reset_n => 1},
853
+ {:ce => 1},
854
+ {:we => 1},
855
+ {:address => @addr_size},
856
+ {:datain => @data_size},
857
+ {:dataout => @data_size},
858
+ ]
859
+ mapping={}
860
+ ports.each do |porth|
861
+ name,size=porth.to_a.first
862
+ for i in 0..size-1
863
+ name=name.to_s
864
+ pname=(size==1)? name : name+"[#{i}]"
865
+ mapping[pname]=pinout.shift
866
+ code << "set_property -dict { PACKAGE_PIN #{mapping[pname]} IOSTANDARD LVCMOS33 } [get_ports { #{pname} }];"
867
+ end
868
+ end
869
+
870
+ file=code.save_as("#{dest_dir}/artix7.xdc",false)
871
+ puts " - mapped #{mapping.size} ports to pinout .................... #{file}"
872
+ end
873
+
874
+ def gen_ruby_sw_if mm
875
+ dest_dir=$working_dir+"/esw"
876
+ if !Dir.exists?(dest_dir)
877
+ FileUtils.mkdir(dest_dir)
878
+ end
879
+ words_in_name=mm.name.to_s.split("_")
880
+ filename=words_in_name.map(&:downcase).join("_")+".rb"
881
+ puts "=> generating Ruby sw"+(" "+filename).rjust(41,'.')
882
+ code=Code.new
883
+ code << "require 'pp'"
884
+ code << "require 'rubyserial'"
885
+ code.newline
886
+ classname=words_in_name.map(&:capitalize).join
887
+ code << "class #{classname}"
888
+ code.indent=2
889
+ code.newline
890
+ code << "def initialize"
891
+ code.indent=4
892
+ code << "@serial=Serial.new '/dev/ttyUSB1',19200,8"
893
+ code.indent=2
894
+ code << "end"
895
+ code.newline
896
+ code << "def to_byte_array num,bytes"
897
+ code.indent=2
898
+ code << "(bytes - 1).downto(0).collect{|byte|((num >> (byte * 8)) & 0xFF)}"
899
+ code.indent=0
900
+ code << "end"
901
+ code.newline
902
+ code << "def byte_array_to_int ary"
903
+ code << " val=0"
904
+ code << " nb_bytes=ary.size"
905
+ code << " ary.each_with_index{|b,i|"
906
+ code << " pow256=256**(nb_bytes-1-i)"
907
+ code << " val+=b*pow256"
908
+ code << " }"
909
+ code << " val"
910
+ code << "end"
911
+ code.newline
912
+ code << "def read_reg address"
913
+ code.indent=4
914
+ code << "# b4 b3 b2 b1 b0"
915
+ code << "@serial.write [ 0x10].pack(\"C*\")"
916
+ code << "@serial.write [ address].pack(\"C*\")"
917
+ code << "bytes=[]"
918
+ code << "for i in 0..3"
919
+ code.indent=6
920
+ code << "byte=nil"
921
+ code << "byte=@serial.getbyte until byte"
922
+ code << "bytes << byte"
923
+ code.indent=4
924
+ code << "end"
925
+ code << "bytes # array of bytes"
926
+ code << "byte_array_to_int(bytes)"
927
+ code.indent=2
928
+ code << "end"
929
+ code.newline
930
+ code << "def write_reg addr,data"
931
+ code.indent=4
932
+ code << "# b4 b3 b2 b1 b0"
933
+ code << "@serial.write [ 0x11].pack(\"C*\")"
934
+ code << "@serial.write [ addr].pack(\"C*\")"
935
+ code << "bytes=to_byte_array(data,4)"
936
+ code << "@serial.write bytes.unpack(\"C*\")"
937
+ code.indent=2
938
+ code << "end"
939
+ code.newline
940
+ mm.zones.each do |zone|
941
+ code << "# === #{zone.name} ==="
942
+ zone.registers.each do |reg|
943
+ code << "def #{zone.name}_#{reg.name}"
944
+ code.indent=4
945
+ code << "read_reg #{reg.address}"
946
+ code.indent=2
947
+ code << "end"
948
+ code.newline
949
+ code << "def #{zone.name}_#{reg.name}=val"
950
+ code.indent=4
951
+ code << "write_reg #{reg.address},val"
952
+ code.indent=2
953
+ code << "end"
954
+ code.newline
955
+ end
956
+ end
957
+ code.indent=0
958
+ code << "end"
959
+ code.newline
960
+ code << "if $PROGRAM_NAME==__FILE__"
961
+ code.indent=2
962
+ code << "#{classname.downcase}=#{classname}.new"
963
+ code << "#{classname}."
964
+ code.indent=0
965
+ code << "end"
966
+ puts code.finalize
967
+ filename="#{dest_dir}/#{filename}"
968
+ code.save_as(filename,verbose=false)
969
+ end
970
+
971
+ end
972
+
973
+ end