Merge branch '96-send-tcp-data-over-m2s' into '93-network-processor'

Resolve "Send TCP data over M2S"

See merge request bslathi19/super6502!76
This commit is contained in:
Byron Lathi
2024-09-23 06:52:23 +00:00
12 changed files with 375 additions and 41 deletions

View File

@@ -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

5
.gitmodules vendored
View File

@@ -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
url = ../interfaces.git
[submodule "hw/super6502_fpga/src/sub/my-fifos"]
path = hw/super6502_fpga/src/sub/my-fifos
url = ../my-fifos.git

View File

@@ -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
src/sub/stream_dmas/sources.list
src/sub/my-fifos/sources.list

View File

@@ -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

View File

@@ -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()

View File

@@ -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
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()

View File

@@ -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
);

View File

@@ -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

View File

@@ -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),

View File

@@ -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

View File

@@ -204,6 +204,11 @@
<efx:design_file name="src/sub/interfaces/axis_intf.sv" version="default" library="default" />
<efx:design_file name="src/sub/interfaces/ip_intf.sv" version="default" library="default" />
<efx:design_file name="src/sub/interfaces/eth_intf.sv" version="default" library="default" />
<efx:design_file name="src/sub/my-fifos/src/axis_saf.sv" version="default" library="default"/>
<efx:design_file name="src/sub/my-fifos/src/dpram.sv" version="default" library="default"/>
<efx:design_file name="src/sub/my-fifos/src/fifo_fwft.sv" version="default" library="default"/>
<efx:design_file name="src/sub/my-fifos/src/fifo.sv" version="default" library="default"/>
<efx:design_file name="src/sub/my-fifos/src/fwft_adapter.sv" version="default" library="default"/>
<efx:top_vhdl_arch name="" />
</efx:design_info>
<efx:constraint_info>