etherbone: add etherbone_tb, able to probe etherbone endpoint

This commit is contained in:
Florent Kermarrec 2015-02-11 14:33:17 +01:00
parent a2279bd2fa
commit 247c30ae26
9 changed files with 256 additions and 174 deletions

View File

@ -87,8 +87,8 @@ udp_protocol = 0x11
etherbone_magic = 0x4e6f etherbone_magic = 0x4e6f
etherbone_version = 1 etherbone_version = 1
etherbone_header_len = 8 etherbone_packet_header_len = 8
etherbone_header = { etherbone_packet_header = {
"magic": HField( 0, 0, 16), "magic": HField( 0, 0, 16),
"version": HField( 2, 4, 4), "version": HField( 2, 4, 4),
@ -130,6 +130,17 @@ def _layout_from_header(header):
_layout.append((k, v.width)) _layout.append((k, v.width))
return _layout 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): def eth_phy_description(dw):
payload_layout = [ payload_layout = [
("data", dw), ("data", dw),
@ -230,36 +241,26 @@ def eth_udp_user_description(dw):
] ]
return EndpointDescription(payload_layout, param_layout, packetized=True) return EndpointDescription(payload_layout, param_layout, packetized=True)
def eth_etherbone_description(dw): def eth_etherbone_packet_description(dw):
payload_layout = [ payload_layout = [
("data", dw), ("data", dw),
("error", dw//8) ("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) return EndpointDescription(payload_layout, param_layout, packetized=True)
def eth_etherbone_description(dw): def eth_etherbone_packet_user_description(dw):
payload_layout = [ payload_layout = [("data", dw)]
("data", dw), param_layout = _layout_from_header(etherbone_packet_header)
("error", dw//8) param_layout = _remove_from_layout(param_layout, "magic", "portsize", "addrsize", "version")
] param_layout += eth_udp_user_description(dw).param_layout
param_layout = _layout_from_header(etherbone_header)
return EndpointDescription(payload_layout, param_layout, packetized=True) return EndpointDescription(payload_layout, param_layout, packetized=True)
def eth_etherbone_user_description(dw): def eth_etherbone_record_description(dw):
payload_layout = [ payload_layout = [("data", dw)]
("data", dw), param_layout = _layout_from_header(etherbone_record_header)
("error", dw//8)
]
param_layout = [
("length", 16),
("ip_address", 32),
("wcount", 8),
("rcount", 8)
]
return EndpointDescription(payload_layout, param_layout, packetized=True) return EndpointDescription(payload_layout, param_layout, packetized=True)
# Generic classes # Generic classes
class Port: class Port:
def connect(self, port): def connect(self, port):

View File

@ -1,121 +1,30 @@
from liteeth.common import * from liteeth.common import *
from liteeth.core.etherbone import common 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)
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")
)
)
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,
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): class LiteEthEtherbone(Module):
def __init__(self, udp, udp_port): def __init__(self, udp, udp_port):
self.submodules.tx = tx = LiteEthEtherboneTX(udp_port) self.submodules.packet = packet = LiteEthEtherbonePacket(udp, udp_port)
self.submodules.rx = rx = LiteEthEtherboneRX()
udp_port = udp.crossbar.get_port(udp_port, dw=32) self.submodules.fsm = fsm = FSM(reset_state="IDLE")
self.comb += [ fsm.act("IDLE",
Record.connect(tx.source, udp_port.sink), packet.source.ack.eq(1),
Record.connect(udp_port.source, rx.sink) If(packet.source.stb & packet.source.sop,
] If(packet.source.pf,
self.master = master = LiteEthEtherboneWishboneMaster() packet.source.ack.eq(0),
self.comb += [ NextState("SEND_PROBE_RESPONSE")
Record.connect(rx.source.connect(master.sink)), )
Record.connect(master.source.connect(tx.sink)) )
] )
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")
)
)

View File

@ -1,19 +1 @@
from liteeth.common import * 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)

View File

@ -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

View File

