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,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