blob: 433f03f9a91cdeb42f7fe5750b3f3a15aef3558d [file] [log] [blame]
Michael Schaffnerb5a88f22019-11-26 19:43:37 -08001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4r"""Helper functions for parsing a systemverilog module header.
5"""
6
7from io import StringIO
8from pathlib import Path
9
10
11class Param():
12 name = ""
13 datatype = ""
14 style = ""
15 value = ""
16
17 def __init__(self, name_="", datatype_="", style_="", value_=""):
18 self.name = name_
19 self.datatype = datatype_
20 self.style = style_
21 self.value = value_
22
23
24class Port():
25 name = ""
26 datatype = ""
27 direction = ""
28
29 def __init__(self, name_="", datatype_="", direction_=""):
30 self.name = name_
31 self.datatype = datatype_
32 self.direction = direction_
33
34
35class Dut():
36 name = ""
37 pkgs = []
38 params = []
39 ports = []
40 deps = []
41 is_cip = False
42
43 def __init__(self, name_="", pkgs_=[], ports_=[], \
44 params_=[], deps_=[], is_cip_=False):
45 self.name = name_
46 self.pkgs = pkgs_
47 self.ports = ports_
48 self.params = params_
49 self.deps = deps_
50 self.is_cip = is_cip_
51
Michael Schaffnerccef8272019-12-06 08:54:55 -080052 # filters out localparams
53 def get_param_style(self, style):
54 params = []
55 for p in self.params:
56 params += [p] if p.style == style else []
57 return params
Michael Schaffnerb5a88f22019-11-26 19:43:37 -080058
59# strip // comments
60def strip_comments(buf):
61 outbuf = ""
62 for line in buf.split('\n'):
63 for k in range(len(line) - 1):
64 if line[k:k + 2] == "//":
65 break
66 else:
67 outbuf += line[k]
68 else:
69 if line:
70 outbuf += line[-1]
71 outbuf += " "
72
73 return outbuf
74
75
76PARENTH_STYLES = {'(': ')', '[': ']', '{': '}'}
77
78
79# parse parenthesis and optionally handle the body using the handler function
80# if no handler is specified, it just calls itself recursively
81def parse_parenthesis(hdl_raw, dut, style='(', handler=None):
82 if style not in PARENTH_STYLES:
83 print("Unknown parenthesis style %s, aborting." % style)
84 else:
85 par_opened = False
86 while hdl_raw:
87 c = hdl_raw.pop(0)
88 if c == style:
89 par_opened = True
90 if handler:
91 handler(hdl_raw, dut)
92 else:
93 parse_parenthesis(hdl_raw, dut, style)
94 if c == PARENTH_STYLES[style]:
95 if not par_opened:
96 hdl_raw.insert(0, c)
97 break
98 return
99
100
101# parse individual port declarations
102# may not be fully correct with unpacked dimensions,
103# but works for the common case
104def parse_port(buf, dut):
105 words = buf.split()
106 if words:
107 if words[0] not in ["input", "inout", "output"]:
108 print("Warning, expected input, inout or output keyword")
109 else:
110 if len(words) > 2:
111 dut.ports += [Port(words[-1], "".join(words[1:-1]), words[0])]
112 elif len(words) == 2:
113 dut.ports += [Port(words[-1], "", words[0])]
114 else:
115 print("Warning, port declaration incomplete")
116 else:
117 print("Warning, port declaration empty")
118 return
119
120
121# parse individual parameter declaration
122def parse_param(buf, dut):
123 words = buf.split('=')
124 value = '='.join(words[1:])
125 words = words[0].split()
126
127 if words:
128 if words[0] not in ["parameter", "localparam"]:
129 print("Warning, expected parameter or localparam keyword")
130 else:
131 if len(words) > 2:
132 dut.params += [
133 Param(words[-1], " ".join(words[1:-1]), words[0], value)
134 ]
135 elif len(words) == 2:
136 dut.params += [Param(words[-1], "", words[0], value)]
137 else:
138 print("Warning, parameter declaration incomplete")
139 else:
140 print("Warning, port declaration empty")
141 return
142
143
144# extract individual declarations
145def parse_declaration(hdl_raw, dut, handler):
146 buf = ""
147 par_opened = 0
148 while hdl_raw:
149 c = hdl_raw.pop(0)
150 # end of this port
151 if c == ',':
152 handler(buf, dut)
153 buf = ""
154 elif c == '(':
155 par_opened = par_opened + 1
156 buf += c
157 elif c == ')':
158 if par_opened:
159 # part of the declaration
160 par_opened = par_opened - 1
161 buf += c
162 else:
163 # end of the declaration list
164 handler(buf, dut)
165 hdl_raw.insert(0, ')')
166 break
167 else:
168 buf += c
169 return
170
171
172def parse_ports(hdl_raw, dut):
173 parse_declaration(hdl_raw, dut, parse_port)
174
175
176def parse_params(hdl_raw, dut):
177 parse_declaration(hdl_raw, dut, parse_param)
178
179
180def parse_module(words, dut):
181 # check for imports first
182 while words:
183 w = words.pop(0)
184 if w == "import":
185 if words:
186 # get package names to import
187 pkg = words.pop(0).split(";")
188 dut.pkgs += [pkg[0]]
189 else:
190 print("Unexpected end")
191 # stop package scan and move on to body
192 elif '#' in w or '(' in w:
193 words.insert(0, w)
194 break
195
196 hdl_raw = list(' '.join(words))
197 while hdl_raw:
198 c = hdl_raw.pop(0)
199 if c == '#':
200 parse_parenthesis(hdl_raw, dut, '(', parse_params)
201 elif c == '(':
202 hdl_raw.insert(0, '(')
203 parse_parenthesis(hdl_raw, dut, '(', parse_ports)
204 elif c == ';':
205 break
206 return
207
208
209# simplistic module declaration parser.
210# this works in most cases, but there are exceptions.
211def parse_file(file):
212 dut = Dut()
213 hdl_raw = ""
214 with open(file, 'r') as fp:
215 hdl_raw = strip_comments(fp.read())
216 # extract first module declaration in file and extract port list
217 # also look for imported packages (either in the module declaration
218 # or before it)
219 words = hdl_raw.split()
220 while words:
221 w = words.pop(0)
222 if w == "import":
223 if words:
224 # get package names to import
225 pkg = words.pop(0).split(";")
226 dut.pkgs += [pkg[0]]
227 else:
228 print("Unexpected end")
229 elif w == "module":
230 if words:
231 # get module name
232 dut.name = words[0]
233 # parse the module params and port list and exit
234 parse_module(words, dut)
235 break
236 return dut