manta: fix code generation from config file, update tests

This commit is contained in:
Fischer Moseley 2024-08-03 19:46:38 -07:00
parent 3ba93efd2f
commit 8f45546b5a
10 changed files with 83 additions and 129 deletions

View File

@ -1,16 +1,16 @@
from manta import Manta
from manta import *
from time import sleep
manta = Manta("manta.yaml")
manta = Manta.from_config("manta.yaml")
i = 0
while True:
# Turn each LED off
for j in range(5):
manta.my_io_core.set_probe(f"LED{j}", 0)
manta.cores.my_io_core.set_probe(f"LED{j}", 0)
# Turn one LED back on
manta.my_io_core.set_probe(f"LED{i}", 1)
manta.cores.my_io_core.set_probe(f"LED{i}", 1)
i = (i + 1) % 5
sleep(0.1)

View File

@ -1,6 +1,6 @@
from manta import Manta
from manta import *
m = Manta("manta.yaml")
manta = Manta.from_config("manta.yaml")
print(m.my_io_core.get_probe("sw"))
m.my_io_core.set_probe("led", 4)
print(manta.cores.my_io_core.get_probe("sw"))
manta.cores.my_io_core.set_probe("led", 4)

View File

@ -1,8 +1,8 @@
from manta import Manta
from manta import *
m = Manta("manta.yaml")
manta = Manta.from_config("manta.yaml")
# Memory addresses can be written to in Python, and then be read out by
# flipping the switches on the FPGA, and watching the LEDs update!
m.my_memory.write(0, 1)
manta.cores.my_memory.write(0, 1)

View File

@ -1,25 +1,25 @@
from manta import Manta
from manta import *
from time import sleep
from random import randint
m = Manta("manta.yaml")
manta = Manta.from_config("manta.yaml")
i = 0
while True:
i = (i + 1) % 16
m.my_io_core.set_probe("led", 2**i)
manta.cores.my_io_core.set_probe("led", 2**i)
m.my_io_core.set_probe("led16_r", randint(0, 1))
m.my_io_core.set_probe("led16_g", randint(0, 1))
m.my_io_core.set_probe("led16_b", randint(0, 1))
manta.cores.my_io_core.set_probe("led16_r", randint(0, 1))
manta.cores.my_io_core.set_probe("led16_g", randint(0, 1))
manta.cores.my_io_core.set_probe("led16_b", randint(0, 1))
print(f'Switches: {m.my_io_core.get_probe("sw")}')
print(f'Switches: {manta.cores.my_io_core.get_probe("sw")}')
print(f"Buttons:")
print(f'btnu: {m.my_io_core.get_probe("btnu")}')
print(f'btnd: {m.my_io_core.get_probe("btnd")}')
print(f'btnr: {m.my_io_core.get_probe("btnr")}')
print(f'btnl: {m.my_io_core.get_probe("btnl")}')
print(f'btnc: {m.my_io_core.get_probe("btnc")}')
print(f'btnu: {manta.cores.my_io_core.get_probe("btnu")}')
print(f'btnd: {manta.cores.my_io_core.get_probe("btnd")}')
print(f'btnr: {manta.cores.my_io_core.get_probe("btnr")}')
print(f'btnl: {manta.cores.my_io_core.get_probe("btnl")}')
print(f'btnc: {manta.cores.my_io_core.get_probe("btnc")}')
print("")
sleep(0.1)

View File

