1 Commits
master ... wtf

Author SHA1 Message Date
Byron Lathi
bb360d3c6d add fin 2024-09-28 00:17:45 -07:00
6 changed files with 695 additions and 7 deletions

View File

@@ -0,0 +1,270 @@
from http import server
from scapy.layers.inet import Ether, IP, TCP
from scapy.layers.l2 import ARP
from scapy.data import IP_PROTOS
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()
con.close()
serversocket.close()
while True:
pkt = t.recv()
if (pkt.proto == IP_PROTOS.tcp):
break
print(pkt)
tcp_fin = Ether(dst=dut_mac, src=tb_mac) / pkt
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}")
t.send(ip_packet)
return
# 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

@@ -0,0 +1,139 @@
module tcp_dest_decap (
input i_clk,
input i_rst,
ip_intf.SLAVE s_ip,
ip_intf.MASTER m_ip,
output wire [15:0] o_tcp_dest,
output wire o_tcp_dest_valid
);
logic [15:0] tcp_dest, tcp_dest_next;
logic [31:0] pipe, pipe_next;
logic [3:0] pipe_valid, pipe_valid_next;
logic [3:0] pipe_last, pipe_last_next;
logic valid;
enum logic [1:0] {PORTS, PASSTHROUGH} state, state_next;
logic [1:0] counter, counter_next;
// We don't need the mac addresses or the ethertype.
assign m_ip.eth_src_mac = '0;
assign m_ip.eth_dest_mac = '0;
assign m_ip.eth_type = '0;
assign o_tcp_dest_valid = valid;
assign o_tcp_dest = tcp_dest;
skidbuffer #(
.DW(160)
) u_tcp_ip_hdr_skidbuffer (
.i_clk (i_clk),
.i_reset (i_rst),
.i_valid (s_ip.ip_hdr_valid),
.o_ready (s_ip.ip_hdr_ready),
.i_data ({
s_ip.ip_version,
s_ip.ip_ihl,
s_ip.ip_dscp,
s_ip.ip_ecn,
s_ip.ip_length,
s_ip.ip_identification,
s_ip.ip_flags,
s_ip.ip_fragment_offset,
s_ip.ip_ttl,
s_ip.ip_protocol,
s_ip.ip_header_checksum,
s_ip.ip_source_ip,
s_ip.ip_dest_ip
}),
.o_valid (m_ip.ip_hdr_valid),
.i_ready (m_ip.ip_hdr_ready),
.o_data ({
m_ip.ip_version,
m_ip.ip_ihl,
m_ip.ip_dscp,
m_ip.ip_ecn,
m_ip.ip_length,
m_ip.ip_identification,
m_ip.ip_flags,
m_ip.ip_fragment_offset,
m_ip.ip_ttl,
m_ip.ip_protocol,
m_ip.ip_header_checksum,
m_ip.ip_source_ip,
m_ip.ip_dest_ip
})
);
always_ff @(posedge i_clk) begin
if (i_rst) begin
tcp_dest <= '0;
pipe <= '0;
pipe_valid <= '0;
pipe_last <= '0;
state <= PORTS;
counter <= '0;
end else begin
tcp_dest <= tcp_dest_next;
pipe <= pipe_next;
pipe_valid <= pipe_valid_next;
pipe_last <= pipe_last_next;
state <= state_next;
counter <= counter_next;
end
end
always_comb begin
tcp_dest_next = tcp_dest;
state_next = state;
pipe_next = pipe;
pipe_valid_next = pipe_valid;
pipe_last_next = pipe_last;
counter_next = pipe;
s_ip.ip_payload_axis_tready = '0;
case (state)
PORTS: begin
s_ip.ip_payload_axis_tready = 1;
valid = '0;
if (s_ip.ip_payload_axis_tvalid) begin
counter_next = counter + 1;
pipe_valid_next = {pipe_valid[2:0], 1'b1};
pipe_next = {pipe_next[23:0], s_ip.ip_payload_axis_tdata};
if (counter == 2'h3) begin
state_next = PASSTHROUGH;
tcp_dest_next = pipe_next[15:0];
end
end
end
PASSTHROUGH: begin
// match ready except if we have seen last, then just finish it out.
pipe_valid_next = {pipe_valid[2:0], s_ip.ip_payload_axis_tvalid};
pipe_last_next = {pipe_last[2:0], s_ip.ip_payload_axis_tlast};
pipe_next = {pipe_next[23:0], s_ip.ip_payload_axis_tdata};
s_ip.ip_payload_axis_tready = m_ip.ip_payload_axis_tready;
m_ip.ip_payload_axis_tvalid = pipe_valid[3];
m_ip.ip_payload_axis_tlast = pipe_last[3];
m_ip.ip_payload_axis_tdata = pipe[31:24];
valid = '1;
if (pipe_last[3] && pipe_valid[3]) begin
state_next = PORTS;
end
end
endcase
end
endmodule

View File

@@ -0,0 +1,49 @@
import tcp_pkg::*;
module tcp_rx_ctrl (
input logic i_clk,
input logic i_rst,
output tcp_pkg::rx_msg_t o_rx_msg,
output logic o_rx_msg_valid,
input logic i_rx_msg_ack,
input logic [31:0] i_seq_number,
input logic [31:0] i_ack_number,
input logic [15:0] i_source_port,
input logic [15:0] i_dest_port,
input logic [7:0] i_flags,
input logic [15:0] i_window_size,
input logic i_hdr_valid,
output logic [31:0] o_ack_number
);
logic [31:0] ack_num, ack_num_next;
assign o_ack_number = ack_num;
always_ff @(posedge i_clk) begin
if (i_rst) begin
ack_num <= '0;
end else begin
ack_num <= ack_num_next;
end
end
always_comb begin
if (i_hdr_valid) begin
if (i_flags == 8'h12) begin
o_rx_msg = RX_MSG_RECV_SYNACK;
o_rx_msg_valid = '1;
ack_num_next = i_seq_number + 1;
end
if (i_flags == 8'h11) begin
o_rx_msg = RX_MSG_RECV_FIN;
o_rx_msg_valid = '1;
end
end
end
endmodule

View File

@@ -0,0 +1,100 @@
import tcp_pkg::*;
module tcp_state_manager(
input wire i_clk,
input wire i_rst,
input wire i_enable,
input wire i_open,
output logic o_open_clr,
input wire i_close,
output logic o_close_clr,
output tcp_pkg::tx_ctrl_t o_tx_ctrl,
output logic o_tx_ctrl_valid,
input logic i_tx_ctrl_ack,
input tcp_pkg::rx_msg_t i_rx_msg,
input wire i_rx_msg_valid,
output logic o_rx_msg_ack
);
enum logic [3:0] {
IDLE,
SYN_RCVD, // In this design, this state should not be reached!
SYN_SENT_1,
SYN_SENT_2,
ESTABLISHED,
WAIT_CLOSE,
LAST_ACK,
TIME_WAIT,
FIN_WAIT_1,
FIN_WAIT_2
} tcp_state, tcp_state_next;
always_ff @(posedge i_clk) begin
if (i_rst) begin
tcp_state <= IDLE;
end else begin
if (~i_enable) begin
tcp_state <= IDLE;
end else begin
tcp_state <= tcp_state_next;
end
end
end
always_comb begin
tcp_state_next = tcp_state;
o_tx_ctrl_valid = '0;
o_tx_ctrl = TX_CTRL_NOP;
o_tx_ctrl_valid = '0;
o_rx_msg_ack = '0;
case (tcp_state)
IDLE: begin
if (i_open) begin
o_tx_ctrl = TX_CTRL_SEND_SYN;
o_tx_ctrl_valid = '1;
if (i_tx_ctrl_ack) begin
tcp_state_next = SYN_SENT_1;
end
end
end
SYN_SENT_1: begin
if (i_rx_msg_valid && i_rx_msg== RX_MSG_RECV_SYNACK) begin
tcp_state_next = SYN_SENT_2;
end
end
SYN_SENT_2: begin
o_tx_ctrl = TX_CTRL_SEND_ACK;
o_tx_ctrl_valid = '1;
if (i_tx_ctrl_ack) begin
tcp_state_next = ESTABLISHED;
end
end
ESTABLISHED: begin
if (i_rx_msg_valid && i_rx_msg == RX_MSG_RECV_FIN) begin
o_tx_ctrl = TX_CTRL_SEND_FIN;
o_tx_ctrl_valid = '1;
tcp_state_next = LAST_ACK;
end
end
LAST_ACK: begin
end
endcase
end
endmodule

View File

@@ -0,0 +1,131 @@
import tcp_pkg::*;
module tcp_tx_ctrl(
input i_clk,
input i_rst,
input tcp_pkg::tx_ctrl_t i_tx_ctrl,
input logic i_tx_ctrl_valid,
output logic o_tx_ctrl_ack,
output logic [15:0] o_ip_len,
output logic [31:0] o_seq_number,
output logic [31:0] o_ack_number,
output logic [7:0] o_flags,
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);
localparam FLAG_PSH = (1 << 3);
localparam FLAG_ACK = (1 << 4);
localparam FLAG_URG = (1 << 5);
localparam FLAG_ECE = (1 << 6);
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, SEND_FIN, SEND_DATA} state, state_next;
always_ff @(posedge i_clk) begin
if (i_rst) begin
state <= IDLE;
seq_num <= '0;
end else begin
state <= state_next;
seq_num <= seq_num_next;
end
end
always_comb begin
state_next = state;
o_ack_number = '0;
o_flags = '0;
o_window_size = 16'b1;
o_hdr_valid = '0;
seq_num_next = seq_num;
o_ip_len = 16'd40; // default length of IP packet
case (state)
IDLE: begin
if (i_tx_ctrl_valid) begin
o_tx_ctrl_ack = '1;
case (i_tx_ctrl)
TX_CTRL_SEND_SYN: state_next = SEND_SYN;
TX_CTRL_SEND_ACK: state_next = SEND_ACK;
TX_CTRL_SEND_FIN: state_next = SEND_FIN;
endcase
end
if (s_axis.tvalid) begin
state_next = SEND_DATA;
end
end
SEND_SYN: begin
o_flags = FLAG_SYN;
o_hdr_valid = '1;
if (i_packet_done) begin
state_next = IDLE;
seq_num_next = seq_num + 1;
end
end
SEND_ACK: begin
o_flags = FLAG_ACK;
o_hdr_valid = '1;
if (i_packet_done) begin
state_next = IDLE;
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
SEND_FIN: begin
o_flags = FLAG_ACK | FLAG_FIN;
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
end
endmodule

View File

@@ -8,12 +8,11 @@ export KICAD7_SYMBOL_DIR=$REPO_TOP/hw/kicad_library/symbols
export KICAD7_3DMODEL_DIR=$REPO_TOP/hw/kicad_library/3dmodels
export KICAD7_FOOTPRINT_DIR=$REPO_TOP/hw/kicad_library/footprints
python3.11 -m venv .user_venv
. .user_venv/bin/activate
module load efinity/2023.1
module load iverilog/12.0
#module load efinity/2023.1
module load verilator
module load gtkwave/3.3_gtk3
# pip install -r requirements.txt
python3.12 -m venv .user_venv
. .user_venv/bin/activate
pip install -r requirements.txt