@ -46,21 +46,23 @@ class LiteEthDepacketizer(Module):
) )
) )
) )
no_payload = Signal()
self.sync += \ self.sync += \
If(fsm.before_entering("COPY"), If(fsm.before_entering("COPY"),
source.sop.eq(1) source.sop.eq(1),
no_payload.eq(sink.eop)
).Elif(source.stb & source.ack, ).Elif(source.stb & source.ack,
source.sop.eq(0) source.sop.eq(0)
) )
self.comb += [ self.comb += [
source.eop.eq(sink.eop), source.eop.eq(sink.eop | no_payload),
source.data.eq(sink.data), source.data.eq(sink.data),
source.error.eq(sink.error), source.error.eq(sink.error),
_decode_header(header_type, self.header, source) _decode_header(header_type, self.header, source)
] ]
fsm.act("COPY", fsm.act("COPY",
sink.ack.eq(source.ack), sink.ack.eq(source.ack),
source.stb.eq(sink.stb), source.stb.eq(sink.stb | no_payload),
If(source.stb & source.ack & source.eop, If(source.stb & source.ack & source.eop,
NextState("IDLE") NextState("IDLE")
) )

View File

@ -28,3 +28,6 @@ udp_tb:
icmp_tb: icmp_tb:
$(CMD) icmp_tb.py $(CMD) icmp_tb.py
etherbone_tb:
$(CMD) etherbone_tb.py

View File

@ -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)

View File

@ -194,6 +194,14 @@ class EtherbonePacket(Packet):
self.encoded = init != [] self.encoded = init != []
self.records = [] 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): def get_records(self):
records = [] records = []
done = False done = False
@ -209,9 +217,9 @@ class EtherbonePacket(Packet):
if not self.encoded: if not self.encoded:
raise ValueError raise ValueError
header = [] header = []
for byte in self[:etherbone_header_len]: for byte in self[:etherbone_packet_header_len]:
header.append(self.pop(0)) 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)) setattr(self, k, get_field_data(v, header))
self.records = self.get_records() self.records = self.get_records()
self.encoded = False self.encoded = False
@ -227,10 +235,10 @@ class EtherbonePacket(Packet):
raise ValueError raise ValueError
self.set_records(self.records) self.set_records(self.records)
header = 0 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") value = merge_bytes(split_bytes(getattr(self, k), math.ceil(v.width/8)), "little")
header += (value << v.offset+(v.byte*8)) 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.insert(0, d)
self.encoded = True self.encoded = True
@ -241,7 +249,7 @@ class EtherbonePacket(Packet):
for d in self: for d in self:
r += "{:02x}".format(d) r += "{:02x}".format(d)
else: 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)) r += k + " : 0x{:0x}\n".format(getattr(self,k))
for i, record in enumerate(self.records): for i, record in enumerate(self.records):
r += record.__repr__(i) r += record.__repr__(i)
@ -255,7 +263,7 @@ class Etherbone(Module):
self.tx_packet = EtherbonePacket() self.tx_packet = EtherbonePacket()
self.rx_packet = EtherbonePacket() self.rx_packet = EtherbonePacket()
self.udp.set_etherbone_callback(self.callback) udp.set_etherbone_callback(self.callback)
def send(self, packet): def send(self, packet):
packet.encode() packet.encode()
@ -263,14 +271,14 @@ class Etherbone(Module):
print_etherbone(">>>>>>>>") print_etherbone(">>>>>>>>")
print_etherbone(packet) print_etherbone(packet)
udp_packet = udp.UDPPacket(packet) udp_packet = udp.UDPPacket(packet)
udp_packet.src_port = 0x1234 udp_packet.src_port = 0x1234 # XXX
udp_packet.dst_port = 0x5678 udp_packet.dst_port = 20000 # XXX
udp_packet.length = len(packet) udp_packet.length = len(packet)
udp_packet.checksum = 0 udp_packet.checksum = 0
self.udp.send(udp_packet) self.udp.send(udp_packet)
def callback(self, packet): def callback(self, packet):
packet = Etherbone(packet) packet = EtherbonePacket(packet)
packet.decode() packet.decode()
if self.debug: if self.debug:
print_etherbone("<<<<<<<<") print_etherbone("<<<<<<<<")
@ -306,13 +314,9 @@ if __name__ == "__main__":
# Packet # Packet
packet = EtherbonePacket() packet = EtherbonePacket()
packet.records = [copy.deepcopy(record) for i in range(8)] packet.records = [copy.deepcopy(record) for i in range(8)]
packet.magic = etherbone_magic
packet.version = etherbone_version
packet.nr = 0 packet.nr = 0
packet.pr = 0 packet.pr = 0
packet.pf = 0 packet.pf = 0
packet.addr_size = 32//8
packet.port_size = 32//8
#print(packet) #print(packet)
packet.encode() packet.encode()
#print(packet) #print(packet)

View File

@ -51,7 +51,7 @@ class UDP(Module):
self.ip.set_udp_callback(self.callback) self.ip.set_udp_callback(self.callback)
def set_etherbone_callback(callback): def set_etherbone_callback(self, callback):
self.etherbone_callback = callback self.etherbone_callback = callback
def send(self, packet): def send(self, packet):
@ -85,7 +85,7 @@ class UDP(Module):
self.process(packet) self.process(packet)
def process(self, packet): def process(self, packet):
if packet.dst_port == 22000: if packet.dst_port == 20000:
if self.etherbone_callback is not None: if self.etherbone_callback is not None:
self.etherbone_callback(packet) self.etherbone_callback(packet)