uart: use wiring.Component for internal bus
This commit is contained in:
parent
31d11aff19
commit
28be273828
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue