ruby_rtl 0.0.1

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,564 @@
1
+ require_relative 'code'
2
+
3
+ module RubyRTL
4
+
5
+ class VhdlGenerator < Visitor
6
+
7
+ attr_accessor :entity
8
+ attr_accessor :ios
9
+ attr_accessor :archi_elements
10
+
11
+ def generate circuit
12
+ puts "[+] VHDL code generation"
13
+
14
+ code=Code.new
15
+ @circuit=circuit
16
+ @ios,@archi_elements=[],[]
17
+ @sigs={}
18
+ @fsm_defs=[]
19
+ @sequentials=[]
20
+ @states={}
21
+ gen_ruby_rtl_type_package
22
+ root=circuit.ast
23
+ return unless root
24
+
25
+ gen_type_package()
26
+ #root.ios.each{|io| io.accept(self)}
27
+ @typedecls=root.decls.select{|decl| decl.is_a? TypeDecl}
28
+ non_typedecls=root.decls.reject{|decl| decl.is_a? TypeDecl}
29
+ non_typedecls.each{|decl| decl.accept(self)}
30
+
31
+ code << gen_ieee_header()
32
+ code << gen_ruby_rtl_package_call()
33
+ code << gen_package_call(circuit)
34
+ code << gen_entity(circuit)
35
+ code.newline
36
+ code << gen_archi(circuit)
37
+ code=clean_vhdl(code)
38
+ puts code.finalize
39
+ code.save_as "#{name=circuit.name.downcase}.vhd"
40
+ end
41
+
42
+ def clean_vhdl(code)
43
+ txt=code.finalize
44
+ txt.gsub! /;(\s*)\)/,")"
45
+ txt.gsub! /,(\s*)\)/,")"
46
+ (code=Code.new) << txt
47
+ return code
48
+ end
49
+
50
+ def gen_ruby_rtl_package_call
51
+ code=Code.new
52
+ code << "library ruby_rtl;"
53
+ code << "use ruby_rtl.ruby_rtl_package.all;"
54
+ code.newline
55
+ code
56
+ end
57
+
58
+ def gen_ruby_rtl_type_package
59
+ code=Code.new
60
+ code << gen_ieee_header
61
+ code << "package ruby_rtl_type_package is "
62
+ code.newline
63
+ code.indent=2
64
+ $typedefs.each do |name,definition|
65
+ case definition
66
+ when RecordType
67
+ code << definition_s=definition.accept(self,name)
68
+ else
69
+ definition_s=definition.accept(self)
70
+ case definition
71
+ when IntType,UIntType,BitType
72
+ header="sub"
73
+ else
74
+ header=""
75
+ end
76
+ code << "#{header}type #{name} is #{definition_s};"
77
+ end
78
+ code.newline
79
+ end
80
+ code.indent=0
81
+ code << "end package;"
82
+ code.save_as "ruby_rtl_type_package.vhd"
83
+ end
84
+
85
+ def gen_type_package
86
+ name=@circuit.name.downcase
87
+ return unless ast=@circuit.ast
88
+ typdecls=ast.decls.select{|decl| decl.is_a?(TypeDecl)}
89
+ package=Code.new
90
+ package << gen_ieee_header
91
+ package << "package #{name}_package is"
92
+ package.indent=2
93
+ package.newline
94
+ typdecls.each do |decl|
95
+ definition=decl.definition.accept(self,decl.name)
96
+ package << definition
97
+ package.newline
98
+ end
99
+ package.indent=0
100
+ package << "end package;"
101
+ puts package.finalize
102
+ package.save_as "#{name}_package.vhd"
103
+ end
104
+
105
+ def gen_ieee_header
106
+ header=Code.new
107
+ header << "-- automatically generated by RubyRTL"
108
+ header << "library ieee;"
109
+ header << "use ieee.std_logic_1164.all;"
110
+ header << "use ieee.numeric_std.all;"
111
+ header.newline
112
+ header
113
+ end
114
+
115
+ def gen_package_call circuit
116
+ name=circuit.name.downcase
117
+ code=Code.new
118
+ code << "library #{name}_lib;"
119
+ code << "use #{name}_lib.#{name}_package.all;"
120
+ code.newline
121
+ code
122
+ end
123
+
124
+ def gen_entity circuit
125
+ entity=Code.new
126
+ entity << "entity #{name=circuit.name.downcase}_c is"
127
+ entity.indent=2
128
+ entity << "port ("
129
+ entity.indent=4
130
+ if circuit.has_sequential_statements
131
+ entity << "reset_n : in std_logic;"
132
+ entity << "clk : in std_logic;"
133
+ end
134
+ ios.each{|io| entity << io}
135
+ entity.indent=2
136
+ entity << ");"
137
+ entity.indent=0
138
+ entity << "end #{name}_c;"
139
+ end
140
+
141
+ def gen_archi circuit
142
+ body=circuit.ast.body
143
+ archi_elements=body.stmts.collect{|stmt| stmt.accept(self)}
144
+ archi=Code.new
145
+ archi << "architecture rtl of #{circuit.name.downcase}_c is"
146
+ archi.indent=2
147
+ @fsm_defs.each do |decl|
148
+ archi << decl
149
+ end
150
+ @typedecls.each do |typedecl|
151
+ archi << typedecl.accept(self)
152
+ end
153
+ @sigs.each do |sig,name|
154
+ archi << "signal #{name} : #{sig.type.accept(self)};"
155
+ end
156
+ archi.indent=0
157
+ archi << "begin"
158
+ archi.indent=2
159
+ archi.newline
160
+ archi_elements.each do |element|
161
+ archi << element
162
+ archi.newline
163
+ end
164
+ archi.indent=0
165
+ archi.newline
166
+ archi << "end rtl;"
167
+ archi
168
+ end
169
+
170
+ def visitComment comment,args=nil
171
+ "-- #{comment.str}"
172
+ end
173
+
174
+ def visitInput input,args=nil
175
+ name=@sigs[input] || input.name
176
+ adapt_name(name,args)
177
+ end
178
+
179
+ def visitOutput output,args=nil
180
+ name=@sigs[output] || output.name
181
+ adapt_name(name,args)
182
+ end
183
+
184
+ def visitSigDecl decl,args=nil
185
+ name=decl.name
186
+ name.sub!(/@/,'')
187
+ type=decl.sig.type.accept(self)
188
+ case decl.sig
189
+ when Input
190
+ ios << "#{name} : in #{type};"
191
+ when Output
192
+ ios << "#{name} : out #{type};"
193
+ when Sig
194
+ @sigs.merge!(decl.sig => name)
195
+ else
196
+ raise "ERROR : visitSigDecl : neither input ou output"
197
+ end
198
+ end
199
+
200
+ def visitSig sig,args=nil
201
+ name=sig.name
202
+ adapt_name(name,args)
203
+ end
204
+
205
+ def adapt_name name,kind
206
+ name=name.to_s
207
+ case kind
208
+ when :comb
209
+ name+="_c"
210
+ when :reg
211
+ name+="_r"
212
+ when :var
213
+ name+="_v"
214
+ end
215
+ name
216
+ end
217
+
218
+ def visitTypeDecl decl,args=nil
219
+ definition=decl.definition.accept(self,decl.name)
220
+ "type #{decl.name} is #{definition};"
221
+ end
222
+
223
+ def visitEnumType enum_type,name
224
+ if name
225
+ "type #{name} is (#{enum_type.items.join(',')});"
226
+ else
227
+ idx=$typedefs.values.index(enum_type)
228
+ $typedefs.keys[idx]
229
+ end
230
+ end
231
+
232
+ def visitRecordType rectype,name
233
+ if name
234
+ code=Code.new
235
+ code << "record"
236
+ code.indent=2
237
+ rectype.hash.each do |name,type|
238
+ type_s=type.accept(self)
239
+ code << "#{name} : #{type_s};"
240
+ end
241
+ code.indent=0
242
+ code << "end record;"
243
+ code
244
+ else
245
+ idx=$typedefs.values.index(rectype)
246
+ $typedefs.keys[idx]
247
+ end
248
+ end
249
+
250
+ def visitMemoryType memtype,name
251
+ if name
252
+ range="0 to #{memtype.size-1}"
253
+ type=memtype.type.accept(self)
254
+ "array(#{range}) of #{type}"
255
+ else
256
+ idx=$typedefs.values.index(memtype)
257
+ $typedefs.keys[idx]
258
+ end
259
+ end
260
+ # ====== body stuff ========
261
+ # === statements ===
262
+ def visitAssign assign,args=nil
263
+ lhs=assign.lhs.accept(self)
264
+ rhs=assign.rhs.accept(self)
265
+ "#{lhs} <= #{rhs};"
266
+ end
267
+
268
+ def visitCompDecl comp_decl,args=nil
269
+ comp=comp_decl.comp
270
+ instance_name=comp_decl.name
271
+ sig_decls=comp.ast.decls.select{|node| node.is_a? SigDecl}
272
+ inputs =sig_decls.select{|decl| decl.sig.is_a? Input}.map(&:sig)
273
+ outputs=sig_decls.select{|decl| decl.sig.is_a? Output}.map(&:sig)
274
+
275
+ instanciation=Code.new
276
+ instanciation << "#{instance_name} : entity work.#{comp.name}_c"
277
+ instanciation.indent=2
278
+ instanciation << "port map("
279
+ instanciation.indent=4
280
+ inputs.each do |input|
281
+ actual_sig_name="#{instance_name}_#{input.name}"
282
+ @sigs.merge!({input => actual_sig_name})
283
+ instanciation << "#{input.name} => #{actual_sig_name},"
284
+ end
285
+ outputs.each do |output|
286
+ actual_sig_name="#{instance_name}_#{output.name}"
287
+ @sigs.merge!(output => actual_sig_name)
288
+ instanciation << "#{output.name} => #{actual_sig_name},"
289
+ end
290
+ instanciation.indent=2
291
+ instanciation << ");"
292
+ instanciation.indent=0
293
+ instanciation.newline
294
+ instanciation
295
+ end
296
+
297
+ def visitCombinatorial comb,args=nil
298
+ code=Code.new
299
+ label=comb.label
300
+ code << "#{label} : process(all) --VHDL'08"
301
+ code << "begin"
302
+ code.indent=2
303
+ code << comb.body.accept(self)
304
+ code.indent=0
305
+ code << "end process;"
306
+ code
307
+ end
308
+
309
+ def visitSequential sequential,args=nil
310
+ code=Code.new
311
+ label=sequential.label
312
+ code << "#{label} : process(clk)"
313
+ code << "begin"
314
+ code.indent=2
315
+ code << "if rising_edge(clk) then"
316
+ code.indent=4
317
+ code << sequential.body.accept(self)
318
+ code.indent=2
319
+ code << "end if;"
320
+ code.indent=0
321
+ code << "end process;"
322
+ code
323
+ end
324
+
325
+ # statement
326
+ def visitBody body,args=nil
327
+ code=Code.new
328
+ body.stmts.each{|stmt| code << stmt.accept(self)}
329
+ code
330
+ end
331
+
332
+ def visitIf if_,args=nil
333
+ cond=if_.cond.accept(self)
334
+ if_body=if_.body.accept(self)
335
+ code=Code.new
336
+ code << "if #{cond} then"
337
+ code.indent=2
338
+ code << if_body
339
+ code.indent=0
340
+ if_.elsifs.each{|elsif_|
341
+ code << elsif_.accept(self)
342
+ }
343
+ code << if_.else.accept(self) if if_.else
344
+ code << "end if;"
345
+ code
346
+ end
347
+
348
+ def visitElsif elsif_,args=nil
349
+ cond=elsif_.cond.accept(self)
350
+ body=elsif_.body.accept(self)
351
+ code=Code.new
352
+ code << "elsif #{cond} then"
353
+ code.indent=2
354
+ code << body
355
+ code.indent=0
356
+ code
357
+ end
358
+
359
+ def visitElse else_,args=nil
360
+ body=else_.body.accept(self)
361
+ code=Code.new
362
+ code << "else "
363
+ code.indent=2
364
+ code << body
365
+ code.indent=0
366
+ code
367
+ end
368
+ # case / switch
369
+ def visitCase case_,args=nil
370
+ cond=case_.cond.accept(self)
371
+ code=Code.new
372
+ code << "case #{cond} is"
373
+ code.indent=2
374
+ code << case_.body.accept(self)
375
+ code.indent=0
376
+ code << "end case;"
377
+ code
378
+ end
379
+
380
+ def visitWhen when_,args=nil
381
+ unless (value=when_.value).is_a? Symbol
382
+ value=when_.value.accept(self)
383
+ end
384
+ code=Code.new
385
+ code << "when #{value} =>"
386
+ code.indent=2
387
+ code << when_.body.accept(self)
388
+ code.indent=0
389
+ code
390
+ end
391
+
392
+ # === FSM ===
393
+ def visitFsm fsm,args=nil
394
+ @fsm=fsm
395
+ state_names=fsm.states.map{|state| state.name}.join(",")
396
+ @fsm_defs << "type #{fsm.name}_state_t is (#{state_names});"
397
+ @fsm_defs << "signal #{fsm.name}_state : #{fsm.name}_state_t;"
398
+ body=Code.new
399
+ body << "#{fsm.name}_update : process(reset_n,clk)"
400
+ body << "begin"
401
+ body.indent=2
402
+ body << "if reset_n='0' then"
403
+ body.indent=4
404
+ body << "#{fsm.name}_state <= #{fsm.states.first.name};"
405
+ fsm.assignments.each do |assign|
406
+ lhs=assign.lhs.accept(self)
407
+ rhs=default_init(assign.lhs.type)
408
+ body << "#{lhs} <= #{rhs};"
409
+ end
410
+ body.indent=2
411
+ body << "elsif rising_edge(clk) then"
412
+ body.indent=4
413
+ body << "if sreset='1' then"
414
+ body.indent=6
415
+ body << "#{fsm.name}_state <= #{fsm.states.first.name};"
416
+ fsm.assignments.each do |assign|
417
+ lhs=assign.lhs.accept(self)
418
+ rhs=default_init(assign.lhs.type)
419
+ body << "#{lhs} <= #{rhs};"
420
+ end
421
+ body.indent=4
422
+ body << "else "
423
+ body.indent=6
424
+ body << state_cases(fsm)
425
+ body.indent=4
426
+ body << "end if;"
427
+ body.indent=2
428
+ body << "end if;"
429
+ body.indent=0
430
+ body << "end process;"
431
+ body
432
+ end
433
+
434
+ def state_cases fsm
435
+ code=Code.new
436
+ code << "case #{fsm.name}_state is"
437
+ code.indent=2
438
+ fsm.states.each do |state|
439
+ code << "when #{state.name} =>"
440
+ code.indent=4
441
+ code << state_body(state)
442
+ code.indent=2
443
+ end
444
+ code << "when others =>"
445
+ code << " null;"
446
+ code.indent=0
447
+ code << "end case;"
448
+ code
449
+ end
450
+
451
+ def state_body state
452
+ code=Code.new
453
+ state.body.each{|stmt| code << stmt.accept(self)}
454
+ code
455
+ end
456
+
457
+ def visitState state,args=nil
458
+ code << "when #{state.name}"
459
+ code
460
+ end
461
+
462
+ def visitNext next_state,args=nil
463
+ "#{@fsm.name}_state <= #{next_state.name};"
464
+ end
465
+
466
+ # === expressions ===
467
+ VHDL_OP={
468
+ "&" => "and",
469
+ "|" => "or",
470
+ "^" => "xor",
471
+ "=="=> "=",
472
+ "%" => "mod",
473
+ "!=" => "/="
474
+ }
475
+ def visitBinary bin,args=nil
476
+ lhs=bin.lhs.accept(self)
477
+ op=VHDL_OP[bin.op] || bin.op
478
+ rhs=bin.rhs.accept(self)
479
+ "(#{lhs} #{op} #{rhs})"
480
+ end
481
+
482
+ def visitFuncCall func,args=nil
483
+ name=func.name
484
+ argus=func.args.map{|arg| arg.accept(self)}.join(',')
485
+ "#{name}(#{argus})"
486
+ end
487
+
488
+ def visitIndexed indexed,args=nil
489
+ lhs=indexed.lhs.accept(self)
490
+ rhs=indexed.rhs.accept(self)
491
+ case indexed.rhs
492
+ when RUIntLit
493
+ else
494
+ conv_int_start="to_integer("
495
+ conv_int_end =")"
496
+ end
497
+ puts indexed.rhs
498
+ "#{lhs}(#{conv_int_start}#{rhs}#{conv_int_end})"
499
+ end
500
+
501
+ # === types
502
+ def visitBitType bit,args=nil
503
+ "std_logic"
504
+ end
505
+
506
+ def visitBitVectorType bv,args=nil
507
+ range="#{bv.bitwidth-1} downto 0"
508
+ "std_logic_vector(#{range})"
509
+ end
510
+
511
+ def visitUIntType uint,args=nil
512
+ range="#{uint.bitwidth-1} downto 0"
513
+ "unsigned(#{range})"
514
+ end
515
+
516
+ def visitIntType int,args=nil
517
+ range="#{int.bitwidth-1} downto 0"
518
+ "signed(#{range})"
519
+ end
520
+
521
+
522
+
523
+ def default_init type
524
+ case type
525
+ when BitType
526
+ return "'0'"
527
+ when BitVectorType
528
+ return "(others=>'0')"
529
+ when UintType
530
+ return "to_unsigned(0,#{type.bitwidth})"
531
+ when IntType
532
+ return "to_signed(0,#{type.bitwidth})"
533
+ else
534
+ raise "Cannot provide default init value for type #{type}"
535
+ end
536
+ ret
537
+ end
538
+
539
+ # === literals
540
+ def visitIntLit int,args=nil
541
+ "to_signed(#{int.val},#{int.type.bitwidth})"
542
+ end
543
+
544
+ def visitUIntLit uint,args=nil
545
+ "to_unsigned(#{uint.val},#{uint.type.bitwidth})"
546
+ end
547
+
548
+ def visitBitLit bit_lit,args=nil
549
+ "'#{bit_lit.val}'"
550
+ end
551
+
552
+ def visitBitVectorLit bit_lit,args=nil
553
+ "\"#{bit_lit.val}\""
554
+ end
555
+
556
+ def visitRIntLit lit,args=nil
557
+ lit.val
558
+ end
559
+
560
+ def visitRUIntLit lit,args=nil
561
+ lit.val
562
+ end
563
+ end
564
+ end
@@ -0,0 +1,163 @@
1
+
2
+ module RubyRTL
3
+ class Visitor
4
+
5
+ def visit circuit
6
+ circuit.ast.each{|node| node.accept(self)}
7
+ end
8
+
9
+ def visitComment node,args=nil
10
+ end
11
+
12
+ def visitSig node,args=nil
13
+ end
14
+
15
+ def visitPort node,args=nil
16
+ end
17
+
18
+ def visitInput node,args=nil
19
+ end
20
+
21
+ def visitOutput node,args=nil
22
+ end
23
+
24
+ def visitTypeDecl node,args=nil
25
+ end
26
+
27
+ def visitSigDecl node,args=nil
28
+ end
29
+
30
+ def visitCompDecl node,args=nil
31
+ end
32
+
33
+ def visitCircuitPart node,args=nil
34
+ end
35
+
36
+ def visitCombinatorial comb,args=nil
37
+ puts comb.body.class
38
+ comb.body.accept(self)
39
+ end
40
+
41
+ def visitSequential seq,args=nil
42
+ puts seq.body.class
43
+ seq.body.accept(self)
44
+ end
45
+
46
+ # === statements
47
+ def visitBody body,args=nil
48
+ body.each{|stmt| stmt.accept(self,args)}
49
+ end
50
+
51
+ def visitAssign node,args=nil
52
+ node.lhs.accept(self)
53
+ node.rhs.accept(self)
54
+ end
55
+
56
+ def visitIf node,args=nil
57
+ node.cond.accept(self)
58
+ node.body.accept(self)
59
+ node.elsifs.each{|elsif_| elsif_.accept(self)}
60
+ node.else.accept(self) if node.else
61
+ end
62
+
63
+ def visitElse else_,args=nil
64
+ else_.body.accept(self)
65
+ end
66
+
67
+ def visitElsif elsif_,args=nil
68
+ elsif_.cond.accept(self)
69
+ elsif_.body.accept(self)
70
+ end
71
+
72
+ def visitCase case_,args=nil
73
+ case_.cond.accept(self)
74
+ case_.body.accept(self)
75
+ end
76
+
77
+ def visitWhen when_,args=nil
78
+ when_.value.accept(self) unless when_.value.is_a?(Symbol)
79
+ when_.body.accept(self)
80
+ end
81
+
82
+ # === fsm
83
+ def visitFsm fsm,args=nil
84
+ fsm.body.accept(self)
85
+ end
86
+
87
+ def visitState state,args=nil
88
+ state.body.accept(self)
89
+ end
90
+
91
+ def visitNext node,args=nil
92
+ end
93
+
94
+ # === expr ===
95
+ def visitBinary node,args=nil
96
+ node.lhs.accept(self)
97
+ node.rhs.accept(self)
98
+ end
99
+
100
+ def visitUnary node,args=nil
101
+ node.expr.accept(self)
102
+ end
103
+
104
+ def visitIndexed indexed,args=nil
105
+ lhs=indexed.lhs.accept(self)
106
+ rhs=indexed.rhs.accept(self)
107
+ end
108
+ # === literals ===
109
+ def visitLiteral node,args=nil
110
+ node
111
+ end
112
+
113
+ def visitBitLit node,args=nil
114
+ node
115
+ end
116
+
117
+ def visitIntLit lit,args=nil
118
+ lit
119
+ end
120
+
121
+ def visitUIntLit lit,args=nil
122
+ lit
123
+ end
124
+
125
+ def visitRIntLit lit,args=nil
126
+ lit
127
+ end
128
+
129
+ def visitRUIntLit lit,args=nil
130
+ lit
131
+ end
132
+ # === types ===
133
+ def visitInteger int,args=nil
134
+ int
135
+ end
136
+
137
+ def visitType node,args=nil
138
+ end
139
+
140
+ def visitBitType node,args=nil
141
+ end
142
+
143
+ def visitBitVectorType node,args=nil
144
+ end
145
+
146
+ def visitIntType node,args=nil
147
+ end
148
+
149
+
150
+ def visitUIntType node,args=nil
151
+ end
152
+
153
+ def visitRIntType node,args=nil
154
+ end
155
+
156
+ def visitRUintType node,args=nil
157
+ end
158
+
159
+ def visitRecordType node,args=nil
160
+ end
161
+
162
+ end
163
+ end
data/lib/ruby_rtl.rb ADDED
@@ -0,0 +1,5 @@
1
+ require_relative "ruby_rtl/ast"
2
+ require_relative "ruby_rtl/dsl"
3
+ require_relative "ruby_rtl/compiler"
4
+ require_relative "ruby_rtl/visitor"
5
+ require_relative "ruby_rtl/vhdl_generator"