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