From be79ba28b559b4b73d2eb43a89fcbcc85942f95a Mon Sep 17 00:00:00 2001 From: Fischer Moseley <42497969+fischermoseley@users.noreply.github.com> Date: Sun, 3 Mar 2024 18:53:08 -0800 Subject: [PATCH] define ABC for cores to inherit from --- src/manta/io_core.py | 25 ++++++---------- src/manta/logic_analyzer/__init__.py | 25 ++++++---------- src/manta/logic_analyzer/fsm.py | 2 +- src/manta/logic_analyzer/trigger_block.py | 2 +- src/manta/manta.py | 4 +-- src/manta/memory_core.py | 25 ++++++---------- src/manta/utils.py | 35 +++++++++++++++++++++-- test/test_logic_analyzer_sim.py | 2 +- test/test_mem_core_sim.py | 16 ++++++++--- 9 files changed, 77 insertions(+), 59 deletions(-) diff --git a/src/manta/io_core.py b/src/manta/io_core.py index 63d6df5..d4b7add 100644 --- a/src/manta/io_core.py +++ b/src/manta/io_core.py @@ -3,7 +3,7 @@ from manta.utils import * from math import ceil -class IOCore(Elaboratable): +class IOCore(MantaCore): """ A module for setting and getting the values of registers of arbitrary size on a FPGA. @@ -34,6 +34,14 @@ class IOCore(Elaboratable): self._make_memory_map() + @property + def top_level_ports(self): + return self._inputs + self._outputs + + @property + def max_addr(self): + return self._max_addr + @classmethod def from_config(cls, config, base_addr, interface): inputs = config.get("inputs", {}) @@ -173,21 +181,6 @@ class IOCore(Elaboratable): return m - def get_top_level_ports(self): - """ - Return the Amaranth signals that should be included as ports in the - top-level Manta module. - """ - return self._inputs + self._outputs - - def get_max_addr(self): - """ - Return the maximum addresses in memory used by the core. The address - space used by the core extends from `base_addr` to the number returned - by this function (including the endpoints). - """ - return self._max_addr - def set_probe(self, name, value): """ Set the value of an output probe on the FPGA. The value may be either diff --git a/src/manta/logic_analyzer/__init__.py b/src/manta/logic_analyzer/__init__.py index 19e3a04..2276a6a 100644 --- a/src/manta/logic_analyzer/__init__.py +++ b/src/manta/logic_analyzer/__init__.py @@ -6,7 +6,7 @@ from manta.logic_analyzer.fsm import LogicAnalyzerFSM, States, TriggerModes from manta.logic_analyzer.playback import LogicAnalyzerPlayback -class LogicAnalyzerCore(Elaboratable): +class LogicAnalyzerCore(MantaCore): """ A module for generating a logic analyzer on the FPGA, with configurable triggers, trigger position, and trigger modes. @@ -44,6 +44,14 @@ class LogicAnalyzerCore(Elaboratable): interface=interface, ) + @property + def max_addr(self): + return self._sample_mem.max_addr + + @property + def top_level_ports(self): + return self._probes + def _check_config(self): # Check for unrecognized options valid_options = [ @@ -176,21 +184,6 @@ class LogicAnalyzerCore(Elaboratable): return m - def get_top_level_ports(self): - """ - Return the Amaranth signals that should be included as ports in the - top-level Manta module. - """ - return self._probes - - def get_max_addr(self): - """ - Return the maximum addresses in memory used by the core. The address - space used by the core extends from `base_addr` to the number returned - by this function (including the endpoints). - """ - return self._sample_mem.get_max_addr() - def capture(self, verbose=False): """ Performs a capture, recording the state of all input probes to the diff --git a/src/manta/logic_analyzer/fsm.py b/src/manta/logic_analyzer/fsm.py index bfe4183..4853963 100644 --- a/src/manta/logic_analyzer/fsm.py +++ b/src/manta/logic_analyzer/fsm.py @@ -66,7 +66,7 @@ class LogicAnalyzerFSM(Elaboratable): space used by the core extends from `base_addr` to the number returned by this function (including the endpoints). """ - return self.registers.get_max_addr() + return self.registers.max_addr def elaborate(self, platform): m = Module() diff --git a/src/manta/logic_analyzer/trigger_block.py b/src/manta/logic_analyzer/trigger_block.py index ff67b45..9db01a2 100644 --- a/src/manta/logic_analyzer/trigger_block.py +++ b/src/manta/logic_analyzer/trigger_block.py @@ -34,7 +34,7 @@ class LogicAnalyzerTriggerBlock(Elaboratable): space used by the core extends from `base_addr` to the number returned by this function (including the endpoints). """ - return self.registers.get_max_addr() + return self.registers.max_addr def clear_triggers(self): # Reset all triggers to disabled with no argument diff --git a/src/manta/manta.py b/src/manta/manta.py index 1a3830b..b5ea59e 100644 --- a/src/manta/manta.py +++ b/src/manta/manta.py @@ -97,13 +97,13 @@ class Manta(Elaboratable): core = MemoryCore.from_config(attrs, base_addr, self.interface) # Make sure we're not out of address space - if core.get_max_addr() > (2**16) - 1: + 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.get_max_addr() + 1 + base_addr = core.max_addr + 1 self._cores[name] = core def _add_friendly_core_names(self): diff --git a/src/manta/memory_core.py b/src/manta/memory_core.py index 7e8e0a5..edcbb74 100644 --- a/src/manta/memory_core.py +++ b/src/manta/memory_core.py @@ -3,7 +3,7 @@ from manta.utils import * from math import ceil -class MemoryCore(Elaboratable): +class MemoryCore(MantaCore): """ A module for generating a memory on the FPGA, with a port tied to Manta's internal bus, and a port provided to user logic. @@ -60,6 +60,14 @@ class MemoryCore(Elaboratable): self.user_write_enable, ] + @property + def top_level_ports(self): + return self._top_level_ports + + @property + def max_addr(self): + return self._max_addr + @classmethod def from_config(cls, config, base_addr, interface): # Check for unrecognized options @@ -251,21 +259,6 @@ class MemoryCore(Elaboratable): self._tie_mems_to_user_logic(m) return m - def get_top_level_ports(self): - """ - Return the Amaranth signals that should be included as ports in the - top-level Manta module. - """ - return self._top_level_ports - - def get_max_addr(self): - """ - Return the maximum addresses in memory used by the core. The address - space used by the core extends from `base_addr` to the number returned - by this function (including the endpoints). - """ - return self._max_addr - def _convert_user_to_bus_addr(self, addrs): """ Convert user address space to bus address space. For instance, for a diff --git a/src/manta/utils.py b/src/manta/utils.py index 20dc6bc..b9bfba8 100644 --- a/src/manta/utils.py +++ b/src/manta/utils.py @@ -1,9 +1,40 @@ +from amaranth import * from amaranth.sim import Simulator -from amaranth.lib import data, enum -from math import ceil +from amaranth.lib import data +from abc import ABC, abstractmethod import os +class MantaCore(ABC, Elaboratable): + + @property + @abstractmethod + def max_addr(self): + """ + Return the maximum addresses in memory used by the core. The address + space used by the core extends from `base_addr` to the number returned + by this function (including the endpoints). + """ + pass + + @property + @abstractmethod + def top_level_ports(self): + """ + Return the Amaranth signals that should be included as ports in the + top-level Manta module. + """ + pass + + @abstractmethod + def elaborate(self, platform): + pass + + # @abstractclassmethod + # def from_config(cls): + # pass + + class InternalBus(data.StructLayout): """ Describes the layout of Manta's internal bus, such that signals of diff --git a/test/test_logic_analyzer_sim.py b/test/test_logic_analyzer_sim.py index eb240a5..aeb9186 100644 --- a/test/test_logic_analyzer_sim.py +++ b/test/test_logic_analyzer_sim.py @@ -96,5 +96,5 @@ def test_single_shot_capture(): yield from write_register(la, 0, 1) yield from write_register(la, 0, 0) - for addr in range(la.get_max_addr()): + for addr in range(la.max_addr): yield from print_data_at_addr(addr) diff --git a/test/test_mem_core_sim.py b/test/test_mem_core_sim.py index 9618687..222827c 100644 --- a/test/test_mem_core_sim.py +++ b/test/test_mem_core_sim.py @@ -2,15 +2,18 @@ from manta.memory_core import MemoryCore from manta.utils import * from random import randint, sample, choice -class MemoryCoreTests(): + +class MemoryCoreTests: def __init__(self, mem_core): self.mem_core = mem_core self.base_addr = mem_core._base_addr - self.max_addr = mem_core.get_max_addr() + self.max_addr = mem_core.max_addr self.width = self.mem_core._width self.depth = self.mem_core._depth - self.bus_addrs = list(range(self.base_addr, self.max_addr)) # include the endpoint! + self.bus_addrs = list( + range(self.base_addr, self.max_addr) + ) # include the endpoint! self.user_addrs = list(range(self.mem_core._depth)) self.model = {} @@ -86,7 +89,10 @@ class MemoryCoreTests(): data = yield (self.mem_core.user_data_out) if data != expected_data: - raise ValueError(f"Read from {addr} yielded {data} instead of {expected_data}") + raise ValueError( + f"Read from {addr} yielded {data} instead of {expected_data}" + ) + mem_core = MemoryCore( mode="bidirectional", @@ -98,6 +104,7 @@ mem_core = MemoryCore( tests = MemoryCoreTests(mem_core) + @simulate(mem_core) def test_bidirectional_testbench(): yield from tests.check_each_address_on_bus_side_contains_zero() @@ -106,6 +113,7 @@ def test_bidirectional_testbench(): yield from tests.check_multiple_writes_then_multiple_reads() yield from tests.check_random_reads_random_writes_random_orders() + # def test_sweep_core_widths(): # for i in range(1, 64): # verify_mem_core(i, 128, 0)