From: Florent Kermarrec Date: Wed, 11 Feb 2015 13:33:17 +0000 (+0100) Subject: etherbone: add etherbone_tb, able to probe etherbone endpoint X-Git-Tag: 24jan2021_ls180~2604^2~35 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=247c30ae266a0cfae2b613e900c2d14a37390fa5;p=litex.git etherbone: add etherbone_tb, able to probe etherbone endpoint --- diff --git a/liteeth/common.py b/liteeth/common.py index 0d57b912..969d030a 100644 --- a/liteeth/common.py +++ b/liteeth/common.py @@ -87,8 +87,8 @@ udp_protocol = 0x11 etherbone_magic = 0x4e6f etherbone_version = 1 -etherbone_header_len = 8 -etherbone_header = { +etherbone_packet_header_len = 8 +etherbone_packet_header = { "magic": HField( 0, 0, 16), "version": HField( 2, 4, 4), @@ -130,6 +130,17 @@ def _layout_from_header(header): _layout.append((k, v.width)) return _layout +def _remove_from_layout(layout, *args): + r = [] + for f in layout: + remove = False + for arg in args: + if f[0] == arg: + remove = True + if not remove: + r.append(f) + return r + def eth_phy_description(dw): payload_layout = [ ("data", dw), @@ -230,36 +241,26 @@ def eth_udp_user_description(dw): ] return EndpointDescription(payload_layout, param_layout, packetized=True) -def eth_etherbone_description(dw): +def eth_etherbone_packet_description(dw): payload_layout = [ ("data", dw), ("error", dw//8) ] - param_layout = _layout_from_header(etherbone_header) + param_layout = _layout_from_header(etherbone_packet_header) return EndpointDescription(payload_layout, param_layout, packetized=True) -def eth_etherbone_description(dw): - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - param_layout = _layout_from_header(etherbone_header) +def eth_etherbone_packet_user_description(dw): + payload_layout = [("data", dw)] + param_layout = _layout_from_header(etherbone_packet_header) + param_layout = _remove_from_layout(param_layout, "magic", "portsize", "addrsize", "version") + param_layout += eth_udp_user_description(dw).param_layout return EndpointDescription(payload_layout, param_layout, packetized=True) -def eth_etherbone_user_description(dw): - payload_layout = [ - ("data", dw), - ("error", dw//8) - ] - param_layout = [ - ("length", 16), - ("ip_address", 32), - ("wcount", 8), - ("rcount", 8) - ] +def eth_etherbone_record_description(dw): + payload_layout = [("data", dw)] + param_layout = _layout_from_header(etherbone_record_header) return EndpointDescription(payload_layout, param_layout, packetized=True) - # Generic classes class Port: def connect(self, port): diff --git a/liteeth/core/etherbone/__init__.py b/liteeth/core/etherbone/__init__.py index f46a9359..5b5bc6f8 100644 --- a/liteeth/core/etherbone/__init__.py +++ b/liteeth/core/etherbone/__init__.py @@ -1,121 +1,30 @@ from liteeth.common import * from liteeth.core.etherbone import common +from liteeth.core.etherbone.packet import * -class LiteEthEtherboneTX(Module): - def __init__(self, udp_port): - self.sink = sink = Sink(eth_etherbone_user_description(32)) - self.source = source = Source(eth_udp_user_description(32)) - ### - self.submodules.packetizer = packetizer = LiteEthEtherbonePacketizer() - self.comb += [ - packetizer.sink.stb.eq(sink.stb), - packetizer.sink.sop.eq(sink.sop), - packetizer.sink.eop.eq(sink.eop), - sink.ack.eq(packetizer.sink.ack), - - packetizer.sink.magic.eq(etherbone_magic), - packetizer.sink.portsize.eq(32), # XXX - packetizer.sink.addrsize.eq(32), # XXX - packetizer.sink.pf.eq(0), # XXX - packetizer.sink.version.eq(etherbone_version), - - packetizer.sink.wff.eq(0), # XXX - packetizer.sink.wca.eq(0), # XXX - packetizer.sink.cyc.eq(0), # XXX - packetizer.sink.rff.eq(0), # XXX - packetizer.sink.rca.eq(0), # XXX - packetizer.sink.bca.eq(0), # XXX - - packetizer.sink.rcount.eq(sink.rcount), - packetier.sink.wconut.eq(sink.wcount), - - packetizer.sink.data.eq(sink.data) - ] - - self.submodules.fsm = fsm = FSM(reset_state="IDLE") - fsm.act("IDLE", - packetizer.source.ack.eq(1), - If(packetizer.source.stb & packetizer.source.sop, - packetizer.source.ack.eq(0), - NextState("SEND") - ) - ) - self.comb += [ - source.src_port.eq(0x1234), # XXX, - source.dst_port.eq(udp_port), - source.ip_address.eq(sink.ip_address), - source.length.eq(sink.length + eth_etherbone_header_len) - ] - fsm.act("SEND", - Record.connect(packetizer.source, source), - If(source.stb & source.eop & source.ack, - NextState("IDLE") - ) - ) - -class LiteEthEtherboneRX(Module): - def __init__(self): - self.sink = sink = Sink(eth_udp_user_description(32)) - self.source = source = Source(eth_etherbone_user_description(32)) - ### - self.submodules.depacketizer = depacketizer = LiteEtherboneDepacketizer() - self.comb += Record.connect(sink, depacketizer.sink) +class LiteEthEtherbone(Module): + def __init__(self, udp, udp_port): + self.submodules.packet = packet = LiteEthEtherbonePacket(udp, udp_port) self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act("IDLE", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.sop, - depacketizer.source.ack.eq(0), - NextState("CHECK") - ) - ) - valid = Signal() - self.sync += valid.eq( - depacketizer.source.stb & - (depacketizer.source.magic == etherbone_magic) & - (depacketizer.source.version == etherbone_version) - ) - - fsm.act("CHECK", - If(valid, - NextState("PRESENT") - ).Else( - NextState("DROP") + packet.source.ack.eq(1), + If(packet.source.stb & packet.source.sop, + If(packet.source.pf, + packet.source.ack.eq(0), + NextState("SEND_PROBE_RESPONSE") + ) ) ) - self.comb += [ - source.sop.eq(depacketizer.source.sop), - source.eop.eq(depacketizer.source.eop), - source.rcount.eq(depacketizer.source.rcount), - source.wcount.eq(depacketizer.source.wcount), - source.data.eq(depacketizer.source.data), - source.error.eq(depacketizer.source.error) - ] - fsm.act("PRESENT", - source.stb.eq(depacketizer.source.stb), - depacketizer.source.ack.eq(source.ack), - If(source.stb & source.eop & source.ack, + fsm.act("SEND_PROBE_RESPONSE", + packet.sink.stb.eq(1), + packet.sink.sop.eq(1), + packet.sink.eop.eq(1), + packet.sink.pr.eq(1), + packet.sink.ip_address.eq(packet.source.ip_address), + packet.sink.length.eq(0), + If(packet.sink.ack, + packet.source.ack.eq(1), NextState("IDLE") ) ) - fsm.act("DROP", - depacketizer.source.ack.eq(1), - If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, - NextState("IDLE") - ) - ) - -class LiteEthEtherbone(Module): - def __init__(self, udp, udp_port): - self.submodules.tx = tx = LiteEthEtherboneTX(udp_port) - self.submodules.rx = rx = LiteEthEtherboneRX() - udp_port = udp.crossbar.get_port(udp_port, dw=32) - self.comb += [ - Record.connect(tx.source, udp_port.sink), - Record.connect(udp_port.source, rx.sink) - ] - self.master = master = LiteEthEtherboneWishboneMaster() - self.comb += [ - Record.connect(rx.source.connect(master.sink)), - Record.connect(master.source.connect(tx.sink)) - ] diff --git a/liteeth/core/etherbone/common.py b/liteeth/core/etherbone/common.py index 0246d4ef..22c8c63d 100644 --- a/liteeth/core/etherbone/common.py +++ b/liteeth/core/etherbone/common.py @@ -1,19 +1 @@ from liteeth.common import * -from liteeth.generic.depacketizer import LiteEthDepacketizer -from liteeth.generic.packetizer import LiteEthPacketizer - -class LiteEthEtherboneDepacketizer(LiteEthDepacketizer): - def __init__(self): - LiteEthDepacketizer.__init__(self, - eth_udp_user_description(32), - eth_etherbone_description(32), - etherbone_header, - etherbone_header_len) - -class LiteEthEtherbonePacketizer(LiteEthPacketizer): - def __init__(self): - LiteEthPacketizer.__init__(self, - eth_etherbone_description(32), - eth_udp_user_description(32), - etherbone_header, - etherbone_header_len) diff --git a/liteeth/core/etherbone/packet.py b/liteeth/core/etherbone/packet.py new file mode 100644 index 00000000..0a2bdbef --- /dev/null +++ b/liteeth/core/etherbone/packet.py @@ -0,0 +1,127 @@ +from liteeth.common import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer +from liteeth.core.etherbone import common + +class LiteEthEtherbonePacketPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_etherbone_packet_description(32), + eth_udp_user_description(32), + etherbone_packet_header, + etherbone_packet_header_len) + +class LiteEthEtherbonePacketTX(Module): + def __init__(self, udp_port): + self.sink = sink = Sink(eth_etherbone_packet_user_description(32)) + self.source = source = Source(eth_udp_user_description(32)) + ### + self.submodules.packetizer = packetizer = LiteEthEtherbonePacketPacketizer() + self.comb += [ + packetizer.sink.stb.eq(sink.stb), + packetizer.sink.sop.eq(sink.sop), + packetizer.sink.eop.eq(sink.eop), + sink.ack.eq(packetizer.sink.ack), + + packetizer.sink.magic.eq(etherbone_magic), + packetizer.sink.port_size.eq(32//8), + packetizer.sink.addr_size.eq(32//8), # XXX add a parameter? + packetizer.sink.pf.eq(sink.pf), + packetizer.sink.pr.eq(sink.pr), + packetizer.sink.nr.eq(sink.nr), + packetizer.sink.version.eq(etherbone_version), + + packetizer.sink.data.eq(sink.data) + ] + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + packetizer.source.ack.eq(1), + If(packetizer.source.stb & packetizer.source.sop, + packetizer.source.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + Record.connect(packetizer.source, source), + source.src_port.eq(0x1234), # XXX, + source.dst_port.eq(udp_port), + source.ip_address.eq(sink.ip_address), + source.length.eq(sink.length + etherbone_packet_header_len), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + +class LiteEthEtherbonePacketDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_udp_user_description(32), + eth_etherbone_packet_description(32), + etherbone_packet_header, + etherbone_packet_header_len) + +class LiteEthEtherbonePacketRX(Module): + def __init__(self): + self.sink = sink = Sink(eth_udp_user_description(32)) + self.source = source = Source(eth_etherbone_packet_user_description(32)) + ### + self.submodules.depacketizer = depacketizer = LiteEthEtherbonePacketDepacketizer() + self.comb += Record.connect(sink, depacketizer.sink) + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.sop, + depacketizer.source.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.sync += valid.eq( + depacketizer.source.stb & + (depacketizer.source.magic == etherbone_magic) + ) + fsm.act("CHECK", + If(valid, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.sop.eq(depacketizer.source.sop), + source.eop.eq(depacketizer.source.eop), + + source.pf.eq(depacketizer.source.pf), + source.pr.eq(depacketizer.source.pr), + source.nr.eq(depacketizer.source.nr), + + source.src_port.eq(sink.src_port), + source.dst_port.eq(sink.dst_port), + source.ip_address.eq(sink.ip_address), + source.length.eq(sink.length - etherbone_packet_header_len) + ] + fsm.act("PRESENT", + source.stb.eq(depacketizer.source.stb), + depacketizer.source.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + depacketizer.source.ack.eq(1), + If(depacketizer.source.stb & depacketizer.source.eop & depacketizer.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthEtherbonePacket(Module): + def __init__(self, udp, udp_port): + self.submodules.tx = tx = LiteEthEtherbonePacketTX(udp_port) + self.submodules.rx = rx = LiteEthEtherbonePacketRX() + udp_port = udp.crossbar.get_port(udp_port, dw=32) + self.comb += [ + Record.connect(tx.source, udp_port.sink), + Record.connect(udp_port.source, rx.sink) + ] + self.sink, self.source = self.tx.sink, self.rx.source diff --git a/liteeth/generic/depacketizer.py b/liteeth/generic/depacketizer.py index d5fd9713..e4388579 100644 --- a/liteeth/generic/depacketizer.py +++ b/liteeth/generic/depacketizer.py @@ -46,21 +46,23 @@ class LiteEthDepacketizer(Module): ) ) ) + no_payload = Signal() self.sync += \ If(fsm.before_entering("COPY"), - source.sop.eq(1) + source.sop.eq(1), + no_payload.eq(sink.eop) ).Elif(source.stb & source.ack, source.sop.eq(0) ) self.comb += [ - source.eop.eq(sink.eop), + source.eop.eq(sink.eop | no_payload), source.data.eq(sink.data), source.error.eq(sink.error), _decode_header(header_type, self.header, source) ] fsm.act("COPY", sink.ack.eq(source.ack), - source.stb.eq(sink.stb), + source.stb.eq(sink.stb | no_payload), If(source.stb & source.ack & source.eop, NextState("IDLE") ) diff --git a/liteeth/test/Makefile b/liteeth/test/Makefile index fb6fcb58..1682923d 100644 --- a/liteeth/test/Makefile +++ b/liteeth/test/Makefile @@ -28,3 +28,6 @@ udp_tb: icmp_tb: $(CMD) icmp_tb.py + +etherbone_tb: + $(CMD) etherbone_tb.py diff --git a/liteeth/test/etherbone_tb.py b/liteeth/test/etherbone_tb.py new file mode 100644 index 00000000..160189f8 --- /dev/null +++ b/liteeth/test/etherbone_tb.py @@ -0,0 +1,54 @@ +from migen.fhdl.std import * +from migen.bus import wishbone +from migen.bus.transactions import * +from migen.sim.generic import run_simulation + +from liteeth.common import * +from liteeth.core import LiteEthUDPIPCore +from liteeth.core.etherbone import LiteEthEtherbone + +from liteeth.test.common import * +from liteeth.test.model import phy, mac, arp, ip, udp, etherbone + +ip_address = 0x12345678 +mac_address = 0x12345678abcd + +class TB(Module): + def __init__(self): + self.submodules.phy_model = phy.PHY(8, debug=True) + self.submodules.mac_model = mac.MAC(self.phy_model, debug=True, loopback=False) + self.submodules.arp_model = arp.ARP(self.mac_model, mac_address, ip_address, debug=False) + self.submodules.ip_model = ip.IP(self.mac_model, mac_address, ip_address, debug=True, loopback=False) + self.submodules.udp_model = udp.UDP(self.ip_model, ip_address, debug=True, loopback=False) + self.submodules.etherbone_model = etherbone.Etherbone(self.udp_model, debug=True) + + self.submodules.core = LiteEthUDPIPCore(self.phy_model, mac_address, ip_address, 100000) + self.submodules.etherbone = LiteEthEtherbone(self.core.udp, 20000) + + # use sys_clk for each clock_domain + self.clock_domains.cd_eth_rx = ClockDomain() + self.clock_domains.cd_eth_tx = ClockDomain() + self.comb += [ + self.cd_eth_rx.clk.eq(ClockSignal()), + self.cd_eth_rx.rst.eq(ResetSignal()), + self.cd_eth_tx.clk.eq(ClockSignal()), + self.cd_eth_tx.rst.eq(ResetSignal()), + ] + + def gen_simulation(self, selfp): + selfp.cd_eth_rx.rst = 1 + selfp.cd_eth_tx.rst = 1 + yield + selfp.cd_eth_rx.rst = 0 + selfp.cd_eth_tx.rst = 0 + + for i in range(100): + yield + + # test probe + packet = etherbone.EtherbonePacket() + packet.pf = 1 + self.etherbone_model.send(packet) + +if __name__ == "__main__": + run_simulation(TB(), ncycles=1024, vcd_name="my.vcd", keep_files=True) \ No newline at end of file diff --git a/liteeth/test/model/etherbone.py b/liteeth/test/model/etherbone.py index 2f05d2e0..9cf7b33f 100644 --- a/liteeth/test/model/etherbone.py +++ b/liteeth/test/model/etherbone.py @@ -194,6 +194,14 @@ class EtherbonePacket(Packet): self.encoded = init != [] self.records = [] + self.magic = etherbone_magic + self.version = etherbone_version + self.addr_size = 32//8 + self.port_size = 32//8 + self.nr = 0 + self.pr = 0 + self.pf = 0 + def get_records(self): records = [] done = False @@ -209,9 +217,9 @@ class EtherbonePacket(Packet): if not self.encoded: raise ValueError header = [] - for byte in self[:etherbone_header_len]: + for byte in self[:etherbone_packet_header_len]: header.append(self.pop(0)) - for k, v in sorted(etherbone_header.items()): + for k, v in sorted(etherbone_packet_header.items()): setattr(self, k, get_field_data(v, header)) self.records = self.get_records() self.encoded = False @@ -227,10 +235,10 @@ class EtherbonePacket(Packet): raise ValueError self.set_records(self.records) header = 0 - for k, v in sorted(etherbone_header.items()): + for k, v in sorted(etherbone_packet_header.items()): value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little") header += (value << v.offset+(v.byte*8)) - for d in split_bytes(header, etherbone_header_len): + for d in split_bytes(header, etherbone_packet_header_len): self.insert(0, d) self.encoded = True @@ -241,7 +249,7 @@ class EtherbonePacket(Packet): for d in self: r += "{:02x}".format(d) else: - for k in sorted(etherbone_header.keys()): + for k in sorted(etherbone_packet_header.keys()): r += k + " : 0x{:0x}\n".format(getattr(self,k)) for i, record in enumerate(self.records): r += record.__repr__(i) @@ -255,7 +263,7 @@ class Etherbone(Module): self.tx_packet = EtherbonePacket() self.rx_packet = EtherbonePacket() - self.udp.set_etherbone_callback(self.callback) + udp.set_etherbone_callback(self.callback) def send(self, packet): packet.encode() @@ -263,14 +271,14 @@ class Etherbone(Module): print_etherbone(">>>>>>>>") print_etherbone(packet) udp_packet = udp.UDPPacket(packet) - udp_packet.src_port = 0x1234 - udp_packet.dst_port = 0x5678 + udp_packet.src_port = 0x1234 # XXX + udp_packet.dst_port = 20000 # XXX udp_packet.length = len(packet) udp_packet.checksum = 0 self.udp.send(udp_packet) def callback(self, packet): - packet = Etherbone(packet) + packet = EtherbonePacket(packet) packet.decode() if self.debug: print_etherbone("<<<<<<<<") @@ -306,13 +314,9 @@ if __name__ == "__main__": # Packet packet = EtherbonePacket() packet.records = [copy.deepcopy(record) for i in range(8)] - packet.magic = etherbone_magic - packet.version = etherbone_version packet.nr = 0 packet.pr = 0 packet.pf = 0 - packet.addr_size = 32//8 - packet.port_size = 32//8 #print(packet) packet.encode() #print(packet) diff --git a/liteeth/test/model/udp.py b/liteeth/test/model/udp.py index 1892a2d5..20e0e6f2 100644 --- a/liteeth/test/model/udp.py +++ b/liteeth/test/model/udp.py @@ -51,7 +51,7 @@ class UDP(Module): self.ip.set_udp_callback(self.callback) - def set_etherbone_callback(callback): + def set_etherbone_callback(self, callback): self.etherbone_callback = callback def send(self, packet): @@ -85,7 +85,7 @@ class UDP(Module): self.process(packet) def process(self, packet): - if packet.dst_port == 22000: + if packet.dst_port == 20000: if self.etherbone_callback is not None: self.etherbone_callback(packet)