add arp_tb and fixes (able to send a valid ARP request to the model)

This commit is contained in:
Florent Kermarrec 2015-01-30 00:03:16 +01:00
parent 08e83af62d
commit 18a7d66b5e
11 changed files with 160 additions and 63 deletions

View File

@ -17,15 +17,15 @@ class LiteEthARPDepacketizer(LiteEthDepacketizer):
eth_mac_description(8), eth_mac_description(8),
eth_arp_description(8), eth_arp_description(8),
arp_header, arp_header,
arp_header_length) arp_header_len)
class LiteEthARPPacketizer(LiteEthDepacketizer): class LiteEthARPPacketizer(LiteEthPacketizer):
def __init__(self): def __init__(self):
LiteEthDepacketizer.__init__(self, LiteEthPacketizer.__init__(self,
eth_arp_description(8), eth_arp_description(8),
eth_mac_description(8), eth_mac_description(8),
arp_header, arp_header,
arp_header_length) arp_header_len)
class LiteSATACommandTX(Module): class LiteSATACommandTX(Module):
def __init__(self, transport): def __init__(self, transport):
@ -37,15 +37,20 @@ class LiteEthARPTX(Module):
self.sink = sink = Sink(_arp_table_description()) self.sink = sink = Sink(_arp_table_description())
self.source = Source(eth_mac_description(8)) self.source = Source(eth_mac_description(8))
### ###
packetiser = LiteEthARPPacketizer() packetizer = LiteEthARPPacketizer()
self.submodules += packetizer self.submodules += packetizer
source = packetizer.sink source = packetizer.sink
counter = Counter(max=arp_packet_length)
self.submodules += counter
fsm = FSM(reset_state="IDLE") fsm = FSM(reset_state="IDLE")
self.submodules += fsm self.submodules += fsm
fsm.act("IDLE", fsm.act("IDLE",
sink.ack.eq(1), sink.ack.eq(1),
If(sink.stb & sink.sop, counter.reset.eq(1),
If(sink.stb,
sink.ack.eq(0),
NextState("SEND") NextState("SEND")
) )
) )
@ -66,20 +71,29 @@ class LiteEthARPTX(Module):
source.destination_ip_address.eq(sink.ip_address) source.destination_ip_address.eq(sink.ip_address)
) )
] ]
fsm.act("SEND_REQUEST", fsm.act("SEND",
source.stb.eq(1), source.stb.eq(1),
source.sop.eq(counter.value == 0),
source.eop.eq(counter.value == arp_packet_length-1),
Record.connect(packetizer.source, self.source), Record.connect(packetizer.source, self.source),
If(self.source.stb & self.source.eop & self.source.ack, self.source.destination_mac_address.eq(source.destination_mac_address),
self.source.source_mac_address.eq(mac_address),
self.source.ethernet_type.eq(ethernet_type_arp),
If(self.source.stb & self.source.ack,
sink.ack.eq(1),
counter.ce.eq(1),
If(self.source.eop,
NextState("IDLE") NextState("IDLE")
) )
) )
)
class LiteEthARPRX(Module): class LiteEthARPRX(Module):
def __init__(self, mac_address, ip_address): def __init__(self, mac_address, ip_address):
self.sink = Sink(eth_mac_description(8)) self.sink = Sink(eth_mac_description(8))
self.source = source = Source(_arp_table_description()) self.source = source = Source(_arp_table_description())
### ###
depacketiser = LiteEthARPDepacketizer() depacketizer = LiteEthARPDepacketizer()
self.submodules += depacketizer self.submodules += depacketizer
self.comb += Record.connect(self.sink, depacketizer.sink) self.comb += Record.connect(self.sink, depacketizer.sink)
sink = depacketizer.source sink = depacketizer.source
@ -118,11 +132,11 @@ class LiteEthARPRX(Module):
source.reply.eq(reply), source.reply.eq(reply),
source.request.eq(request) source.request.eq(request)
), ),
NextState.eq("TERMINATE") NextState("TERMINATE")
), ),
fsm.act("TERMINATE", fsm.act("TERMINATE",
sink.ack.eq(1), sink.ack.eq(1),
If(sink.stb & sink.source.eop & sink.source.ack, If(sink.stb & sink.eop,
NextState("IDLE") NextState("IDLE")
) )
) )
@ -173,9 +187,9 @@ class LiteEthARPTable(Module):
fsm.act("CHECK_TABLE", fsm.act("CHECK_TABLE",
# XXX add a kind of CAM? # XXX add a kind of CAM?
If(found, If(found,
NexState.eq("PRESENT_RESPONSE") NextState("PRESENT_RESPONSE")
).Else( ).Else(
NextState.eq("SEND_REQUEST") NextState("SEND_REQUEST")
) )
) )
fsm.act("SEND_REQUEST", fsm.act("SEND_REQUEST",

View File

@ -46,7 +46,6 @@ class LiteEthDepacketizer(Module):
source.sop.eq(0) source.sop.eq(0)
) )
self.comb += [ self.comb += [
source.sop.eq(sop),
source.eop.eq(sink.eop), source.eop.eq(sink.eop),
source.data.eq(sink.data), source.data.eq(sink.data),
source.error.eq(sink.error), source.error.eq(sink.error),

View File

@ -1,11 +1,19 @@
from liteeth.common import * from liteeth.common import *
import math
def reverse_bytes(v):
n = math.ceil(flen(v)//8)
r = []
for i in reversed(range(n)):
r.append(v[i*8:min((i+1)*8, flen(v))])
return Cat(iter(r))
def _encode_header(h_dict, h_signal, obj): def _encode_header(h_dict, h_signal, obj):
r = [] r = []
for k, v in sorted(h_dict.items()): for k, v in sorted(h_dict.items()):
start = v.word*32+v.offset start = v.byte*8+v.offset
end = start+v.width end = start+v.width
r.append(h_signal[start:end].eq(getattr(obj, k))) r.append(h_signal[start:end].eq(reverse_bytes(getattr(obj, k))))
return r return r
class LiteEthPacketizer(Module): class LiteEthPacketizer(Module):
@ -20,7 +28,7 @@ class LiteEthPacketizer(Module):
counter = Counter(max=header_length) counter = Counter(max=header_length)
self.submodules += counter self.submodules += counter
self.comb += header.eq(_encode_header(header_type, header, sink)) self.comb += _encode_header(header_type, header, sink)
self.sync += [ self.sync += [
If(load, If(load,
header_reg.eq(header) header_reg.eq(header)
@ -34,14 +42,15 @@ class LiteEthPacketizer(Module):
fsm.act("IDLE", fsm.act("IDLE",
sink.ack.eq(1), sink.ack.eq(1),
counter.reset.eq(1),
If(sink.stb & sink.sop, If(sink.stb & sink.sop,
load.eq(1),
sink.ack.eq(0), sink.ack.eq(0),
source.stb.eq(1), source.stb.eq(1),
source.sop.eq(1), source.sop.eq(1),
source.eop.eq(0), source.eop.eq(0),
source.data.eq(header[:8]), source.data.eq(header[:8]),
If(source.stb & source.ack, If(source.stb & source.ack,
load.eq(1),
NextState("SEND_HEADER"), NextState("SEND_HEADER"),
) )
) )
@ -52,8 +61,9 @@ class LiteEthPacketizer(Module):
source.eop.eq(sink.eop), source.eop.eq(sink.eop),
source.data.eq(header_reg[8:16]), source.data.eq(header_reg[8:16]),
If(source.stb & source.ack, If(source.stb & source.ack,
sink.ack.eq(1), shift.eq(1),
If(counter == header_length-2, counter.ce.eq(1),
If(counter.value == header_length-2,
NextState("COPY") NextState("COPY")
) )
) )
@ -61,7 +71,7 @@ class LiteEthPacketizer(Module):
fsm.act("COPY", fsm.act("COPY",
source.stb.eq(sink.stb), source.stb.eq(sink.stb),
source.sop.eq(0), source.sop.eq(0),
source.eop.eq(sink_eop), source.eop.eq(sink.eop),
source.data.eq(sink.data), source.data.eq(sink.data),
source.error.eq(sink.error), source.error.eq(sink.error),
If(source.stb & source.ack, If(source.stb & source.ack,

View File

@ -8,12 +8,12 @@ class LiteEthIPV4Depacketizer(LiteEthDepacketizer):
eth_mac_description(8), eth_mac_description(8),
eth_ipv4_description(8), eth_ipv4_description(8),
ipv4_header, ipv4_header,
ipv4_header_length) ipv4_header_len)
class LiteEthIPV4Packetizer(LiteEthDepacketizer): class LiteEthIPV4Packetizer(LiteEthPacketizer):
def __init__(self): def __init__(self):
LiteEthDepacketizer.__init__(self, LiteEthPacketizer.__init__(self,
eth_ipv4_description(8), eth_ipv4_description(8),
eth_mac_description(8), eth_mac_description(8),
ipv4_header, ipv4_header,
ipv4_header_length) ipv4_header_len)

View File

@ -10,15 +10,15 @@ class LiteEthMACDepacketizer(LiteEthDepacketizer):
eth_phy_description(8), eth_phy_description(8),
eth_mac_description(8), eth_mac_description(8),
mac_header, mac_header,
mac_header_length) mac_header_len)
class LiteEthMACPacketizer(LiteEthDepacketizer): class LiteEthMACPacketizer(LiteEthPacketizer):
def __init__(self): def __init__(self):
LiteEthDepacketizer.__init__(self, LiteEthPacketizer.__init__(self,
eth_mac_description(8), eth_mac_description(8),
eth_phy_description(8), eth_phy_description(8),
mac_header, mac_header,
mac_header_length) mac_header_len)
class LiteEthMAC(Module, AutoCSR): class LiteEthMAC(Module, AutoCSR):
def __init__(self, phy, dw, interface="mac", endianness="be", def __init__(self, phy, dw, interface="mac", endianness="be",
@ -34,6 +34,7 @@ class LiteEthMAC(Module, AutoCSR):
Record.connect(self.core.source, depacketizer.sink) Record.connect(self.core.source, depacketizer.sink)
] ]
self.sink, self.source = packetizer.sink, depacketizer.source self.sink, self.source = packetizer.sink, depacketizer.source
pass
elif interface == "wishbone": elif interface == "wishbone":
self.submodules.interface = wishbone.LiteEthMACWishboneInterface(dw, 2, 2) self.submodules.interface = wishbone.LiteEthMACWishboneInterface(dw, 2, 2)
self.comb += [ self.comb += [

View File

@ -6,6 +6,10 @@ class LiteEthMACCore(Module, AutoCSR):
def __init__(self, phy, dw, endianness="be", with_hw_preamble_crc=True): def __init__(self, phy, dw, endianness="be", with_hw_preamble_crc=True):
if dw < phy.dw: if dw < phy.dw:
raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw)) raise ValueError("Core data width({}) must be larger than PHY data width({})".format(dw, phy.dw))
rx_pipeline = [phy]
tx_pipeline = [phy]
# Preamble / CRC (optional) # Preamble / CRC (optional)
if with_hw_preamble_crc: if with_hw_preamble_crc:
self._hw_preamble_crc = CSRStatus(reset=1) self._hw_preamble_crc = CSRStatus(reset=1)
@ -21,6 +25,10 @@ class LiteEthMACCore(Module, AutoCSR):
self.submodules += RenameClockDomains(crc32_inserter, "eth_tx") self.submodules += RenameClockDomains(crc32_inserter, "eth_tx")
self.submodules += RenameClockDomains(crc32_checker, "eth_rx") self.submodules += RenameClockDomains(crc32_checker, "eth_rx")
tx_pipeline += [preamble_inserter, crc32_inserter]
rx_pipeline += [preamble_checker, crc32_checker]
if dw != phy.dw:
# Delimiters # Delimiters
tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw) tx_last_be = last_be.LiteEthMACTXLastBE(phy.dw)
rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw) rx_last_be = last_be.LiteEthMACRXLastBE(phy.dw)
@ -34,20 +42,20 @@ class LiteEthMACCore(Module, AutoCSR):
self.submodules += RenameClockDomains(tx_converter, "eth_tx") self.submodules += RenameClockDomains(tx_converter, "eth_tx")
self.submodules += RenameClockDomains(rx_converter, "eth_rx") self.submodules += RenameClockDomains(rx_converter, "eth_rx")
tx_pipeline += [tx_last_be, tx_converter]
rx_pipeline += [rx_last_be, rx_converter]
# Cross Domain Crossing # Cross Domain Crossing
tx_cdc = AsyncFIFO(eth_phy_description(dw), 4) tx_cdc = AsyncFIFO(eth_phy_description(dw), 4)
rx_cdc = AsyncFIFO(eth_phy_description(dw), 4) rx_cdc = AsyncFIFO(eth_phy_description(dw), 4)
self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"}) self.submodules += RenameClockDomains(tx_cdc, {"write": "sys", "read": "eth_tx"})
self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"}) self.submodules += RenameClockDomains(rx_cdc, {"write": "eth_rx", "read": "sys"})
tx_pipeline += [tx_cdc]
rx_pipeline += [rx_cdc]
# Graph # Graph
if with_hw_preamble_crc: self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline))
rx_pipeline = [phy, preamble_checker, crc32_checker, rx_last_be, rx_converter, rx_cdc]
tx_pipeline = [tx_cdc, tx_converter, tx_last_be, crc32_inserter, preamble_inserter, phy]
else:
rx_pipeline = [phy, rx_last_be, rx_converter, rx_cdc]
tx_pipeline = [tx_cdc, tx_converter, tx_last_be, phy]
self.submodules.rx_pipeline = Pipeline(*rx_pipeline) self.submodules.rx_pipeline = Pipeline(*rx_pipeline)
self.submodules.tx_pipeline = Pipeline(*tx_pipeline)
self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source self.sink, self.source = self.tx_pipeline.sink, self.rx_pipeline.source

View File

@ -12,3 +12,6 @@ mac_core_tb:
mac_wishbone_tb: mac_wishbone_tb:
$(CMD) mac_wishbone_tb.py $(CMD) mac_wishbone_tb.py
arp_tb:
$(CMD) arp_tb.py

54
liteeth/test/arp_tb.py Normal file
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.mac import LiteEthMAC
from liteeth.arp import LiteEthARP
from liteeth.test.common import *
from liteeth.test.model import phy, mac, arp
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=True)
self.submodules.core = LiteEthMAC(phy=self.phy_model, dw=8, with_hw_preamble_crc=True)
self.submodules.arp = LiteEthARP(mac_address, ip_address)
# 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()),
]
self.comb += [
Record.connect(self.arp.source, self.core.sink),
Record.connect(self.core.source, self.arp.sink)
]
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
selfp.arp.table.request.ip_address = 0x12345678
selfp.arp.table.request.stb = 1
if __name__ == "__main__":
run_simulation(TB(), ncycles=256, vcd_name="my.vcd", keep_files=True)

View File

@ -4,6 +4,8 @@ from liteeth.common import *
from liteeth.mac.common import * from liteeth.mac.common import *
from liteeth.test.common import * from liteeth.test.common import *
from liteeth.test.model import mac
def print_arp(s): def print_arp(s):
print_with_prefix(s, "[ARP]") print_with_prefix(s, "[ARP]")
@ -39,10 +41,10 @@ class ARPPacket(Packet):
return r return r
class ARP(Module): class ARP(Module):
def __init__(self, mac, ip_address, mac_address, debug=False): def __init__(self, mac, mac_address, ip_address, debug=False):
self.mac = mac self.mac = mac
self.mac_address = mac_address
self.ip_address = ip_address self.ip_address = ip_address
self.mac_address = mac_addres
self.debug = debug self.debug = debug
self.tx_packets = [] self.tx_packets = []
self.tx_packet = ARPPacket() self.tx_packet = ARPPacket()
@ -53,22 +55,26 @@ class ARP(Module):
self.mac.set_arp_callback(self.callback) self.mac.set_arp_callback(self.callback)
def send(self, packet): def send(self, packet):
packet.encode()
if self.debug: if self.debug:
print_arp(">>>>>>>>") print_arp(">>>>>>>>")
print_arp(packet) print_arp(packet)
packet.encode() mac_packet = mac.MACPacket(packet)
self.mac.send(MACPacket(packet)) mac_packet.destination_mac_address = packet.destination_mac_address
mac_packet.source_mac_address = packet.source_mac_address
mac_packet.ethernet_type = ethernet_type_arp
self.mac.send(mac_packet)
def callback(self, packet): def callback(self, packet):
packet = ARPPacket(datas) packet = ARPPacket(packet)
packet.decode() packet.decode()
if self.debug: if self.debug:
print_arp("<<<<<<<<") print_arp("<<<<<<<<")
print_arp(packet) print_arp(packet)
self.process_packet() self.process(packet)
def process(self, packet): def process(self, packet):
if len(packet) != arp_packet_length-arp_header_length: if len(packet) != arp_packet_length-arp_header_len:
raise ValueError raise ValueError
if packet.hardware_type != arp_hwtype_ethernet: if packet.hardware_type != arp_hwtype_ethernet:
raise ValueError raise ValueError
@ -85,9 +91,10 @@ class ARP(Module):
def process_request(self, request): def process_request(self, request):
if request.destination_ip_address == self.ip_address: if request.destination_ip_address == self.ip_address:
reply = ARPPacket([0]*(arp_packet_length-arp_header_length)) reply = ARPPacket([0]*(arp_packet_length-arp_header_len))
reply.hardware_type = arp_hwtype_ethernet reply.hardware_type = arp_hwtype_ethernet
reply.protocol_type = arp_proto_ip reply.protocol_type = arp_proto_ip
reply.operation = arp_opcode_reply
reply.hardware_address_length = 6 reply.hardware_address_length = 6
reply.protocol_address_length = 4 reply.protocol_address_length = 4
reply.source_mac_address = self.mac_address reply.source_mac_address = self.mac_address
@ -100,9 +107,10 @@ class ARP(Module):
self.table[reply.source_ip_address] = reply.source_mac_address self.table[reply.source_ip_address] = reply.source_mac_address
def request(self, ip_address): def request(self, ip_address):
request = ARPPacket([0]*(arp_packet_length-arp_header_length)) request = ARPPacket([0]*(arp_packet_length-arp_header_len))
request.hardware_type = arp_hwtype_ethernet request.hardware_type = arp_hwtype_ethernet
request.protocol_type = arp_proto_ip request.protocol_type = arp_proto_ip
request.operation = arp_opcode_request
request.hardware_address_length = 6 request.hardware_address_length = 6
request.protocol_address_length = 4 request.protocol_address_length = 4
request.source_mac_address = self.mac_address request.source_mac_address = self.mac_address

View File

@ -118,10 +118,10 @@ class MAC(Module):
if self.loopback: if self.loopback:
self.send(packet) self.send(packet)
else: else:
if self.ethernet_type == ethernet_type_ip: if packet.ethernet_type == ethernet_type_ip:
if self.ip_callback is not None: if self.ip_callback is not None:
self.ip_callback(packet) self.ip_callback(packet)
elif self.ethernet_type == ethernet_type_arp: elif packet.ethernet_type == ethernet_type_arp:
if self.arp_callback is not None: if self.arp_callback is not None:
self.arp_callback(packet) self.arp_callback(packet)
else: else:

View File

@ -8,12 +8,12 @@ class LiteEthUDPDepacketizer(LiteEthDepacketizer):
eth_ipv4_description(8), eth_ipv4_description(8),
eth_udp_description(8), eth_udp_description(8),
udp_header, udp_header,
udp_header_length) udp_header_len)
class LiteEthUDPPacketizer(LiteEthDepacketizer): class LiteEthUDPPacketizer(LiteEthPacketizer):
def __init__(self): def __init__(self):
LiteEthDepacketizer.__init__(self, LiteEthPacketizer.__init__(self,
eth_udp_description(8), eth_udp_description(8),
eth_ipv4_description(8), eth_ipv4_description(8),
udp_header, udp_header,
udp_header_length) udp_header_len)