diff --git a/liteeth/core/__init__.py b/liteeth/core/__init__.py index 26fc9f1f..fc14d8e7 100644 --- a/liteeth/core/__init__.py +++ b/liteeth/core/__init__.py @@ -14,9 +14,9 @@ # IP Core ------------------------------------------------------------------------------------------ class LiteEthIPCore(Module, AutoCSR): - def __init__(self, phy, mac_address, ip_address, clk_freq, with_icmp=True, dw=8): + def __init__(self, phy, mac_address, ip_address, clk_freq, with_icmp=True, dw=8, anti_underflow=0): ip_address = convert_ip(ip_address) - self.submodules.mac = LiteEthMAC(phy, dw, interface="crossbar", with_preamble_crc=True) + self.submodules.mac = LiteEthMAC(phy, dw, interface="crossbar", with_preamble_crc=True, anti_underflow=anti_underflow) self.submodules.arp = LiteEthARP(self.mac, mac_address, ip_address, clk_freq, dw=dw) self.submodules.ip = LiteEthIP(self.mac, mac_address, ip_address, self.arp.table, dw=dw) if with_icmp: @@ -25,8 +25,8 @@ def __init__(self, phy, mac_address, ip_address, clk_freq, with_icmp=True, dw=8) # UDP IP Core -------------------------------------------------------------------------------------- class LiteEthUDPIPCore(LiteEthIPCore): - def __init__(self, phy, mac_address, ip_address, clk_freq, with_icmp=True, dw=8): + def __init__(self, phy, mac_address, ip_address, clk_freq, with_icmp=True, dw=8, anti_underflow=0): ip_address = convert_ip(ip_address) LiteEthIPCore.__init__(self, phy, mac_address, ip_address, clk_freq, dw=dw, - with_icmp=with_icmp) + with_icmp=with_icmp, anti_underflow=anti_underflow) self.submodules.udp = LiteEthUDP(self.ip, ip_address, dw=dw) diff --git a/liteeth/mac/__init__.py b/liteeth/mac/__init__.py index b660e799..dd63cc56 100644 --- a/liteeth/mac/__init__.py +++ b/liteeth/mac/__init__.py @@ -21,7 +21,8 @@ def __init__(self, phy, dw, hw_mac = None, timestamp = None, full_memory_we = False, - with_sys_datapath = False): + with_sys_datapath = False, + anti_underflow = 0): assert dw%8 == 0 assert interface in ["crossbar", "wishbone", "hybrid"] @@ -31,7 +32,8 @@ def __init__(self, phy, dw, phy = phy, dw = dw, with_sys_datapath = with_sys_datapath, - with_preamble_crc = with_preamble_crc + with_preamble_crc = with_preamble_crc, + anti_underflow = anti_underflow ) self.csrs = [] if interface == "crossbar": diff --git a/liteeth/mac/anti_underflow.py b/liteeth/mac/anti_underflow.py new file mode 100644 index 00000000..cb9a314b --- /dev/null +++ b/liteeth/mac/anti_underflow.py @@ -0,0 +1,46 @@ +# +# This file is part of LiteEth. +# +# SPDX-License-Identifier: BSD-2-Clause + +import math + +from liteeth.common import * + +# MAC Gap ------------------------------------------------------------------------------------------ + +class LiteEthAntiUnderflow(Module): + def __init__(self, dw, depth=32): + ''' + buffers a whole packet and releases it at once + workaround for driving PHYs with wide datapaths which expect a + continuous stream + ''' + self.sink = sink = stream.Endpoint(eth_phy_description(dw)) + self.source = source = stream.Endpoint(eth_phy_description(dw)) + + # # # + + self.submodules.fifo = fifo = stream.SyncFIFO( + eth_phy_description(dw), + depth, + buffered=True + ) + + self.comb += [ + sink.connect(fifo.sink), + fifo.source.connect(source, omit=['valid', 'ready']) + ] + + self.submodules.fsm = fsm = FSM(reset_state="STORE") + fsm.act("STORE", + If(sink.valid & sink.last | (fifo.level >= fifo.depth - 1), + NextState("FLUSH") + ) + ) + fsm.act("FLUSH", + fifo.source.connect(source, keep=['valid', 'ready']), + If(fifo.source.valid & fifo.source.last, + NextState("STORE") + ) + ) diff --git a/liteeth/mac/core.py b/liteeth/mac/core.py index 9c7c641b..42ed635c 100644 --- a/liteeth/mac/core.py +++ b/liteeth/mac/core.py @@ -9,6 +9,7 @@ from liteeth.common import * from liteeth.mac import gap, preamble, crc, padding, last_be +from liteeth.mac.anti_underflow import LiteEthAntiUnderflow from liteeth.phy.model import LiteEthPHYModel from migen.genlib.cdc import PulseSynchronizer @@ -21,7 +22,8 @@ class LiteEthMACCore(Module, AutoCSR): def __init__(self, phy, dw, with_sys_datapath = False, with_preamble_crc = True, - with_padding = True): + with_padding = True, + anti_underflow = 0): # Endpoints. self.sink = stream.Endpoint(eth_phy_description(dw)) @@ -100,6 +102,12 @@ def add_gap(self): self.submodules += tx_gap self.pipeline.append(tx_gap) + def add_anti_underflow(self, depth): + au = LiteEthAntiUnderflow(phy_dw, depth) + au = ClockDomainsRenamer("eth_tx")(au) + self.submodules += au + self.pipeline.append(au) + def do_finalize(self): self.submodules += stream.Pipeline(*self.pipeline) @@ -127,10 +135,12 @@ def do_finalize(self): # Gap insertion has to occurr in phy tx domain to ensure gap is correctly maintained if not getattr(phy, "integrated_ifg_inserter", False): tx_datapath.add_gap() + if anti_underflow > 0: + tx_datapath.add_anti_underflow(anti_underflow) tx_datapath.pipeline.append(phy) - # print("tx_datapath.pipeline:") - # for p in tx_datapath.pipeline: - # print(p) + print("tx_datapath.pipeline:") + for p in tx_datapath.pipeline: + print(p) self.submodules.tx_datapath = tx_datapath # RX Data-Path (PHY --> Core). diff --git a/test/test_etherbone.py b/test/test_etherbone.py index 4f12b181..31b2288a 100644 --- a/test/test_etherbone.py +++ b/test/test_etherbone.py @@ -24,15 +24,22 @@ class DUT(Module): def __init__(self, dw): - assertStall = dw != 64 - self.submodules.phy_model = phy.PHY(dw, assertStall=assertStall, pcap_file='dump_wishbone.pcap') + self.submodules.phy_model = phy.PHY(dw, assertStall=True, pcap_file='dump_wishbone.pcap') self.submodules.mac_model = mac.MAC(self.phy_model) self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address) self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address) self.submodules.udp_model = udp.UDP(self.ip_model, ip_address) self.submodules.etherbone_model = etherbone.Etherbone(self.udp_model, debug=False) - self.submodules.core = LiteEthUDPIPCore(self.phy_model, mac_address + 1, ip_address + 1, 100000, with_icmp=False, dw=dw) + self.submodules.core = LiteEthUDPIPCore( + self.phy_model, + mac_address + 1, + ip_address + 1, + 100000, + with_icmp=False, + dw=dw, + anti_underflow=4 + ) self.submodules.etherbone = LiteEthEtherbone(self.core.udp, 0x1234, buffer_depth=8) self.submodules.sram = wishbone.SRAM(1024) @@ -117,7 +124,7 @@ def do_reads(self, dut, writes_datas): self.assertEqual(writes_datas, reads_datas) def main_generator(self, dut): - writes_datas = [((0xA + j) << 28) + j for j in range(6)] + writes_datas = [((0xA + j) << 24) + j for j in range(8)] # push IP address into ARP table to speed up sim. yield dut.core.arp.table.cached_valid.eq(1)