HeavyX/heavycomps/heavycomps/cores/eth_rgmii.py

160 lines
6.2 KiB
Python

from nmigen import *
from nmigen_stdio.eth import rgmii
from nmigen_soc import csr, wishbone
from nmigen.utils import *
from nmigen.lib.fifo import AsyncFIFO
from nmigen.lib.cdc import FFSynchronizer, ResetSynchronizer
__all__ = ["EthRGMIICore"]
class EthRGMIICore(Elaboratable):
def __init__(self, *, rx, tx, bus_data_width,
rx_buffer_size=2**15, tx_buffer_size=2**15,
bus_type="wishbone", bus_granularity=None, **kwargs):
if not isinstance(rx, bool):
raise TypeError("Must include either rx=True or False "
"to indicate whether RX is used or not, not rx={}"
.format(repr(rx)))
self.rx = rx
if not isinstance(tx, bool):
raise TypeError("Must include either tx=True or False "
"to indicate whether TX is used or not, not tx={}"
.format(repr(tx)))
self.tx = tx
if bus_data_width < 9:
raise ValueError("Bus data width must be at least 10, not {}"
.format(bus_data_width))
if bus_type not in ["wishbone"]:
raise ValueError("Bus type must be "
"\"wishbone\", not {}".format(bus_type))
self.bus_type = bus_type
if bus_type == "wishbone":
self.rxdat_bus = wishbone.Interface(
addr_width=0,
data_width=bus_data_width,
granularity=bus_granularity,
**kwargs
)
if rx:
self.rx_buffer_size = rx_buffer_size
self.rx_stb = Signal()
self.rx_eop = Signal()
self.rx_io = rgmii.EthRGMIIRX(clk_domain="eth_rx")
if tx:
self.tx_buffer_size = tx_buffer_size
self.tx_stb = Signal()
self.tx_eop = Signal()
self.tx_io = rgmii.EthRGMIITX(clk_domain="eth_tx")
bus_dw = bus_data_width
if bus_granularity is None:
bus_granularity = data_width
bus_gr = bus_granularity
with csr.Bank(name="eth", addr_width=max(1, log2_int(bus_dw//bus_gr)),
data_width=bus_gr, type="mux") as self.csr:
self.csr.r += csr.Register("flags", "rw", width=bus_dw)
with self.csr.r.flags as reg:
if rx:
reg.f += [
csr.Field("rx_ready", "r", startbit=0, reset_value=1),
csr.Field("rx_clear", "rw", startbit=8),
]
if tx:
reg += [
csr.Field("tx_ready", "r", startbit=4, reset_value=1),
csr.Field("tx_send", "rw", startbit=12)
]
self.wb2csr = csr.WishboneCSRBridge(self.csr.mux.bus, data_width=bus_data_width)
self.csr_bus = self.wb2csr.wb_bus
def elaborate(self, platform):
m = Module()
if self.rx:
m.submodules.rx = rx = self.rx_io
if self.tx:
m.submodules.tx = tx = self.tx_io
m.submodules += self.csr, self.wb2csr
###########
# RX
if self.rx:
# RX FIFO ([7:0]=data ; [8]=is eop?)
rx_fifo = AsyncFIFO(width=9, depth=self.rx_buffer_size)
m.submodules += DomainRenamer({"write": "eth_rx", "read": "sync"})(rx_fifo)
rx_clearing = Signal()
m.d.comb += [
# Write to FIFO
# RX stops when clearing buffer
rx_fifo.w_en.eq(rx.source.stb & ~rx_clearing),
rx_fifo.w_data.eq(Cat(rx.source.payload.data, rx.source.eop)),
]
if self.bus_type == "wishbone":
# Reading while clearing buffer would return invalid data
with m.If(~rx_clearing):
rxdat_r_en = Signal()
m.d.comb += [
rxdat_r_en.eq(self.rxdat_bus.cyc & self.rxdat_bus.stb & ~self.rxdat_bus.we &
~self.rxdat_bus.ack & rx_fifo.r_rdy)
]
# Read from FIFO
m.d.sync += rx_fifo.r_en.eq(rxdat_r_en)
with m.If(self.rxdat_bus.sel[0] & rxdat_r_en):
m.d.sync += self.rxdat_bus.dat_r.eq(rx_fifo.r_data)
with m.Else():
m.d.sync += self.rxdat_bus.dat_r.eq(Cat(Repl(0, 9), 1)) # [9]=no data?
# RX data bus ACK
with m.If(self.rxdat_bus.cyc & self.rxdat_bus.stb & ~self.rxdat_bus.we):
m.d.sync += self.rxdat_bus.ack.eq(1)
with m.If(self.rxdat_bus.ack):
m.d.sync += self.rxdat_bus.ack.eq(0)
# CSR - Buffer is ready
# Set to 0 once buffer is full
# Resets to 1 only when buffer has been completely cleared
rx_ready_prev = Signal(reset=1)
m.d.comb += self.csr.r.flags.f.rx_ready.set_stb.eq(1)
with m.If(~rx_fifo.w_rdy):
m.d.comb += self.csr.r.flags.f.rx_ready.set_val.eq(0)
m.d.sync += rx_ready_prev.eq(0)
with m.Else():
m.d.comb += self.csr.r.flags.f.rx_ready.set_val.eq(rx_ready_prev)
with m.If(~rx_ready_prev & ~rx_fifo.r_rdy):
m.d.sync += rx_ready_prev.eq(1)
# CSR - Buffer clearing
# Starts clearing after flag is set to 1
# Ignores flag during clearing
with m.If(self.csr.r.flags.f.rx_clear.s & ~rx_clearing & rx_fifo.r_rdy):
m.d.sync += [
rx_fifo.r_en.eq(1),
rx_clearing.eq(1)
]
with m.If(rx_clearing & ~rx_fifo.r_rdy):
m.d.sync += [
rx_fifo.r_en.eq(0),
rx_clearing.eq(0)
]
# Useful signals
m.submodules += [
FFSynchronizer(rx.source.stb, self.rx_stb),
FFSynchronizer(rx.source.eop, self.rx_eop),
]
###########
# TX
if self.tx:
raise NotImplementedError("TX logic has not been implemented")
return m