add autogenerated instantiations and connections for LA cores
This commit is contained in:
parent
8c645a5115
commit
7d98988b87
|
|
@ -36,6 +36,7 @@ class UARTInterface:
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
self.ser = serial.Serial(self.port, self.baudrate)
|
self.ser = serial.Serial(self.port, self.baudrate)
|
||||||
|
|
||||||
def read(self, bytes):
|
def read(self, bytes):
|
||||||
|
|
@ -50,29 +51,54 @@ class UARTInterface:
|
||||||
def rx_hdl(self):
|
def rx_hdl(self):
|
||||||
pkgutil.get_data(__name__, "rx_uart.sv").decode()
|
pkgutil.get_data(__name__, "rx_uart.sv").decode()
|
||||||
|
|
||||||
|
|
||||||
class IOCore:
|
class IOCore:
|
||||||
def __init__(self, config, interface):
|
def __init__(self, config, interface):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
|
|
||||||
class LogicAnalyzerCore:
|
class LogicAnalyzerCore:
|
||||||
def __init__(self, config, interface):
|
def __init__(self, config, interface):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
|
|
||||||
# load config
|
# load config
|
||||||
assert "sample_depth" in config, "Sample depth not found for logic analyzer core."
|
assert (
|
||||||
|
"sample_depth" in config
|
||||||
|
), "Sample depth not found for logic analyzer core."
|
||||||
self.sample_depth = config["sample_depth"]
|
self.sample_depth = config["sample_depth"]
|
||||||
|
|
||||||
assert "probes" in config, "No probe definitions found."
|
assert "probes" in config, "No probe definitions found."
|
||||||
assert len(config["probes"]) > 0, "Must specify at least one probe."
|
assert len(config["probes"]) > 0, "Must specify at least one probe."
|
||||||
|
|
||||||
for probe_name, probe_width in config["probes"].items():
|
for probe_name, probe_width in config["probes"].items():
|
||||||
assert probe_width > 0, f"Probe {probe_name} is of invalid width - it must be of at least width one."
|
assert (
|
||||||
|
probe_width > 0
|
||||||
|
), f"Probe {probe_name} is of invalid width - it must be of at least width one."
|
||||||
|
|
||||||
self.probes = config["probes"]
|
self.probes = config["probes"]
|
||||||
|
|
||||||
assert "triggers" in config, "No triggers found."
|
assert "triggers" in config, "No triggers found."
|
||||||
assert len(config["triggers"]) > 0, "Must specify at least one trigger."
|
assert len(config["triggers"]) > 0, "Must specify at least one trigger."
|
||||||
self.triggers = config["triggers"]
|
self.triggers = config["triggers"]
|
||||||
|
|
||||||
|
def inst(self):
|
||||||
|
hdl = f"""
|
||||||
|
la_core {self.name} (
|
||||||
|
.clk(),
|
||||||
|
|
||||||
|
.addr_i(),
|
||||||
|
.wdata_i(),
|
||||||
|
.rdata_i(),
|
||||||
|
.rw_i(),
|
||||||
|
.valid_i(),
|
||||||
|
|
||||||
|
.addr_o(),
|
||||||
|
.wdata_o(),
|
||||||
|
.rdata_o(),
|
||||||
|
.rw_o(),
|
||||||
|
.valid_o());\n\n"""
|
||||||
|
|
||||||
|
return hdl
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.interface.open()
|
self.interface.open()
|
||||||
|
|
@ -186,13 +212,13 @@ class Manta:
|
||||||
def __init__(self, config_filepath):
|
def __init__(self, config_filepath):
|
||||||
config = self.read_config_file(config_filepath)
|
config = self.read_config_file(config_filepath)
|
||||||
|
|
||||||
# set interface
|
# set interface
|
||||||
if "uart" in config:
|
if "uart" in config:
|
||||||
self.interface = UARTInterface(config["uart"])
|
self.interface = UARTInterface(config["uart"])
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unrecognized interface specified.")
|
raise ValueError("Unrecognized interface specified.")
|
||||||
|
|
||||||
# check that cores were provided
|
# check that cores were provided
|
||||||
assert "cores" in config, "No cores found."
|
assert "cores" in config, "No cores found."
|
||||||
assert len(config["cores"]) > 0, "Must specify at least one core."
|
assert len(config["cores"]) > 0, "Must specify at least one core."
|
||||||
|
|
||||||
|
|
@ -202,18 +228,21 @@ class Manta:
|
||||||
core = config["cores"][core_name]
|
core = config["cores"][core_name]
|
||||||
|
|
||||||
# make sure a type was specified for this core
|
# make sure a type was specified for this core
|
||||||
assert "type" in core, f"No type specified for core {core_name}."
|
assert "type" in core, f"No type specified for core {core_name}."
|
||||||
|
|
||||||
# add the core to ourself
|
# add the core to ourself
|
||||||
if core["type"] == "logic_analyzer":
|
if core["type"] == "logic_analyzer":
|
||||||
new_core = LogicAnalyzerCore(core, self.interface)
|
new_core = LogicAnalyzerCore(core, self.interface)
|
||||||
|
|
||||||
elif core["type"] == "io":
|
elif core["type"] == "io":
|
||||||
new_core = IOCore(core, self.interface)
|
new_core = IOCore(core, self.interface)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unrecognized core type specified for {core_name}.")
|
raise ValueError(f"Unrecognized core type specified for {core_name}.")
|
||||||
|
|
||||||
|
# TODO: update class defs so that we don't monkey-patch like this. this is not good. i am lazy
|
||||||
|
setattr(new_core, 'name', core_name)
|
||||||
|
|
||||||
# add friendly name, so users can do Manta.my_logic_analyzer.read() for example
|
# add friendly name, so users can do Manta.my_logic_analyzer.read() for example
|
||||||
setattr(self, core_name, new_core)
|
setattr(self, core_name, new_core)
|
||||||
self.cores.append(new_core)
|
self.cores.append(new_core)
|
||||||
|
|
@ -239,59 +268,96 @@ class Manta:
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
def generate_connections(self):
|
||||||
|
# generates hdl for registers that connect two modules together
|
||||||
|
|
||||||
|
# make pairwise cores
|
||||||
|
core_pairs = [(self.cores[i - 1], self.cores[i]) for i in range(1, len(self.cores))]
|
||||||
|
|
||||||
|
conns = []
|
||||||
|
for core_pair in core_pairs:
|
||||||
|
src = core_pair[0].name
|
||||||
|
dst = core_pair[1].name
|
||||||
|
|
||||||
|
hdl = f"\treg [15:0] {src}_{dst}_addr;\n"
|
||||||
|
hdl += f"\treg [15:0] {src}_{dst}_wdata;\n"
|
||||||
|
hdl += f"\treg [15:0] {src}_{dst}_rdata;\n"
|
||||||
|
hdl += f"\treg {src}_{dst}_rw;\n"
|
||||||
|
hdl += f"\treg {src}_{dst}_valid;\n"
|
||||||
|
conns.append(hdl)
|
||||||
|
|
||||||
|
return conns
|
||||||
|
|
||||||
|
def generate_instantiations(self):
|
||||||
|
# generates hdl for modules that need to be connected together
|
||||||
|
|
||||||
|
insts = []
|
||||||
|
for i, core in enumerate(self.cores):
|
||||||
|
# should probably check if core is LogicAnalyzerCore or IOCore
|
||||||
|
|
||||||
|
hdl = core.inst()
|
||||||
|
|
||||||
|
if (i < len(self.cores)-1):
|
||||||
|
dst = self.cores[i+1]
|
||||||
|
|
||||||
|
hdl = hdl.replace(".addr_o()", f".addr_o({core.name}_{dst.name}_addr)")
|
||||||
|
hdl = hdl.replace(".wdata_o()", f".wdata_o({core.name}_{dst.name}_wdata)")
|
||||||
|
hdl = hdl.replace(".rdata_o()", f".rdata_o({core.name}_{dst.name}_rdata)")
|
||||||
|
hdl = hdl.replace(".rw_o()", f".rw_o({core.name}_{dst.name}_rw)")
|
||||||
|
hdl = hdl.replace(".valid_o()", f".valid_o({core.name}_{dst.name}_valid)")
|
||||||
|
|
||||||
|
if (i > 0):
|
||||||
|
src = self.cores[i-1]
|
||||||
|
hdl = hdl.replace(".addr_i()", f".addr_i({src.name}_{core.name}_addr)")
|
||||||
|
hdl = hdl.replace(".wdata_i()", f".wdata_i({src.name}_{core.name}_wdata)")
|
||||||
|
hdl = hdl.replace(".rdata_i()", f".rdata_i({src.name}_{core.name}_rdata)")
|
||||||
|
hdl = hdl.replace(".rw_i()", f".rw_i({src.name}_{core.name}_rw)")
|
||||||
|
hdl = hdl.replace(".valid_i()", f".valid_i({src.name}_{core.name}_valid)")
|
||||||
|
|
||||||
|
insts.append(hdl)
|
||||||
|
|
||||||
|
return insts
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
# this occurs in two steps: generating manta and the top-level,
|
# this occurs in two steps: generating manta and the top-level,
|
||||||
# and pasting in all the HDL from earlier.
|
# and pasting in all the HDL from earlier.
|
||||||
|
|
||||||
uart_rx_hdl = pkgutil.get_data(__name__, "rx_uart.sv").decode()
|
uart_rx_hdl = pkgutil.get_data(__name__, "rx_uart.v").decode()
|
||||||
bridge_rx_hdl = pkgutil.get_data(__name__, "bridge_rx.sv").decode()
|
bridge_rx_hdl = pkgutil.get_data(__name__, "bridge_rx.v").decode()
|
||||||
bridge_tx_hdl = pkgutil.get_data(__name__, "bridge_tx.sv").decode()
|
bridge_tx_hdl = pkgutil.get_data(__name__, "bridge_tx.v").decode()
|
||||||
uart_tx_hdl = pkgutil.get_data(__name__, "uart_tx.sv").decode()
|
uart_tx_hdl = pkgutil.get_data(__name__, "uart_tx.v").decode()
|
||||||
|
|
||||||
# make pairwise cores
|
|
||||||
core_pairs = [(cores[i-1], cores[i]) for i in range(1, len(cores))]
|
|
||||||
|
|
||||||
# write HDL to instantiate and connect them
|
"""
|
||||||
connections = []
|
ok so the way this works is that we have two lists, instantiations and connections
|
||||||
for src, dest in core_pairs:
|
we make connections first, and it works by pairwise iterating over pairs of cores and
|
||||||
# wait who's source and who's destination? have to know both the src, dest,
|
doing the 'reg[N:0] src_dest_name' thing
|
||||||
# and also current core at any given moment
|
|
||||||
|
|
||||||
# so then is the solution src src_current current current_dst dst
|
once we have that, we then generate the instantiations. we iterate through the same pairs,
|
||||||
hdl = src.inst()
|
setting outputs on the src, and inputs on the dest. this leaves the inputs of the first node
|
||||||
hdl += hdl.replace(".addr_i()", f".addr_i({src}_{dest}_addr)")
|
and the outputs of the last node unconnected, but we'll have a final step to take care of that.
|
||||||
hdl += hdl.replace(".wdata_i()", f".wdata_i({src}_{dest}_wdata)")
|
"""
|
||||||
hdl += hdl.replace(".rdata_i()", f".rdata_i({src}_{dest}_rdata)")
|
|
||||||
hdl += hdl.replace(".rw_i()", f".rw_i({src}_{dest}_rw)")
|
|
||||||
hdl += hdl.replace(".valid_i()", f".valid_i({src}_{dest}_valid)")
|
|
||||||
|
|
||||||
hdl += "\n"
|
|
||||||
hdl += f"reg[15:0] {src}_{dest}_addr\n"
|
|
||||||
hdl += f"reg[15:0] {src}_{dest}_wdata\n"
|
|
||||||
hdl += f"reg[15:0] {src}_{dest}_rdata\n"
|
|
||||||
hdl += f"reg {src}_{dest}_rw\n"
|
|
||||||
hdl += f"reg {src}_{dest}_valid\n\n"
|
|
||||||
connections.append(hdl)
|
|
||||||
|
|
||||||
# write HDL to instantiate them, now that we know src and dest
|
|
||||||
instantiations = []
|
|
||||||
for core in cores:
|
|
||||||
hdl = core.inst()
|
|
||||||
hdl = hdl.replace(".addr_i()", f".addr_i({src}_{dest}_addr)")
|
|
||||||
hdl = hdl.replace(".addr_i()", f".addr_i({src}_{dest}_addr)")
|
|
||||||
|
|
||||||
# for core in cores:
|
|
||||||
# registers = ''
|
|
||||||
# registers += f'{core}{}'
|
|
||||||
# # wire cores together
|
|
||||||
|
|
||||||
# add preamble to top of file
|
# add preamble to top of file
|
||||||
user = os.environ.get("USER", os.environ.get("USERNAME"))
|
user = os.environ.get("USER", os.environ.get("USERNAME"))
|
||||||
timestamp = datetime.now().strftime("%d %b %Y at %H:%M:%S")
|
timestamp = datetime.now().strftime("%d %b %Y at %H:%M:%S")
|
||||||
|
|
||||||
hdl = "This manata definitinon was autogenerated on {timestamp} by {user}\n\n"
|
hdl = f"/* This manata definition was generated on {timestamp} by {user}\n"
|
||||||
hdl += "If this breaks or if you've got dank formal verification memes,\n"
|
hdl += " *\n"
|
||||||
hdl += "please contact fischerm [at] mit.edu\n"
|
hdl += " * If this breaks or if you've got dank formal verification memes,\n"
|
||||||
|
hdl += " * please contact fischerm [at] mit.edu\n"
|
||||||
|
hdl += " */\n"
|
||||||
|
|
||||||
|
insts = self.generate_instantiations()
|
||||||
|
conns = self.generate_connections()
|
||||||
|
for i, inst in enumerate(insts):
|
||||||
|
hdl += inst
|
||||||
|
|
||||||
|
if (i != len(insts)-1):
|
||||||
|
hdl += conns[i]
|
||||||
|
|
||||||
|
return hdl
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue