Add in wrapper generation code (build_sw command). Previous versions
were tested, but this instance hasn't been yet.
This commit is contained in:
parent
f93b1b877e
commit
249ed20178
|
@ -1,3 +1,4 @@
|
|||
*.o
|
||||
*.a
|
||||
cf-config.yaml
|
||||
*.swp
|
||||
|
|
|
@ -15,6 +15,7 @@ import pathlib
|
|||
# centrifuge
|
||||
import pkg.util as util
|
||||
import pkg.buildaccel as buildaccel
|
||||
import pkg.buildsw as buildsw
|
||||
|
||||
from os.path import dirname as up
|
||||
|
||||
|
@ -81,6 +82,8 @@ def main(args):
|
|||
buildaccel.generate_hw(accel_config)
|
||||
elif args.task == 'clean_hw':
|
||||
buildaccel.clean_hw(accel_config)
|
||||
elif args.task == 'generate_sw':
|
||||
buildsw.generateSW(accel_config)
|
||||
else:
|
||||
print("Command: " + str(args.task) + " not yet implemented")
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from .generate_wrapper import *
|
|
@ -0,0 +1,351 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
import pathlib
|
||||
import collections
|
||||
import logging
|
||||
from .. import util
|
||||
|
||||
class MmioArg():
|
||||
def __init__(self, name, addr, size=1):
|
||||
self.name = name
|
||||
self.addr = int(addr, 0)
|
||||
self.size = size
|
||||
|
||||
def incrementSize(self):
|
||||
if self.size < 2:
|
||||
self.size += 1
|
||||
else:
|
||||
raise RuntimeError("Invalid size for argument " + self.name + ": " + str(self.size + 1))
|
||||
|
||||
def cType(self):
|
||||
"""Returns a string representing the C type of this argument"""
|
||||
if self.size == 0:
|
||||
return "void"
|
||||
elif self.size == 1:
|
||||
return "uint32_t"
|
||||
elif self.size == 2:
|
||||
return "uint64_t"
|
||||
else:
|
||||
raise RuntimeError("Unsupported variable size: " + str(self.size))
|
||||
|
||||
def generateHeader(signature):
|
||||
"""Given the signature of the accelerated function, return an appropriate
|
||||
header file. """
|
||||
|
||||
header = ("#ifndef ACCEL_WRAPPER_H\n"
|
||||
"#define ACCEL_WRAPPER_H\n")
|
||||
|
||||
header += '\n'
|
||||
header += signature + ";\n"
|
||||
header += "#endif"
|
||||
return header
|
||||
|
||||
def ident(level):
|
||||
"""Return a string of spaces representing an indentation to level 'level'"""
|
||||
return " "*n
|
||||
|
||||
def cleanRoccArg(body):
|
||||
"""Cleans up a RoccArg. Returns True if the argument was cleaned and can be
|
||||
used, returns False if the argument should be ignored."""
|
||||
|
||||
reIgnore = re.compile('ap_clk.*|ap_rst.*|\S+_req_full_n|\S+_rsp_empty_n')
|
||||
reBaseName = re.compile('ap_(\S+)|(\S+)_datain')
|
||||
|
||||
if reIgnore.match(body):
|
||||
return None
|
||||
|
||||
m = reBaseName.match(body)
|
||||
if not m:
|
||||
raise ValueError("Could not parse argument name: " + body)
|
||||
|
||||
return m.group(m.lastindex)
|
||||
|
||||
def parseVerilogRocc(vpath):
|
||||
"""Parse a centrifuge-generated verilog file to extract the information
|
||||
needed to generate a RoCC wrapper.
|
||||
|
||||
vpath: Path to main verilog function file
|
||||
|
||||
Returns: (inputs, retVal)
|
||||
inputs - list of argument names
|
||||
retVal - boolean indicating whether or not a return value is present
|
||||
"""
|
||||
|
||||
# Input/Output statements in the verilog. We assume only one module in the file.
|
||||
reInput = re.compile('^\s*input\s+\[.*:.*\]\s*(.*)')
|
||||
reReturnVal = re.compile('^\s*output\s+\[(.*):(.*)\]\s*ap_return;')
|
||||
|
||||
print("Parsing: ",vpath)
|
||||
inputs = []
|
||||
retVal = False
|
||||
with open(vpath, 'r') as vf:
|
||||
for line in vf.readlines():
|
||||
inMatch = reInput.match(line)
|
||||
if inMatch:
|
||||
argName = cleanRoccArg(inMatch.group(1))
|
||||
if argName:
|
||||
inputs.append(argName)
|
||||
else:
|
||||
if reReturnVal.match(line):
|
||||
retVal = True
|
||||
|
||||
return inputs, retVal
|
||||
|
||||
def parseVerilogTL(vpath):
|
||||
"""Parse a centrifuge-generated verilog file to extract the information
|
||||
needed to generate tilelink wrappers.
|
||||
|
||||
vpath: Path to the verilog file containing control signal info (path-like object)
|
||||
|
||||
returns: (returnSize, Args)
|
||||
retVal: MmioArg representing the return value (or None if no return).
|
||||
Args: List of MmioArg representing the arguments to the accelerated function
|
||||
"""
|
||||
with open(vpath, 'r') as vf:
|
||||
print("Parsing: ",vpath)
|
||||
reStart = re.compile("^//------------------------Address Info------------------")
|
||||
reEnd = re.compile("^//------------------------Parameter----------------------")
|
||||
reAddr = re.compile("(0x\S+) : Data signal of (\S+)")
|
||||
|
||||
inHeader = False
|
||||
args = collections.OrderedDict()
|
||||
retVal = None
|
||||
for line in vf.readlines():
|
||||
if not inHeader:
|
||||
if reStart.match(line):
|
||||
inHeader = True
|
||||
else:
|
||||
if reEnd.match(line):
|
||||
break
|
||||
else:
|
||||
m = reAddr.search(line)
|
||||
if m:
|
||||
name = m.group(2)
|
||||
addr = m.group(1)
|
||||
if name == 'ap_return':
|
||||
if retVal is None:
|
||||
retVal = MmioArg(name, addr)
|
||||
else:
|
||||
retVal.incrementSize()
|
||||
elif name in args:
|
||||
args[name].incrementSize()
|
||||
else:
|
||||
args[name] = MmioArg(name, addr)
|
||||
|
||||
return (list(args.values()), retVal)
|
||||
|
||||
def generateWrapperRocc(fname, roccIdx, inputs, retVal):
|
||||
"""Returns a Rocc C wrapper given a function.
|
||||
|
||||
fname - name of the function
|
||||
inputs - list of argument names
|
||||
retVal - boolean indicating whether or not a value is returned
|
||||
"""
|
||||
|
||||
# current indentation level
|
||||
lvl = 0
|
||||
|
||||
cWrapper = ('#include "rocc.h"\n'
|
||||
'\n'
|
||||
'#define ACCEL_WRAPPER\n'
|
||||
'#include "accel.h"\n'
|
||||
'\n')
|
||||
|
||||
cWrapper += ident(lvl) + "#define XCUSTOM_ACC " + roccIdx + "\n"
|
||||
cWrapper += "\n"
|
||||
|
||||
signature = ""
|
||||
if retVal:
|
||||
retStr = "uint64_t"
|
||||
else:
|
||||
retStr = "void"
|
||||
|
||||
signature += retStr + " " + fname + "("
|
||||
argStrs = []
|
||||
for arg in inputs:
|
||||
argStrs.append("uint64_t " + arg)
|
||||
signature += ", ".join(argStrs)
|
||||
signature += ")"
|
||||
cWrapper += signature + "\n"
|
||||
cWrapper += "{\n"
|
||||
|
||||
lvl = 1
|
||||
if retVal:
|
||||
cWrapper += ident(lvl) + "uint64_t ret_val;\n"
|
||||
cWrapper += "\n"
|
||||
|
||||
if len(inputs) == 0:
|
||||
cWrapper += ident(lvl) + "ROCC_INSTRUCTION_D(XCUSTOM_ACC, ret_val, 0);\n"
|
||||
elif len(inputs) == 1:
|
||||
cWrapper += ident(lvl) + "ROCC_INSTRUCTION_DS(XCUSTOM_ACC, ret_val, " + inputs[0] + ", 0);\n"
|
||||
elif len(inputs) == 2:
|
||||
cWrapper += ident(lvl) + "ROCC_INSTRUCTION_DSS(XCUSTOM_ACC, ret_val, " + inputs[0] + ", " + inputs[1] + ", 0);\n"
|
||||
else:
|
||||
raise ValueError("Too many inputs. Rocc only supports up to 2 arguments, was passed " + len(inputs))
|
||||
|
||||
else:
|
||||
if len(inputs) == 0:
|
||||
cWrapper += ident(lvl) + "ROCC_INSTRUCTION(XCUSTOM_ACC, 0);\n"
|
||||
elif len(inputs) == 1:
|
||||
cWrapper += ident(lvl) + "ROCC_INSTRUCTION_S(XCUSTOM_ACC, " + inputs[0] + ", 0);\n"
|
||||
elif len(inputs) == 2:
|
||||
cWrapper += ident(lvl) + "ROCC_INSTRUCTION_SS(XCUSTOM_ACC, " + inputs[0] + ", 0);\n"
|
||||
else:
|
||||
raise ValueError("Too many inputs. Rocc only supports up to 2 arguments, was passed " + len(inputs))
|
||||
|
||||
cWrapper += ident(lvl) + "ROCC_BARRIER();\n"
|
||||
|
||||
cWrapper += '\n'
|
||||
if retVal:
|
||||
cWrapper += ident(lvl) + "return ret_val;\n"
|
||||
|
||||
cWrapper += "}"
|
||||
|
||||
return cWrapper, generateHeader(signature)
|
||||
|
||||
def generateWrapperTL(fname, baseAddr, args, retVal):
|
||||
"""Given a set of mmio address/varialble pairs, produce the C wrapper
|
||||
(returned as a string).
|
||||
|
||||
fname: Name to use for the function
|
||||
baseAddr: MMIO base address to use
|
||||
retVal: MmioArg representing the return value (may be None)
|
||||
args: List of MmioArg representing the function inputs
|
||||
"""
|
||||
|
||||
cWrapper = ('#include "mmio.h"\n'
|
||||
'#define ACCEL_WRAPPER\n'
|
||||
'#include "accel.h"\n'
|
||||
'\n'
|
||||
'#define AP_DONE_MASK 0b10\n')
|
||||
|
||||
# MMIO Constants
|
||||
cWrapper += "#define ACCEL_BASE " + str(baseAddr) + "\n"
|
||||
cWrapper += "#define ACCEL_INT 0x4\n"
|
||||
for arg in args + ([retVal] if retVal is not None else []):
|
||||
cWrapper += "#define ACCEL_"+arg.name+"_0 "+ hex(arg.addr) + "\n"
|
||||
if arg.size == 2:
|
||||
cWrapper += "#define ACCEL_"+arg.name+"_1 " + hex(arg.addr + 0x4) + "\n"
|
||||
cWrapper += "\n"
|
||||
|
||||
# Create the function signature
|
||||
signature = ""
|
||||
if retVal is None:
|
||||
retStr = "void"
|
||||
else:
|
||||
retStr = retVal.cType()
|
||||
|
||||
signature += retStr + " " + fname + "("
|
||||
argStrs = []
|
||||
for arg in args:
|
||||
argStrs.append(arg.cType() + " " + arg.name)
|
||||
signature += ", ".join(argStrs)
|
||||
signature += ")"
|
||||
|
||||
cWrapper += signature + "\n"
|
||||
cWrapper += "{\n"
|
||||
|
||||
# Pass Args to MMIO
|
||||
cWrapper += " //Disable Interrupts\n"
|
||||
cWrapper += " reg_write32(ACCEL_BASE + ACCEL_INT, 0x0);\n"
|
||||
for arg in args:
|
||||
cWrapper += " reg_write32(ACCEL_BASE + ACCEL_"+arg.name+"_0, (uint32_t) "+arg.name+");\n"
|
||||
if arg.size == 2:
|
||||
cWrapper += " reg_write32(ACCEL_BASE + ACCEL_"+arg.name+"_1, (uint32_t) ("+arg.name+" >> 32));\n"
|
||||
|
||||
# Execute Accelerator
|
||||
cWrapper += (" // Write to ap_start to start the execution \n"
|
||||
" reg_write32(ACCEL_BASE, 0x1);\n"
|
||||
"\n"
|
||||
" // Done?\n"
|
||||
" int done = 0;\n"
|
||||
" while (!done){\n"
|
||||
" done = reg_read32(ACCEL_BASE) & AP_DONE_MASK;\n"
|
||||
" }\n")
|
||||
|
||||
# Handle returns (if any)
|
||||
if retVal is not None:
|
||||
cWrapper += "\n"
|
||||
cWrapper += " " + retVal.cType() + " ret_val = 0;\n"
|
||||
cWrapper += " ret_val = reg_read32(ACCEL_BASE + ACCEL_"+retVal.name+"_0);\n"
|
||||
if retVal.size == 2:
|
||||
cWrapper += " ret_val |= reg_read32(ACCEL_BASE + ACCEL_"+retVal.name+"_1) >> 32;\n"
|
||||
|
||||
cWrapper += "}"
|
||||
|
||||
return cWrapper, generateHeader(signature)
|
||||
|
||||
def generateSW(accels):
|
||||
"""Generate all software wrappers for the specified set of accelerators (util.AccelConfig)"""
|
||||
logger = logging.getLogger()
|
||||
|
||||
# Create the directory for generated outputs. We may overwrite things in
|
||||
# this directory as-needed.
|
||||
accels.gensw_dir.mkdir(exist_ok=True)
|
||||
|
||||
for accel in accels.rocc_accels:
|
||||
inputs, retVal = parseVerilogRocc(accel.verilog_dir / (accel.name + ".v"))
|
||||
cWrapper, hWrapper = generateWrapperRocc(accel.func, accel.rocc_insn_id, inputs, retVal)
|
||||
accel.wrapper_dir = accels.gensw_dir / accel.name
|
||||
accel.wrapper_dir.mkdir(exist_ok=True)
|
||||
|
||||
cPath = accel.wrapper_dir / (accel.name + '_rocc_wrapper.c')
|
||||
cPath.unlink(missing_ok=True)
|
||||
with open(cPath , 'w') as cF:
|
||||
cF.write(cWrapper)
|
||||
|
||||
hPath = accel.wrapper_dir / (accel.name + '_rocc_wrapper.h')
|
||||
hPath.unlink(missing_ok=True)
|
||||
with open(hPath , 'w') as hF:
|
||||
hF.write(hWrapper)
|
||||
|
||||
for accel in accels.tl_accels:
|
||||
funcArgs, retVal = parseVerilogTL(accel.verilog_dir / (accel.name + "_control_s_axi.v"))
|
||||
cWrapper, hWrapper = generateWrapperTL(accel.func, accel.base_addr, funcArgs, retVal)
|
||||
accel.wrapper_dir = accels.gensw_dir / accel.name
|
||||
accel.wrapper_dir.mkdir(exist_ok=True)
|
||||
|
||||
cPath = accel.wrapper_dir / (accel.name + '_tl_wrapper.c')
|
||||
cPath.unlink(missing_ok=True)
|
||||
with open(cPath , 'w') as cF:
|
||||
cF.write(cWrapper)
|
||||
|
||||
hPath = accel.wrapper_dir / (accel.name + '_tl_wrapper.h')
|
||||
hPath.unlink(missing_ok=True)
|
||||
with open(hPath , 'w') as hF:
|
||||
hF.write(hWrapper)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate software wrappers for a given centrifuge-generated function.")
|
||||
|
||||
parser.add_argument('-n', '--fname', required=True, help="Name of function to accelerate")
|
||||
parser.add_argument('-b', '--base', required=True, help="Base address of function (if tilelink), RoCC index (if rocc)")
|
||||
parser.add_argument('-p', '--prefix', default="", help="Optional prefix for function")
|
||||
parser.add_argument('-m', '--mode', required=True,
|
||||
help="Function integration mode (either 'tl' or 'rocc')")
|
||||
parser.add_argument('-s', '--source', required=True, type=pathlib.Path,
|
||||
help="Path to the source directory to use when generating (e.g. 'centrifuge/accel/hls_example_func/').")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.mode == 'tl':
|
||||
funcArgs, retVal = parseVerilogTL(
|
||||
args.source / 'src' / 'main' / 'verilog' / (args.prefix + args.fname + "_control_s_axi.v"))
|
||||
|
||||
cWrapper, hWrapper = generateWrapperTL(args.fname, args.base, funcArgs, retVal)
|
||||
elif args.mode == 'rocc':
|
||||
inputs, retVal = parseVerilogRocc(
|
||||
args.source / 'src' / 'main' / 'verilog' / (args.prefix + args.fname + ".v"))
|
||||
cWrapper, hWrapper = generateWrapperRocc(args.fname, args.base, inputs, retVal)
|
||||
else:
|
||||
raise NotImplementedError("Mode '" + args.mode + "' not supported.")
|
||||
|
||||
|
||||
with open(args.source / 'src' / 'main' / 'c' / 'accel_wrapper.c', 'w') as cF:
|
||||
cF.write(cWrapper)
|
||||
|
||||
with open(args.source / 'src' / 'main' / 'c' / 'accel_wrapper.h', 'w') as hF:
|
||||
hF.write(hWrapper)
|
||||
|
|
@ -60,6 +60,7 @@ class AccelConfig:
|
|||
|
||||
self.accel_name = self.accel_json_path.stem
|
||||
self.accel_json_dir = accel_json_path.parent
|
||||
self.gensw_dir = self.accel_json_dir / 'centrifuge_wrappers'
|
||||
self.hw_accel_dir = genhw_dir / self.accel_name
|
||||
self.accel_json = self.parse_json(self.accel_json_path)
|
||||
self.rocc_accels = []
|
||||
|
|
Loading…
Reference in New Issue