diff --git a/src/manta/ethernet/bridge.py b/src/manta/ethernet/bridge.py index 2e3d8b8..e2f2995 100644 --- a/src/manta/ethernet/bridge.py +++ b/src/manta/ethernet/bridge.py @@ -6,15 +6,11 @@ from manta.utils import * class EthernetBridge(wiring.Component): - sink: In(StreamSignature(32)) source: Out(StreamSignature(32)) + sink: In(StreamSignature(32)) - def __init__(self): - super().__init__() - - # TODO: use In() and Out() for InternalBus connections - self.bus_o = Signal(InternalBus()) - self.bus_i = Signal(InternalBus()) + bus_source: Out(InternalBusSignature) + bus_sink: In(InternalBusSignature) def elaborate(self, platform): m = Module() @@ -79,14 +75,14 @@ class EthernetBridge(wiring.Component): with m.If(self.sink.valid): # we have the length and the address to read from, let's go! - m.d.sync += self.bus_o.addr.eq(self.sink.data) - m.d.sync += self.bus_o.data.eq(0) - m.d.sync += self.bus_o.rw.eq(0) - m.d.sync += self.bus_o.valid.eq(1) + m.d.sync += self.bus_source.p.addr.eq(self.sink.data) + m.d.sync += self.bus_source.p.data.eq(0) + m.d.sync += self.bus_source.p.rw.eq(0) + m.d.sync += self.bus_source.p.valid.eq(1) with m.If(read_len == 0): # we've sent the last read request in this batch to the bus - m.d.sync += self.bus_o.last.eq(1) + m.d.sync += self.bus_source.p.last.eq(1) m.d.sync += read_len.eq(0) m.next = "READ" @@ -96,22 +92,22 @@ class EthernetBridge(wiring.Component): # Clock out read requests to the bus with m.If(read_len > 0): - m.d.sync += self.bus_o.addr.eq(self.bus_o.addr + 1) + m.d.sync += self.bus_source.p.addr.eq(self.bus_source.p.addr + 1) m.d.sync += read_len.eq(read_len - 1) with m.If(read_len == 1): - m.d.sync += self.bus_o.last.eq(1) + m.d.sync += self.bus_source.p.last.eq(1) with m.Else(): - m.d.sync += self.bus_o.eq( + m.d.sync += self.bus_source.p.eq( 0 ) # TODO: it's probably overzealous to set the whole bus to zero, but it makes debugging easy so we're doing it xD # Clock out any read data from the bus - with m.If(self.bus_i.valid): - m.d.sync += self.source.data.eq(self.bus_i.data) + with m.If(self.bus_sink.p.valid): + m.d.sync += self.source.data.eq(self.bus_sink.p.data) m.d.sync += self.source.valid.eq(1) - m.d.sync += self.source.last.eq(self.bus_i.last) + m.d.sync += self.source.last.eq(self.bus_sink.p.last) with m.If(self.source.last): m.d.sync += self.source.data.eq(0) @@ -121,17 +117,17 @@ class EthernetBridge(wiring.Component): with m.State("WRITE_WAIT_FOR_ADDR"): with m.If(self.sink.valid): - m.d.sync += self.bus_o.addr.eq(self.sink.data) + m.d.sync += self.bus_source.p.addr.eq(self.sink.data) m.next = "WRITE_FIRST" # Don't want to increment address on the first write, # and I'm lazy so I'm making a new state to keep track of that with m.State("WRITE_FIRST"): with m.If(self.sink.valid): - m.d.sync += self.bus_o.data.eq(self.sink.data) - m.d.sync += self.bus_o.rw.eq(1) - m.d.sync += self.bus_o.valid.eq(1) - m.d.sync += self.bus_o.last.eq(self.sink.last) + m.d.sync += self.bus_source.p.data.eq(self.sink.data) + m.d.sync += self.bus_source.p.rw.eq(1) + m.d.sync += self.bus_source.p.valid.eq(1) + m.d.sync += self.bus_source.p.last.eq(self.sink.last) with m.If(self.sink.last): m.d.sync += self.sink.ready.eq(0) @@ -142,11 +138,11 @@ class EthernetBridge(wiring.Component): with m.State("WRITE"): with m.If(self.sink.valid): - m.d.sync += self.bus_o.addr.eq(self.bus_o.addr + 1) - m.d.sync += self.bus_o.data.eq(self.sink.data) - m.d.sync += self.bus_o.rw.eq(1) - m.d.sync += self.bus_o.valid.eq(1) - m.d.sync += self.bus_o.last.eq(self.sink.last) + m.d.sync += self.bus_source.p.addr.eq(self.bus_source.p.addr + 1) + m.d.sync += self.bus_source.p.data.eq(self.sink.data) + m.d.sync += self.bus_source.p.rw.eq(1) + m.d.sync += self.bus_source.p.valid.eq(1) + m.d.sync += self.bus_source.p.last.eq(self.sink.last) with m.If(self.sink.last): m.d.sync += self.sink.ready.eq(0) @@ -159,9 +155,9 @@ class EthernetBridge(wiring.Component): m.next = "WRITE" with m.State("WRITE_WAIT_FOR_LAST"): - m.d.sync += self.bus_o.eq(0) + m.d.sync += self.bus_source.p.eq(0) - with m.If(self.bus_i.last): + with m.If(self.bus_sink.p.last): m.d.sync += seq_num_expected.eq(seq_num_expected + 1) m.d.sync += self.source.data.eq( EthernetMessageHeader.concat_signals( diff --git a/src/manta/memory_core.py b/src/manta/memory_core.py index adbafba..a89f124 100644 --- a/src/manta/memory_core.py +++ b/src/manta/memory_core.py @@ -13,6 +13,9 @@ class MemoryCore(MantaCore): and the other provided to user logic. """ + bus_source: Out(InternalBusSignature) + bus_sink: In(InternalBusSignature) + def __init__(self, mode, width, depth): """ Create a Memory Core with the given width and depth. @@ -33,46 +36,28 @@ class MemoryCore(MantaCore): depth (int): The depth of the memory, in entries. """ + super().__init__() self._mode = mode self._width = width self._depth = depth self._n_mems = ceil(self._width / 32) - # Bus Connections - self.bus_i = Signal(InternalBus()) - self.bus_o = Signal(InternalBus()) - # User Ports if self._mode == "fpga_to_host": self.user_addr = Signal(range(self._depth)) self.user_data_in = Signal(self._width) self.user_write_enable = Signal() - self._top_level_ports = [ - self.user_addr, - self.user_data_in, - self.user_write_enable, - ] elif self._mode == "host_to_fpga": self.user_addr = Signal(range(self._depth)) self.user_data_out = Signal(self._width) - self._top_level_ports = [ - self.user_addr, - self.user_data_out, - ] elif self._mode == "bidirectional": self.user_addr = Signal(range(self._depth)) self.user_data_in = Signal(self._width) self.user_data_out = Signal(self._width) self.user_write_enable = Signal() - self._top_level_ports = [ - self.user_addr, - self.user_data_in, - self.user_data_out, - self.user_write_enable, - ] # Define memories n_full = self._width // 32 @@ -87,10 +72,6 @@ class MemoryCore(MantaCore): Memory(shape=n_partial, depth=self._depth, init=[0] * self._depth) ] - @property - def top_level_ports(self): - return self._top_level_ports - @property def max_addr(self): return self.base_addr + (self._depth * self._n_mems) @@ -155,11 +136,11 @@ class MemoryCore(MantaCore): # Throw BRAM operations into the front of the pipeline with m.If( - (self.bus_i.valid) - & (self.bus_i.addr >= start_addr) - & (self.bus_i.addr <= stop_addr) + (self.bus_sink.p.valid) + & (self.bus_sink.p.addr >= start_addr) + & (self.bus_sink.p.addr <= stop_addr) ): - m.d.sync += read_port.addr.eq(self.bus_i.addr - start_addr) + m.d.sync += read_port.addr.eq(self.bus_sink.p.addr - start_addr) # Pull BRAM reads from the back of the pipeline with m.If( @@ -168,7 +149,7 @@ class MemoryCore(MantaCore): & (self._bus_pipe[2].addr >= start_addr) & (self._bus_pipe[2].addr <= stop_addr) ): - m.d.sync += self.bus_o.data.eq(read_port.data) + m.d.sync += self.bus_source.p.data.eq(read_port.data) elif self._mode == "host_to_fpga": write_port = mem.write_port() @@ -176,13 +157,13 @@ class MemoryCore(MantaCore): # Throw BRAM operations into the front of the pipeline with m.If( - (self.bus_i.valid) - & (self.bus_i.addr >= start_addr) - & (self.bus_i.addr <= stop_addr) + (self.bus_sink.p.valid) + & (self.bus_sink.p.addr >= start_addr) + & (self.bus_sink.p.addr <= stop_addr) ): - m.d.sync += write_port.addr.eq(self.bus_i.addr - start_addr) - m.d.sync += write_port.data.eq(self.bus_i.data) - m.d.sync += write_port.en.eq(self.bus_i.rw) + m.d.sync += write_port.addr.eq(self.bus_sink.p.addr - start_addr) + m.d.sync += write_port.data.eq(self.bus_sink.p.data) + m.d.sync += write_port.en.eq(self.bus_sink.p.rw) elif self._mode == "bidirectional": read_port = mem.read_port() @@ -193,14 +174,14 @@ class MemoryCore(MantaCore): # Throw BRAM operations into the front of the pipeline with m.If( - (self.bus_i.valid) - & (self.bus_i.addr >= start_addr) - & (self.bus_i.addr <= stop_addr) + (self.bus_sink.p.valid) + & (self.bus_sink.p.addr >= start_addr) + & (self.bus_sink.p.addr <= stop_addr) ): - m.d.sync += read_port.addr.eq(self.bus_i.addr - start_addr) - m.d.sync += write_port.addr.eq(self.bus_i.addr - start_addr) - m.d.sync += write_port.data.eq(self.bus_i.data) - m.d.sync += write_port.en.eq(self.bus_i.rw) + m.d.sync += read_port.addr.eq(self.bus_sink.p.addr - start_addr) + m.d.sync += write_port.addr.eq(self.bus_sink.p.addr - start_addr) + m.d.sync += write_port.data.eq(self.bus_sink.p.data) + m.d.sync += write_port.en.eq(self.bus_sink.p.rw) # Pull BRAM reads from the back of the pipeline with m.If( @@ -209,7 +190,7 @@ class MemoryCore(MantaCore): & (self._bus_pipe[2].addr >= start_addr) & (self._bus_pipe[2].addr <= stop_addr) ): - m.d.sync += self.bus_o.data.eq(read_port.data) + m.d.sync += self.bus_source.p.data.eq(read_port.data) def _tie_mems_to_user_logic(self, m): # Handle write ports @@ -239,13 +220,15 @@ class MemoryCore(MantaCore): m.submodules[f"mem_{i}"] = mem # Pipeline the bus to accommodate the two clock-cycle delay in the memories - self._bus_pipe = [Signal(InternalBus()) for _ in range(3)] - m.d.sync += self._bus_pipe[0].eq(self.bus_i) + self._bus_pipe = [ + Signal(InternalBusLayout, name=f"bus_pipe_{i}") for i in range(3) + ] + m.d.sync += self._bus_pipe[0].eq(self.bus_sink.p) for i in range(1, 3): m.d.sync += self._bus_pipe[i].eq(self._bus_pipe[i - 1]) - m.d.sync += self.bus_o.eq(self._bus_pipe[2]) + m.d.sync += self.bus_source.p.eq(self._bus_pipe[1]) self._tie_mems_to_bus(m) self._tie_mems_to_user_logic(m) diff --git a/src/manta/uart/__init__.py b/src/manta/uart/__init__.py index 1b7e705..e162dff 100644 --- a/src/manta/uart/__init__.py +++ b/src/manta/uart/__init__.py @@ -11,12 +11,19 @@ from manta.uart.transmitter import UARTTransmitter from manta.utils import * -class UARTInterface(Elaboratable): +class UARTInterface(wiring.Component): """ A synthesizable module for UART communication between a host machine and the FPGA. """ + # Top-Level Ports + rx: In(1) + tx: Out(1) + + bus_source: Out(InternalBusSignature) + bus_sink: In(InternalBusSignature) + def __init__(self, port, baudrate, clock_freq, stall_interval=16, chunk_size=256): """ This function is the main mechanism for configuring a UART Interface @@ -60,6 +67,7 @@ class UARTInterface(Elaboratable): provided, or the clock frequency or baudrate is invalid. """ + super().__init__() self._port = port self._baudrate = baudrate @@ -69,13 +77,6 @@ class UARTInterface(Elaboratable): self._stall_interval = stall_interval self._check_config() - # Top-Level Ports - self.rx = Signal() - self.tx = Signal() - - self.bus_o = Signal(InternalBus()) - self.bus_i = Signal(InternalBus()) - @classmethod def from_config(cls, config): integer_options = [ @@ -196,13 +197,6 @@ class UARTInterface(Elaboratable): self._serial_device = Serial(chosen_port, self._baudrate, timeout=1) return self._serial_device - def get_top_level_ports(self): - """ - Return the Amaranth signals that should be included as ports in the - top-level Manta module. - """ - return [self.rx, self.tx] - @property def clock_freq(self): return self._clock_freq @@ -343,8 +337,7 @@ class UARTInterface(Elaboratable): wiring.connect(m, cobs_encode.source, uart_tx.sink) m.d.comb += self.tx.eq(uart_tx.tx) - # TODO: replace these with wiring.Connect - m.d.comb += self.bus_o.eq(bridge.bus_o) - m.d.comb += bridge.bus_i.eq(self.bus_i) + wiring.connect(m, bridge.bus_source, wiring.flipped(self.bus_source)) + wiring.connect(m, wiring.flipped(self.bus_sink), bridge.bus_sink) return m diff --git a/src/manta/utils.py b/src/manta/utils.py index d2ed07c..2bd66a4 100644 --- a/src/manta/utils.py +++ b/src/manta/utils.py @@ -11,7 +11,7 @@ from amaranth.lib.wiring import In, Out from amaranth.sim import Simulator -class MantaCore(ABC, Elaboratable): +class MantaCore(ABC, wiring.Component): # These attributes are meant to be settable and gettable, but max_addr and # top_level_ports are intended to be only gettable. Do not implement # setters for them in subclasses. @@ -29,15 +29,6 @@ class MantaCore(ABC, Elaboratable): """ 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 to_config(self): """ @@ -87,22 +78,17 @@ class CoreContainer: self._last_used_addr = value.max_addr + 1 -class InternalBus(data.StructLayout): - """ - Describes the layout of Manta's internal bus, such that signals of - the appropriate dimension can be instantiated with Signal(InternalBus()). - """ +InternalBusLayout = data.StructLayout( + { + "addr": 32, + "data": 32, + "rw": 1, + "valid": 1, + "last": 1, + } +) - def __init__(self): - super().__init__( - { - "addr": 32, - "data": 32, - "rw": 1, - "valid": 1, - "last": 1, - } - ) +InternalBusSignature = wiring.Signature({"p": Out(InternalBusLayout)}) class StreamSignature(wiring.Signature): diff --git a/test/test_uart_bridge_sim.py b/test/test_uart_bridge_sim.py index 2908724..53845f2 100644 --- a/test/test_uart_bridge_sim.py +++ b/test/test_uart_bridge_sim.py @@ -26,8 +26,8 @@ class UARTHardwarePlusMemoryCore(wiring.Component): # the _clocks_per_baud attribute self.uart = uart - m.d.comb += uart.bus_i.eq(mem_core.bus_o) - m.d.comb += mem_core.bus_i.eq(uart.bus_o) + wiring.connect(m, uart.bus_source, mem_core.bus_sink) + wiring.connect(m, mem_core.bus_source, uart.bus_sink) m.d.comb += self.tx.eq(uart.tx) m.d.comb += uart.rx.eq(self.rx)