manta/src/manta/io.py

143 lines
5.2 KiB
Python

from .verilog_manipulator import *
class IOCoreProbe:
def __init__(self, name, width, direction, base_addr, interface):
self.name = name
self.width = width
self.direction = direction
self.base_addr = base_addr
self.interface = interface
def set(self, data):
# make sure that we're an output probe
assert self.direction == "output", "Cannot set value of input port."
# check that value is within range for the width of the probe
assert isinstance(data, int), "Data must be an integer."
if data > 0:
assert data <= (2**self.width) - 1, f"Unsigned value too large for probe of width {self.width}"
elif data < 0:
assert data >= -(2**(self.width-1))-1, f"Signed value too large for probe of width {self.width}"
assert data <= (2**(self.width-1))-1, f"Signed value too large for probe of width {self.width}"
self.interface.write_register(self.base_addr, data)
def get(self):
return self.interface.read_register(self.base_addr)
class IOCore:
def __init__(self, config, name, base_addr, interface):
self.name = name
self.base_addr = base_addr
self.interface = interface
# Warn if unrecognized options have been given
for option in config:
if option not in ["type", "inputs", "outputs"]:
print(f"Warning: Ignoring unrecognized option '{option}' in IO core '{self.name}'")
# make sure we have ports defined
assert ('inputs' in config) or ('outputs' in config), "No input or output ports specified."
# add input probes to core
self.probes = []
probe_base_addr = self.base_addr
if 'inputs' in config:
for name, width in config["inputs"].items():
# make sure inputs are of reasonable width
assert isinstance(width, int), f"Probe {name} must have integer width."
assert width > 0, f"Probe {name} must have positive width."
probe = IOCoreProbe(name, width, "input", probe_base_addr, self.interface)
# add friendly name, so users can do Manta.my_io_core.my_probe.set() for example
setattr(self, name, probe)
self.probes.append(probe)
self.max_addr = probe_base_addr
probe_base_addr += 1
# add output probes to core
if 'outputs' in config:
for name, width in config["outputs"].items():
# make sure inputs are of reasonable width
assert isinstance(width, int), f"Probe {name} must have integer width."
assert width > 0, f"Probe {name} must have positive width."
probe = IOCoreProbe(name, width, "output", probe_base_addr, self.interface)
# add friendly name, so users can do Manta.my_io_core.my_probe.set() for example
setattr(self, name, probe)
self.probes.append(probe)
self.max_addr = probe_base_addr
probe_base_addr += 1
def hdl_inst(self):
inst = VerilogManipulator("io/io_core_inst_tmpl.v")
inst.sub(self.name, "/* MODULE_NAME */")
inst.sub(self.name + "_inst", "/* INST_NAME */")
probes = {probe.name:probe.width for probe in self.probes}
inst_ports = inst.net_conn(probes, trailing_comma=True)
inst.sub(inst_ports, "/* INST_PORTS */")
return inst.get_hdl()
def hdl_def(self):
io_core = VerilogManipulator("io/io_core_def_tmpl.v")
io_core.sub(self.name, "/* MODULE_NAME */")
io_core.sub(self.max_addr, "/* MAX_ADDR */")
# generate declaration
top_level_ports = ',\n'.join(self.hdl_top_level_ports())
top_level_ports += ','
io_core.sub(top_level_ports, "/* TOP_LEVEL_PORTS */")
# generate memory handling
rcsb = "" # read case statement body
wcsb = "" # write case statement body
for probe in self.probes:
# add to read block
if probe.width == 16:
rcsb += f"{probe.base_addr}: rdata_o <= {probe.name};\n"
else:
rcsb += f"{probe.base_addr}: rdata_o <= {{{16-probe.width}'b0, {probe.name}}};\n"
# if output, add to write block
if probe.direction == "output":
if probe.width == 1:
wcsb += f"{probe.base_addr}: {probe.name} <= wdata_i[0];\n"
elif probe.width == 16:
wcsb += f"{probe.base_addr}: {probe.name} <= wdata_i;\n"
else:
wcsb += f"{probe.base_addr}: {probe.name} <= wdata_i[{probe.width-1}:0];\n"
# remove trailing newline
rcsb = rcsb.rstrip()
wcsb = wcsb.rstrip()
io_core.sub(rcsb, "/* READ_CASE_STATEMENT_BODY */")
io_core.sub(wcsb, "/* WRITE_CASE_STATEMENT_BODY */")
return io_core.get_hdl()
def hdl_top_level_ports(self):
ports = []
for probe in self.probes:
net_type = "input wire " if probe.direction == "input" else "output reg "
name_def = probe.name if probe.width == 1 else f"[{probe.width-1}:0] {probe.name}"
ports.append(net_type + name_def)
return ports