forked from kevinpt/hdlparse
-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathverilog_parser.py
More file actions
280 lines (221 loc) · 7.83 KB
/
verilog_parser.py
File metadata and controls
280 lines (221 loc) · 7.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# -*- coding: utf-8 -*-
# Copyright © 2017 Kevin Thibedeau
# Distributed under the terms of the MIT license
"""Verilog documentation parser"""
import io
import os
from collections import OrderedDict
from .minilexer import MiniLexer
verilog_tokens = {
# state
'root': [
# patterns
# pattern, action, new_state
(r'\bmodule\s+(\w+)\s*', 'module', 'module'),
(r'/\*', 'block_comment', 'block_comment'),
(r'//#+(.*)\n', 'metacomment'),
(r'//.*\n', None),
],
'module': [
(r'parameter\s*(signed|integer|realtime|real|time)?\s*(\[[^]]+\])?', 'parameter_start', 'parameters'),
(
r'^[\(\s]*(input|inout|output)\s+(reg|supply0|supply1|tri|triand|trior|tri0|tri1|wire|wand|wor)?'
r'\s*(signed)?\s*((\[[^]]+\])+)?',
'module_port_start', 'module_port'),
(r'\bendmodule\b', 'end_module', '#pop'),
(r'/\*', 'block_comment', 'block_comment'),
(r'//#\s*{{(.*)}}\n', 'section_meta'),
(r'//.*\n', None),
],
'parameters': [
(r'\s*parameter\s*(signed|integer|realtime|real|time)?\s*(\[[^]]+\])?', 'parameter_start'),
(r'\s*(\w+)\s*=\s*((?:(?!\/\/|[,)]).)*)', 'param_item'),
(r'//#+(.*)\n', 'metacomment'),
(r',', None),
(r'//.*\n', None),
(r'[);]', None, '#pop'),
],
'module_port': [
(
r'\s*(input|inout|output)\s+(reg|supply0|supply1|tri|triand|trior|tri0|tri1|wire|wand|wor)?'
r'\s*(signed)?\s*((\[[^]]+\])+)?',
'module_port_start'),
(r'\s*(\w+)\s*,?', 'port_param'),
(r'/\*', 'block_comment', 'block_comment'),
(r'[);]', None, '#pop'),
(r'//#\s*{{(.*)}}\n', 'section_meta'),
(r'//#+(.*)\n', 'metacomment'),
(r'//.*\n', None),
],
'block_comment': [
(r'\*/', 'end_comment', '#pop'),
],
}
VerilogLexer = MiniLexer(verilog_tokens)
class VerilogObject:
"""Base class for parsed Verilog objects"""
def __init__(self, name, desc=None):
self.name = name
self.kind = 'unknown'
self.desc = [] if desc is None else desc
class VerilogParameter:
"""Parameter and port to a module"""
def __init__(self, name, mode=None, data_type=None, default_value=None, desc=None):
self.name = name
self.mode = mode
self.data_type = data_type
self.default_value = default_value
self.desc = [] if desc is None else desc
def __str__(self):
if self.mode is not None:
param = f"{self.name} : {self.mode} {self.data_type}"
else:
param = f"{self.name} : {self.data_type}"
if self.default_value is not None:
param = f"{param} := {self.default_value}"
return param
def __repr__(self):
return f"VerilogParameter('{self.name}')"
class VerilogModule(VerilogObject):
"""Module definition"""
def __init__(self, name, ports, generics=None, sections=None, desc=None):
VerilogObject.__init__(self, name, desc)
self.kind = 'module'
# Verilog params
self.generics = generics if generics is not None else []
self.ports = ports
self.sections = sections if sections is not None else {}
def __repr__(self):
return f"VerilogModule('{self.name}') {self.ports}"
def parse_verilog_file(fname):
"""Parse a named Verilog file
Args:
fname (str): File to parse
Returns:
List of parsed objects
"""
with open(fname, 'rt', encoding='UTF-8') as fh:
text = fh.read()
return parse_verilog(text)
def parse_verilog(text):
"""Parse a text buffer of Verilog code
Args:
text (str): Source code to parse
Returns:
List of parsed objects.
"""
lex = VerilogLexer
name = None
mode = 'input'
port_type = 'wire'
param_type = ''
metacomments = []
generics = []
ports = OrderedDict()
sections = []
port_param_index = 0
last_item = None
objects = []
for _, action, groups in lex.run(text):
if action == 'metacomment':
comment = groups[0].strip()
if last_item is None:
metacomments.append(comment)
else:
last_item.desc.append(comment)
if action == 'section_meta':
sections.append((port_param_index, groups[0]))
elif action == 'module':
name = groups[0]
generics = []
ports = OrderedDict()
sections = []
port_param_index = 0
elif action == 'parameter_start':
net_type, vec_range = groups
new_param_type = ''
if net_type is not None:
new_param_type += net_type
if vec_range is not None:
new_param_type += ' ' + vec_range
param_type = new_param_type
elif action == 'param_item':
param_name, default_value = groups
param = VerilogParameter(param_name, 'in', param_type, default_value)
generics.append(param)
last_item = param
elif action == 'module_port_start':
new_mode, net_type, signed, vec_range = groups[0:4]
new_port_type = ''
if net_type is not None:
new_port_type += net_type
if signed is not None:
new_port_type += ' ' + signed
if vec_range is not None:
new_port_type += ' ' + vec_range
# Start with new mode
mode = new_mode
port_type = new_port_type
elif action == 'port_param':
port_ident = groups[0]
port_obj = VerilogParameter(port_ident, mode, port_type)
ports[port_ident] = port_obj
port_param_index += 1
last_item = port_obj
elif action == 'end_module':
vobj = VerilogModule(name, ports.values(), generics, dict(sections), metacomments)
objects.append(vobj)
last_item = None
metacomments = []
return objects
def is_verilog(fname):
"""Identify file as Verilog by its extension
Args:
fname (str): File name to check
Returns:
True when file has a Verilog extension.
"""
return os.path.splitext(fname)[-1].lower() in ('.vlog', '.v', '.sv')
class VerilogExtractor:
"""Utility class that caches parsed objects"""
def __init__(self):
self.object_cache = {}
def extract_objects(self, fname, type_filter=None):
"""Extract objects from a source file
Args:
fname(str): Name of file to read from
type_filter (class, optional): Object class to filter results
Returns:
List of objects extracted from the file.
"""
objects = []
if fname in self.object_cache:
objects = self.object_cache[fname]
else:
with io.open(fname, 'rt', encoding='utf-8') as fh:
text = fh.read()
objects = parse_verilog(text)
self.object_cache[fname] = objects
if type_filter:
objects = [o for o in objects if isinstance(o, type_filter)]
return objects
def extract_objects_from_source(self, text, type_filter=None):
"""Extract object declarations from a text buffer
Args:
text (str): Source code to parse
type_filter (class, optional): Object class to filter results
Returns:
List of parsed objects.
"""
objects = parse_verilog(text)
if type_filter:
objects = [o for o in objects if isinstance(o, type_filter)]
return objects
def is_array(self, data_type):
"""Check if a type is an array type
Args:
data_type (str): Data type
Returns:
True when a data type is an array.
"""
return '[' in data_type