Uses argparse's "choices" argument, remove unused method and use abc and for abstract methods. Also updates the python filename in the Makefile
236 lines
9.0 KiB
Python
Executable File
236 lines
9.0 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
|
|
import argparse
|
|
import re
|
|
from abc import ABC, abstractmethod
|
|
|
|
parser = argparse.ArgumentParser(description=
|
|
'''Generates a Blarney wrapper for the given Bluespec generated verilog file
|
|
containing a module definition of a purely combinational CHERI function.
|
|
''')
|
|
parser.add_argument('verilog_files', metavar='VERILOG_FILE', type=str, nargs='+',
|
|
help='The file(s) to process')
|
|
parser.add_argument('--output', '-o', metavar='OUTPUT_FILE', type=str, nargs='?',
|
|
default="",
|
|
help='The output Blarney Haskell module to generate')
|
|
parser.add_argument('--generator', metavar='GENERATOR', type=str, nargs='?',
|
|
choices=['blarney','sv','systemverilog'], default='blarney',
|
|
help='The generator to be used')
|
|
args = parser.parse_args()
|
|
|
|
# Generic wrapper for a Verilog module
|
|
class Wrapper:
|
|
def __init__(self, size, name, ins, out):
|
|
self.size = size
|
|
self.name = name
|
|
self.ins = ins
|
|
self.out = out
|
|
def verilogModuleName(self):
|
|
return "module_wrap{:d}_{:s}".format(self.size, self.name)
|
|
def verilogInputNames(self):
|
|
return ["wrap{:d}_{:s}_{:s}".format(self.size, self.name, nm)
|
|
for nm in [x[0] for x in self.ins]]
|
|
def verilogOutputName(self):
|
|
return "wrap{:d}_{:s}".format(self.size, self.name)
|
|
|
|
|
|
# Generic generator class
|
|
# Describes the minimum functionality that a generator needs to implement.
|
|
# A generator takes some list of Verilog modules (which includes information
|
|
# about the module name, inputs, outputs, etc) and generates a list of file
|
|
# contents that should be written.
|
|
class Generator(ABC):
|
|
# namehint is a hint for naming, and each specific generator subclass will
|
|
# interpret it in its own way. In many cases it may be the single generated
|
|
# filename
|
|
def __init__(self, namehint, mods = None):
|
|
self.namehint = namehint
|
|
if mods is not None:
|
|
self.modules = mods
|
|
else:
|
|
self.modules = list()
|
|
|
|
def addVerilogModule(self, mod):
|
|
self.modules.append(mod)
|
|
|
|
# Generates a list of tuples containing output file names and output file
|
|
# contents to be written to disk
|
|
@abstractmethod
|
|
def emit(self):
|
|
pass
|
|
|
|
# Generates Blarney files
|
|
# When the namehint is not empty, it is used as the filename and .hs is appended
|
|
# otherwise the old default filename of CHERIBlarneyWrappers.hs is used
|
|
class BlarneyGenerator(Generator):
|
|
def emit(self):
|
|
modname = "CHERIBlarneyWrappers"
|
|
filename = modname + ".hs"
|
|
if self.namehint is not None and self.namehint != "":
|
|
modname = self.namehint
|
|
filename = self.namehint + ".hs"
|
|
|
|
contents = "module " + modname + " where\n\n"
|
|
contents += "import Blarney\n"
|
|
contents += "import Blarney.Core.BV\n"
|
|
for mod in self.modules:
|
|
print(mod.name)
|
|
contents += "\n"
|
|
ins_names = [x[0] for x in mod.ins]
|
|
ins_wdths = [x[1] for x in mod.ins]
|
|
str_type = "{:s} :: {:s}{:s}{:s}".format(
|
|
mod.name,
|
|
" -> ".join(["Bit {:d}".format(n) for n in ins_wdths]),
|
|
" -> " if mod.ins else "",
|
|
"Bit {:d}".format(mod.out[1]))
|
|
str_decl = "{:s} {:s} = FromBV $\n makePrim1 (Custom \"{:s}\" [{:s}] [{:s}] [] False Nothing) [{:s}]".format(
|
|
mod.name, " ".join(ins_names),
|
|
mod.verilogModuleName(),
|
|
", ".join(["(\"{:s}\", {:d})".format(n, w)
|
|
for (n, w) in zip(mod.verilogInputNames(),
|
|
ins_wdths)]),
|
|
"(\"{:s}\", {:d})".format(mod.verilogOutputName(), mod.out[1]),
|
|
", ".join(["toBV {:s}".format(nm) for nm in ins_names]))
|
|
contents += "{:s}\n{:s}".format(str_type, str_decl)
|
|
contents += "\n".format(str_decl)
|
|
|
|
|
|
return [(filename, contents)]
|
|
|
|
# generates SystemVerilog files
|
|
# when the namehint is non-empty it is used as a prefix for the file name
|
|
# generates a _pkg.sv file containing:
|
|
# a typedef of cheri_cap_t which is an "opaque" capability
|
|
# a typedef of cheri_cap_dec_t which is a decompressed capability
|
|
# generates a _mod.sv file containing a module which combinationally takes an "opaque"
|
|
# capability as the input and gives a decompressed capability as the output
|
|
class SystemVerilogGenerator(Generator):
|
|
def emit(self):
|
|
cap_type_name = "cheri_cap_t" # the name of the opaque cap type
|
|
cap_dec_type_name = "cheri_cap_dec_t" # the name of the expanded cap type
|
|
cap_dec_mod_name = "cheri_cap_expander" # the name of the expanding module
|
|
cap_in_signal_name = "cap_i" # the name of the input signal to the expanding module
|
|
cap_out_signal_name = "cap_o" # the name of the output signal to the expanding module
|
|
cap_search_string = "cap" # the string required for inferring capability width
|
|
|
|
pkg_file_name = "cheri_pkg.sv"
|
|
module_file_name = "{:s}.sv".format(cap_dec_mod_name)
|
|
|
|
# prepend namehint if non-empty
|
|
if self.namehint is not None and self.namehint != "":
|
|
pkg_file_name = self.namehint + "_" + pkg_file_name
|
|
module_file_name = self.namehint + "_" + module_file_name
|
|
|
|
pkg_name = pkg_file_name[:-3]
|
|
dec_mod_name = module_file_name[:-3]
|
|
|
|
# find the size of a capability by assuming that any no-input modules
|
|
# with "cap" in the name have a capability output
|
|
cap_size = None
|
|
for mod in self.modules:
|
|
if len(mod.ins) != 0 or cap_search_string not in mod.name.lower():
|
|
continue
|
|
cap_size = mod.out[1]
|
|
break
|
|
|
|
if cap_size == None:
|
|
# the above method failed to find a capability size
|
|
# to fix, can either implement a better method or just hard-code the capability size
|
|
raise NotImplementedError("Unable to determine capability size from input files")
|
|
|
|
|
|
cap_type_def_text = " typedef logic [{:d}:0] {:s};\n".format(cap_size-1, cap_type_name)
|
|
|
|
# assume all modules with one capability-sized input are "getters"
|
|
# these will be the fields of the decompressed capability struct
|
|
struct_elems = list()
|
|
for mod in self.modules:
|
|
if len(mod.ins) == 1 and mod.ins[0][1] == cap_size:
|
|
struct_elems.append(mod)
|
|
|
|
# structure definition
|
|
struct_def_text = " typedef struct packed {\n"
|
|
for mod in struct_elems:
|
|
struct_def_text += " logic [{:d}:{:d}] {:s};\n".format(mod.out[1]-1, 0, mod.name)
|
|
struct_def_text += " }} {:s};\n".format(cap_dec_type_name)
|
|
|
|
# package definition
|
|
pkg_def_text = "package {:s};\n".format(pkg_name)
|
|
pkg_def_text += cap_type_def_text
|
|
pkg_def_text += struct_def_text
|
|
pkg_def_text += "endpackage\n"
|
|
|
|
# module definition
|
|
module_def_text = "module {:s} (\n".format(cap_dec_mod_name)
|
|
module_def_text += " input {:s}::{:s} {:s},\n".format(pkg_name, cap_type_name, cap_in_signal_name)
|
|
module_def_text += " output {:s}::{:s} {:s}\n".format(pkg_name, cap_dec_type_name, cap_out_signal_name)
|
|
module_def_text += ");\n"
|
|
module_def_text += " import {:s}::*;\n".format(pkg_name)
|
|
|
|
# module instantiations
|
|
for mod in struct_elems:
|
|
module_def_text += " {:s} {:s}_mod (\n".format(mod.verilogModuleName(), mod.name)
|
|
module_def_text += " .{:s}({:s}),\n".format(mod.verilogInputNames()[0], cap_in_signal_name)
|
|
module_def_text += " .{:s}({:s}.{:s})\n".format(mod.verilogOutputName(), cap_out_signal_name, mod.name)
|
|
module_def_text += " );\n"
|
|
|
|
module_def_text += "endmodule\n"
|
|
|
|
return [(pkg_file_name, pkg_def_text),
|
|
(module_file_name, module_def_text)]
|
|
|
|
def main():
|
|
# define module regexp
|
|
modDecl = re.compile("^module\s+module_wrap(\d+)_(\w+)\(")
|
|
# TODO handle size 1
|
|
#
|
|
# gather the list of modules
|
|
wrappers = []
|
|
for fname in args.verilog_files:
|
|
size = 0
|
|
name = None
|
|
ins = []
|
|
out = ("",0)
|
|
with open(fname, "r") as f:
|
|
for ln in f:
|
|
modM = modDecl.match(ln)
|
|
if modM:
|
|
size = int(modM.group(1))
|
|
name = modM.group(2)
|
|
break
|
|
if not name:
|
|
print("Couldn't find a valid Verilog module definition")
|
|
exit(-1)
|
|
# define input/output regexp
|
|
inDecl = re.compile("^\s*input(\s+\[(\d+)\s+:\s+0\])?\s+wrap(\d+)_"+name+"_(\w+);")
|
|
outDecl = re.compile("^\s*output(\s+\[(\d+)\s+:\s+0\])?\s+wrap(\d+)_"+name+";")
|
|
|
|
for ln in f:
|
|
inM = inDecl.match(ln)
|
|
outM = outDecl.match(ln)
|
|
if inM:
|
|
ins.append((inM.group(4), (int(inM.group(2)) + 1) if inM.group(1) else 1))
|
|
elif outM:
|
|
out = (name, (int(outM.group(2)) + 1) if outM.group(1) else 1)
|
|
#else:
|
|
# print("===>> no match for line: {:s}".format(ln))
|
|
wrappers.append(Wrapper(size, name, ins, out))
|
|
|
|
# choose the right generator based on the input argument
|
|
gen = None
|
|
if args.generator.lower() in ["systemverilog", "sv"]:
|
|
gen = SystemVerilogGenerator(args.output, wrappers)
|
|
elif args.generator.lower() in ["blarney"]:
|
|
gen = BlarneyGenerator(args.output, wrappers)
|
|
else:
|
|
print("Invalid generator selected; exiting")
|
|
return
|
|
|
|
for out in gen.emit():
|
|
with open(out[0], "w") as f:
|
|
f.write(out[1])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|