etherbone: add etherbone_tb, able to probe etherbone endpoint
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 11 Feb 2015 13:33:17 +0000 (14:33 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 11 Feb 2015 13:33:17 +0000 (14:33 +0100)
liteeth/common.py
liteeth/core/etherbone/__init__.py
liteeth/core/etherbone/common.py
liteeth/core/etherbone/packet.py [new file with mode: 0644]
liteeth/generic/depacketizer.py
liteeth/test/Makefile
liteeth/test/etherbone_tb.py [new file with mode: 0644]
liteeth/test/model/etherbone.py
liteeth/test/model/udp.py

index 0d57b9126626a826fdd4dbd1182350a8502e109d..969d030a4a9d8b1f66445bc8541ccc164b86c3be 100644 (file)
@@ -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):
index f46a9359a04e76c2b0f2b065be6fd44ebc909488..5b5bc6f839174dcc737c3eed1f897cb38ce7c4c4 100644 (file)
 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))
-               ]
index 0246d4ef5d416c0818359570ad4a525529df84ff..22c8c63dcb97bf32cf51bec67832caf491cb8da6 100644 (file)
@@ -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 (file)
index 0000000..0a2bdbe
--- /dev/null
@@ -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
index d5fd97139e4f73617ace4f3e51d044b7af5c07e2..e43885793e19dee16ae5aec7d12ebd73a24c2269 100644 (file)
@@ -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")
                        )
index fb6fcb58949506c5e35813a35070b95808842166..1682923d7eb2219df54667978dd0472f4c1c12ba 100644 (file)
@@ -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 (file)
index 0000000..160189f
--- /dev/null
@@ -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
index 2f05d2e0a40ec4bce4b937fd3530c861475568f8..9cf7b33feb463dec2f1414b53469ba7edad69b7a 100644 (file)
@@ -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)
index 1892a2d56e07d079cdbcebeab7b569dcaf7af253..20e0e6f2a279ce380f5a19002a05b2235ca58572 100644 (file)
@@ -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)