From 798fb6f20f9b1d58e84e847c59572a7f9d507081 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 13 Oct 2024 18:41:32 -0700 Subject: [PATCH] Get some fin support --- .../sim/cocotb/tests/scapy_irl_test.py | 287 ++++++++++++++---- .../src/tcp_state_manager.sv | 30 ++ 2 files changed, 255 insertions(+), 62 deletions(-) diff --git a/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/scapy_irl_test.py b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/scapy_irl_test.py index c50aac7..c291779 100644 --- a/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/scapy_irl_test.py +++ b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/scapy_irl_test.py @@ -1,7 +1,3 @@ -from http import server -from turtle import xcor -from scapy.layers.inet import Ether, IP, TCP -from scapy.layers.l2 import ARP from scapy.data import IP_PROTOS from scapy import sendrecv @@ -18,8 +14,9 @@ from cocotbext.axi import AxiLiteBus, AxiLiteMaster, AxiLiteRam from cocotbext.eth import MiiPhy, GmiiFrame import struct -from scapy.layers.inet import Ether, IP, TCP -from scapy.layers.l2 import ARP +from scapy.layers.inet import IP, TCP +from scapy.layers.l2 import ARP, Ether +from scapy.packet import Packet from scapy.utils import PcapWriter from scapy.layers.tuntap import TunTapInterface @@ -27,6 +24,8 @@ import logging from decimal import Decimal +import random + CLK_PERIOD_NS = 10 MII_CLK_PERIOD_NS = 40 @@ -95,10 +94,37 @@ def ip_to_hex(ip: str) -> int: return result -@cocotb.test() +# @cocotb.test() async def test_irl(dut): tb = TB(dut) + async def read_tcp_from_dut(): + resp = await tb.mii_phy.tx.recv() # type: GmiiFrame + packet = Ether(resp.get_payload()) + tb.log.info(f"Packet Type: {packet.type:x}") + + ip_packet = packet.payload + assert isinstance(ip_packet, IP) + + tcp_packet = ip_packet.payload + assert isinstance(tcp_packet, TCP) + + tb.log.info(f"Source Port: {tcp_packet.sport}") + tb.log.info(f"Dest Port: {tcp_packet.dport}") + tb.log.info(f"Seq: {tcp_packet.seq}") + tb.log.info(f"Ack: {tcp_packet.ack}") + tb.log.info(f"Data Offs: {tcp_packet.dataofs}") + tb.log.info(f"flags: {tcp_packet.flags}") + tb.log.info(f"window: {tcp_packet.window}") + tb.log.info(f"Checksum: {tcp_packet.chksum}") + + return ip_packet + + ############################# + # Reset DUT # + ############################# + + await tb.cycle_reset() dut_ip = "172.0.0.2" @@ -106,14 +132,18 @@ async def test_irl(dut): tb_mac = "02:00:00:11:22:33" + dut_port = random.randint(1024, 65535) + tb_port = random.randint(1024, 65535) + serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - serversocket.bind((tb_ip, 5678)) + serversocket.bind((tb_ip, tb_port)) serversocket.listen(1) t = TunTapInterface('tun0') - dut_port = 1234 - tb_port = 5678 + ############################### + # Configure DUT Network block # + ############################### await tb.axil_master.write_dword(0x0, 0x1807) @@ -150,35 +180,25 @@ async def test_irl(dut): assert arp_request.psrc == dut_ip, "ARP psrc does not match expected" assert arp_request.pdst == tb_ip, "ARP pdst does not match expected" + # hardcode the ARP response for now arp_response = Ether(dst=dut_mac, src=tb_mac) arp_response /= ARP(op="is-at", hwsrc=tb_mac, hwdst=dut_mac, psrc=tb_ip, pdst=dut_ip) arp_response = arp_response.build() await tb.mii_phy.rx.send(GmiiFrame.from_payload(arp_response)) - resp = await tb.mii_phy.tx.recv() # type: GmiiFrame - packet = Ether(resp.get_payload()) - tb.log.info(f"Packet Type: {packet.type:x}") - ip_packet = packet.payload - assert isinstance(ip_packet, IP) + ############################### + # Start TCP handshake # + ############################### - tcp_packet = ip_packet.payload - assert isinstance(tcp_packet, TCP) - - tb.log.info(f"Source Port: {tcp_packet.sport}") - tb.log.info(f"Dest Port: {tcp_packet.dport}") - tb.log.info(f"Seq: {tcp_packet.seq}") - tb.log.info(f"Ack: {tcp_packet.ack}") - tb.log.info(f"Data Offs: {tcp_packet.dataofs}") - tb.log.info(f"flags: {tcp_packet.flags}") - tb.log.info(f"window: {tcp_packet.window}") - tb.log.info(f"Checksum: {tcp_packet.chksum}") + ip_packet = await read_tcp_from_dut() t.send(ip_packet) while True: pkt = t.recv() + assert isinstance(pkt, Packet) if (pkt.proto == IP_PROTOS.tcp): break print(pkt) @@ -187,29 +207,16 @@ async def test_irl(dut): await tb.mii_phy.rx.send(GmiiFrame.from_payload(tcp_synack.build())) - resp = await tb.mii_phy.tx.recv() # type: GmiiFrame - packet = Ether(resp.get_payload()) - tb.log.info(f"Packet Type: {packet.type:x}") - - ip_packet = packet.payload - assert isinstance(ip_packet, IP) - - tcp_packet = ip_packet.payload - assert isinstance(tcp_packet, TCP) - - tb.log.info(f"Source Port: {tcp_packet.sport}") - tb.log.info(f"Dest Port: {tcp_packet.dport}") - tb.log.info(f"Seq: {tcp_packet.seq}") - tb.log.info(f"Ack: {tcp_packet.ack}") - tb.log.info(f"Data Offs: {tcp_packet.dataofs}") - tb.log.info(f"flags: {tcp_packet.flags}") - tb.log.info(f"window: {tcp_packet.window}") - tb.log.info(f"Checksum: {tcp_packet.chksum}") + ip_packet = await read_tcp_from_dut() t.send(ip_packet) con, addr = serversocket.accept() + ############################### + # Send data from DUT to host # + ############################### + # Construct a descriptor in memry tb.axil_ram.write_dword(0x00000000, 0x00001000) tb.axil_ram.write_dword(0x00000004, 64) @@ -232,11 +239,16 @@ async def test_irl(dut): con.recv(64) tb.log.info("Received 64 packets") + ############################### + # Close connection from host # + ############################### + con.close() serversocket.close() while True: pkt = t.recv() + assert isinstance(pkt, Packet) if (pkt.proto == IP_PROTOS.tcp): break print(pkt) @@ -249,6 +261,7 @@ async def test_irl(dut): while True: pkt = t.recv() + assert isinstance(pkt, Packet) if (pkt.proto == IP_PROTOS.tcp): break print(pkt) @@ -257,24 +270,7 @@ async def test_irl(dut): await tb.mii_phy.rx.send(GmiiFrame.from_payload(tcp_fin.build())) - resp = await tb.mii_phy.tx.recv() # type: GmiiFrame - packet = Ether(resp.get_payload()) - tb.log.info(f"Packet Type: {packet.type:x}") - - ip_packet = packet.payload - assert isinstance(ip_packet, IP) - - tcp_packet = ip_packet.payload - assert isinstance(tcp_packet, TCP) - - tb.log.info(f"Source Port: {tcp_packet.sport}") - tb.log.info(f"Dest Port: {tcp_packet.dport}") - tb.log.info(f"Seq: {tcp_packet.seq}") - tb.log.info(f"Ack: {tcp_packet.ack}") - tb.log.info(f"Data Offs: {tcp_packet.dataofs}") - tb.log.info(f"flags: {tcp_packet.flags}") - tb.log.info(f"window: {tcp_packet.window}") - tb.log.info(f"Checksum: {tcp_packet.chksum}") + ip_packet = await read_tcp_from_dut() t.send(ip_packet) @@ -282,6 +278,7 @@ async def test_irl(dut): while True: pkt = t.recv() + assert isinstance(pkt, Packet) if (pkt.proto == IP_PROTOS.tcp): break print(pkt) @@ -291,3 +288,169 @@ async def test_irl(dut): await tb.mii_phy.rx.send(GmiiFrame.from_payload(tcp_fin.build())) await Timer(Decimal(CLK_PERIOD_NS * 1000), units='ns') + + +@cocotb.test() +async def test_close(dut): + tb = TB(dut) + + async def read_tcp_from_dut(): + resp = await tb.mii_phy.tx.recv() # type: GmiiFrame + packet = Ether(resp.get_payload()) + tb.log.info(f"Packet Type: {packet.type:x}") + + ip_packet = packet.payload + assert isinstance(ip_packet, IP) + + tcp_packet = ip_packet.payload + assert isinstance(tcp_packet, TCP) + + tb.log.info(f"Source Port: {tcp_packet.sport}") + tb.log.info(f"Dest Port: {tcp_packet.dport}") + tb.log.info(f"Seq: {tcp_packet.seq}") + tb.log.info(f"Ack: {tcp_packet.ack}") + tb.log.info(f"Data Offs: {tcp_packet.dataofs}") + tb.log.info(f"flags: {tcp_packet.flags}") + tb.log.info(f"window: {tcp_packet.window}") + tb.log.info(f"Checksum: {tcp_packet.chksum}") + + return ip_packet + + def get_pkt_from_host(): + while True: + pkt = t.recv() + assert isinstance(pkt, Packet) + if (pkt.proto == IP_PROTOS.tcp): + break + print(pkt) + return pkt + + ############################# + # Reset DUT # + ############################# + + + await tb.cycle_reset() + + dut_ip = "172.0.0.2" + tb_ip = "172.0.0.1" + + tb_mac = "02:00:00:11:22:33" + + dut_port = random.randint(1024, 65535) + tb_port = random.randint(1024, 65535) + + serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serversocket.bind((tb_ip, tb_port)) + serversocket.listen(1) + t = TunTapInterface('tun0') + + + ############################### + # Configure DUT Network block # + ############################### + + await tb.axil_master.write_dword(0x0, 0x1807) + + await tb.axil_master.write_dword(0x200, dut_port) + await tb.axil_master.write_dword(0x204, ip_to_hex(dut_ip)) + await tb.axil_master.write_dword(0x208, tb_port) + await tb.axil_master.write_dword(0x20c, ip_to_hex(tb_ip)) + await tb.axil_master.write_dword(0x210, 0x3) + + resp = await tb.mii_phy.tx.recv() # type: GmiiFrame + + packet = Ether(resp.get_payload()) + + tb.log.info(f"Packet Type: {packet.type:x}") + + assert packet.type == 0x806, "Packet type is not ARP!" + + + arp_request = packet.payload + assert isinstance(arp_request, ARP) + + tb.log.info(f"Arp OP: {arp_request.op}") + tb.log.info(f"Arp hwsrc: {arp_request.hwsrc}") + tb.log.info(f"Arp hwdst: {arp_request.hwdst}") + tb.log.info(f"Arp psrc: {arp_request.psrc}") + tb.log.info(f"Arp pdst: {arp_request.pdst}") + + dut_mac = arp_request.hwsrc + dut_ip = arp_request.psrc + + assert arp_request.op == 1, "ARP type is not request!" + assert arp_request.hwsrc == "02:00:00:aa:bb:cc", "ARP hwsrc does not match expected" + assert arp_request.hwdst == "00:00:00:00:00:00", "ARP hwdst does not match expected" + assert arp_request.psrc == dut_ip, "ARP psrc does not match expected" + assert arp_request.pdst == tb_ip, "ARP pdst does not match expected" + + # hardcode the ARP response for now + arp_response = Ether(dst=dut_mac, src=tb_mac) + arp_response /= ARP(op="is-at", hwsrc=tb_mac, hwdst=dut_mac, psrc=tb_ip, pdst=dut_ip) + arp_response = arp_response.build() + + await tb.mii_phy.rx.send(GmiiFrame.from_payload(arp_response)) + + + ############################### + # Start TCP handshake # + ############################### + + ip_packet = await read_tcp_from_dut() + + t.send(ip_packet) + + + pkt = get_pkt_from_host() + tcp_synack = Ether(dst=dut_mac, src=tb_mac) / pkt + + await tb.mii_phy.rx.send(GmiiFrame.from_payload(tcp_synack.build())) + + ip_packet = await read_tcp_from_dut() + + t.send(ip_packet) + + con, addr = serversocket.accept() + + tb.log.info(f"con_timeout: {con.timeout}") + + ############################### + # Close connection from DUT # + ############################### + + tb.log.info("Closing connection from the DUT side") + await tb.axil_master.write_dword(0x210, 5) + + ip_packet = await read_tcp_from_dut() + + tb.log.info("Sending packet to host") + t.send(ip_packet) + + pkt = get_pkt_from_host() + tcp_synack = Ether(dst=dut_mac, src=tb_mac) / pkt + + tb.log.info("Sending reply to DUT, this should be an ACK?") + await tb.mii_phy.rx.send(GmiiFrame.from_payload(tcp_synack.build())) + + tb.log.info(tcp_synack.flags) + + # Host will send an ack first, then a finack? + + tb.log.info("Closing server socket") + con.close() + serversocket.close() + + pkt = get_pkt_from_host() + tcp_synack = Ether(dst=dut_mac, src=tb_mac) / pkt + + tb.log.info("Sending packet to DUT, this should be a FINACK?") + await tb.mii_phy.rx.send(GmiiFrame.from_payload(tcp_synack.build())) + + pkt = get_pkt_from_host() + tcp_synack = Ether(dst=dut_mac, src=tb_mac) / pkt + + ip_packet = await read_tcp_from_dut() + + tb.log.info("Sending packet to host") + t.send(ip_packet) \ No newline at end of file diff --git a/hw/super6502_fpga/src/sub/network_processor/src/tcp_state_manager.sv b/hw/super6502_fpga/src/sub/network_processor/src/tcp_state_manager.sv index 9240bd1..af380db 100644 --- a/hw/super6502_fpga/src/sub/network_processor/src/tcp_state_manager.sv +++ b/hw/super6502_fpga/src/sub/network_processor/src/tcp_state_manager.sv @@ -91,6 +91,36 @@ always_comb begin o_tx_ctrl_valid = '1; tcp_state_next = LAST_ACK; end + + if (i_close) begin + o_tx_ctrl = TX_CTRL_SEND_FIN; + o_tx_ctrl_valid = '1; + tcp_state_next = FIN_WAIT_1; + end + end + + FIN_WAIT_1: begin + if (i_rx_msg_valid) begin + if (i_rx_msg == RX_MSG_RECV_ACK) begin + tcp_state_next = FIN_WAIT_2; + end else if (i_rx_msg == RX_MSG_RECV_FIN) begin + tcp_state_next = TIME_WAIT; + o_tx_ctrl_valid = '1; + o_tx_ctrl = TX_CTRL_SEND_ACK; + end + end + end + + FIN_WAIT_2: begin + if (i_rx_msg == RX_MSG_RECV_FIN) begin + tcp_state_next = TIME_WAIT; + o_tx_ctrl = TX_CTRL_SEND_ACK; + o_tx_ctrl_valid = '1; + end + end + + TIME_WAIT: begin + tcp_state_next = IDLE; end LAST_ACK: begin