diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e77ed88..742f640 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ stages: build: stage: build + when: manual tags: - efinity - linux @@ -18,6 +19,7 @@ build: sim: stage: sim + needs: [] tags: - linux - efinity @@ -25,3 +27,18 @@ sim: - source init_env.sh - make sim +ntw_sim: + stage: sim + needs: [] + tags: + - linux + script: + - source init_env.sh + - cd hw/super6502_fpga/src/sub/network_processor/sim/cocotb + - make -j `nproc` + artifacts: + when: always + paths: + - hw/super6502_fpga/src/sub/network_processor/sim/cocotb/results.xml + reports: + junit: hw/super6502_fpga/src/sub/network_processor/sim/cocotb/results.xml \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 15d4af3..8b5ab54 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,4 +21,7 @@ url = ../stream_dmas.git [submodule "hw/super6502_fpga/src/sub/interfaces"] path = hw/super6502_fpga/src/sub/interfaces - url = ../interfaces.git \ No newline at end of file + url = ../interfaces.git +[submodule "hw/super6502_fpga/src/sub/my-fifos"] + path = hw/super6502_fpga/src/sub/my-fifos + url = ../my-fifos.git diff --git a/hw/super6502_fpga/sources.list b/hw/super6502_fpga/sources.list index adb8699..ae7fd14 100644 --- a/hw/super6502_fpga/sources.list +++ b/hw/super6502_fpga/sources.list @@ -7,4 +7,5 @@ src/sub/rtl-common/sources.list src/sub/sd_controller_wrapper/sources.list src/sub/wb2axip/sources.list src/sub/verilog-ethernet/sources.list -src/sub/stream_dmas/sources.list \ No newline at end of file +src/sub/stream_dmas/sources.list +src/sub/my-fifos/sources.list \ No newline at end of file diff --git a/hw/super6502_fpga/src/sub/my-fifos b/hw/super6502_fpga/src/sub/my-fifos new file mode 160000 index 0000000..a19156c --- /dev/null +++ b/hw/super6502_fpga/src/sub/my-fifos @@ -0,0 +1 @@ +Subproject commit a19156c9cd559faa020ea7223a9995e68b41f8c2 diff --git a/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/Makefile b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/Makefile index 6512db4..88ec829 100644 --- a/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/Makefile +++ b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/Makefile @@ -19,6 +19,6 @@ GPI_IMPL := vpi export PYTHONPATH := $(PWD)/tests:$(PYTHONPATH) export TOPLEVEL_LANG -MODULE=tcp_test +MODULE=scapy_irl_test include $(shell cocotb-config --makefiles)/Makefile.sim \ No newline at end of file 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 new file mode 100644 index 0000000..82c45e9 --- /dev/null +++ b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/scapy_irl_test.py @@ -0,0 +1,231 @@ +from http import server +from scapy.layers.inet import Ether, IP, TCP +from scapy.layers.l2 import ARP + +from scapy import sendrecv + +from scapy.config import conf + +from scapy.supersocket import L3RawSocket + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import Timer +from cocotb.triggers import RisingEdge +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.utils import PcapWriter + +from scapy.layers.tuntap import TunTapInterface +import logging + +from decimal import Decimal + +CLK_PERIOD_NS = 10 + +MII_CLK_PERIOD_NS = 40 + + +import socket + +# In order for this to work, you need to run these commands: +# sudo ip tuntap add name tun0 mode tun user $USER +# sudo ip a add 172.0.0.1 peer 172.0.0.2 dev tun0 +# sudo ip link set tun0 up + + +def main(): + serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serversocket.bind(("172.0.0.1", 5678)) + serversocket.listen(5) + + t = TunTapInterface('tun0') + + tcp_syn = IP(src="172.0.0.2", dst="172.0.0.1")/TCP(sport=1234, dport=5678, seq=0, ack=0, flags="S") + t.send(tcp_syn) + + pkt = t.recv() + print(pkt) + + +if __name__ == "__main__": + main() + + + +class TB: + def __init__(self, dut): + self.dut = dut + + self.log = logging.getLogger("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD_NS, units="ns").start()) + cocotb.start_soon(Clock(dut.mii_rx_clk, MII_CLK_PERIOD_NS, units="ns").start()) + cocotb.start_soon(Clock(dut.mii_tx_clk, MII_CLK_PERIOD_NS, units="ns").start()) + + + self.axil_master = AxiLiteMaster(AxiLiteBus.from_prefix(dut, "s_regs_axil"), dut.clk, dut.rst) + self.axil_ram = AxiLiteRam(AxiLiteBus.from_prefix(dut, "m_dma_axil"), dut.clk, dut.rst, size=2**16) + + self.mii_phy = MiiPhy(dut.mii_txd, dut.mii_tx_er, dut.mii_tx_en, dut.mii_tx_clk, + dut.mii_rxd, dut.mii_rx_er, dut.mii_rx_dv, dut.mii_rx_clk, None, speed=100e6) + + async def cycle_reset(self): + self.dut.rst.setimmediatevalue(0) + await RisingEdge(self.dut.clk) # type: ignore + await RisingEdge(self.dut.clk) # type: ignore + self.dut.rst.value = 1 + await RisingEdge(self.dut.clk) # type: ignore + await RisingEdge(self.dut.clk) # type: ignore + self.dut.rst.value = 0 + await RisingEdge(self.dut.clk) # type: ignore + await RisingEdge(self.dut.clk) # type: ignore + +def ip_to_hex(ip: str) -> int: + octets = [int(i) for i in ip.split(".")] + + result = int.from_bytes(struct.pack("BBBB", octets[0], octets[1], octets[2], octets[3])) + + return result + +@cocotb.test() +async def test_irl(dut): + tb = TB(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" + + serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + serversocket.bind((tb_ip, 5678)) + serversocket.listen(5) + t = TunTapInterface('tun0') + + + dut_port = 1234 + tb_port = 5678 + + 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" + + 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) + + 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}") + + t.send(ip_packet) + + pkt = t.recv() + print(pkt) + + tcp_synack = Ether(dst=dut_mac, src=tb_mac) / pkt + + 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}") + + t.send(ip_packet) + + con, addr = serversocket.accept() + + # Construct a descriptor in memry + tb.axil_ram.write_dword(0x00000000, 0x00001000) + tb.axil_ram.write_dword(0x00000004, 64) + tb.axil_ram.write_dword(0x00000008, 0) + tb.axil_ram.write_dword(0x0000000c, 0) + + test_data = bytearray([x % 256 for x in range(256)]) + + tb.axil_ram.write(0x1000, test_data) + + + + await tb.axil_master.write_dword(0x22c, 0) + await tb.axil_master.write_dword(0x220, 0x00000000) + await tb.axil_master.write_dword(0x224, 0x00000000) + + resp = await tb.mii_phy.tx.recv() # type: GmiiFrame + packet = Ether(resp.get_payload()) + + t.send(packet.payload) + + # con.recv(64) + + serversocket.close() \ No newline at end of file diff --git a/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/tcp_test.py b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/tcp_test.py index 5a3adbb..c350673 100644 --- a/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/tcp_test.py +++ b/hw/super6502_fpga/src/sub/network_processor/sim/cocotb/tests/tcp_test.py @@ -175,4 +175,41 @@ async def test_simple(dut): tb.log.info(f"window: {tcp_packet.window}") tb.log.info(f"Checksum: {tcp_packet.chksum}") - assert tcp_packet.ack == tb_seq + 1 \ No newline at end of file + assert tcp_packet.ack == tb_seq + 1 + + # Try to send a packet from M2S + + # Construct a descriptor in memry + tb.axil_ram.write_dword(0x00000000, 0x00001000) + tb.axil_ram.write_dword(0x00000004, 64) + tb.axil_ram.write_dword(0x00000008, 0) + tb.axil_ram.write_dword(0x0000000c, 0) + + test_data = bytearray([x % 256 for x in range(256)]) + + tb.axil_ram.write(0x1000, test_data) + + + + await tb.axil_master.write_dword(0x22c, 0) + await tb.axil_master.write_dword(0x220, 0x00000000) + await tb.axil_master.write_dword(0x224, 0x00000000) + + resp = await tb.mii_phy.tx.recv() # type: GmiiFrame + packet = Ether(resp.get_payload()) + pktdump.write(packet) + tb.log.info(f"Packet Type: {packet.type:x}") + + 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}") + + pktdump.close() \ No newline at end of file diff --git a/hw/super6502_fpga/src/sub/network_processor/src/network_processor.sv b/hw/super6502_fpga/src/sub/network_processor/src/network_processor.sv index 4179be6..2b61060 100644 --- a/hw/super6502_fpga/src/sub/network_processor/src/network_processor.sv +++ b/hw/super6502_fpga/src/sub/network_processor/src/network_processor.sv @@ -261,8 +261,8 @@ ip_complete #( .tx_error_arp_failed (), // should go to stats register .local_mac (48'h020000aabbcc), // should be a register - .local_ip (32'hac000002), // should be a register - .gateway_ip (32'hac000001), // should be a register + .local_ip (32'hAC000002), // should be a register + .gateway_ip (32'hAC000001), // should be a register .subnet_mask (32'hffffff00), // should be a register .clear_arp_cache ('0) // should come from sw ); diff --git a/hw/super6502_fpga/src/sub/network_processor/src/tcp_packet_generator.sv b/hw/super6502_fpga/src/sub/network_processor/src/tcp_packet_generator.sv index 62110bb..034b2f5 100644 --- a/hw/super6502_fpga/src/sub/network_processor/src/tcp_packet_generator.sv +++ b/hw/super6502_fpga/src/sub/network_processor/src/tcp_packet_generator.sv @@ -127,19 +127,36 @@ always_comb begin 18: m_ip.ip_payload_axis_tdata = '0; 19: begin m_ip.ip_payload_axis_tdata = '0; - m_ip.ip_payload_axis_tlast = '1; + m_ip.ip_payload_axis_tlast = ~s_axis_data.tvalid; // kinda hacky end endcase if (m_ip.ip_payload_axis_tready) begin counter_next = counter + 1; + if (counter == 19) begin + state_next = DATA; + end + if (m_ip.ip_payload_axis_tlast) begin state_next = IDLE; o_packet_done = '1; end end end + + DATA: begin + state_next = DATA; + s_axis_data.tready = m_ip.ip_payload_axis_tready; + m_ip.ip_payload_axis_tvalid = s_axis_data.tvalid; + m_ip.ip_payload_axis_tdata = s_axis_data.tdata; + m_ip.ip_payload_axis_tlast = s_axis_data.tlast; + + if (s_axis_data.tlast && s_axis_data.tvalid && s_axis_data.tready) begin + state_next = IDLE; + o_packet_done = '1; + end + end endcase end diff --git a/hw/super6502_fpga/src/sub/network_processor/src/tcp_stream.sv b/hw/super6502_fpga/src/sub/network_processor/src/tcp_stream.sv index c49ed07..285a0e5 100644 --- a/hw/super6502_fpga/src/sub/network_processor/src/tcp_stream.sv +++ b/hw/super6502_fpga/src/sub/network_processor/src/tcp_stream.sv @@ -35,6 +35,8 @@ axis_intf s2m_axis(); axis_intf m2s_post_saf_axis(); axis_intf s2m_pre_saf_axis(); +axis_intf m_tx_ctrl_axis_data(); + // regs tcp_stream_regs_pkg::tcp_stream_regs__in_t hwif_in; tcp_stream_regs_pkg::tcp_stream_regs__out_t hwif_out; @@ -47,6 +49,7 @@ tcp_pkg::rx_msg_t rx_msg; logic rx_msg_valid; logic rx_msg_ack; +logic [15:0] w_saf_pkt_len; logic [15:0] w_tx_ip_len; logic [31:0] w_tx_seq_number; logic [31:0] w_tx_ack_number; @@ -133,42 +136,27 @@ tcp_state_manager u_tcp_state_manager ( // tx buffer -axis_fifo #( - .DEPTH(4096), - .DATA_WIDTH(DATA_WIDTH), - .FRAME_FIFO(1) +axis_saf_fifo #( + .DATA_DEPTH_L2(12), + .CTRL_DEPTH_L2(7), + .DATA_MEM("distributed"), + .CTRL_MEM("distributed") ) m2s_saf_fifo ( - .clk (clk), - .rst (rst), + .sclk (clk), + .srst (rst), + .s_axis (m2s_axis), - .s_axis_tdata (m2s_axis.tdata), - .s_axis_tkeep (m2s_axis.tkeep), - .s_axis_tvalid (m2s_axis.tvalid), - .s_axis_tready (m2s_axis.tready), - .s_axis_tlast (m2s_axis.tlast), - .s_axis_tid (m2s_axis.tid), - .s_axis_tdest (m2s_axis.tdest), - .s_axis_tuser (m2s_axis.tuser), + .mclk (clk), + .mrst (rst), + .m_axis (m2s_post_saf_axis), - .m_axis_tdata (m2s_post_saf_axis.tdata), - .m_axis_tkeep (m2s_post_saf_axis.tkeep), - .m_axis_tvalid (m2s_post_saf_axis.tvalid), - .m_axis_tready (m2s_post_saf_axis.tready), - .m_axis_tlast (m2s_post_saf_axis.tlast), - .m_axis_tid (m2s_post_saf_axis.tid), - .m_axis_tdest (m2s_post_saf_axis.tdest), - .m_axis_tuser (m2s_post_saf_axis.tuser), - - .pause_req ('0), - .pause_ack (), - - .status_depth (), - .status_depth_commit (), - .status_overflow (), - .status_bad_frame (), - .status_good_frame () + .o_len (w_saf_pkt_len), + .o_rx_pkt (), + .o_tx_pkt (), + .o_drop () ); + // tx control tcp_tx_ctrl u_tcp_tx_ctrl ( .i_clk (clk), @@ -185,6 +173,10 @@ tcp_tx_ctrl u_tcp_tx_ctrl ( .o_window_size (w_tx_window_size), .o_hdr_valid (w_tx_hdr_valid), + .s_axis_len (w_saf_pkt_len), + .s_axis (m2s_post_saf_axis), + .m_axis (m_tx_ctrl_axis_data), + .i_packet_done (w_tx_packet_done) ); @@ -193,7 +185,7 @@ tcp_packet_generator u_tcp_packet_generator ( .i_clk (clk), .i_rst (rst), - .s_axis_data (m2s_post_saf_axis), + .s_axis_data (m_tx_ctrl_axis_data), .i_ip_len (w_tx_ip_len), .i_seq_number (w_tx_seq_number), diff --git a/hw/super6502_fpga/src/sub/network_processor/src/tcp_tx_ctrl.sv b/hw/super6502_fpga/src/sub/network_processor/src/tcp_tx_ctrl.sv index 3efc4da..e32e7c4 100644 --- a/hw/super6502_fpga/src/sub/network_processor/src/tcp_tx_ctrl.sv +++ b/hw/super6502_fpga/src/sub/network_processor/src/tcp_tx_ctrl.sv @@ -15,9 +15,22 @@ module tcp_tx_ctrl( output logic [15:0] o_window_size, output logic o_hdr_valid, + axis_intf.SLAVE s_axis, + input logic [15:0] s_axis_len, + axis_intf.MASTER m_axis, + input wire i_packet_done ); +assign m_axis.tdata = s_axis.tdata; +assign m_axis.tkeep = s_axis.tkeep; +assign m_axis.tvalid = s_axis.tvalid; +assign s_axis.tready = m_axis.tready; +assign m_axis.tlast = s_axis.tlast; +assign m_axis.tid = s_axis.tid; +assign m_axis.tdest = s_axis.tdest; +assign m_axis.tuser = s_axis.tuser; + localparam FLAG_FIN = (1 << 0); localparam FLAG_SYN = (1 << 1); localparam FLAG_RST = (1 << 2); @@ -30,7 +43,7 @@ localparam FLAG_CWR = (1 << 7); logic [31:0] seq_num, seq_num_next; assign o_seq_number = seq_num; -enum logic [2:0] {IDLE, SEND_SYN, SEND_ACK} state, state_next; +enum logic [2:0] {IDLE, SEND_SYN, SEND_ACK, SEND_DATA} state, state_next; always_ff @(posedge i_clk) begin if (i_rst) begin @@ -43,6 +56,8 @@ always_ff @(posedge i_clk) begin end always_comb begin + state_next = state; + o_ack_number = '0; o_flags = '0; o_window_size = 16'b1; @@ -62,6 +77,10 @@ always_comb begin TX_CTRL_SEND_ACK: state_next = SEND_ACK; endcase end + + if (s_axis.tvalid) begin + state_next = SEND_DATA; + end end SEND_SYN: begin @@ -80,7 +99,18 @@ always_comb begin if (i_packet_done) begin state_next = IDLE; - seq_num_next = seq_num + 1; + seq_num_next = seq_num; + end + end + + SEND_DATA: begin + o_flags = FLAG_ACK | FLAG_PSH; + o_ip_len = 16'd40 + s_axis_len; // default length of IP packet + o_hdr_valid = '1; + + if (i_packet_done) begin + state_next = IDLE; + seq_num_next = seq_num + s_axis_len; end end endcase diff --git a/hw/super6502_fpga/super6502_fpga.xml b/hw/super6502_fpga/super6502_fpga.xml index 9c54f5a..25a33b8 100644 --- a/hw/super6502_fpga/super6502_fpga.xml +++ b/hw/super6502_fpga/super6502_fpga.xml @@ -204,6 +204,11 @@ + + + + +