alway_comb nested if support
[sv2nmigen.git] / absyn.py
1 from lib2to3.pytree import Node, Leaf
2 from lib2to3.pgen2 import token
3 from lib2to3.pygram import python_symbols as syms
4
5 preamble = """# this file has been generated by sv2nmigen
6
7 from nmigen import Signal, Module, Const, Cat, Elaboratable
8
9
10
11 """
12
13
14 def port_decl_do_not_use(comment, dt, name):
15 if dt is None or dt.dims is None:
16 width = '' # width: 1
17 else:
18 width = dt.dims
19 # XXX TODO, better checking, should be using data structure... *sigh*
20 width = width[1:-1] # strip brackets
21 width = width.split(':')
22 assert width[0] == '0'
23 width = width[1]
24 return 'self.%s = Signal(%s) # %s' % (name, width, comment)
25
26
27 indent_debug = 0
28
29
30 class PortDecl:
31 def __init__(self, comment, dt, name):
32 self.comment = comment
33 self.dt = dt
34 self.name = name
35
36 def initNode(self):
37 return port_decl_do_not_use(self.comment, self.dt, self.name)
38
39
40 class Assignment:
41 def __init__(self, left, op, right):
42 self.left = left
43 self.op = op
44 self.right = right
45
46 def debugInfo(self):
47 return "Assignment:"+str(self.left) + " "+str(self.op)+" "+str(self.right)
48
49
50 class CondStatement:
51 def __init__(self, cond, ifpart, elsepart):
52 self.cond = cond
53 self.ifpart = ifpart
54 self.elsepart = elsepart
55
56
57 def makeBlock(x):
58 if(type(x) == Assignment):
59 return [x]
60 else:
61 return x.statements
62
63
64 class Absyn:
65 def __init__(self, outputfn):
66 self.outputfn = outputfn
67 self.outputfile = None
68 self.assign = []
69 self.ports = []
70 self.wires = []
71 self.comb = []
72
73 def open(self):
74 if(self.outputfile is None):
75 self.outputfile = open(self.outputfn, "w")
76 self.outputfile.write(preamble)
77
78 def printpy(self, p):
79 self.open()
80 self.outputfile.write(str(p)+"\n")
81
82 def assign(self, p):
83 p = list(p)
84 if(p[1] == "assign"):
85 self.printpy(p[4])
86
87 def assign3(self, left, op, right):
88 return Assignment(left, op, right)
89
90 def cond_statement3(self, cond, ifpart, elsepart):
91 return CondStatement(cond, ifpart, elsepart)
92
93 def indent(self, count):
94 if(indent_debug):
95 return Leaf(token.INDENT, '>>> '*count)
96 else:
97 return Leaf(token.INDENT, ' '*4*count)
98
99 def dedent(self, count):
100 return Leaf(token.DEDENT, '')
101
102 def nl(self):
103 return Leaf(token.NEWLINE, '\n')
104
105 def port_decl(self, comment, dt, name):
106 port = PortDecl(comment, dt, name)
107 self.ports += [port]
108 return port
109
110 def isPort(self, name):
111 for p in self.ports:
112 if(str(p.name) == str(name)):
113 return True
114 return False
115
116 def initFunc(self, ports, params):
117 params = [Leaf(token.LPAR, '('), Leaf(
118 token.NAME, "self")] + [Leaf(token.RPAR, ')')]
119 # TODO handle sv params
120 fn = [Leaf(token.NAME, 'def'),
121 Leaf(token.NAME, '__init__', prefix=' '),
122 Node(syms.parameters, params),
123 Leaf(token.COLON, ':'),
124 self.nl()
125 ]
126 fndef = Node(syms.funcdef, fn)
127 stmts = Node(syms.stmt, [fndef])
128 for port in ports:
129 stmts.children.append(self.indent(2))
130 stmts.children.append(port.initNode())
131 stmts.children.append(self.nl())
132 return stmts
133
134 def do_assign(self, a, stmts, indent):
135 stmts.children.append(self.indent(indent))
136 stmts.children.append(Leaf(token.STRING, "m.d.comb += "))
137 if(self.isPort(a.left)):
138 stmts.children.append(Leaf(token.STRING, "self."))
139 stmts.children.append(Leaf(token.STRING, a.left))
140 stmts.children.append(Leaf(token.STRING, ".eq("))
141 if(self.isPort(a.right)):
142 stmts.children.append(Leaf(token.STRING, "self."))
143 stmts.children.append(Leaf(token.STRING, a.right))
144 stmts.children.append(Leaf(token.STRING, ")"))
145 stmts.children.append(self.nl())
146
147 def do_ifblock(self, c, stmts, indent):
148 stmts.children.append(self.indent(indent))
149 stmts.children.append(Leaf(token.STRING, "with m.If("))
150 if(self.isPort(c.cond)):
151 stmts.children.append(Leaf(token.STRING, "self."))
152 stmts.children.append(Leaf(token.STRING, c.cond))
153 stmts.children.append(Leaf(token.STRING, "):"))
154 stmts.children.append(self.nl())
155
156 for c1 in makeBlock(c.ifpart):
157 if(type(c1) == Assignment):
158 self.do_assign(c1, stmts, indent+1)
159 else:
160 self.do_ifblock(c1, stmts, indent+1)
161
162 if(c.elsepart):
163 stmts.children.append(self.indent(indent))
164 stmts.children.append(Leaf(token.STRING, "with m.Else():"))
165 stmts.children.append(self.nl())
166
167 for c1 in makeBlock(c.elsepart):
168 if(type(c1) == Assignment):
169 self.do_assign(c1, stmts, indent+1)
170 else:
171 self.do_ifblock(c1, stmts, indent+1)
172
173 def elaborateFunc(self):
174 params = [Leaf(token.LPAR, '('), Leaf(
175 token.NAME, "self, platform=None"), Leaf(token.RPAR, ')')]
176 fn = [Leaf(token.NAME, 'def'),
177 Leaf(token.NAME, 'elaborate', prefix=' '),
178 Node(syms.parameters, params),
179 Leaf(token.COLON, ':'),
180 self.nl()
181 ]
182 fndef = Node(syms.funcdef, fn)
183 stmts = Node(syms.stmt, [fndef])
184 stmts.children.append(self.indent(2))
185 stmts.children.append(Leaf(token.STRING, "m = Module()"))
186 stmts.children.append(self.nl())
187
188 for w in self.wires:
189 wirename = w[0]
190 hasdims = (len(w) >= 4)
191 stmts.children.append(self.indent(2))
192 stmts.children.append(Leaf(token.STRING, wirename))
193 stmts.children.append(Leaf(token.STRING, " = Signal("))
194 if(hasdims):
195 stmts.children.append(Leaf(token.STRING, str(w[3])))
196 stmts.children.append(Leaf(token.STRING, ")"))
197 stmts.children.append(self.nl())
198
199 # refactor: assignments non cond
200 for a in self.assign:
201 self.do_assign(a, stmts)
202
203 for c in self.comb:
204 print("comb", c)
205 if(type(c) == Assignment):
206 self.do_assign(c, stmts, 2)
207 else:
208 self.do_ifblock(c, stmts, 2)
209
210 stmts.children.append(self.indent(2))
211 stmts.children.append(Leaf(token.STRING, "return m"))
212 stmts.children.append(self.nl())
213 return stmts
214
215 def module_1(self, p):
216 params = p[7]
217 ports = p[8]
218 clsname = [Leaf(token.NAME, 'class'),
219 Leaf(token.NAME, p[4], prefix=' '),
220 Leaf(token.LPAR, '('),
221 Leaf(token.NAME, 'Elaboratable'),
222 Leaf(token.LPAR, ')'),
223 Leaf(token.COLON, ':'),
224 self.nl(),
225 ]
226
227 suite = Node(syms.suite, [Leaf(token.NEWLINE, '\n'),
228 self.indent(1),
229 self.initFunc(ports, params),
230 self.indent(1),
231 self.elaborateFunc()
232
233 ])
234 clsdecl = Node(syms.classdef, clsname + [suite])
235 clsdecl = Node(syms.compound_stmt, [clsdecl])
236
237 self.printpy(str(clsdecl))
238 return clsdecl
239
240 def module_item_2(self, signaltype, dims, mlist):
241 if(signaltype == "wire"):
242 for m in mlist:
243 if(dims):
244 self.wires.append(m+dims)
245 else:
246 self.wires.append(m)
247
248 def appendComments(self, data):
249 self.open()
250 self.outputfile.write(data)
251 #lines = data.split("\n")
252 # for line in lines:
253 # self.printpy("#"+line)
254
255 # combinatorical assign
256 def cont_assign_1(self, p):
257 self.assign += [Assignment(p[1], p[2], p[3])]
258
259 # cond assigmments and other nested blocks
260 def always_comb(self, p3, p1):
261 print("always_comb")
262 slist = p3[6]
263 self.comb += slist.statements