ruby_rtl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,183 @@
1
+ module RubyRTL
2
+
3
+ class ASTBuilder
4
+
5
+ attr_accessor :ast
6
+
7
+ # no 'initialize' :
8
+ # - @ast is not initialized here
9
+ # - this allows to avoid calling "super" in every circuit.
10
+ #
11
+ # - dont forget the parenthesis !
12
+
13
+ def input *arg
14
+ @ast||=Root.new
15
+ process_sig_decl(:input,*arg)
16
+ end
17
+
18
+ def output *arg
19
+ @ast||=Root.new
20
+ @last=@ast
21
+ process_sig_decl(:output,*arg)
22
+ end
23
+
24
+ # define a type
25
+ def typedef h
26
+ @ast||=Root.new
27
+ @last=@ast
28
+ name_sym,definition=h.first
29
+ @ast.decls << decl=TypeDecl.new(name_sym,definition)
30
+ @last=decl
31
+ $typedefs||={} # Global var !
32
+ $typedefs[name_sym]=definition
33
+ decl
34
+ end
35
+
36
+ def wire *arg
37
+ process_sig_decl(:sig,*arg)
38
+ end
39
+
40
+ alias :signal :wire
41
+
42
+ def comment str
43
+ (@last.comments||=[]) << Comment.new(str)
44
+ end
45
+
46
+ def component name_obj_or_class_h
47
+ comp_name,obj_or_klass=name_obj_or_class_h.first
48
+ comp_name=comp_name.to_sym if comp_name.is_a? String
49
+ case klass=comp=obj_or_klass
50
+ when Class
51
+ comp=klass.new # but no parameters :-(
52
+ end
53
+ cname="@#{comp_name}"
54
+ instance_variable_set(cname,comp)
55
+ self.class.__send__(:attr_accessor, comp_name)
56
+ @ast.body << CompDecl.new(comp_name,comp)
57
+ comp
58
+ end
59
+
60
+ # syntax : ASSIGN( y <= e), instead of ASSIGN(y,e)
61
+ def assign(var_expr_leq)
62
+ @ast||=Root.new
63
+ var,expr=var_expr_leq.lhs,var_expr_leq.rhs
64
+ @ast.body << Assign.new(var,expr)
65
+ end
66
+
67
+ def differential_ast &block
68
+ before=@ast.body.stmts.clone
69
+ instance_eval(&block)
70
+ after=@ast.body.stmts
71
+ diff=after-before
72
+ @ast.body.stmts=before
73
+ return diff
74
+ end
75
+
76
+ def If(cond,&block)
77
+ diff=differential_ast(&block)
78
+ @ast.body << If.new(cond,Body.new(diff))
79
+ end
80
+
81
+ def Elsif(cond,&block)
82
+ diff=differential_ast(&block)
83
+ @ast.body << Elsif.new(cond,Body.new(diff))
84
+ end
85
+
86
+ def Else(&block)
87
+ diff=differential_ast(&block)
88
+ @ast.body << Else.new(Body.new(diff))
89
+ end
90
+
91
+ # here, we need a trick to evaluate the block.
92
+ # we ask the current ast builder object to evaluate
93
+ # the block, in its current context.
94
+ # We then try to find the difference between ast before and after
95
+ # the evaluation.
96
+ def combinatorial(label=nil,&block)
97
+ diff=differential_ast(&block)
98
+ @ast.body << Combinatorial.new(name,Body.new(diff))
99
+ end
100
+
101
+ alias :comb :combinatorial
102
+
103
+ def sequential(label=nil,&block)
104
+ @has_sequential_statements=true
105
+ diff=differential_ast(&block)
106
+ @ast.body << Sequential.new(label,Body.new(diff))
107
+ end
108
+
109
+ alias :seq :sequential
110
+
111
+ def name
112
+ self.class.to_s
113
+ end
114
+ # === fsm stuff
115
+ def fsm name, &block
116
+ @has_sequential_statements=true
117
+ diff=differential_ast(&block)
118
+ @ast.body << Fsm.new(name,Body.new(diff))
119
+ end
120
+
121
+ def state name, &block
122
+ @has_sequential_statements=true
123
+ diff=differential_ast(&block)
124
+ @ast.body << State.new(name,Body.new(diff))
125
+ end
126
+
127
+ def next_state name
128
+ @ast.body << Next.new(name)
129
+ end
130
+
131
+ private
132
+ def process_sig_decl kind,*arg
133
+ case arg
134
+ when String
135
+ decl_sig(kind,vname=arg.to_sym,type=:bit)
136
+ when Symbol
137
+ decl_sig(kind,vname=arg,type=:bit)
138
+ when Array
139
+ # strange ! recursivity seems to fail
140
+ # output(element) # FAILS.
141
+ arg.each do |element|
142
+ case element
143
+ when String
144
+ decl_sig(kind,vname=element.to_sym,type=:bit)
145
+ when Symbol
146
+ decl_sig(kind,vname=element,type=:bit)
147
+ when Hash
148
+ element.each do |vname,type|
149
+ decl_sig(kind,vname,type)
150
+ end
151
+ else
152
+ "ERROR : wrong output declaration in list : '#{arg}'"
153
+ end
154
+ end
155
+ when Record
156
+ decl_sig(:input,vname=arg,type)
157
+ else
158
+ raise "ERROR : wrong input declaration '#{arg}'"
159
+ end
160
+ end
161
+
162
+ def decl_sig kind,vname_sym,type=:bit
163
+ @ast||=Root.new
164
+ klass=Object.const_get(kind.capitalize)
165
+ io=klass.new(vname_sym,type)
166
+ vname="@#{vname_sym}"
167
+ instance_variable_set(vname, io)
168
+ self.class.__send__(:attr_accessor, vname_sym)
169
+ @ast.decls << sig=SigDecl.new(vname_sym.to_s,io)
170
+ sig
171
+ end
172
+
173
+ def Case(arg,&block)
174
+ diff=differential_ast(&block)
175
+ @ast.body << Case.new(arg,Body.new(diff))
176
+ end
177
+
178
+ def When(arg,&block)
179
+ diff=differential_ast(&block)
180
+ @ast.body << When.new(arg,Body.new(diff))
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,85 @@
1
+ module RubyRTL
2
+
3
+ class ASTPrinter
4
+
5
+ attr_accessor :code,:nodes_decl,:nodes_cnx
6
+
7
+ def run ast,suffix=nil
8
+ puts "[+] printing ast in dot file"
9
+ @verbose=false
10
+ @nodes_decl=Code.new
11
+ @nodes_cnx=Code.new
12
+ @printed_cnx={} #Cosmetic ! to keep track of already printed cnx source->sink
13
+ @code=Code.new
14
+ code << "digraph G {"
15
+ code.indent=2
16
+ code << "ordering=out;"
17
+ code << "ranksep=.4;"
18
+ code << "bgcolor=\"lightgrey\";"
19
+ code.newline
20
+ code << "node [shape=box, fixedsize=false, fontsize=12, fontname=\"Helvetica-bold\", fontcolor=\"blue\""
21
+ code << " width=.25, height=.25, color=\"black\", fillcolor=\"white\", style=\"filled, solid, bold\"];"
22
+ code << "edge [arrowsize=.5, color=\"black\", style=\"bold\"]"
23
+ @visited=[]
24
+ process(ast)
25
+ code << @nodes_decl
26
+ code << @nodes_cnx
27
+ code.indent=0
28
+ code << "}"
29
+ clean(code)
30
+ dot_filename="#{ast.name}_ast#{suffix}.dot"
31
+ File.open(dot_filename,'w'){|f| f.puts code.finalize}
32
+ end
33
+
34
+ def process node,level=1
35
+ kname=node.class.name.split("::")[1] || "#{node.class}"
36
+ id=node.object_id
37
+ indent=" "*level
38
+
39
+ #puts indent+"processing #{node}"
40
+ (nodes_decl << "#{id} [label=\"#{kname}\"]")
41
+ unless @visited.include?(id)
42
+ node.instance_variables.each{|vname|
43
+ #puts indent+"-"+vname.to_s
44
+ ivar=node.instance_variable_get(vname)
45
+ vname=vname.to_s[1..-1]
46
+ if ivar
47
+ case ivar
48
+ when Array
49
+ ivar.each_with_index{|e,idx|
50
+ sink=process(e,level+1)
51
+ @printed_cnx[id]||=[]
52
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}[#{idx}]\"]" if not @printed_cnx[id].include? sink
53
+ @printed_cnx[id] << sink
54
+ }
55
+ when Symbol,Integer,String
56
+ val=ivar.to_s
57
+ sink="#{ivar.object_id}"
58
+ nodes_decl << "#{sink} [label=\"#{val}\",color=\"red\"]"
59
+ @printed_cnx[id]||=[]
60
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
61
+ @printed_cnx[id] << sink
62
+ else
63
+ sink=process(ivar,level+1)
64
+ @printed_cnx[id]||=[]
65
+ nodes_cnx << "#{id} -> #{sink} [label=\"#{vname}\"]" if not @printed_cnx[id].include? sink
66
+ @printed_cnx[id] << sink
67
+ end
68
+ end
69
+ }
70
+ end
71
+ @visited << id
72
+ return id
73
+ end
74
+
75
+ # suppress syndrom : name=""not correct""
76
+ def clean code
77
+ #puts "=> cleaning dot code"
78
+ code.lines.each_with_index do |line,idx|
79
+ if line=~/\"\"(.*)\"\"/
80
+ code.lines[idx].gsub!(/\"\"(.*)\"\"/,"\"#{$1}\"")
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,50 @@
1
+ class Code
2
+
3
+ attr_accessor :indent,:lines
4
+
5
+ def initialize str=nil
6
+ @lines=[]
7
+ (@lines << str) if str
8
+ @indent=0
9
+ end
10
+
11
+ def size
12
+ @lines.size
13
+ end
14
+
15
+ def <<(thing)
16
+ if (code=thing).is_a? Code
17
+ code.lines.each do |line|
18
+ @lines << " "*@indent+line.to_s
19
+ end
20
+ elsif thing.is_a? Array
21
+ thing.each do |kode|
22
+ @lines << kode
23
+ end
24
+ elsif thing.nil?
25
+ else
26
+ @lines << " "*@indent+thing.to_s
27
+ end
28
+ end
29
+
30
+ def finalize
31
+ return @lines.join("\n") if @lines.any?
32
+ ""
33
+ end
34
+
35
+ def newline
36
+ @lines << " "
37
+ end
38
+
39
+ def save_as filename,verbose=true,sep="\n"
40
+ str=self.finalize
41
+ File.open(filename,'w'){|f| f.puts(str)}
42
+ return filename
43
+ end
44
+
45
+ def size
46
+ @lines.size
47
+ end
48
+
49
+
50
+ end
@@ -0,0 +1,61 @@
1
+ require_relative 'ast_printer'
2
+ require_relative 'dsl_printer'
3
+ require_relative 'contextual_analyzer'
4
+ require_relative 'type_checker'
5
+ require_relative 'vhdl_generator'
6
+ require_relative 'sexp_generator'
7
+
8
+ module RubyRTL
9
+
10
+ class Compiler
11
+
12
+ def initialize
13
+ header
14
+ @printer=DSLPrinter.new
15
+ @dot_printer=ASTPrinter.new
16
+ @analyzer=ContextualAnalyzer.new
17
+ @checker=TypeChecker.new
18
+ @vgen=VhdlGenerator.new
19
+ @sexp_gen=SexpGenerator.new
20
+ end
21
+
22
+ def header
23
+ puts "RubyRTL compiler "
24
+ end
25
+
26
+ def compile circuit
27
+ print_ast(circuit)
28
+ analyze(circuit)
29
+ print_dsl(circuit)
30
+ type_check(circuit)
31
+ print_dsl(circuit)
32
+ generate(circuit)
33
+ generate_sexp(circuit)
34
+ end
35
+
36
+ def print_dsl circuit
37
+ @printer.print circuit
38
+ end
39
+
40
+ def print_ast circuit,file_suffix=""
41
+ @dot_printer.run(circuit,file_suffix)
42
+ end
43
+
44
+ def analyze circuit
45
+ @analyzer.analyze(circuit)
46
+ end
47
+
48
+ def type_check circuit
49
+ @checker.check circuit
50
+ end
51
+
52
+ def generate circuit
53
+ @vgen.generate(circuit)
54
+ end
55
+
56
+ def generate_sexp circuit
57
+ @sexp_gen.generate(circuit)
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,88 @@
1
+ require_relative 'code'
2
+ require_relative 'visitor'
3
+
4
+ module RubyRTL
5
+
6
+ class ContextualAnalyzer < Visitor
7
+
8
+ def analyze circuit
9
+ puts "[+] contextual analysis"
10
+ root=circuit.ast
11
+ if root
12
+ #root.ios.each{|io| io.accept(self)}
13
+ root.decls.each{|decl| decl.accept(self)}
14
+ root.body.accept(self)
15
+ end
16
+ end
17
+
18
+ def visitBody body,args=nil
19
+ #reconnect Else/Elsifs objects to their parent If
20
+ reconnectElseParts(body)
21
+ #attach comments to their adequate Ast nodes
22
+ attachComments(body)
23
+ body.stmts.each{|stmt| stmt.accept(self,args)}
24
+ end
25
+
26
+ def reconnectElseParts body
27
+ ifs=body.select{|stmt| stmt.is_a? If}
28
+ to_delete=[]
29
+ ifs.each do |if_|
30
+ index_if=body.stmts.index(if_)
31
+ do_iterate=true
32
+ index=index_if
33
+ while do_iterate
34
+ case elsei=body.stmts[index+1]
35
+ when Else
36
+ if_.else=elsei
37
+ to_delete << elsei
38
+ elsei.accept(self) # dont forget to visit it
39
+ when Elsif
40
+ if_.elsifs << elsei
41
+ to_delete << elsei
42
+ elsei.accept(self)
43
+ else
44
+ do_iterate=false
45
+ end
46
+ index+=1
47
+ end
48
+ end
49
+ to_delete.each{|stmt| body.stmts.delete(stmt)}
50
+ end
51
+
52
+ def attachComments body
53
+ #niy
54
+ end
55
+
56
+ def visitFsm fsm,args=nil
57
+ @fsm=fsm
58
+ @tmp_ary=[] #helper
59
+ puts " |-[+] visiting fsm '#{fsm.name}'"
60
+ # default assignements
61
+ fsm.default_assigns=fsm.body.select{|e| e.is_a? Assign}
62
+ # build a state hash : state_name => state
63
+ state_nodes=fsm.body.select{|e| e.is_a? State}
64
+ # fsm.states=states_nodes.inject({}){|hash,state| hash.merge!( state.name=> state)}
65
+ # build a state array
66
+ fsm.states=state_nodes
67
+ # don't forget to visit the states
68
+ @in_state=true
69
+ fsm.states.each{|state| state.accept(self)}
70
+ @in_state=false
71
+ end
72
+
73
+ def visitState state,args=nil
74
+ state.body.accept(self)
75
+ end
76
+
77
+ def visitAssign assign,args=nil
78
+ if @in_state
79
+ puts "pushing #{assign}"
80
+ unless @tmp_ary.include?(id=assign.lhs.object_id)
81
+ @tmp_ary << id
82
+ @fsm.assignments << assign
83
+ end
84
+ end
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,198 @@
1
+ require_relative 'ast'
2
+ require_relative 'ast_builder'
3
+
4
+ module RubyRTL
5
+
6
+ def build_type arg
7
+ case arg
8
+ when Symbol
9
+ case sym=arg.to_s
10
+ when "bit"
11
+ sym="bit"
12
+ ret=BitType.new
13
+ when "byte"
14
+ ret=IntType.new(8)
15
+ when /\Abv(\d+)/
16
+ ret=BitVectorType.new($1.to_i)
17
+ when /\Auint(\d+)?/
18
+ nbits=($1 || 32).to_i
19
+ ret=UIntType.new(nbits)
20
+ when /\Aint(\d+)?/
21
+ nbits=($1 || 32).to_i
22
+ ret=IntType.new(nbits)
23
+ else
24
+ unless ret=$typedefs[arg] # global var !
25
+ raise "DSL syntax error : unknow type '#{sym}'"
26
+ end
27
+ end
28
+ $typedefs||={}
29
+ $typedefs[sym]||=ret
30
+ when Integer
31
+ val=arg
32
+ if val==1
33
+ name="bit"
34
+ ret=BitType.new if val==1
35
+ else
36
+ name="bv#{val}"
37
+ ret=BitVectorType.new(val)
38
+ end
39
+ $typedefs||={}
40
+ $typedefs[name]||=ret
41
+ when Hash
42
+ ret=arg
43
+ when IntType,UIntType,BitType,BitVectorType
44
+ ret=arg
45
+ else
46
+ raise "ERROR : DSL syntax error. build_type for #{arg} (#{arg.class})"
47
+ end
48
+ ret
49
+ end
50
+
51
+ class Sig < Ast
52
+ attr_accessor :name
53
+ attr_accessor :type
54
+ attr_accessor :subscript_of
55
+ attr_accessor :subsignals
56
+
57
+ def initialize name,type=:bit
58
+ @name=name
59
+ @type=build_type(type)
60
+ end
61
+
62
+ def treat_int(other)
63
+ case other
64
+ when Integer
65
+ if other >=0
66
+ #return UIntLit.new(other)
67
+ return RUIntLit.new(other)
68
+ else
69
+ #return IntLit.new(other)
70
+ return RIntLit.new(other)
71
+ end
72
+ else
73
+ return other
74
+ end
75
+ end
76
+
77
+ def |(other)
78
+ other=treat_int(other)
79
+ Binary.new(self,"|",other)
80
+ end
81
+
82
+ def &(other)
83
+ other=treat_int(other)
84
+ Binary.new(self,"&",other)
85
+ end
86
+
87
+ def ^(other)
88
+ other=treat_int(other)
89
+ Binary.new(self,"^",other)
90
+ end
91
+
92
+ # comparison
93
+ def <(other)
94
+ other=treat_int(other)
95
+ Binary.new(self,"<",other)
96
+ end
97
+
98
+ def <=(other)
99
+ other=treat_int(other)
100
+ Binary.new(self,"<=",other)
101
+ end
102
+
103
+ def >(other)
104
+ other=treat_int(other)
105
+ Binary.new(self,"<",other)
106
+ end
107
+
108
+ def >=(other)
109
+ other=treat_int(other)
110
+ Binary.new(self,"<=",other)
111
+ end
112
+
113
+ def ==(other)
114
+ other=treat_int(other)
115
+ Binary.new(self,"==",other)
116
+ end
117
+ # arith
118
+ def +(other)
119
+ other=treat_int(other)
120
+ Binary.new(self,"+",other)
121
+ end
122
+
123
+ def -(other)
124
+ other=treat_int(other)
125
+ Binary.new(self,"-",other)
126
+ end
127
+
128
+ def *(other)
129
+ other=treat_int(other)
130
+ Binary.new(self,"*",other)
131
+ end
132
+
133
+ def /(other)
134
+ other=treat_int(other)
135
+ Binary.new(self,"/",other)
136
+ end
137
+
138
+ def !=(other)
139
+ other=treat_int(other)
140
+ Binary.new(self,"!=",other)
141
+ end
142
+
143
+ # unary expressions
144
+ def !@
145
+ Unary.new("!",self)
146
+ end
147
+
148
+ def -@
149
+ Unary.new("-",self)
150
+ end
151
+
152
+ def [](index)
153
+ @indexed||={}
154
+ index=treat_int(index)
155
+ @indexed[index.to_s]||=Indexed.new(self,index,@type.type)
156
+ end
157
+
158
+ def coerce(other)
159
+ [IntLit.new(other), self]
160
+ end
161
+ end
162
+
163
+ class Indexed < Sig
164
+ attr_accessor :lhs,:rhs
165
+ def initialize lhs,rhs,type
166
+ super("foo",type)
167
+ @lhs,@rhs=lhs,rhs
168
+ end
169
+ end
170
+
171
+ def Memory size,type
172
+ p type=build_type(type)
173
+ MemoryType.new(size,type)
174
+ end
175
+
176
+ def Record hash
177
+ h={}
178
+ hash.each do |name,type|
179
+ type||=$typedefs[type]
180
+ type=build_type(type)
181
+ h[name]=type
182
+ end
183
+ RecordType.new(h)
184
+ end
185
+
186
+ def Struct hash
187
+ Record(hash) # call to method Record
188
+ end
189
+
190
+ def Enum *elems
191
+ EnumType.new(elems)
192
+ end
193
+
194
+ def Bit val
195
+ BitLit.new(val)
196
+ end
197
+
198
+ end