blob: 433f03f9a91cdeb42f7fe5750b3f3a15aef3558d [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
r"""Helper functions for parsing a systemverilog module header.
"""
from io import StringIO
from pathlib import Path
class Param():
name = ""
datatype = ""
style = ""
value = ""
def __init__(self, name_="", datatype_="", style_="", value_=""):
self.name = name_
self.datatype = datatype_
self.style = style_
self.value = value_
class Port():
name = ""
datatype = ""
direction = ""
def __init__(self, name_="", datatype_="", direction_=""):
self.name = name_
self.datatype = datatype_
self.direction = direction_
class Dut():
name = ""
pkgs = []
params = []
ports = []
deps = []
is_cip = False
def __init__(self, name_="", pkgs_=[], ports_=[], \
params_=[], deps_=[], is_cip_=False):
self.name = name_
self.pkgs = pkgs_
self.ports = ports_
self.params = params_
self.deps = deps_
self.is_cip = is_cip_
# filters out localparams
def get_param_style(self, style):
params = []
for p in self.params:
params += [p] if p.style == style else []
return params
# strip // comments
def strip_comments(buf):
outbuf = ""
for line in buf.split('\n'):
for k in range(len(line) - 1):
if line[k:k + 2] == "//":
break
else:
outbuf += line[k]
else:
if line:
outbuf += line[-1]
outbuf += " "
return outbuf
PARENTH_STYLES = {'(': ')', '[': ']', '{': '}'}
# parse parenthesis and optionally handle the body using the handler function
# if no handler is specified, it just calls itself recursively
def parse_parenthesis(hdl_raw, dut, style='(', handler=None):
if style not in PARENTH_STYLES:
print("Unknown parenthesis style %s, aborting." % style)
else:
par_opened = False
while hdl_raw:
c = hdl_raw.pop(0)
if c == style:
par_opened = True
if handler:
handler(hdl_raw, dut)
else:
parse_parenthesis(hdl_raw, dut, style)
if c == PARENTH_STYLES[style]:
if not par_opened:
hdl_raw.insert(0, c)
break
return
# parse individual port declarations
# may not be fully correct with unpacked dimensions,
# but works for the common case
def parse_port(buf, dut):
words = buf.split()
if words:
if words[0] not in ["input", "inout", "output"]:
print("Warning, expected input, inout or output keyword")
else:
if len(words) > 2:
dut.ports += [Port(words[-1], "".join(words[1:-1]), words[0])]
elif len(words) == 2:
dut.ports += [Port(words[-1], "", words[0])]
else:
print("Warning, port declaration incomplete")
else:
print("Warning, port declaration empty")
return
# parse individual parameter declaration
def parse_param(buf, dut):
words = buf.split('=')
value = '='.join(words[1:])
words = words[0].split()
if words:
if words[0] not in ["parameter", "localparam"]:
print("Warning, expected parameter or localparam keyword")
else:
if len(words) > 2:
dut.params += [
Param(words[-1], " ".join(words[1:-1]), words[0], value)
]
elif len(words) == 2:
dut.params += [Param(words[-1], "", words[0], value)]
else:
print("Warning, parameter declaration incomplete")
else:
print("Warning, port declaration empty")
return
# extract individual declarations
def parse_declaration(hdl_raw, dut, handler):
buf = ""
par_opened = 0
while hdl_raw:
c = hdl_raw.pop(0)
# end of this port
if c == ',':
handler(buf, dut)
buf = ""
elif c == '(':
par_opened = par_opened + 1
buf += c
elif c == ')':
if par_opened:
# part of the declaration
par_opened = par_opened - 1
buf += c
else:
# end of the declaration list
handler(buf, dut)
hdl_raw.insert(0, ')')
break
else:
buf += c
return
def parse_ports(hdl_raw, dut):
parse_declaration(hdl_raw, dut, parse_port)
def parse_params(hdl_raw, dut):
parse_declaration(hdl_raw, dut, parse_param)
def parse_module(words, dut):
# check for imports first
while words:
w = words.pop(0)
if w == "import":
if words:
# get package names to import
pkg = words.pop(0).split(";")
dut.pkgs += [pkg[0]]
else:
print("Unexpected end")
# stop package scan and move on to body
elif '#' in w or '(' in w:
words.insert(0, w)
break
hdl_raw = list(' '.join(words))
while hdl_raw:
c = hdl_raw.pop(0)
if c == '#':
parse_parenthesis(hdl_raw, dut, '(', parse_params)
elif c == '(':
hdl_raw.insert(0, '(')
parse_parenthesis(hdl_raw, dut, '(', parse_ports)
elif c == ';':
break
return
# simplistic module declaration parser.
# this works in most cases, but there are exceptions.
def parse_file(file):
dut = Dut()
hdl_raw = ""
with open(file, 'r') as fp:
hdl_raw = strip_comments(fp.read())
# extract first module declaration in file and extract port list
# also look for imported packages (either in the module declaration
# or before it)
words = hdl_raw.split()
while words:
w = words.pop(0)
if w == "import":
if words:
# get package names to import
pkg = words.pop(0).split(";")
dut.pkgs += [pkg[0]]
else:
print("Unexpected end")
elif w == "module":
if words:
# get module name
dut.name = words[0]
# parse the module params and port list and exit
parse_module(words, dut)
break
return dut