diff --git a/liteeth/common.py b/liteeth/common.py index 07e168cf1..ec90bf0a9 100644 --- a/liteeth/common.py +++ b/liteeth/common.py @@ -75,6 +75,15 @@ udp_header = { udp_protocol = 0x11 +icmp_header_len = 8 +icmp_header = { + "msgtype": HField( 0, 0, 8), + "code": HField( 1, 0, 8), + "checksum": HField( 2, 0, 16), + "quench": HField( 4, 0, 32) +} +icmp_protocol = 0x01 + def reverse_bytes(v): n = math.ceil(flen(v)/8) r = [] @@ -163,6 +172,22 @@ def eth_udp_user_description(dw): ] return EndpointDescription(layout, packetized=True) +def eth_icmp_description(dw): + layout = _layout_from_header(icmp_header) + [ + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(layout, packetized=True) + +def eth_icmp_user_description(dw): + layout = _layout_from_header(icmp_header) + [ + ("ip_address", 32), + ("length", 16), + ("data", dw), + ("error", dw//8) + ] + return EndpointDescription(layout, packetized=True) + # Generic modules @DecorateModule(InsertReset) @DecorateModule(InsertCE) diff --git a/liteeth/core/__init__.py b/liteeth/core/__init__.py index 5fd6ac4d6..862f09d2d 100644 --- a/liteeth/core/__init__.py +++ b/liteeth/core/__init__.py @@ -3,12 +3,14 @@ from liteeth.mac import LiteEthMAC from liteeth.core.arp import LiteEthARP from liteeth.core.ip import LiteEthIP from liteeth.core.udp import LiteEthUDP +from liteeth.core.icmp import LiteEthICMP class LiteEthIPCore(Module, AutoCSR): def __init__(self, phy, mac_address, ip_address, clk_freq): self.submodules.mac = LiteEthMAC(phy, 8, interface="crossbar", with_hw_preamble_crc=True) self.submodules.arp = LiteEthARP(self.mac, mac_address, ip_address, clk_freq) self.submodules.ip = LiteEthIP(self.mac, mac_address, ip_address, self.arp.table) + self.submodules.icmp = LiteEthICMP(self.ip, ip_address) class LiteEthUDPIPCore(LiteEthIPCore): def __init__(self, phy, mac_address, ip_address, clk_freq): diff --git a/liteeth/core/icmp/__init__.py b/liteeth/core/icmp/__init__.py new file mode 100644 index 000000000..8dff0319a --- /dev/null +++ b/liteeth/core/icmp/__init__.py @@ -0,0 +1,135 @@ +from liteeth.common import * +from liteeth.generic.depacketizer import LiteEthDepacketizer +from liteeth.generic.packetizer import LiteEthPacketizer + +class LiteEthICMPDepacketizer(LiteEthDepacketizer): + def __init__(self): + LiteEthDepacketizer.__init__(self, + eth_ipv4_user_description(8), + eth_icmp_description(8), + icmp_header, + icmp_header_len) + +class LiteEthICMPPacketizer(LiteEthPacketizer): + def __init__(self): + LiteEthPacketizer.__init__(self, + eth_icmp_description(8), + eth_ipv4_user_description(8), + icmp_header, + icmp_header_len) + +class LiteEthICMPTX(Module): + def __init__(self, ip_address): + self.sink = Sink(eth_icmp_user_description(8)) + self.source = Source(eth_ipv4_user_description(8)) + ### + packetizer = LiteEthICMPPacketizer() + self.submodules += packetizer + self.comb += [ + packetizer.sink.stb.eq(self.sink.stb), + packetizer.sink.sop.eq(self.sink.sop), + packetizer.sink.eop.eq(self.sink.eop), + self.sink.ack.eq(packetizer.sink.ack), + packetizer.sink.msgtype.eq(self.sink.msgtype), + packetizer.sink.code.eq(self.sink.code), + packetizer.sink.checksum.eq(self.sink.checksum), + packetizer.sink.quench.eq(self.sink.quench), + packetizer.sink.data.eq(self.sink.data) + ] + sink = packetizer.source + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("SEND") + ) + ) + fsm.act("SEND", + Record.connect(packetizer.source, self.source), + self.source.length.eq(self.sink.length + icmp_header_len), + self.source.protocol.eq(icmp_protocol), + self.source.ip_address.eq(self.sink.ip_address), + If(self.source.stb & self.source.eop & self.source.ack, + NextState("IDLE") + ) + ) + +class LiteEthICMPRX(Module): + def __init__(self, ip_address): + self.sink = Sink(eth_ipv4_user_description(8)) + self.source = source = Source(eth_icmp_user_description(8)) + ### + depacketizer = LiteEthICMPDepacketizer() + self.submodules += depacketizer + self.comb += Record.connect(self.sink, depacketizer.sink) + sink = depacketizer.source + + self.submodules.fsm = fsm = FSM(reset_state="IDLE") + fsm.act("IDLE", + sink.ack.eq(1), + If(sink.stb & sink.sop, + sink.ack.eq(0), + NextState("CHECK") + ) + ) + valid = Signal() + self.comb += valid.eq( + sink.stb & + (self.sink.protocol == icmp_protocol) & + (self.sink.ip_address == ip_address) + ) + fsm.act("CHECK", + If(valid, + NextState("PRESENT") + ).Else( + NextState("DROP") + ) + ) + self.comb += [ + source.sop.eq(sink.sop), + source.eop.eq(sink.eop), + source.msgtype.eq(sink.msgtype), + source.code.eq(sink.code), + source.checksum.eq(sink.checksum), + source.quench.eq(sink.quench), + source.ip_address.eq(self.sink.ip_address), + source.length.eq(self.sink.length - icmp_header_len), + source.data.eq(sink.data), + source.error.eq(sink.error) + ] + fsm.act("PRESENT", + source.stb.eq(sink.stb), + sink.ack.eq(source.ack), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + fsm.act("DROP", + sink.ack.eq(1), + If(source.stb & source.eop & source.ack, + NextState("IDLE") + ) + ) + +class LiteEthICMPEcho(Module): + def __init__(self): + self.sink = Sink(eth_icmp_user_description(8)) + self.source = Source(eth_icmp_user_description(8)) + ### + self.comb += [ + Record.connect(self.sink, self.source), + self.source.msgtype.eq(0x0), + self.source.checksum.eq(~((~self.sink.checksum)-0x0800)) + ] + +class LiteEthICMP(Module): + def __init__(self, ip, ip_address): + self.submodules.tx = LiteEthICMPTX(ip_address) + self.submodules.rx = LiteEthICMPRX(ip_address) + ip_port = ip.crossbar.get_port(icmp_protocol) + self.comb += [ + Record.connect(self.tx.source, ip_port.sink), + Record.connect(ip_port.source, self.rx.sink) + ]