diff --git a/liteeth/__init__.py b/liteeth/__init__.py index 85adb3ac1..50b5c0f3b 100644 --- a/liteeth/__init__.py +++ b/liteeth/__init__.py @@ -4,21 +4,22 @@ from liteeth.generic.dispatcher import Dispatcher from liteeth.mac import LiteEthMAC class LiteEthIPStack(Module, AutoCSR): - def __init__(self, phy): + def __init__(self, phy, + mac_address= 0x12345678abcd, + ip_address="192.168.0.10"): self.phy = phy self.submodules.mac = mac = LiteEthMAC(phy, 8, interface="mac", with_hw_preamble_crc=True) - self.submodules.arp = arp = LiteEthARP() + self.submodules.arp = arp = LiteEthARP(mac_address, ip_address) self.submodules.ip = ip = LiteEthMACIP() # MAC dispatch self.submodules.mac_dispatcher = mac_dispatcher = Dispatcher(mac.source, [arp.sink, ip.sink], one_hot=True) - self.comb += [ - If(mac.source.eth_type == ethernet_type_arp, - mac_dispatcher.sel.eq(1) - ).Elif(mac.source.eth_type == ethernet_type_ip, - mac_dispatcher.sel.eq(2) - ) - ] + self.comb += \ + Case(mac.source.eth_type, { + ethernet_type_arp : [mac_dispatcher.sel.eq(1)], + ethernet_type_ip : [mac_dispatcher.sel.eq(2)], + "default" : [mac_dispatcher.sel.eq(0)], + }) # MAC arbitrate self.submodules.mac_arbiter = mac_arbiter = Arbiter([arp.source, ip.source], mac.sink) diff --git a/liteeth/arp/__init__.py b/liteeth/arp/__init__.py index 3e09b0ede..78a5535c0 100644 --- a/liteeth/arp/__init__.py +++ b/liteeth/arp/__init__.py @@ -2,6 +2,15 @@ from liteeth.common import * from liteeth.generic.depacketizer import LiteEthDepacketizer from liteeth.generic.packetizer import LiteEthPacketizer +def _arp_table_description(): + layout = [ + ("reply", 1), + ("request", 1), + ("ip_address", 32), + ("mac_address", 48) + ] + return EndpointDescription(layout, packetized=False) + class LiteEthARPDepacketizer(LiteEthDepacketizer): def __init__(self): LiteEthDepacketizer.__init__(self, @@ -17,3 +26,183 @@ class LiteEthARPPacketizer(LiteEthDepacketizer): eth_mac_description(8), arp_header, arp_header_length) + +class LiteSATACommandTX(Module): + def __init__(self, transport): + self.sink = sink = Sink(command_tx_description(32)) + + +class LiteEthARPTX(Module): + def __init__(self, mac_address, ip_address): + self.sink = sink = Sink(_arp_table_description()) + self.source = Source(eth_mac_description(8)) + ### + packetiser = LiteEthARPPacketizer() + self.submodules += packetizer + source = packetizer.sink + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + NextState("SEND") + ) + ) + self.comb += [ + source.hardware_type.eq(arp_hwtype_ethernet), + source.protocol_type.eq(arp_proto_ip), + source.hardware_address_length.eq(6), + source.protocol_address_length.eq(4), + source.source_mac_address.eq(mac_address), + source.source_ip_address.eq(ip_address), + If(sink.reply, + source.operation.eq(arp_opcode_reply), + source.destination_mac_address.eq(sink.mac_address), + source.destination_ip_address.eq(sink.ip_address) + ).Elif(sink.request, + source.operation.eq(arp_opcode_request), + source.destination_mac_address.eq(0xffffffffffff), + source.destination_ip_address.eq(sink.ip_address) + ) + ] + fsm.act("SEND_REQUEST", + source.stb.eq(1), + Record.connect(packetizer.source, self.source), + If(self.source.stb & self.source.eop & self.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthARPRX(Module): + def __init__(self, mac_address, ip_address): + self.sink = Sink(eth_mac_description(8)) + self.source = source = Source(_arp_table_description()) + ### + depacketiser = LiteEthARPDepacketizer() + self.submodules += depacketizer + self.comb += Record.connect(self.sink, depacketizer.sink) + sink = depacketizer.source + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + NextState("CHECK") + ) + ) + valid = Signal() + self.comb += valid.eq( + sink.stb & + (sink.hardware_type == arp_hwtype_ethernet) & + (sink.protocol_type == arp_proto_ip) & + (sink.hardware_address_length == 6) & + (sink.protocol_address_length == 4) & + (sink.destination_ip_address == ip_address) + ) + reply = Signal() + request = Signal() + self.comb += Case(sink.operation, { + arp_opcode_request : [request.eq(1)], + arp_opcode_reply : [reply.eq(1)], + "default" : [] + }) + self.comb += [ + source.ip_address.eq(sink.source_ip_address), + source.mac_address.eq(sink.source_mac_address) + ] + fsm.act("CHECK", + If(valid, + source.stb.eq(1), + source.reply.eq(reply), + source.request.eq(request) + ), + NextState.eq("TERMINATE") + ), + fsm.act("TERMINATE", + sink.ack.eq(1), + If(sink.stb & sink.source.eop & sink.source.ack, + NextState("IDLE") + ) + ) + +arp_table_request_layout = [ + ("ip_address", 32) +] + +arp_table_response_layout = [ + ("failed", 1), + ("mac_address", 48) + +] + +class LiteEthARPTable(Module): + def __init__(self): + self.sink = sink = Sink(_arp_table_description()) # from arp_rx + self.source = source = Source(_arp_table_description()) # to arp_tx + + # Request/Response interface + self.request = request = Sink(arp_table_request_layout) + self.response = response = Source(arp_table_response_layout) + + fsm = FSM(reset_state="IDLE") + self.submodules += fsm + fsm.act("IDLE", + If(sink.stb & sink.request, + NextState("SEND_REPLY") + ).Elif(sink.stb & sink.reply, + NextState("UPDATE_TABLE") + ).Elif(request.stb, + NextState("CHECK_TABLE") + ) + ) + fsm.act("SEND_REPLY", + source.stb.eq(1), + source.reply.eq(1), + source.ip_address.eq(sink.ip_address), + If(source.ack, + NextState("IDLE") + ) + ) + fsm.act("UPDATE_TABLE", + # XXX update memory + NextState("IDLE") + ) + found = Signal() + fsm.act("CHECK_TABLE", + # XXX add a kind of CAM? + If(found, + NexState.eq("PRESENT_RESPONSE") + ).Else( + NextState.eq("SEND_REQUEST") + ) + ) + fsm.act("SEND_REQUEST", + source.stb.eq(1), + source.request.eq(1), + source.ip_address.eq(request.ip_address), + If(source.ack, + NextState("IDLE") + ) + ) + fsm.act("PRESENT_RESPONSE", + response.stb.eq(1), + response.failed.eq(0), # XXX add timeout to trigger failed + response.mac_address.eq(0x12345678abcd), # XXX get mac address from table + If(response.ack, + NextState("IDLE") + ) + ) + +class LiteEthARP(Module): + def __init__(self, mac_address, ip_address): + self.submodules.tx = LiteEthARPTX(mac_address, ip_address) + self.submodules.rx = LiteEthARPRX(mac_address, ip_address) + self.submodules.table = LiteEthARPTable() + self.comb += [ + Record.connect(self.rx.source, self.table.sink), + Record.connect(self.table.source, self.tx.sink) + ] + self.sink, self.source = self.rx.sink, self.tx.source + self.request, self.response = self.table.request, self.table.response