reggae_eda 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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