From 2d6864bbf30c0da49f51147bf42d2fc686d662c2 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Fri, 13 Jan 2023 16:07:31 +0000 Subject: [PATCH 01/11] New wrap script to support more languages (inc SV) At the moment, the original Blarney support is kept and support for a SystemVerilog wrapper is added. --- CHERICapWrap.py | 235 +++++++++++++++++++++++++++++++++++++++++ CHERICapWrapBlarney.py | 93 ---------------- 2 files changed, 235 insertions(+), 93 deletions(-) create mode 100755 CHERICapWrap.py delete mode 100755 CHERICapWrapBlarney.py diff --git a/CHERICapWrap.py b/CHERICapWrap.py new file mode 100755 index 0000000..a220a97 --- /dev/null +++ b/CHERICapWrap.py @@ -0,0 +1,235 @@ +#! /usr/bin/env python3 + +import argparse +import re + +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='?', + 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) + def emit(self): + raise NotImplementedError("Please Implement this method") + + +# 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: + # 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 + def emit(self): + raise NotImplementedError("Method not implemented in subclass") + +# 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() diff --git a/CHERICapWrapBlarney.py b/CHERICapWrapBlarney.py deleted file mode 100755 index 43f5aa8..0000000 --- a/CHERICapWrapBlarney.py +++ /dev/null @@ -1,93 +0,0 @@ -#! /usr/bin/env python3 - -import argparse -import re - -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="CHERIBlarneyWrappers", - help='The output Blarney Haskell module to generate') -args = parser.parse_args() - -class BlarneyWrapper: - 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) - def emitBlarney(self): - ins_names = [x[0] for x in self.ins] - ins_wdths = [x[1] for x in self.ins] - str_type = "{:s} :: {:s}{:s}{:s}".format( - self.name, - " -> ".join(["Bit {:d}".format(n) for n in ins_wdths]), - " -> " if self.ins else "", - "Bit {:d}".format(self.out[1])) - str_decl = "{:s} {:s} = FromBV $\n makePrim1 (Custom \"{:s}\" [{:s}] [{:s}] [] False Nothing) [{:s}]".format( - self.name, " ".join(ins_names), - self.verilogModuleName(), - ", ".join(["(\"{:s}\", {:d})".format(n, w) - for (n, w) in zip(self.verilogInputNames(), - ins_wdths)]), - "(\"{:s}\", {:d})".format(self.verilogOutputName(), self.out[1]), - ", ".join(["toBV {:s}".format(nm) for nm in ins_names])) - return "{:s}\n{:s}".format(str_type, str_decl) - -def main(): - # define module regexp - modDecl = re.compile("^module\s+module_wrap(\d+)_(\w+)\(") - # TODO handle size 1 - # - 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(BlarneyWrapper(size, name, ins, out)) - - with open(args.output+".hs", "w") as f: - #print("module CHERI{:d} where\n".format(size)) - f.write("module "+args.output+" where\n\n") - f.write("import Blarney\n") - f.write("import Blarney.Core.BV\n") - for w in wrappers: - f.write("\n{:s}\n".format(w.emitBlarney())) - -if __name__ == "__main__": - main() From a6664e512cc6368beae22506e4abb6c067791616 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Wed, 18 Jan 2023 18:04:52 +0000 Subject: [PATCH 02/11] Implement changes suggested by @gameboo + bugfix Uses argparse's "choices" argument, remove unused method and use abc and for abstract methods. Also updates the python filename in the Makefile --- CHERICapWrap.py | 10 +++++----- Makefile | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHERICapWrap.py b/CHERICapWrap.py index a220a97..d59b52e 100755 --- a/CHERICapWrap.py +++ b/CHERICapWrap.py @@ -2,6 +2,7 @@ import argparse import re +from abc import ABC, abstractmethod parser = argparse.ArgumentParser(description= '''Generates a Blarney wrapper for the given Bluespec generated verilog file @@ -13,7 +14,7 @@ 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='?', - default='Blarney', + choices=['blarney','sv','systemverilog'], default='blarney', help='The generator to be used') args = parser.parse_args() @@ -31,8 +32,6 @@ class Wrapper: for nm in [x[0] for x in self.ins]] def verilogOutputName(self): return "wrap{:d}_{:s}".format(self.size, self.name) - def emit(self): - raise NotImplementedError("Please Implement this method") # Generic generator class @@ -40,7 +39,7 @@ class Wrapper: # 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: +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 @@ -56,8 +55,9 @@ class Generator: # Generates a list of tuples containing output file names and output file # contents to be written to disk + @abstractmethod def emit(self): - raise NotImplementedError("Method not implemented in subclass") + pass # Generates Blarney files # When the namehint is not empty, it is used as the filename and .hs is appended diff --git a/Makefile b/Makefile index 10591f2..d6d327a 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ all: verilog-wrappers blarney-wrappers verilog-wrappers: CHERICapWrap.bsv CHERICap.bsv CHERICC_Fat.bsv bsc $(BSCFLAGS) -verilog -u $< -blarney-wrappers: CHERICapWrapBlarney.py verilog-wrappers - ./CHERICapWrapBlarney.py -o CHERIBlarneyWrappers *.v +blarney-wrappers: CHERICapWrap.py verilog-wrappers + ./CHERICapWrap.py -o CHERIBlarneyWrappers *.v .PHONY: clean clean-verilog-wrappers From fcadf1aead7dbed08673c2b60ea2d8ddd405a7d8 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Sat, 30 Sep 2023 16:52:15 +0100 Subject: [PATCH 03/11] Add barebones fusesoc generator core This adds a fusesoc generator to create the verilog files from the bluespec source. --- fusesoc/cheri-cap-lib-verilog-generator.core | 15 ++++ fusesoc/fusesoc-script.py | 92 ++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 fusesoc/cheri-cap-lib-verilog-generator.core create mode 100644 fusesoc/fusesoc-script.py diff --git a/fusesoc/cheri-cap-lib-verilog-generator.core b/fusesoc/cheri-cap-lib-verilog-generator.core new file mode 100644 index 0000000..f9c7d9b --- /dev/null +++ b/fusesoc/cheri-cap-lib-verilog-generator.core @@ -0,0 +1,15 @@ +CAPI=2: +name: "ucam:cheri:cheri-cap-lib-verilog-generator" +description: "Generates Verilog versions of the cheri-cap-lib functions" + +# the only parameter that this generator takes from the yaml file is +# capwidth, which will be defined as a macro during bluespec compilation +# the generated core file will have the name: +# "ucam:cheri:cheri-cap-lib-verilog-autogen-$WIDTH" +# where $WIDTH is the number after CAP in the capwidth parameter +generators: + cheri-cap-lib-gen: + interpreter: python3 + command: fusesoc-script.py + # TODO caching? for now, no + diff --git a/fusesoc/fusesoc-script.py b/fusesoc/fusesoc-script.py new file mode 100644 index 0000000..72b80fa --- /dev/null +++ b/fusesoc/fusesoc-script.py @@ -0,0 +1,92 @@ +import yaml +import subprocess +import argparse +import shutil +import os +import glob + +# get yaml file location from single argument +argparser = argparse.ArgumentParser() +argparser.add_argument('yaml_file') + +args = argparser.parse_args() + +# load yaml file +with open(args.yaml_file, "r") as yamlfile: + try: + print(yamlfile) + yamlcfg = yaml.safe_load(yamlfile) + except yaml.YAMLError as exc: + print(exc) + + +# TODO debug +print(yamlcfg) +print(yamlcfg['parameters']['capwidth']) + +# use capwidth if provided, or default to CAP64 +cfgwidth = yamlcfg["parameters"]["capwidth"] +capwidth = "CAP64" if cfgwidth in [None, ""] else cfgwidth + +# TODO debug +print("capwidth:") +print(capwidth) + +# get current working directory for file generation +workdir = os.getcwd() + +# location of source files is actually one level above the location of the core file +bsv_src_root = yamlcfg["files_root"] + "/.." + +# set up bluespec command + arguments +bscargs = list() +bscargs.extend(["bsc"]) +bscargs.extend(["-vdir", workdir]) +bscargs.extend(["-bdir", workdir]) +bscargs.extend(["-simdir", workdir]) +bscargs.extend(["-D", "{}".format(capwidth)]) +bscargs.extend(["-D", "RISCV"]) +bscargs.extend(["-verilog"]) +bscargs.extend(["-u"]) +bscargs.extend(["CHERICapWrap.bsv"]) +# TODO debug +print(bscargs) + +# blocking call to bsc to generate verilog files +subprocess.call(bscargs, cwd=bsv_src_root) + + + + +print("CCL AUTOGEN NAME:") +print("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:])) + + +# collect verilog files +vfiles = glob.glob(os.path.join(os.getcwd(), "*.v")) +print("verilog files:") +print(vfiles) + +# write core file +with open("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:]), 'w') as f: + txt = 'CAPI=2:\n' + txt += 'name: "ucam:cheri:cheri-cap-lib-verilog-autogen-{}"\n'.format(capwidth[3:]) + txt += 'description: "Autogenerated Verilog & SystemVerilog versions of the cheri-cap-lib functions"\n\n' + + txt += 'filesets:\n' + txt += ' files_v:\n' + txt += ' files:\n' + txt += ' - ' + txt += '\n - '.join([os.path.basename(file) for file in vfiles]) + txt += '\n' + txt += ' file_type: verilogSource\n' + txt += '\n' + + txt += 'targets:\n' + txt += ' default:\n' + txt += ' description: "Default target that contains the Verilog files"\n' + txt += ' filesets:\n' + txt += ' - files_v\n' + + f.write(txt) + From 61e7b3e668bf5f0c82466d0d4e10151b872d10bc Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Sat, 30 Sep 2023 17:09:56 +0100 Subject: [PATCH 04/11] Add test core and targets To test, run: fusesoc --cores-root . run --setup --target test ucam:cheri:cheri-cap-lib-test --- fusesoc/cheri-cap-lib-test.core | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 fusesoc/cheri-cap-lib-test.core diff --git a/fusesoc/cheri-cap-lib-test.core b/fusesoc/cheri-cap-lib-test.core new file mode 100644 index 0000000..ffadd87 --- /dev/null +++ b/fusesoc/cheri-cap-lib-test.core @@ -0,0 +1,50 @@ +CAPI=2: +name: "ucam:cheri:cheri-cap-lib-test" +description: "Targets to test cheri-cap-lib generator" + +# different parameterizations of the generator, one is 64bit and the other is 128bit +generate: + cheri-cap-lib-64: + generator: cheri-cap-lib-gen + parameters: + capwidth : "CAP64" + + cheri-cap-lib-128: + generator: cheri-cap-lib-gen + parameters: + capwidth : "CAP128" + +# cores need to explicitly depend on the generator, so an empty fileset with +# a dependency on the generator's core is needed, like so: +filesets: + cheri-cap-lib-dep: + depend: + - ucam:cheri:cheri-cap-lib-verilog-generator + +# the filelist can then be placed in the filesets section of the dependant core +# to add the dependency, so that fusesoc can find the generator + +targets: + test64: + description: "Default target that generates 64bit versions" + default_tool: verilator + filesets: + - cheri-cap-lib-dep + generate: + - cheri-cap-lib-64 + test128: + description: "Default target that generates 128bit versions" + default_tool: verilator + filesets: + - cheri-cap-lib-dep + generate: + - cheri-cap-lib-128 + test: + description: "Default target that generates both 64bit and 128bit versions" + default_tool: verilator + filesets: + - cheri-cap-lib-dep + generate: + - cheri-cap-lib-64 + - cheri-cap-lib-128 + From fd1f7c12a297c0f50f4533578b1261c3de05eb6c Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Sat, 30 Sep 2023 18:01:28 +0100 Subject: [PATCH 05/11] Add SystemVerilog wrappers to generator --- fusesoc/fusesoc-script.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fusesoc/fusesoc-script.py b/fusesoc/fusesoc-script.py index 72b80fa..a3b066a 100644 --- a/fusesoc/fusesoc-script.py +++ b/fusesoc/fusesoc-script.py @@ -67,6 +67,21 @@ vfiles = glob.glob(os.path.join(os.getcwd(), "*.v")) print("verilog files:") print(vfiles) +# generate systemverilog files +subprocess.call(["python3", bsv_src_root + "/CHERICapWrap.py", "--generator", "sv"] + vfiles, cwd=workdir) + +# collect systemverilog files and package files +pkgfiles = glob.glob(os.path.join(os.getcwd(), "*_pkg.sv")) +svfiles = glob.glob(os.path.join(os.getcwd(), "*.sv")) +# +# remove package files from systemverilog files +for pkgfile in pkgfiles: + svfiles.remove(pkgfile) + +print("svfiles and pkgfiles:") +print(svfiles) +print(pkgfiles) + # write core file with open("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:]), 'w') as f: txt = 'CAPI=2:\n' @@ -74,12 +89,24 @@ with open("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:]), 'w') as txt += 'description: "Autogenerated Verilog & SystemVerilog versions of the cheri-cap-lib functions"\n\n' txt += 'filesets:\n' + txt += ' files_pkg:\n' + txt += ' files:\n' + txt += ' - ' + txt += '\n - '.join([os.path.basename(file) for file in pkgfiles]) + txt += '\n' + txt += ' file_type: systemVerilogSource\n' txt += ' files_v:\n' txt += ' files:\n' txt += ' - ' txt += '\n - '.join([os.path.basename(file) for file in vfiles]) txt += '\n' txt += ' file_type: verilogSource\n' + txt += ' files_sv:\n' + txt += ' files:\n' + txt += ' - ' + txt += '\n - '.join([os.path.basename(file) for file in svfiles]) + txt += '\n' + txt += ' file_type: systemVerilogSource\n' txt += '\n' txt += 'targets:\n' @@ -87,6 +114,9 @@ with open("cheri-cap-lib-verilog-autogen-{}.core".format(capwidth[3:]), 'w') as txt += ' description: "Default target that contains the Verilog files"\n' txt += ' filesets:\n' txt += ' - files_v\n' + txt += ' - files_pkg\n' + txt += ' - files_sv\n' f.write(txt) + From 06eff948d9461082ed7bba385d335b05c8e1a220 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Sat, 30 Sep 2023 18:24:52 +0100 Subject: [PATCH 06/11] Fix bsv_src_root files_root is actually the directory where the fusesoc command was executed, but what we want here is the location of the python file being executed so we can find the bluespec sources --- fusesoc/fusesoc-script.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fusesoc/fusesoc-script.py b/fusesoc/fusesoc-script.py index a3b066a..59a7522 100644 --- a/fusesoc/fusesoc-script.py +++ b/fusesoc/fusesoc-script.py @@ -36,7 +36,9 @@ print(capwidth) workdir = os.getcwd() # location of source files is actually one level above the location of the core file -bsv_src_root = yamlcfg["files_root"] + "/.." +bsv_src_root = os.path.join(os.path.dirname(__file__), "..") +print("bsv_src_root") +print(bsv_src_root) # set up bluespec command + arguments bscargs = list() From a8898154d4661803db0c8a6eafc85ab734d4d5d5 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Sat, 30 Sep 2023 18:27:35 +0100 Subject: [PATCH 07/11] Add functions to CHERICapWrap.bsv --- CHERICapWrap.bsv | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHERICapWrap.bsv b/CHERICapWrap.bsv index 0c5402a..53b297b 100644 --- a/CHERICapWrap.bsv +++ b/CHERICapWrap.bsv @@ -95,5 +95,11 @@ function Bool `W(validAsType) (`CAPTYPE dummy, Bit#(CapAddrW) checkType) = valid function `CAPTYPE `W(fromMem) (Tuple2#(Bool, Bit#(CapW)) mem_cap) = fromMem(mem_cap); (* noinline *) function Tuple2#(Bool, Bit#(CapW)) `W(toMem) (`CAPTYPE cap) = toMem(cap); +(* noinline *) +function Bit#(CapAddrW) `W(getRepresentableAlignmentMask) (`CAPTYPE dummy, Bit#(CapAddrW) length) = alignmentMask (dummy, length); +(* noinline *) +function Bit#(CapAddrW) `W(getRepresentableLength) (`CAPTYPE dummy, Bit#(CapAddrW) length) = roundLength (dummy, length); +(* noinline *) +function Bit#(2) `W(getBaseAlignment) (`CAPTYPE cap) = getBaseAlignment(cap); endpackage From ed0e4dfa65bdbeaef7526bfe18966f2ab565a930 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Mon, 2 Oct 2023 17:25:31 +0100 Subject: [PATCH 08/11] Add capability size to name of modules and files --- CHERICapWrap.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/CHERICapWrap.py b/CHERICapWrap.py index d59b52e..5ce9dfc 100755 --- a/CHERICapWrap.py +++ b/CHERICapWrap.py @@ -104,16 +104,24 @@ class BlarneyGenerator(Generator): # 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 +# In the filename, "cheri" is appended with the in-memory capability width +# (i.e. the filename will be "cheri64_pkg.sv") so that differently sized capabilities +# can exist 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 + # get the size of in-memory capability by assuming that the name of the + # modules matches "module_wrap$SIZE_..." and taking just the size part + in_mem_cap_size = self.modules[0].verilogModuleName()[11:] + in_mem_cap_size = in_mem_cap_size[:in_mem_cap_size.find("_")] - pkg_file_name = "cheri_pkg.sv" + 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{:s}_cap_expander".format(in_mem_cap_size) # 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{:s}_pkg.sv".format(in_mem_cap_size) module_file_name = "{:s}.sv".format(cap_dec_mod_name) # prepend namehint if non-empty From e4e1ed4c69831e51a2fe3ec460460687abaaf29a Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Mon, 2 Oct 2023 18:03:06 +0100 Subject: [PATCH 09/11] Add some documentation regarding FuseSoC --- fusesoc/cheri-cap-lib-test.core | 6 ++++++ readme.adoc | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/fusesoc/cheri-cap-lib-test.core b/fusesoc/cheri-cap-lib-test.core index ffadd87..48a03bd 100644 --- a/fusesoc/cheri-cap-lib-test.core +++ b/fusesoc/cheri-cap-lib-test.core @@ -28,8 +28,12 @@ targets: test64: description: "Default target that generates 64bit versions" default_tool: verilator + # this adds a dependency on the generator, so that it appears on the + # dependency tree filesets: - cheri-cap-lib-dep + # this invokes the 64bit parameterization of the generator which is + # declared above generate: - cheri-cap-lib-64 test128: @@ -44,6 +48,8 @@ targets: default_tool: verilator filesets: - cheri-cap-lib-dep + # this invokes both the 64bit and 128bit parameterizations of the generator + # which is declared above generate: - cheri-cap-lib-64 - cheri-cap-lib-128 diff --git a/readme.adoc b/readme.adoc index 5dcd9be..d803cef 100644 --- a/readme.adoc +++ b/readme.adoc @@ -48,3 +48,22 @@ toc::[] == The CHERI CAP LIB API include::CHERI_CAP_API.adoc[] + +== FuseSoC + +``cheri-cap-lib`` supports being used as a generator within FuseSoC. The scripts +and cores for this are within the ``fusesoc`` directory, and the file +``cheri-cap-lib-test.core`` shows how a core can declare a dependency on and +use the ``cheri-cap-lib`` generator. + +The FuseSoC generator will generate Verilog modules for each of the functions +in ``CHERICapWrap.bsv``, as well as a SystemVerilog package and module to +make working with these modules _slightly_ simpler in SystemVerilog. + +To generate both the 64bit and 128bit wrappers and packages, run +``fusesoc --cores-root . run --target test --setup ucam:cheri:cheri-cap-lib-test`` + +The files will be generated in +``build/ucam_cheri_cheri-cap-lib-test_0/test-verilator/generated/ucam_cheri_cheri-cap-lib-test-cheri-cap-lib-64_0/`` +and +``build/ucam_cheri_cheri-cap-lib-test_0/test-verilator/generated/ucam_cheri_cheri-cap-lib-test-cheri-cap-lib-128_0/`` From f3284354d2d4ec9457ad66c18e2e07dc1acb590e Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Wed, 4 Oct 2023 19:58:48 +0100 Subject: [PATCH 10/11] Remove "is" and "get" from generated SV fields This should make using the fields a little prettier --- CHERICapWrap.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/CHERICapWrap.py b/CHERICapWrap.py index 5ce9dfc..30659fb 100755 --- a/CHERICapWrap.py +++ b/CHERICapWrap.py @@ -121,6 +121,24 @@ class SystemVerilogGenerator(Generator): 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 + + # this wrapper generator is intended to make access to the "getter" + # modules (i.e. getAddr, getTop, etc) easier/cleaner + # ideally, to access the field it is cleaner to say "cap.address" rather than + # "cap.getAddress" or "cap.validCap" rather than "cap.isValidCap" + # the following is a list of "keywords" to remove from the field names + keywords_to_remove = ["get", "is"] + def mod_name_to_field_name(modname): + for kw in keywords_to_remove: + if modname.startswith(kw): + # remove the keyword from the start + modname = modname[len(kw):] + # make first letter lowercase + modname = modname[0].lower() + modname[1:] + # return after removing the first found keyword + return modname + return modname + pkg_file_name = "cheri{:s}_pkg.sv".format(in_mem_cap_size) module_file_name = "{:s}.sv".format(cap_dec_mod_name) @@ -159,7 +177,7 @@ class SystemVerilogGenerator(Generator): # 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 += " logic [{:d}:{:d}] {:s};\n".format(mod.out[1]-1, 0, mod_name_to_field_name(mod.name)) struct_def_text += " }} {:s};\n".format(cap_dec_type_name) # package definition @@ -179,7 +197,7 @@ class SystemVerilogGenerator(Generator): 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 += " .{:s}({:s}.{:s})\n".format(mod.verilogOutputName(), cap_out_signal_name, mod_name_to_field_name(mod.name)) module_def_text += " );\n" module_def_text += "endmodule\n" From 321c985fc8ad852c8557f81134886937cd4bdc05 Mon Sep 17 00:00:00 2001 From: Ivan Ribeiro Date: Sun, 8 Oct 2023 14:06:04 +0100 Subject: [PATCH 11/11] Update fusesoc docs per version of OT repo used The previous documentation was using the python requirements from the ibex repository rather than the OpenTitan repo. --- readme.adoc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/readme.adoc b/readme.adoc index d803cef..2980af3 100644 --- a/readme.adoc +++ b/readme.adoc @@ -64,6 +64,14 @@ To generate both the 64bit and 128bit wrappers and packages, run ``fusesoc --cores-root . run --target test --setup ucam:cheri:cheri-cap-lib-test`` The files will be generated in -``build/ucam_cheri_cheri-cap-lib-test_0/test-verilator/generated/ucam_cheri_cheri-cap-lib-test-cheri-cap-lib-64_0/`` +``build/ucam_cheri_cheri-cap-lib-test_0/src/ucam_cheri_cheri-cap-lib-verilog-autogen-64_0/`` and -``build/ucam_cheri_cheri-cap-lib-test_0/test-verilator/generated/ucam_cheri_cheri-cap-lib-test-cheri-cap-lib-128_0/`` +``build/ucam_cheri_cheri-cap-lib-test_0/src/ucam_cheri_cheri-cap-lib-verilog-autogen-128_0/`` + +These fusesoc cores and instructions work using fusesoc 0.3 as installed +by running +``pip3 install -r python_requirements.txt`` +within the OpenTitan GitHub repository as of commit +https://github.com/lowRISC/opentitan/commit/2438b17afe4fef0d815be2e890763c288c449918 + +