@ -69,13 +69,13 @@ def wrong_args():
def gen(config_path, output_path):
m = Manta(config_path)
m.generate_verilog(output_path)
manta = Manta.from_config(config_path)
manta.generate_verilog(output_path)
def inst(config_path):
m = Manta(config_path)
ports = m.get_top_level_ports()
manta = Manta.from_config(config_path)
ports = manta.get_top_level_ports()
hdl = ",\n ".join([f".{p.name}({p.name})" for p in ports])
foo = """
@ -88,8 +88,8 @@ manta manta_inst(
def capture(config_path, logic_analyzer_name, export_paths):
m = Manta(config_path)
la = getattr(m, logic_analyzer_name)
manta = Manta.from_config(config_path)
la = getattr(manta.cores, logic_analyzer_name)
cap = la.capture()
for path in export_paths:

View File

@ -22,7 +22,7 @@ class EthernetInterface(Elaboratable):
self._host_ip_addr = host_ip_addr
self._udp_port = udp_port
self._phy = phy
self._clk_freq = clk_freq
self._clk_freq = float(clk_freq)
self._additional_config = kwargs
self._check_config()
@ -81,6 +81,10 @@ class EthernetInterface(Elaboratable):
if not 0 <= int(byte) <= 255:
raise ValueError(f"Invalid byte in FPGA IP: {byte}")
@classmethod
def from_config(cls, config):
return EthernetInterface(**config)
def to_config(self):
config = {
"fpga_ip_addr": self._fpga_ip_addr,

View File

@ -40,7 +40,7 @@ class IOCore(MantaCore):
return self._max_addr
@classmethod
def from_config(cls, config, base_addr, interface):
def from_config(cls, config):
inputs = config.get("inputs", {})
outputs = config.get("outputs", {})
@ -117,7 +117,7 @@ class IOCore(MantaCore):
output_signals += [Signal(width, name=name, init=initial_value)]
return cls(base_addr, interface, inputs=input_signals, outputs=output_signals)
return cls(inputs=input_signals, outputs=output_signals)
def to_config(self):
config = {}

View File

@ -5,6 +5,8 @@ from manta.io_core import IOCore
from manta.memory_core import MemoryCore
from manta.logic_analyzer import LogicAnalyzerCore
from manta.utils import *
import yaml
import json
class Manta(Elaboratable):
@ -25,117 +27,61 @@ class Manta(Elaboratable):
for core in self.cores._cores.values():
core.interface = value
# def __init__(self, config):
# # Load config from either a configuration file or a dictionary.
# # Users primarily use the config file, but the dictionary is
# # included for internal tests.
@classmethod
def from_config(cls, config_path):
# if isinstance(config, str):
# self._config = self._read_config_file(config)
# Load config from either YAML or JSON
extension = config_path.split(".")[-1]
if extension not in ["yaml", "yml", "json"]:
raise ValueError(f"Configuration file {config_path} has unrecognized file type.")
# if isinstance(config, dict):
# self._config = config
with open(config_path, "r") as f:
if extension in ["yaml", "yml"]:
config = yaml.safe_load(f)
# self._check_config()
elif extension in ["json"]:
config = json.load(f)
# self._get_interface()
# self._get_cores()
# self._add_friendly_core_names()
# Validate config
if "cores" not in config:
raise ValueError("No cores specified in configuration file.")
# def _read_config_file(self, path):
# """
# Takes a path to configuration file, and return the configuration as a
# python dictionary.
# """
if not len(config["cores"]) > 0:
raise ValueError("Must specify at least one core.")
# extension = path.split(".")[-1]
for name, attrs in config["cores"].items():
# Make sure core type is specified
if "type" not in attrs:
raise ValueError(f"No type specified for core {name}.")
# if "json" in extension:
# import json
# with open(path, "r") as f:
# return json.load(f)
if attrs["type"] not in ["logic_analyzer", "io", "memory"]:
raise ValueError(f"Unrecognized core type specified for {name}.")
# elif "yaml" in extension or "yml" in extension:
# import yaml
# with open(path, "r") as f:
# return yaml.safe_load(f)
# Make Manta object, and configure it
manta = Manta()
# else:
# raise ValueError("Unable to recognize configuration file extension.")
# Add interface
if "uart" in config:
manta.interface = UARTInterface.from_config(config["uart"])
# def _check_config(self):
# if "cores" not in self._config:
# raise ValueError("No cores specified in configuration file.")
elif "ethernet" in config:
manta.interface = EthernetInterface.from_config(config["ethernet"])
# if not len(self._config["cores"]) > 0:
# raise ValueError("Must specify at least one core.")
# Add cores
for name, attrs in config["cores"].items():
if attrs["type"] == "io":
core = IOCore.from_config(attrs)
# for name, attrs in self._config["cores"].items():
# # Make sure core type is specified
# if "type" not in attrs:
# raise ValueError(f"No type specified for core {name}.")
elif attrs["type"] == "logic_analyzer":
core = LogicAnalyzerCore.from_config(attrs)
# if attrs["type"] not in ["logic_analyzer", "io", "memory"]:
# raise ValueError(f"Unrecognized core type specified for {name}.")
elif attrs["type"] == "memory":
core = MemoryCore.from_config(attrs)
# def _get_interface(self):
# """
# Returns an instance of an interface object (UARTInterface or
# EthernetInterface) configured with the parameters in the
# config file.
# """
# if "uart" in self._config:
# self.interface = UARTInterface.from_config(self._config["uart"])
setattr(manta.cores, name, core)
# elif "ethernet" in self._config:
# self.interface = EthernetInterface(self._config["ethernet"])
return manta
# else:
# raise ValueError("No recognized interface specified.")
# def _get_cores(self):
# """
# Creates instances of the cores (IOCore, LogicAnalyzerCore, MemoryCore)
# specified in the user's configuration, and returns them as a list.
# """
# self._cores = {}
# base_addr = 0
# for name, attrs in self._config["cores"].items():
# if attrs["type"] == "io":
# core = IOCore.from_config(attrs, base_addr, self.interface)
# elif attrs["type"] == "logic_analyzer":
# core = LogicAnalyzerCore(attrs, base_addr, self.interface)
# elif attrs["type"] == "memory":
# core = MemoryCore.from_config(attrs, base_addr, self.interface)
# # Make sure we're not out of address space
# if core.max_addr > (2**16) - 1:
# raise ValueError(
# f"Ran out of address space to allocate to core {name}."
# )
# # Make the next core's base address start one address after the previous one's
# base_addr = core.max_addr + 1
# self._cores[name] = core
# def _add_friendly_core_names(self):
# """
# Add cores to the instance under a friendly name - ie, a core named `my_core` belonging
# to a Manta instance `m` could be obtained with `m.cores["my_core"]`, but this allows
# it to be obtained with `m.my_core`. Which is way nicer.
# """
# for name, instance in self._cores.items():
# if not hasattr(self, name):
# setattr(self, name, instance)
# else:
# raise ValueError(
# "Cannot add object to Manta instance - name is already taken!"
# )
def elaborate(self, platform):
m = Module()

View File

@ -88,7 +88,7 @@ class MemoryCore(MantaCore):
}
@classmethod
def from_config(cls, config, base_addr, interface):
def from_config(cls, config):
# Check for unrecognized options
valid_options = ["type", "depth", "width", "mode"]
for option in config:
@ -125,7 +125,7 @@ class MemoryCore(MantaCore):
if mode not in ["fpga_to_host", "host_to_fpga", "bidirectional"]:
raise ValueError("Unrecognized mode provided to memory core.")
return cls(mode, width, depth, base_addr, interface)
return cls(mode, width, depth)
def _tie_mems_to_bus(self, m):
for i, mem in enumerate(self._mems):

View File

@ -76,6 +76,10 @@ class CoreContainer:
self._cores[name] = value
value.interface = self._manta.interface
value.base_addr = self._last_used_addr
if value.max_addr > (2**16)-1:
raise ValueError(f"Ran out of address space while allocating core.")
self._last_used_addr = value.max_addr + 1