diff --git a/cmake/FindCoyoteHW.cmake b/cmake/FindCoyoteHW.cmake index 779b6f42..2a0f982b 100644 --- a/cmake/FindCoyoteHW.cmake +++ b/cmake/FindCoyoteHW.cmake @@ -64,6 +64,8 @@ set(EN_RDMA 0 CACHE STRING "Enable RDMA stack.") set(N_RDMA_AXI 1 CACHE STRING "Number of RDMA streams.") set(EN_TCP 0 CACHE STRING "Enable TCP/IP stack.") set(N_TCP_AXI 1 CACHE STRING "Number of TCP/IP streams.") +set(EN_SNIFFER 0 CACHE STRING "Enable packet sniffer.") +set(SNIFFER_VFPGA_ID 0 CACHE STRING "ID of vFPGA to receive packet sniffer data stream.") # QSFP ports set(EN_NET_0 1 CACHE STRING "QSFP port 0.") @@ -338,7 +340,7 @@ macro(validation_checks_hw) set(UDP_STACK_EN 0 CACHE BOOL "Enable UDP/IP stack") # Top net enabled - if(EN_RDMA OR EN_TCP) + if(EN_RDMA OR EN_TCP OR EN_SNIFFER) set(EN_NET 1) else() set(EN_NET 0) diff --git a/examples_hw/CMakeLists.txt b/examples_hw/CMakeLists.txt index fb4c7d80..248b5157 100644 --- a/examples_hw/CMakeLists.txt +++ b/examples_hw/CMakeLists.txt @@ -243,3 +243,31 @@ if(EXAMPLE STREQUAL "perf_fpga") create_hw() endif() + + +# +# Packet Sniffer +# @breif: Packet Sniffer +# +# @note: Add a target device (FDEV_NAME) +# + +if(EXAMPLE STREQUAL "packet_sniffer") + message("** Packet Sniffer") + set(N_REGIONS 1) + set(EN_STRM 0) + set(EN_MEM 1) + set(N_CARD_AXI 1) + set(EN_SNIFFER 1) + set(SNIFFER_VFPGA_ID 0) + set(EN_RDMA 1) + # set(EN_TCP 1) + + validation_checks_hw() + + load_apps ( + VFPGA_C0_0 "apps/packet_sniffer" + ) + + create_hw() +endif() \ No newline at end of file diff --git a/examples_hw/apps/packet_sniffer/hdl/packet_scheduler.sv b/examples_hw/apps/packet_sniffer/hdl/packet_scheduler.sv new file mode 100644 index 00000000..541e3508 --- /dev/null +++ b/examples_hw/apps/packet_sniffer/hdl/packet_scheduler.sv @@ -0,0 +1,137 @@ +/** + * Packet Sniifer RX/TX Scheduler & Merger + */ +import lynxTypes::*; + +module packet_sniffer_sched ( + input logic aclk, + input logic aresetn, + + AXI4S.s sink_rx, + AXI4S.s sink_tx, + AXI4S.m src +); + +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) sink_rx_r (); +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) sink_tx_r (); + +logic sink_rx_first, sink_tx_first; +always_ff @(posedge aclk) begin + if (aresetn == 1'b0) begin + sink_rx_first <= 1; + sink_tx_first <= 1; + end else begin + if (sink_rx.tvalid & sink_rx.tready) begin + sink_rx_first <= 0; + if (sink_rx.tlast) begin + sink_rx_first <= 1; + end + end + if (sink_tx.tvalid & sink_tx.tready) begin + sink_tx_first <= 0; + if (sink_tx.tlast) begin + sink_tx_first <= 1; + end + end + end +end + +axis_data_fifo_rx_sniffer_vfpga axis_data_fifo_rx_sniffer_vfpga_inst ( + .s_axis_aresetn(aresetn), + .s_axis_aclk(aclk), + .s_axis_tvalid(sink_rx.tvalid), + .s_axis_tready(sink_rx.tready), + .s_axis_tdata(sink_rx.tdata), + .s_axis_tkeep(sink_rx.tkeep), + .s_axis_tlast(sink_rx.tlast), + //.m_axis_aclk(aclk), + .m_axis_tvalid(sink_rx_r.tvalid), + .m_axis_tready(sink_rx_r.tready), + .m_axis_tdata(sink_rx_r.tdata), + .m_axis_tkeep(sink_rx_r.tkeep), + .m_axis_tlast(sink_rx_r.tlast) +); + +axis_data_fifo_tx_sniffer_vfpga axis_data_fifo_tx_sniffer_vfpga_inst ( + .s_axis_aresetn(aresetn), + .s_axis_aclk(aclk), + .s_axis_tvalid(sink_tx.tvalid), + .s_axis_tready(sink_tx.tready), + .s_axis_tdata(sink_tx.tdata), + .s_axis_tkeep(sink_tx.tkeep), + .s_axis_tlast(sink_tx.tlast), + //.m_axis_aclk(aclk), + .m_axis_tvalid(sink_tx_r.tvalid), + .m_axis_tready(sink_tx_r.tready), + .m_axis_tdata(sink_tx_r.tdata), + .m_axis_tkeep(sink_tx_r.tkeep), + .m_axis_tlast(sink_tx_r.tlast) +); + +logic [63:0] packets_order; // save order of arriving packets (0 for rx and 1 for tx) +logic [6:0] packets_cnt; // the number valid bits in packets_order +logic [63:0] packets_order_next; +logic [6:0] packets_cnt_next; +always_ff @(posedge aclk) begin + if (aresetn == 1'b0) begin + packets_order <= 0; + packets_cnt <= 0; + end else begin + packets_order <= packets_order_next; + packets_cnt <= packets_cnt_next; + end +end + +always_comb begin + src.tvalid = 0; + src.tdata = 0; + src.tkeep = 0; + src.tlast = 0; + sink_rx_r.tready = 0; + sink_tx_r.tready = 0; + + if (packets_cnt > 0) begin + if (packets_order[0] == 1'b0) begin + // rx -> src + src.tvalid = sink_rx_r.tvalid; + src.tdata = sink_rx_r.tdata; + src.tkeep = sink_rx_r.tkeep; + src.tlast = sink_rx_r.tlast; + sink_rx_r.tready = src.tready; + end else begin + // tx -> src + src.tvalid = sink_tx_r.tvalid; + src.tdata = sink_tx_r.tdata; + src.tkeep = sink_tx_r.tkeep; + src.tlast = sink_tx_r.tlast; + sink_tx_r.tready = src.tready; + end + end +end + +logic packets_dec_flg; // 1: src finished sending a packet +logic [1:0] packets_inc_cnt; // 0, 1 or 2 +logic [63:0] packets_inc_content; +always_comb begin + packets_dec_flg = 0; + packets_inc_cnt = 0; + packets_inc_content = 64'b00; + if (src.tlast & src.tvalid & src.tready) begin + packets_dec_flg = 1; + end + if (sink_rx.tvalid & sink_rx.tready & sink_rx_first & sink_tx.tvalid & sink_tx.tready & sink_tx_first) begin + packets_inc_cnt = 2; + packets_inc_content = 64'b10; + end else if (sink_rx.tvalid & sink_rx.tready & sink_rx_first) begin + packets_inc_cnt = 1; + packets_inc_content = 64'b00; + end else if (sink_tx.tvalid & sink_tx.tready & sink_tx_first) begin + packets_inc_cnt = 1; + packets_inc_content = 64'b01; + end + + packets_cnt_next = packets_cnt + packets_inc_cnt - packets_dec_flg; + packets_order_next = (packets_order | (packets_inc_content << packets_cnt)) >> packets_dec_flg; +end + +endmodule \ No newline at end of file diff --git a/examples_hw/apps/packet_sniffer/hdl/packet_sniffer_slv.sv b/examples_hw/apps/packet_sniffer/hdl/packet_sniffer_slv.sv new file mode 100644 index 00000000..cad01b58 --- /dev/null +++ b/examples_hw/apps/packet_sniffer/hdl/packet_sniffer_slv.sv @@ -0,0 +1,312 @@ +/** + * Packet Sniifer slave + */ +import lynxTypes::*; + +module packet_sniffer_slv ( + input logic aclk, + input logic aresetn, + + AXI4L.s axi_ctrl, + + output logic [0:0] sniffer_ctrl_0, + output logic [0:0] sniffer_ctrl_1, + output logic [63:0] sniffer_ctrl_filter, + input logic [1:0] sniffer_state, + input logic [31:0] sniffer_size, + input logic [63:0] sniffer_timer, + output logic [VADDR_BITS-1:0] sniffer_host_vaddr, + output logic [LEN_BITS-1:0] sniffer_host_len, + output logic [PID_BITS-1:0] sniffer_host_pid, + output logic [DEST_BITS-1:0] sniffer_host_dest +); + +// -- Decl ---------------------------------------------------------- +// ------------------------------------------------------------------ +// Constants +localparam integer N_REGS = 10; +localparam integer ADDR_LSB = $clog2(AXIL_DATA_BITS/8); +localparam integer ADDR_MSB = $clog2(N_REGS); +localparam integer AXI_ADDR_BITS = ADDR_LSB + ADDR_MSB; + +// Internal registers +logic [AXI_ADDR_BITS-1:0] axi_awaddr; +logic axi_awready; +logic [AXI_ADDR_BITS-1:0] axi_araddr; +logic axi_arready; +logic [1:0] axi_bresp; +logic axi_bvalid; +logic axi_wready; +logic [AXIL_DATA_BITS-1:0] axi_rdata; +logic [1:0] axi_rresp; +logic axi_rvalid; + +// Registers +logic [N_REGS-1:0][AXIL_DATA_BITS-1:0] slv_reg; +logic slv_reg_rden; +logic slv_reg_wren; +logic aw_en; + +// -- Def ----------------------------------------------------------- +// ------------------------------------------------------------------ + +// -- Register map ----------------------------------------------------------------------- +// 0 (WR) : Ctrl reg to start/end sniffering +localparam integer SNIFFER_CTRL_REG_0 = 0; +// 1 (WR) : Ctrl reg to indicate valid host memory information +localparam integer SNIFFER_CTRL_REG_1 = 1; +// 2 (WR) : Ctrl reg to set sniffer filter +localparam integer SNIFFER_FILTER_REG = 2; +// 3 (RO) : Current state of sniffer +localparam integer SNIFFER_STATE_REG = 3; +// 4 (RO) : Size of captured packets +localparam integer SNIFFER_SIZE_REG = 4; +// 5 (RO) : Internal Timer +localparam integer SNIFFER_TIMER_REG = 5; +// 6 (WR) : Vaddr +localparam integer SNIFFER_VADDR_REG = 6; +// 7 (WR) : Length +localparam integer SNIFFER_LEN_REG = 7; +// 8 (WR) : Pid +localparam integer SNIFFER_PID_REG = 8; +// 9 (WR) : Dest +localparam integer SNIFFER_DEST_REG = 9; + +// Write process +assign slv_reg_wren = axi_wready && axi_ctrl.wvalid && axi_awready && axi_ctrl.awvalid; + +always_ff @(posedge aclk) begin + if ( aresetn == 1'b0 ) begin + slv_reg <= 0; + end + else begin + // Control + if(slv_reg_wren) begin + case (axi_awaddr[ADDR_LSB+:ADDR_MSB]) + SNIFFER_CTRL_REG_0: // Control 0 + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_CTRL_REG_0][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + SNIFFER_CTRL_REG_1: // Control 1 + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_CTRL_REG_1][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + SNIFFER_FILTER_REG: // Filter config + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_FILTER_REG][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + SNIFFER_VADDR_REG: // Vaddr + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_VADDR_REG][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + SNIFFER_LEN_REG: // Length + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_LEN_REG][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + SNIFFER_PID_REG: // PID + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_PID_REG][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + SNIFFER_DEST_REG: // DEST + for (int i = 0; i < (AXIL_DATA_BITS/8); i++) begin + if(axi_ctrl.wstrb[i]) begin + slv_reg[SNIFFER_DEST_REG][(i*8)+:8] <= axi_ctrl.wdata[(i*8)+:8]; + end + end + default : ; + endcase + end + end +end + +// Read process +assign slv_reg_rden = axi_arready & axi_ctrl.arvalid & ~axi_rvalid; + +always_ff @(posedge aclk) begin + if( aresetn == 1'b0 ) begin + axi_rdata <= 0; + end + else begin + if(slv_reg_rden) begin + axi_rdata <= 0; + + case (axi_araddr[ADDR_LSB+:ADDR_MSB]) + SNIFFER_CTRL_REG_0: + axi_rdata[0:0] <= slv_reg[SNIFFER_CTRL_REG_0][0:0]; + SNIFFER_CTRL_REG_1: + axi_rdata[0:0] <= slv_reg[SNIFFER_CTRL_REG_1][0:0]; + SNIFFER_FILTER_REG: + axi_rdata[63:0] <= slv_reg[SNIFFER_FILTER_REG][63:0]; + SNIFFER_STATE_REG: + axi_rdata[1:0] <= sniffer_state; + SNIFFER_SIZE_REG: + axi_rdata[31:0] <= sniffer_size; + SNIFFER_TIMER_REG: + axi_rdata[63:0] <= sniffer_timer; + SNIFFER_VADDR_REG: + axi_rdata[VADDR_BITS-1:0] <= slv_reg[SNIFFER_VADDR_REG][VADDR_BITS-1:0]; + SNIFFER_LEN_REG: + axi_rdata[LEN_BITS-1:0] <= slv_reg[SNIFFER_LEN_REG][LEN_BITS-1:0]; + SNIFFER_PID_REG: + axi_rdata[PID_BITS-1:0] <= slv_reg[SNIFFER_PID_REG][PID_BITS-1:0]; + SNIFFER_DEST_REG: + axi_rdata[DEST_BITS-1:0] <= slv_reg[SNIFFER_DEST_REG][DEST_BITS-1:0]; + default: ; + endcase + end + end +end + +// Output +always_comb begin + sniffer_ctrl_0 = slv_reg[SNIFFER_CTRL_REG_0][1:0]; + sniffer_ctrl_1 = slv_reg[SNIFFER_CTRL_REG_1][1:0]; + sniffer_ctrl_filter = slv_reg[SNIFFER_FILTER_REG][63:0]; + sniffer_host_vaddr = slv_reg[SNIFFER_VADDR_REG][VADDR_BITS-1:0]; + sniffer_host_len = slv_reg[SNIFFER_LEN_REG][LEN_BITS-1:0]; + sniffer_host_pid = slv_reg[SNIFFER_PID_REG][PID_BITS-1:0]; + sniffer_host_dest = slv_reg[SNIFFER_DEST_REG][DEST_BITS-1:0]; +end + + +// -------------------------------------------------------------------------------------- +// AXI CTRL +// -------------------------------------------------------------------------------------- +// Don't edit + +// I/O +assign axi_ctrl.awready = axi_awready; +assign axi_ctrl.arready = axi_arready; +assign axi_ctrl.bresp = axi_bresp; +assign axi_ctrl.bvalid = axi_bvalid; +assign axi_ctrl.wready = axi_wready; +assign axi_ctrl.rdata = axi_rdata; +assign axi_ctrl.rresp = axi_rresp; +assign axi_ctrl.rvalid = axi_rvalid; + +// awready and awaddr +always_ff @(posedge aclk) begin + if ( aresetn == 1'b0 ) + begin + axi_awready <= 1'b0; + axi_awaddr <= 0; + aw_en <= 1'b1; + end + else + begin + if (~axi_awready && axi_ctrl.awvalid && axi_ctrl.wvalid && aw_en) + begin + axi_awready <= 1'b1; + aw_en <= 1'b0; + axi_awaddr <= axi_ctrl.awaddr; + end + else if (axi_ctrl.bready && axi_bvalid) + begin + aw_en <= 1'b1; + axi_awready <= 1'b0; + end + else + begin + axi_awready <= 1'b0; + end + end +end + +// arready and araddr +always_ff @(posedge aclk) begin + if ( aresetn == 1'b0 ) + begin + axi_arready <= 1'b0; + axi_araddr <= 0; + end + else + begin + if (~axi_arready && axi_ctrl.arvalid) + begin + axi_arready <= 1'b1; + axi_araddr <= axi_ctrl.araddr; + end + else + begin + axi_arready <= 1'b0; + end + end +end + +// bvalid and bresp +always_ff @(posedge aclk) begin + if ( aresetn == 1'b0 ) + begin + axi_bvalid <= 0; + axi_bresp <= 2'b0; + end + else + begin + if (axi_awready && axi_ctrl.awvalid && ~axi_bvalid && axi_wready && axi_ctrl.wvalid) + begin + axi_bvalid <= 1'b1; + axi_bresp <= 2'b0; + end + else + begin + if (axi_ctrl.bready && axi_bvalid) + begin + axi_bvalid <= 1'b0; + end + end + end +end + +// wready +always_ff @(posedge aclk) begin + if ( aresetn == 1'b0 ) + begin + axi_wready <= 1'b0; + end + else + begin + if (~axi_wready && axi_ctrl.wvalid && axi_ctrl.awvalid && aw_en ) + begin + axi_wready <= 1'b1; + end + else + begin + axi_wready <= 1'b0; + end + end +end + +// rvalid and rresp (1Del?) +always_ff @(posedge aclk) begin + if ( aresetn == 1'b0 ) + begin + axi_rvalid <= 0; + axi_rresp <= 0; + end + else + begin + if (axi_arready && axi_ctrl.arvalid && ~axi_rvalid) + begin + axi_rvalid <= 1'b1; + axi_rresp <= 2'b0; + end + else if (axi_rvalid && axi_ctrl.rready) + begin + axi_rvalid <= 1'b0; + end + end +end + +endmodule // perf_fpga slave \ No newline at end of file diff --git a/examples_hw/apps/packet_sniffer/init_ip.tcl b/examples_hw/apps/packet_sniffer/init_ip.tcl new file mode 100644 index 00000000..451ef2bc --- /dev/null +++ b/examples_hw/apps/packet_sniffer/init_ip.tcl @@ -0,0 +1,9 @@ +# Debug ILA +create_ip -name ila -vendor xilinx.com -library ip -version 6.2 -module_name ila_packet_sniffer_vfpga +set_property -dict [list CONFIG.C_DATA_DEPTH {8192} CONFIG.C_PROBE38_WIDTH {1} CONFIG.C_PROBE37_WIDTH {64} CONFIG.C_PROBE36_WIDTH {1} CONFIG.C_PROBE35_WIDTH {1} CONFIG.C_PROBE34_WIDTH {512} CONFIG.C_PROBE33_WIDTH {1} CONFIG.C_PROBE32_WIDTH {64} CONFIG.C_PROBE31_WIDTH {1} CONFIG.C_PROBE30_WIDTH {1} CONFIG.C_PROBE29_WIDTH {512} CONFIG.C_PROBE28_WIDTH {1} CONFIG.C_PROBE27_WIDTH {1} CONFIG.C_PROBE26_WIDTH {64} CONFIG.C_PROBE25_WIDTH {1} CONFIG.C_PROBE24_WIDTH {1} CONFIG.C_PROBE23_WIDTH {512} CONFIG.C_PROBE22_WIDTH {4} CONFIG.C_PROBE21_WIDTH {1} CONFIG.C_PROBE20_WIDTH {32} CONFIG.C_PROBE9_WIDTH {28} CONFIG.C_PROBE8_WIDTH {48} CONFIG.C_PROBE7_WIDTH {4} CONFIG.C_PROBE6_WIDTH {6} CONFIG.C_PROBE5_WIDTH {64} CONFIG.C_PROBE4_WIDTH {32} CONFIG.C_PROBE3_WIDTH {2} CONFIG.C_PROBE2_WIDTH {64} CONFIG.C_PROBE1_WIDTH {1} CONFIG.C_PROBE0_WIDTH {1} CONFIG.C_NUM_OF_PROBES {39} CONFIG.C_EN_STRG_QUAL {1} CONFIG.ALL_PROBE_SAME_MU_CNT {2}] [get_ips ila_packet_sniffer_vfpga] + +# FIFO +create_ip -name axis_data_fifo -vendor xilinx.com -library ip -version 2.0 -module_name axis_data_fifo_rx_sniffer_vfpga +set_property -dict [list CONFIG.TDATA_NUM_BYTES {64} CONFIG.IS_ACLK_ASYNC {0} CONFIG.FIFO_DEPTH {64} CONFIG.HAS_TKEEP {1} CONFIG.HAS_TLAST {1} ] [get_ips axis_data_fifo_rx_sniffer_vfpga] +create_ip -name axis_data_fifo -vendor xilinx.com -library ip -version 2.0 -module_name axis_data_fifo_tx_sniffer_vfpga +set_property -dict [list CONFIG.TDATA_NUM_BYTES {64} CONFIG.IS_ACLK_ASYNC {0} CONFIG.FIFO_DEPTH {64} CONFIG.HAS_TKEEP {1} CONFIG.HAS_TLAST {1} ] [get_ips axis_data_fifo_tx_sniffer_vfpga] diff --git a/examples_hw/apps/packet_sniffer/vfpga_top.svh b/examples_hw/apps/packet_sniffer/vfpga_top.svh new file mode 100644 index 00000000..a4e75dda --- /dev/null +++ b/examples_hw/apps/packet_sniffer/vfpga_top.svh @@ -0,0 +1,270 @@ +always_comb notify.tie_off_m(); + +// I/O +AXI4SR axis_sink_int[N_CARD_AXI](); +AXI4SR axis_src_int[N_CARD_AXI](); + +for (genvar i = 0; i < N_CARD_AXI; i++) begin + axisr_reg inst_reg_sink_0 (.aclk(aclk), .aresetn(aresetn), .s_axis(axis_card_recv[i]), .m_axis(axis_sink_int[i])); + axisr_reg inst_reg_src_0 (.aclk(aclk), .aresetn(aresetn), .s_axis(axis_src_int[i]), .m_axis(axis_card_send[i])); +end + +// packet sniffer slave +// CSRs +logic [0:0] sniffer_ctrl_0; // control 0 (see below for details) +logic [0:0] sniffer_ctrl_1; // control 1 (see below for details) +logic [63:0] sniffer_ctrl_filter; // sniffer filter config +logic [1:0] sniffer_state; // state (see below for details) +logic [31:0] sniffer_size; // size of captured packets +logic [63:0] sniffer_timer; // internal timer +logic [PID_BITS-1:0] sniffer_host_pid; // host pid +logic [DEST_BITS-1:0] sniffer_host_dest; // host dest +logic [VADDR_BITS-1:0] sniffer_host_vaddr; // host memory vaddr +logic [LEN_BITS-1:0] sniffer_host_len; // host memory length +// internal regs +logic [31:0] wrote_len; // size of wrote data length (should be equal to sniffer_size, otherwise insufficient memory bandwidth) +logic [31:0] wrote_len_n; +logic req_sent_flg; +logic [3:0] outstanding_req; // number of outstanding sq_wr requests +logic within_packet_not_first; +logic within_packet; +logic within_packet_not_last; +// fixed parameters +parameter SIZE_PER_REQ_BIT = 15; +parameter SIZE_PER_REQ = 1 << SIZE_PER_REQ_BIT; // size per sq_wr request + + +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_sniffer_merged (); +packet_sniffer_sched inst_packet_sniffer_sched ( + .aclk(aclk), + .aresetn(aresetn), + .sink_rx(axis_rx_sniffer), + .sink_tx(axis_tx_sniffer), + .src(axis_sniffer_merged) +); + +always_ff @(posedge aclk) begin + if (aresetn == 1'b0) begin + within_packet_not_first <= 0; + end else begin + if (axis_sniffer_merged.tvalid && axis_sniffer_merged.tready) begin + within_packet_not_first <= 1; + if (axis_sniffer_merged.tlast) begin + within_packet_not_first <= 0; + end + end + end +end +assign within_packet = within_packet_not_first | (axis_sniffer_merged.tvalid & axis_sniffer_merged.tready); +assign within_packet_not_last = within_packet & (~(axis_sniffer_merged.tvalid & axis_sniffer_merged.tready & axis_sniffer_merged.tlast)); + +always_comb begin + axis_sniffer_merged.tready = 1'b1; // this should always be 1!!! + filter_config.valid = 1'b1; + filter_config.data = sniffer_ctrl_filter; + // e.g. 64'b00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000 (ignore udp/ipv4 payload) +end + +/* + * + * sniffer_ctrl_0: 1 to start sniffering, 0 to end sniffering + * sniffer_ctrl_1: 1 for host memory information ready (offloaded to card), 0 for host memory information invalid + * + * sniffer_state == 2'b00: idle [if sniffer_ctrl_0 == 1 && sniffer_ctrl_1 == 1 goto state 2'b01] + * == 2'b01: sniffing [if sniffer_ctrl_0 == 0 goto state 2'b11] + * == 2'b11: finishing [if all memory requests finished goto state 2'b00] + * + */ + +// +// CSRs +// +packet_sniffer_slv inst_slave ( + .aclk(aclk), + .aresetn(aresetn), + .axi_ctrl(axi_ctrl), + .sniffer_ctrl_0(sniffer_ctrl_0), + .sniffer_ctrl_1(sniffer_ctrl_1), + .sniffer_ctrl_filter(sniffer_ctrl_filter), + .sniffer_state(sniffer_state), + .sniffer_size(sniffer_size), + .sniffer_timer(sniffer_timer), + .sniffer_host_pid(sniffer_host_pid), + .sniffer_host_dest(sniffer_host_dest), + .sniffer_host_vaddr(sniffer_host_vaddr), + .sniffer_host_len(sniffer_host_len) +); + +AXI4SR axis_sink_active (); +AXI4SR axis_src_active (); + +// sink (card mem -> vfpga) +// not used +assign axis_sink_active.tvalid = axis_sink_int[0].tvalid; +assign axis_sink_active.tkeep = axis_sink_int[0].tkeep; +assign axis_sink_active.tlast = axis_sink_int[0].tlast; +assign axis_sink_active.tdata = axis_sink_int[0].tdata; +assign axis_sink_active.tid = axis_sink_int[0].tid; +assign axis_sink_int[0].tready = axis_sink_active.tready; +// src (vfpga -> card mem) +assign axis_src_int[0].tvalid = axis_src_active.tvalid; +assign axis_src_int[0].tkeep = axis_src_active.tkeep; +assign axis_src_int[0].tlast = axis_src_active.tlast; +assign axis_src_int[0].tdata = axis_src_active.tdata; +assign axis_src_int[0].tid = axis_src_active.tid; +assign axis_src_active.tready = axis_src_int[0].tready; + +// Regs +always_ff @(posedge aclk) begin + if (aresetn == 1'b0) begin + wrote_len <= 0; + req_sent_flg <= 0; + outstanding_req <= 0; + end else begin + if (sniffer_state == 2'b00) begin + wrote_len <= 0; + req_sent_flg <= 0; + outstanding_req <= 0; + end else begin + if (axis_src_active.tvalid && axis_src_active.tready) begin + req_sent_flg <= 0; + wrote_len <= wrote_len + 64; + end + if (sq_wr.valid && sq_wr.ready && cq_wr.valid && cq_wr.ready) begin + req_sent_flg <= 1; + outstanding_req <= outstanding_req; + end else if (sq_wr.valid && sq_wr.ready) begin + req_sent_flg <= 1; + outstanding_req <= outstanding_req + 1; + end else if (cq_wr.valid && cq_wr.ready) begin + outstanding_req <= outstanding_req - 1; + end + end + end +end + +// States +always_ff @(posedge aclk) begin + if (aresetn == 1'b0) begin + sniffer_state <= 2'b00; + sniffer_size <= 0; + sniffer_timer <= 0; + end else begin + case (sniffer_state) + 2'b00: begin + if (sniffer_ctrl_0 && sniffer_ctrl_1 && (~within_packet_not_last)) begin + sniffer_state <= 2'b01; + sniffer_size <= 0; + sniffer_timer <= 0; + end + end + 2'b01: begin + sniffer_timer <= sniffer_timer + 1; + if (axis_src_active.tvalid) begin + sniffer_size <= sniffer_size + 64; + end + if ((~sniffer_ctrl_0) && (~within_packet_not_last)) begin + sniffer_state <= 2'b11; + end + end + 2'b11: begin + if (wrote_len[SIZE_PER_REQ_BIT-1:0] == 0) begin + // if (outstanding_req == 0 && wrote_len[SIZE_PER_REQ_BIT-1:0] == 0) begin + sniffer_state <= 2'b00; + end + end + default: ; + endcase + end +end + +// DP +always_comb begin + wrote_len_n = wrote_len + 64; + + // Requests + // Read from card (deactivated) + sq_rd.data = 0; + sq_rd.data.opcode = LOCAL_READ; + sq_rd.data.strm = STRM_CARD; + sq_rd.data.mode = 0; + sq_rd.data.rdma = 0; + sq_rd.data.remote = 0; + sq_rd.data.pid = sniffer_host_pid; + sq_rd.data.dest = sniffer_host_dest; + sq_rd.data.last = 1'b1; + sq_rd.data.vaddr = sniffer_host_vaddr; + sq_rd.data.len = 0; + sq_rd.valid = 1'b0; + // Write to card + sq_wr.data = 0; + sq_wr.data.opcode = LOCAL_WRITE; + sq_wr.data.strm = STRM_CARD; + sq_wr.data.mode = 0; + sq_wr.data.rdma = 0; + sq_wr.data.remote = 0; + sq_wr.data.pid = sniffer_host_pid; + sq_wr.data.dest = sniffer_host_dest; + sq_wr.data.last = 1'b1; + sq_wr.data.vaddr = sniffer_host_vaddr + wrote_len; + sq_wr.data.len = SIZE_PER_REQ; + sq_wr.valid = ((sniffer_state == 2'b01) && req_sent_flg == 0 && wrote_len[SIZE_PER_REQ_BIT-1:0] == 0) ? 1'b1 : 1'b0; + + cq_rd.ready = 1'b1; + cq_wr.ready = 1'b1; + + // Data + axis_sink_active.tready = 1'b1; + + axis_src_active.tdata = axis_sniffer_merged.tdata; + axis_src_active.tkeep = ~0; + axis_src_active.tid = 0; + axis_src_active.tlast = ((sniffer_state == 2'b01 || sniffer_state == 2'b11) && wrote_len_n[SIZE_PER_REQ_BIT-1:0] == 0) ? 1'b1 : 1'b0; + axis_src_active.tvalid = (sniffer_state == 2'b01 && wrote_len < sniffer_host_len) ? (axis_sniffer_merged.tvalid) : ( + (sniffer_state == 2'b11 && wrote_len < sniffer_host_len) ? (wrote_len[SIZE_PER_REQ_BIT-1:0] != 0) : 1'b0); +end + +// Debug + +ila_packet_sniffer_vfpga inst_ila_packet_sniffer_vfpga ( + .clk(aclk), + .probe0(sniffer_ctrl_0), // 1 + .probe1(sniffer_ctrl_1), // 1 + .probe2(sniffer_ctrl_filter), // 64 + .probe3(sniffer_state), // 2 + .probe4(sniffer_size), // 32 + .probe5(sniffer_timer), // 64 + .probe6(sniffer_host_pid), // 6 + .probe7(sniffer_host_dest), // 4 + .probe8(sniffer_host_vaddr), // 48 + .probe9(sniffer_host_len), // 28 + .probe10(sq_wr.valid), + .probe11(sq_wr.ready), + .probe12(cq_wr.valid), + .probe13(cq_wr.ready), + .probe14(axis_sink_int[0].tvalid), + .probe15(axis_sink_int[0].tready), + .probe16(axis_sink_int[0].tlast), + .probe17(axis_src_int[0].tvalid), + .probe18(axis_src_int[0].tready), + .probe19(axis_src_int[0].tlast), + .probe20(wrote_len), // 32 + .probe21(req_sent_flg), // 1 + .probe22(outstanding_req), // 4 + .probe23(axis_sniffer_merged.tdata), // 512 + .probe24(axis_sniffer_merged.tvalid), // 1 + .probe25(axis_sniffer_merged.tready), // 1 + .probe26(axis_sniffer_merged.tkeep), // 64 + .probe27(axis_sniffer_merged.tlast), // 1 + .probe28(within_packet_not_last), // 1 + .probe29(axis_rx_sniffer.tdata), // 512 + .probe30(axis_rx_sniffer.tvalid), + .probe31(axis_rx_sniffer.tready), + .probe32(axis_rx_sniffer.tkeep), // 64 + .probe33(axis_rx_sniffer.tlast), + .probe34(axis_tx_sniffer.tdata), // 512 + .probe35(axis_tx_sniffer.tvalid), + .probe36(axis_tx_sniffer.tready), + .probe37(axis_tx_sniffer.tkeep), // 64 + .probe38(axis_tx_sniffer.tlast) +); diff --git a/examples_sw/CMakeLists.txt b/examples_sw/CMakeLists.txt index 76d945f8..e95c6266 100644 --- a/examples_sw/CMakeLists.txt +++ b/examples_sw/CMakeLists.txt @@ -97,12 +97,25 @@ if(EXAMPLE STREQUAL "tcp_iperf") message("** Example: TCP - iperf") endif() +# Network - Packet Sniffer +if(EXAMPLE STREQUAL "packet_sniffer") + set(TARGET_DIR "${CYT_DIR}/examples_sw/apps/packet_sniffer") + message("** Example: Packet Sniffer") +endif() + + # # Create build targets # set(EXEC test) +# file(GLOB ALL_SRC +# "${TARGET_DIR}/*.cpp" +# ) +# add_executable(${EXEC} ${ALL_SRC}) if(EXAMPLE STREQUAL "kmeans") add_executable(${EXEC} ${TARGET_DIR}/main.cpp ${TARGET_DIR}/utils.cpp) +elseif(EXAMPLE STREQUAL "packet_sniffer") + add_executable(${EXEC} ${TARGET_DIR}/main.cpp ${TARGET_DIR}/conversion.cpp) else() add_executable(${EXEC} ${TARGET_DIR}/main.cpp) endif() diff --git a/examples_sw/apps/packet_sniffer/README.md b/examples_sw/apps/packet_sniffer/README.md new file mode 100644 index 00000000..b7933096 --- /dev/null +++ b/examples_sw/apps/packet_sniffer/README.md @@ -0,0 +1,28 @@ +# Packet Sniffer + +SW side for examples_hw/packet_sniffer: Cooperate with vFPGA and convert captured data into pcap file. + +## Parameters +### Main Parameters +- `[--npages | -n] ` The number of huge pages used as packet sniffer buffer (defualt: 8). +- `[--device | -d] ` The ID of device (default: 0). +- `[--vfpga | -v] ` The ID of sniffer vFPGA (default: 0). +- `[--raw-filename | -r] ` Filename of raw captured data (default capture.txt). +- `[--pcap-filename | -p] ` Filename to save converted pcap data (default: capture.pcap). +- `[--conversion-only | -c] ` Only convert previously captured data (default: false). + +### Filter Configuration +- `[--no-ipv4] ` Ignore IPv4 (defalut: false). +- `[--no-ipv6] ` Ignore IPv6 (defalut: false). +- `[--no-arp] ` Ignore ARP (defalut: false). +- `[--no-icmp-v4] ` Ignore ICMP on IPv4 (defalut: false). +- `[--no-icmp-v6] ` Ignore ICMP on IPv6 (defalut: false). +- `[--no-udp-v4] ` Ignore UDP on IPv4 (defalut: false). +- `[--no-udp-payload-v4] ` Ignore UDP Payload on IPv4 (defalut: false). +- `[--no-udp-v6] ` Ignore UDP on IPv6 (defalut: false). +- `[--no-udp-payload-v6] ` Ignore UDP Payload on IPv6 (defalut: false). +- `[--no-tcp-v4] ` Ignore TCP on IPv4 (defalut: false). +- `[--no-tcp-payload-v4] ` Ignore TCP Payload on IPv4 (defalut: false). +- `[--no-roce-v4] ` Ignore RoCEv2 on IPv4 (defalut: false). +- `[--no-roce-payload-v4] ` Ignore RoCEv2 Payload on IPv4 (defalut: false). + diff --git a/examples_sw/apps/packet_sniffer/conversion.cpp b/examples_sw/apps/packet_sniffer/conversion.cpp new file mode 100644 index 00000000..898a2211 --- /dev/null +++ b/examples_sw/apps/packet_sniffer/conversion.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include + +#include "include/conversion.hpp" + +#pragma GCC diagnostic ignored "-Wunused-result" + +struct filter_config { + bool ignore_udp_ipv4_payload; + bool ignore_udp_ipv6_payload; + bool ignore_tcp_ipv4_payload; + bool ignore_rocev2_ipv4_payload; +} filter_cfg; + +// Definitions copied from pcap.h +struct pcap_file_header { + u_int magic; + u_short version_major; + u_short version_minor; + int thiszone; /* gmt to local correction; this is always 0 */ + u_int sigfigs; /* accuracy of timestamps; this is always 0 */ + u_int snaplen; /* max length saved portion of each pkt */ + u_int linktype; /* data link type (LINKTYPE_*) */ +}; +struct pcap_pkthdr { + struct timeval ts; /* time stamp */ + u_int caplen; /* length of portion present */ + u_int len; /* length of this packet (off wire) */ +}; + +void pcap_conversion(std::string raw, std::string pcap) { + FILE *raw_f = fopen(raw.c_str(), "r"); + FILE *pcap_f = fopen(pcap.c_str(), "wb"); + + // Parsing Filter Congifuration + uint64_t raw_filter_config = 0; + fscanf(raw_f, "%lx", &raw_filter_config); + filter_cfg.ignore_udp_ipv4_payload = (raw_filter_config & (1ULL << 23)) ? true : false; + filter_cfg.ignore_udp_ipv6_payload = (raw_filter_config & (1ULL << 25)) ? true : false; + filter_cfg.ignore_tcp_ipv4_payload = (raw_filter_config & (1ULL << 27)) ? true : false; + filter_cfg.ignore_rocev2_ipv4_payload = (raw_filter_config & (1ULL << 31)) ? true : false; + + // Write PCAP Header + struct pcap_file_header pcap_hdr = {0xa1b2c3d4, 2, 4, 0, 0, 65535, 1}; + fwrite(&pcap_hdr, 1, 24, pcap_f); + + // Read Output + char tmp[100]; + unsigned char *buf = (unsigned char *)malloc(100 * 1024 * 1024); // 100M + int buf_len = 0; + while (fscanf(raw_f, "%s", tmp) != EOF) { + for (int i = 0; i < 8; ++i) { + fscanf(raw_f, "%hhx", (buf + (buf_len++))); + } + } + + int n_packet = 0; + int n_bytes_read = 0; + struct pcap_pkthdr pkt_hdr; + while (n_bytes_read < buf_len) { + pkt_hdr.ts.tv_sec = n_packet; + pkt_hdr.ts.tv_usec = 0; + pkt_hdr.caplen = 14; // eth frame header len + pkt_hdr.len = 14; + if (buf[n_bytes_read + 12] == 0x86 && buf[n_bytes_read + 13] == 0xdd) { // IPv6 + if (filter_cfg.ignore_udp_ipv6_payload && buf[n_bytes_read + 20] == 0x11) { // ignore UDP payload + pkt_hdr.caplen += 40; // IPv6 header len + pkt_hdr.caplen += 8; // UDP header len + } else { + pkt_hdr.caplen += 40; // IPv6 header len + pkt_hdr.caplen += buf[n_bytes_read + 18] * 256 + buf[n_bytes_read + 19]; // IPv6 payload len + if (pkt_hdr.caplen < 64) pkt_hdr.caplen = 64; // possible padding + } + pkt_hdr.len += 40; + pkt_hdr.len += buf[n_bytes_read + 18] * 256 + buf[n_bytes_read + 19]; + if (pkt_hdr.len < 64) pkt_hdr.len = 64; + } else if (buf[n_bytes_read + 12] == 0x08 && buf[n_bytes_read + 13] == 0x00) { // IPv4 + if (filter_cfg.ignore_udp_ipv4_payload && buf[n_bytes_read + 23] == 0x11) { // ignore UDP payload + pkt_hdr.caplen += 20; // IPv4 header (min) len + pkt_hdr.caplen += 8; // UDP header len + } else if (filter_cfg.ignore_tcp_ipv4_payload && buf[n_bytes_read + 23] == 0x06) { // ignore TCP payload + pkt_hdr.caplen += 20; // IPv4 header (min) len + pkt_hdr.caplen += 20; // TCP header (min) len + } else if (filter_cfg.ignore_rocev2_ipv4_payload && buf[n_bytes_read + 23] == 0x11 && buf[n_bytes_read + 36] == 0xb7 && buf[n_bytes_read + 37] == 0x12) { // ignore RoCEv2 payload + pkt_hdr.caplen += 20; // IPv4 header (min) len + pkt_hdr.caplen += 8; // UDP header len + pkt_hdr.caplen += 12; // RoCEv2 header len + // determine roce optional header len + if (buf[n_bytes_read + 42] == 0x10 || buf[n_bytes_read + 42] == 0x0d || buf[n_bytes_read + 42] == 0x0f || buf[n_bytes_read + 42] == 0x11) { + // AETH + pkt_hdr.caplen += 4; + } else if (buf[n_bytes_read + 42] == 0x0a || buf[n_bytes_read + 42] == 0x06 || buf[n_bytes_read + 42] == 0x0b || buf[n_bytes_read + 42] == 0x0c) { + // RETH + pkt_hdr.caplen += 16; + } + } else { + pkt_hdr.caplen += buf[n_bytes_read + 16] * 256 + buf[n_bytes_read + 17]; // IPv4 total len + if (pkt_hdr.caplen < 64) pkt_hdr.caplen = 64; // possible padding + } + pkt_hdr.len += buf[n_bytes_read + 16] * 256 + buf[n_bytes_read + 17]; + if (pkt_hdr.len < 64) pkt_hdr.len = 64; + } else { // Other + if (buf[n_bytes_read + 12] == 0x88 && buf[n_bytes_read + 13] == 0xcc) { // LLDP + int cnt = 0; + while (!(buf[n_bytes_read + 14 + cnt] == 0x00 && buf[n_bytes_read + 15 + cnt] == 0x00)) { + int tlv_len = (buf[n_bytes_read + 14 + cnt] * 256 + buf[n_bytes_read + 15 + cnt]) & 0b111111111; + cnt += (tlv_len + 2); + } + cnt += 2; // end of LLDPDU + pkt_hdr.caplen += cnt; + pkt_hdr.len += cnt; + } else if (buf[n_bytes_read + 12] == 0x08 && buf[n_bytes_read + 13] == 0x06) { // ARP + pkt_hdr.caplen += 28; + pkt_hdr.len += 28; + } else { // Assume IEEE 802.3 Ethernet Header + int eth_len = buf[n_bytes_read + 12] * 256 + buf[n_bytes_read + 13]; + pkt_hdr.caplen += eth_len; + pkt_hdr.len += eth_len; + if (eth_len > 1500) { + // https://notes.networklessons.com/ethernet-frame-types + fprintf(stderr, "Unrecognized Ethernet Frame Type %04x!\n", eth_len); + } + } + } + // fprintf(stdout, "packet len %d\n", pkt_hdr.caplen); + fwrite(&pkt_hdr.ts, 1, 8, pcap_f); + fwrite(&pkt_hdr.caplen, 1, 4, pcap_f); + fwrite(&pkt_hdr.len, 1, 4, pcap_f); + fwrite(buf + n_bytes_read, 1, pkt_hdr.caplen, pcap_f); + ++n_packet; + n_bytes_read += ((pkt_hdr.caplen - 1) / 64 * 64 + 64); + } + + fclose(raw_f); + fclose(pcap_f); +} + +#pragma GCC diagnostic warning "-Wunused-result" \ No newline at end of file diff --git a/examples_sw/apps/packet_sniffer/include/conversion.hpp b/examples_sw/apps/packet_sniffer/include/conversion.hpp new file mode 100644 index 00000000..54345f83 --- /dev/null +++ b/examples_sw/apps/packet_sniffer/include/conversion.hpp @@ -0,0 +1,8 @@ +#ifndef CONVERSION_H +#define CONVERSION_H + +#include + +void pcap_conversion(std::string raw, std::string pcap); + +#endif \ No newline at end of file diff --git a/examples_sw/apps/packet_sniffer/main.cpp b/examples_sw/apps/packet_sniffer/main.cpp new file mode 100644 index 00000000..d9fe1b16 --- /dev/null +++ b/examples_sw/apps/packet_sniffer/main.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef EN_AVX +#include +#endif +#include +#include +#include + +#include "cBench.hpp" +#include "cThread.hpp" +#include "include/conversion.hpp" + +using namespace std; +using namespace fpga; + +/* Def params */ +constexpr auto const defDevice = 0; +constexpr auto const defTargetVfid = 0; +constexpr auto const defHostMemPages = 8; +constexpr auto const defFilterConfig = 0; + +enum class SnifferCSRs : uint32_t { + CTRL_0 = 0, // to start sniffing + CTRL_1 = 1, // to notify host memory info ready + CTRL_FILTER = 2, // filter configuration + SNIFFER_STATE = 3, + SNIFFER_SIZE = 4, + SNIFFER_TIMER = 5, + HOST_VADDR = 6, + HOST_LEN = 7, + HOST_PID = 8, + HOST_DEST = 9 +}; + +enum class SnifferState : uint8_t { + IDLE = 0b00, + SNIFFING = 0b01, + FINISHING = 0b11, +}; + +void getAllCSRs(cThread &t) { + std::cout << "CTRL_0: " << t.getCSR(static_cast(SnifferCSRs::CTRL_0)) << std::endl + << "CTRL_1: " << t.getCSR(static_cast(SnifferCSRs::CTRL_1)) << std::endl + << "CTRL_FILTER: " << t.getCSR(static_cast(SnifferCSRs::CTRL_FILTER)) << std::endl + << "SNIFFER_STATE: " << t.getCSR(static_cast(SnifferCSRs::SNIFFER_STATE)) << std::endl + << "SNIFFER_SIZE: " << t.getCSR(static_cast(SnifferCSRs::SNIFFER_SIZE)) << std::endl + << "SNIFFER_TIMER: " << t.getCSR(static_cast(SnifferCSRs::SNIFFER_TIMER)) << std::endl + << "HOST_VADDR: " << t.getCSR(static_cast(SnifferCSRs::HOST_VADDR)) << std::endl + << "HOST_LEN: " << t.getCSR(static_cast(SnifferCSRs::HOST_LEN)) << std::endl + << "HOST_PID: " << t.getCSR(static_cast(SnifferCSRs::HOST_PID)) << std::endl + << "HOST_DEST: " << t.getCSR(static_cast(SnifferCSRs::HOST_DEST)) << std::endl; +} + +int main(int argc, char *argv[]) { + // --------------------------------------------------------------- + // Args + // --------------------------------------------------------------- + boost::program_options::options_description programDescription("Options:"); + programDescription.add_options() + ("npages,n", boost::program_options::value(), "Number of Memory Pages") + ("device,d", boost::program_options::value(), "Target device") + ("vfpga,v", boost::program_options::value(), "vFPGAs id") + ("no-ipv4", boost::program_options::value(), "Ignore IPv4") + ("no-ipv6", boost::program_options::value(), "Ignore IPv6") + ("no-arp", boost::program_options::value(), "Ignore ARP") + ("no-icmp-v4", boost::program_options::value(), "Ignore ICMP on IPv4") + ("no-icmp-v6", boost::program_options::value(), "Ignore ICMP on IPv6") + ("no-udp-v4", boost::program_options::value(), "Ignore UDP on IPv4") + ("no-udp-payload-v4", boost::program_options::value(), "Ignore UDP Payload on IPv4") + ("no-udp-v6", boost::program_options::value(), "Ignore UDP on IPv6") + ("no-udp-payload-v6", boost::program_options::value(), "Ignore UDP Payload on IPv6") + ("no-tcp-v4", boost::program_options::value(), "Ignore TCP on IPv4") + ("no-tcp-payload-v4", boost::program_options::value(), "Ignore TCP Payload on IPv4") + ("no-roce-v4", boost::program_options::value(), "Ignore RoCEv2 on IPv4") + ("no-roce-payload-v4", boost::program_options::value(), "Ignore RoCEv2 Payload on IPv4") + ("raw-filename,r", boost::program_options::value(), "Filename to save raw captured data") + ("pcap-filename,p", boost::program_options::value(), "Filename to save converted pcap data") + ("conversion-only,c", boost::program_options::value(), "Only convert previously captured data"); + + boost::program_options::variables_map commandLineArgs; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, programDescription), commandLineArgs); + boost::program_options::notify(commandLineArgs); + + uint32_t cs_device = defDevice; + uint32_t target_vfid = defTargetVfid; + uint32_t host_mem_pages = defHostMemPages; + uint64_t filter_config = defFilterConfig; + std::string raw_file = "capture.txt"; + std::string pcap_file = "capture.pcap"; + bool conversion_only = false; + + if (commandLineArgs.count("raw-filename") > 0) raw_file = commandLineArgs["raw-filename"].as(); + if (commandLineArgs.count("pcap-filename") > 0) pcap_file = commandLineArgs["pcap-filename"].as(); + + if (commandLineArgs.count("device") > 0) cs_device = commandLineArgs["device"].as(); + if (commandLineArgs.count("npages") > 0) host_mem_pages = commandLineArgs["npages"].as(); + if (commandLineArgs.count("vfpga") > 0) target_vfid = commandLineArgs["vfpga"].as(); + + if (commandLineArgs.count("no-ipv4") > 0) filter_config |= (1ULL << 8); + if (commandLineArgs.count("no-ipv6") > 0) filter_config |= (1ULL << 9); + if (commandLineArgs.count("no-arp") > 0) filter_config |= (1ULL << 16); + if (commandLineArgs.count("no-icmp-v4") > 0) filter_config |= (1ULL << 18); + if (commandLineArgs.count("no-icmp-v6") > 0) filter_config |= (1ULL << 20); + if (commandLineArgs.count("no-udp-v4") > 0) filter_config |= (1ULL << 22); + if (commandLineArgs.count("no-udp-payload-v4") > 0) filter_config |= (1ULL << 23); + if (commandLineArgs.count("no-udp-v6") > 0) filter_config |= (1ULL << 24); + if (commandLineArgs.count("no-udp-payload-v6") > 0) filter_config |= (1ULL << 25); + if (commandLineArgs.count("no-tcp-v4") > 0) filter_config |= (1ULL << 26); + if (commandLineArgs.count("no-tcp-payload-v4") > 0) filter_config |= (1ULL << 27); + if (commandLineArgs.count("no-roce-v4") > 0) filter_config |= (1ULL << 30); + if (commandLineArgs.count("no-roce-payload-v4") > 0) filter_config |= (1ULL << 31); + + if (commandLineArgs.count("conversion-only") > 0) conversion_only = true; + + PR_HEADER("PARAMS"); + if (conversion_only) { + printf("Conversion Only Mode\n"); + printf("Raw captured data file: %s\n", raw_file.c_str()); + printf("PCAP file: %s\n", pcap_file.c_str()); + } else { + printf("Device ID: %d\n", cs_device); + printf("Target vFPGA ID: %d\n", target_vfid); + printf("Number of Mmeory Pages: %d\n", host_mem_pages); + printf("Filter Config: %lx\n", filter_config); + printf("Raw captured data file: %s\n", raw_file.c_str()); + printf("PCAP file: %s\n", pcap_file.c_str()); + } + + if (conversion_only) { + pcap_conversion(raw_file, pcap_file); + return 0; + } + + + // --------------------------------------------------------------- + // Init + // --------------------------------------------------------------- + + // vfpga handler and mem alloc + cThread cthread(target_vfid, getpid(), cs_device); + void *hMem = cthread.getMem({CoyoteAlloc::HPF, (uint32_t)hugePageSize * host_mem_pages}); + memset(hMem, 0, hugePageSize * host_mem_pages); + // offload memory to card + sgEntry *hmem_sg = (sgEntry *)malloc(sizeof(sgEntry)); + hmem_sg->sync.addr = (void *)((uintptr_t)hMem); + cthread.invoke(CoyoteOper::LOCAL_OFFLOAD, hmem_sg, {false, false, false}, 1); + + // Reset CSRs + cthread.setCSR(0, static_cast(SnifferCSRs::CTRL_0)); + cthread.setCSR(0, static_cast(SnifferCSRs::CTRL_1)); + + PR_HEADER("STARTUP CHECK"); + getAllCSRs(cthread); + + // --------------------------------------------------------------- + // Set Memory Address + // --------------------------------------------------------------- + cthread.setCSR(reinterpret_cast(hMem), static_cast(SnifferCSRs::HOST_VADDR)); + cthread.setCSR(hugePageSize * host_mem_pages, static_cast(SnifferCSRs::HOST_LEN)); + cthread.setCSR(cthread.getHpid(), static_cast(SnifferCSRs::HOST_PID)); + cthread.setCSR(0, static_cast(SnifferCSRs::HOST_DEST)); + cthread.setCSR(1, static_cast(SnifferCSRs::CTRL_1)); + + PR_HEADER("MEMORY SET"); + getAllCSRs(cthread); + + // --------------------------------------------------------------- + // Start Sniffer + // --------------------------------------------------------------- + char cmd = 'h'; + do { + switch (cmd) { + case 'p': + getAllCSRs(cthread); + break; + default: + printf("\n"); + printf("-- h: help\n"); + printf("-- p: print CSRs\n"); + printf("-- s: start sniffer\n"); + break; + } + printf("> "); + } while (scanf(" %c", &cmd) != -1 && cmd != 's'); + + PR_HEADER("STARTING SNIFFER"); + cthread.setCSR(filter_config, static_cast(SnifferCSRs::CTRL_FILTER)); + cthread.setCSR(1, static_cast(SnifferCSRs::CTRL_0)); + while (static_cast(cthread.getCSR(static_cast(SnifferCSRs::SNIFFER_STATE))) == static_cast(SnifferState::IDLE)); + PR_HEADER("SNIFFER STARTED"); + getAllCSRs(cthread); + + // --------------------------------------------------------------- + // Stop Sniffer + // --------------------------------------------------------------- + cmd = 'h'; + do { + switch (cmd) { + case 'p': + getAllCSRs(cthread); + break; + default: + printf("\n"); + printf("-- h: help\n"); + printf("-- p: print CSRs\n"); + printf("-- s: stop sniffer\n"); + break; + } + printf("> "); + } while (scanf(" %c", &cmd) != -1 && cmd != 's'); + + PR_HEADER("STOPPING SNIFFER"); + cthread.setCSR(0, static_cast(SnifferCSRs::CTRL_0)); + while (static_cast(cthread.getCSR(static_cast(SnifferCSRs::SNIFFER_STATE))) != static_cast(SnifferState::IDLE)); + PR_HEADER("SNIFFER STOPPED"); + getAllCSRs(cthread); + + // --------------------------------------------------------------- + // Sync Back Memory + // --------------------------------------------------------------- + sleep(1); + cthread.invoke(CoyoteOper::LOCAL_SYNC, hmem_sg, {false, false, false}, 1); + + // Save data + PR_HEADER("SAVING DATA"); + uint32_t captured_sz = static_cast(cthread.getCSR(static_cast(SnifferCSRs::SNIFFER_SIZE))); + printf("Captured size: %u Bytes\n", captured_sz); + printf("Total Memory size: %llu Bytes\n", hugePageSize * host_mem_pages); + FILE *raw_f = fopen(raw_file.c_str(), "w"); + fprintf(raw_f, "%lx\n", filter_config); + for (uint32_t i = 0; i * 8 < captured_sz && i * 8 < hugePageSize * host_mem_pages; ++i) { + // for (uint32_t i = 0; i * 8 < 128; ++i) { + uint64_t *ptr = ((uint64_t *)hMem) + i; + uint8_t *ptr_u8 = (uint8_t *)ptr; + fprintf(raw_f, "%08x: ", i * 8); + fprintf(raw_f, "%02x %02x %02x %02x %02x %02x %02x %02x\n", + *(ptr_u8 + 0), *(ptr_u8 + 1), *(ptr_u8 + 2), *(ptr_u8 + 3), + *(ptr_u8 + 4), *(ptr_u8 + 5), *(ptr_u8 + 6), *(ptr_u8 + 7)); + } + fclose(raw_f); + + pcap_conversion(raw_file, pcap_file); + + PR_HEADER("CLEAN UP"); + // Cleanup CSRs + cthread.setCSR(0, static_cast(SnifferCSRs::CTRL_0)); + cthread.setCSR(0, static_cast(SnifferCSRs::CTRL_1)); + + return 0; +} \ No newline at end of file diff --git a/hw/hdl/network/stack/network_packet_sniffer.sv b/hw/hdl/network/stack/network_packet_sniffer.sv new file mode 100644 index 00000000..116e467e --- /dev/null +++ b/hw/hdl/network/stack/network_packet_sniffer.sv @@ -0,0 +1,423 @@ +/** + * Copyright (c) 2024, Systems Group, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +`timescale 1ns / 1ps + +import lynxTypes::*; + +/** + * @brief Packet Filter + * + * Filter and select packets basing on given configuration + */ +module packet_filter ( + /* Network stream */ + AXI4S.s rx_axis_net, // RX + AXI4S.s tx_axis_net, // TX + AXI4S.m rx_pass_axis_net, // RX pass-through + AXI4S.m tx_pass_axis_net, // TX pass-through + + /* Filtered stream */ + AXI4S.m rx_filtered_axis, + AXI4S.m tx_filtered_axis, + + /* Filter configuration */ + input wire [63:0] local_filter_config, + // Bit 00-07: reserved + // Bit 08: ignore all ipv4 + // Bit 09: ignore all ipv6 + // Bit 10-15: reserved + // Bit 16: ignore arp + // Bit 17: reserved + // Bit 18: ignore icmp(ipv4) + // Bit 19: reserved + // Bit 20: ignore icmp(ipv6) + // Bit 21: reserved + // Bit 22: ignore udp(ipv4) + // Bit 23: ignore udp(ipv4) data field & checksum + // Bit 24: ignore udp(ipv6) + // Bit 25: ignore udp(ipv6) data field & checksum + // Bit 26: ignore tcp(ipv4) + // Bit 27: ignore tcp(ipv4) data field & checksum + // Bit 28-29: reserved + // Bit 30: ignore roce(ipv4) + // Bit 31: ignore roce(ipv4) data field & checksum + // Bit 32-63: reserved + + input wire nclk, + input wire nresetn_r +); + +/** + * Stream Pass-Through + */ +// RX +assign rx_axis_net.tready = rx_pass_axis_net.tready; //& rx_filtered_axis.tready; +assign rx_pass_axis_net.tvalid = rx_axis_net.tvalid; //& rx_axis_net.tready; +assign rx_pass_axis_net.tdata = rx_axis_net.tdata; +assign rx_pass_axis_net.tkeep = rx_axis_net.tkeep; +assign rx_pass_axis_net.tlast = rx_axis_net.tlast; +// TX +assign tx_axis_net.tready = tx_pass_axis_net.tready; //& tx_filtered_axis.tready; +assign tx_pass_axis_net.tvalid = tx_axis_net.tvalid; //& tx_axis_net.tready; +assign tx_pass_axis_net.tdata = tx_axis_net.tdata; +assign tx_pass_axis_net.tkeep = tx_axis_net.tkeep; +assign tx_pass_axis_net.tlast = tx_axis_net.tlast; + +/** + * Filter Flags + */ + +// RX +logic rx_ipv4, rx_ipv6; +logic [5:0] rx_ip_header_len; +logic rx_arp; +logic rx_icmp_ipv4, rx_icmp_ipv6; +logic rx_udp_ipv4, rx_udp_ipv6; +logic rx_tcp_ipv4; +logic rx_rocev2_ipv4; +logic rx_rocev2_ipv4_aeth, rx_rocev2_ipv4_reth; + +// TX +logic tx_ipv4, tx_ipv6; +logic [5:0] tx_ip_header_len; +logic tx_arp; +logic tx_icmp_ipv4, tx_icmp_ipv6; +logic tx_udp_ipv4, tx_udp_ipv6; +logic tx_tcp_ipv4; +logic tx_rocev2_ipv4; +logic tx_rocev2_ipv4_aeth, tx_rocev2_ipv4_reth; + +// Flags for state machine +reg rx_in_pkt_not_first, tx_in_pkt_not_first; +logic rx_in_pkt_first, tx_in_pkt_first; +// logic rx_in_pkt_last, tx_in_pkt_last; +assign rx_in_pkt_first = ~rx_in_pkt_not_first & rx_axis_net.tvalid & rx_axis_net.tready; +assign tx_in_pkt_first = ~tx_in_pkt_not_first & tx_axis_net.tvalid & tx_axis_net.tready; +// assign rx_in_pkt_last = rx_axis_net.tlast & rx_axis_net.tvalid & rx_axis_net.tready; +// assign tx_in_pkt_last = tx_axis_net.tlast & tx_axis_net.tvalid & tx_axis_net.tready; +always @(posedge nclk) begin + if (~nresetn_r) begin + rx_in_pkt_not_first <= 1'b0; + tx_in_pkt_not_first <= 1'b0; + end else begin + if (rx_axis_net.tvalid & rx_axis_net.tready) begin + if (rx_axis_net.tlast) begin + rx_in_pkt_not_first <= 1'b0; + end else begin + rx_in_pkt_not_first <= 1'b1; + end + end + if (tx_axis_net.tvalid & tx_axis_net.tready) begin + if (tx_axis_net.tlast) begin + tx_in_pkt_not_first <= 1'b0; + end else begin + tx_in_pkt_not_first <= 1'b1; + end + end + end +end + +always_comb begin + // Variable IPv4 header length is not supported! + // Protocol deetection for packets with non-20-bytes header might fail + + // RX + rx_ipv4 = {rx_axis_net.tdata[12*8+7:12*8], rx_axis_net.tdata[13*8+7:13*8]} == 16'h0800; + rx_ipv6 = {rx_axis_net.tdata[12*8+7:12*8], rx_axis_net.tdata[13*8+7:13*8]} == 16'h86dd; + // rx_ip_header_len = rx_ipv4 ? {rx_axis_net.tdata[14*8+3:14*8], 2'b00} : (rx_ipv6 ? 6'd40 : 6'b0); + rx_ip_header_len = rx_ipv4 ? 6'd20 : (rx_ipv6 ? 6'd40 : 6'b0); + rx_arp = {rx_axis_net.tdata[12*8+7:12*8], rx_axis_net.tdata[13*8+7:13*8]} == 16'h0806; + rx_icmp_ipv4 = rx_ipv4 & rx_axis_net.tdata[23*8+7:23*8] == 8'h01; + rx_icmp_ipv6 = rx_ipv6 & rx_axis_net.tdata[20*8+7:20*8] == 8'h3a; + rx_udp_ipv4 = rx_ipv4 & rx_axis_net.tdata[23*8+7:23*8] == 8'h11; + rx_udp_ipv6 = rx_ipv6 & rx_axis_net.tdata[20*8+7:20*8] == 8'h11; + rx_tcp_ipv4 = rx_ipv4 & rx_axis_net.tdata[23*8+7:23*8] == 8'h06; + // rx_rocev2_ipv4 = rx_udp_ipv4 & {rx_axis_net.tdata[(16+rx_ip_header_len)*8+7:(16+rx_ip_header_len)*8], rx_axis_net.tdata[(17+rx_ip_header_len)*8+7:(17+rx_ip_header_len)*8]} == 16'hb712; + rx_rocev2_ipv4 = rx_udp_ipv4 & {rx_axis_net.tdata[36*8+7:36*8], rx_axis_net.tdata[37*8+7:37*8]} == 16'hb712; + rx_rocev2_ipv4_aeth = rx_axis_net.tdata[42*8+7:42*8] == 8'h10 | rx_axis_net.tdata[42*8+7:42*8] == 8'h0d | rx_axis_net.tdata[42*8+7:42*8] == 8'h0f | rx_axis_net.tdata[42*8+7:42*8] == 8'h11; // OP = RC_READ_RESP_(ONLY/FIRST/LAST), RC_ACK + rx_rocev2_ipv4_reth = rx_axis_net.tdata[42*8+7:42*8] == 8'h0a | rx_axis_net.tdata[42*8+7:42*8] == 8'h06 | rx_axis_net.tdata[42*8+7:42*8] == 8'h0b | rx_axis_net.tdata[42*8+7:42*8] == 8'h0c; // OP = RC_WRITE_(ONLY/FIRST), RC_WRITE_ONLY_WITH_IMM, RC_READ_REQUEST + + // TX + tx_ipv4 = {tx_axis_net.tdata[12*8+7:12*8], tx_axis_net.tdata[13*8+7:13*8]} == 16'h0800; + tx_ipv6 = {tx_axis_net.tdata[12*8+7:12*8], tx_axis_net.tdata[13*8+7:13*8]} == 16'h86dd; + // tx_ip_header_len = tx_ipv4 ? {tx_axis_net.tdata[14*8+3:14*8], 2'b00} : (tx_ipv6 ? 6'd40 : 6'b0); + tx_ip_header_len = tx_ipv4 ? 6'd20 : (tx_ipv6 ? 6'd40 : 6'b0); + tx_arp = {tx_axis_net.tdata[12*8+7:12*8], tx_axis_net.tdata[13*8+7:13*8]} == 16'h0806; + tx_icmp_ipv4 = tx_ipv4 & tx_axis_net.tdata[23*8+7:23*8] == 8'h01; + tx_icmp_ipv6 = tx_ipv6 & tx_axis_net.tdata[20*8+7:20*8] == 8'h3a; + tx_udp_ipv4 = tx_ipv4 & tx_axis_net.tdata[23*8+7:23*8] == 8'h11; + tx_udp_ipv6 = tx_ipv6 & tx_axis_net.tdata[20*8+7:20*8] == 8'h11; + tx_tcp_ipv4 = tx_ipv4 & tx_axis_net.tdata[23*8+7:23*8] == 8'h06; + // tx_rocev2_ipv4 = tx_udp_ipv4 & {tx_axis_net.tdata[(16+rx_ip_header_len)*8+7:(16+rx_ip_header_len)*8], tx_axis_net.tdata[(17+rx_ip_header_len)*8+7:(17+rx_ip_header_len)*8]} == 16'hb712; + tx_rocev2_ipv4 = tx_udp_ipv4 & {tx_axis_net.tdata[36*8+7:36*8], tx_axis_net.tdata[37*8+7:37*8]} == 16'hb712; + tx_rocev2_ipv4_aeth = tx_axis_net.tdata[42*8+7:42*8] == 8'h10 | tx_axis_net.tdata[42*8+7:42*8] == 8'h0d | tx_axis_net.tdata[42*8+7:42*8] == 8'h0f | tx_axis_net.tdata[42*8+7:42*8] == 8'h11; // OP = RC_READ_RESP_(ONLY/FIRST/LAST), RC_ACK + tx_rocev2_ipv4_reth = tx_axis_net.tdata[42*8+7:42*8] == 8'h0a | tx_axis_net.tdata[42*8+7:42*8] == 8'h06 | tx_axis_net.tdata[42*8+7:42*8] == 8'h0b | tx_axis_net.tdata[42*8+7:42*8] == 8'h0c; // OP = RC_WRITE_(ONLY/FIRST), RC_WRITE_ONLY_WITH_IMM, RC_READ_REQUEST +end + +reg rx_filter_dropped; +logic rx_filter_dropping; +reg [63:0] rx_header_remain; +logic [63:0] rx_header_remain_next; +reg tx_filter_dropped; +logic tx_filter_dropping; +reg [63:0] tx_header_remain; +logic [63:0] tx_header_remain_next; +always @(posedge nclk) begin + if (~nresetn_r) begin + rx_filter_dropped <= 1'b0; + rx_header_remain <= 64'b0; + tx_filter_dropped <= 1'b0; + tx_header_remain <= 64'b0; + end else begin + if (rx_axis_net.tvalid & rx_axis_net.tready) begin + rx_header_remain <= rx_header_remain_next; + if (rx_axis_net.tlast) begin + rx_filter_dropped <= 1'b0; + end else if (rx_filter_dropping) begin + rx_filter_dropped <= rx_filter_dropping; + end + end + if (tx_axis_net.tvalid & tx_axis_net.tready) begin + tx_header_remain <= tx_header_remain_next; + if (tx_axis_net.tlast) begin + tx_filter_dropped <= 1'b0; + end else if (tx_filter_dropping) begin + tx_filter_dropped <= tx_filter_dropping; + end + end + end +end + +assign rx_filtered_axis.tdata = rx_axis_net.tdata; +assign tx_filtered_axis.tdata = tx_axis_net.tdata; +always_comb begin + // RX + rx_filtered_axis.tvalid = rx_axis_net.tvalid; + rx_filtered_axis.tkeep = rx_axis_net.tkeep; + rx_filtered_axis.tlast = rx_axis_net.tlast; + rx_filter_dropping = 1'b0; + rx_header_remain_next = 64'b0; + // drop all logic + if ((rx_in_pkt_first && + ((rx_ipv4 & local_filter_config[8]) || + (rx_ipv6 & local_filter_config[9]) || + (rx_arp & local_filter_config[16]) || + (rx_icmp_ipv4 & local_filter_config[18]) || + (rx_icmp_ipv6 & local_filter_config[20]) || + (rx_udp_ipv4 & local_filter_config[22]) || + (rx_udp_ipv6 & local_filter_config[24]) || + (rx_tcp_ipv4 & local_filter_config[26]) || + (rx_rocev2_ipv4 & local_filter_config[30]))) || + (rx_in_pkt_not_first && + (rx_filter_dropped)) + ) begin + rx_filtered_axis.tvalid = 1'b0; + rx_filtered_axis.tkeep = 64'b0; + rx_filtered_axis.tlast = 1'b0; + rx_filter_dropping = 1'b1; + // select header logic + end else if (rx_in_pkt_first) begin + if (rx_udp_ipv4 & local_filter_config[23]) begin + // 14 (eth header) + 20 (ipv4 header) + 8 (udp header) = 42B + rx_filtered_axis.tkeep = 64'h0000_03ff_ffff_ffff; + rx_filtered_axis.tlast = 1'b1; + rx_filter_dropping = 1'b1; + end else if (rx_udp_ipv6 & local_filter_config[25]) begin + // 14 (eth header) + 40 (ipv4 header) + 8 (udp header) = 62B + rx_filtered_axis.tkeep = 64'h3fff_ffff_ffff_ffff; + rx_filtered_axis.tlast = 1'b1; + rx_filter_dropping = 1'b1; + end else if (rx_tcp_ipv4 & local_filter_config[27]) begin + // 14 (eth header) + 20 (ipv4 header) + 20 (tcp header) = 54B + // note: TCP Options will also be ignored + rx_filtered_axis.tkeep = 64'h003f_ffff_ffff_ffff; + rx_filtered_axis.tlast = 1'b1; + rx_filter_dropping = 1'b1; + end else if (rx_rocev2_ipv4 & local_filter_config[31]) begin + // 14 (eth header) + 20 (ipv4 header) + 8 (udp header) + 12 (rocev2 bth) = 54B + // additional header? + if (tx_rocev2_ipv4_aeth) begin + // 54 + 4 = 58B + rx_filtered_axis.tkeep = 64'h03ff_ffff_ffff_ffff; + rx_filtered_axis.tlast = 1'b1; + rx_filter_dropping = 1'b1; + end else if (tx_rocev2_ipv4_reth) begin + // 54 + 16 = 70B + rx_header_remain_next = 64'h0000_0000_0000_003f; + end else begin + // 54B + rx_filtered_axis.tkeep = 64'h003f_ffff_ffff_ffff; + rx_filtered_axis.tlast = 1'b1; + rx_filter_dropping = 1'b1; + end + end + // long header (64 < len <= 128) logic + end else if (rx_header_remain > 64'b0 && rx_axis_net.tvalid && rx_axis_net.tready) begin + rx_header_remain_next = 64'b0; + rx_filtered_axis.tkeep = rx_header_remain; + rx_filtered_axis.tlast = 1'b1; + rx_filter_dropping = 1'b1; + end + + // TX + tx_filtered_axis.tvalid = tx_axis_net.tvalid; + tx_filtered_axis.tkeep = tx_axis_net.tkeep; + tx_filtered_axis.tlast = tx_axis_net.tlast; + tx_filter_dropping = 1'b0; + tx_header_remain_next = 64'b0; + // drop all logic + if ((tx_in_pkt_first && + ((tx_ipv4 & local_filter_config[8]) || + (tx_ipv6 & local_filter_config[9]) || + (tx_arp & local_filter_config[16]) || + (tx_icmp_ipv4 & local_filter_config[18]) || + (tx_icmp_ipv6 & local_filter_config[20]) || + (tx_udp_ipv4 & local_filter_config[22]) || + (tx_udp_ipv6 & local_filter_config[24]) || + (tx_tcp_ipv4 & local_filter_config[26]) || + (tx_rocev2_ipv4 & local_filter_config[30]))) || + (tx_in_pkt_not_first && + (tx_filter_dropped)) + ) begin + tx_filtered_axis.tvalid = 1'b0; + tx_filtered_axis.tkeep = 64'b0; + tx_filtered_axis.tlast = 1'b0; + tx_filter_dropping = 1'b1; + // select header logic + end else if (tx_in_pkt_first) begin + if (tx_udp_ipv4 & local_filter_config[23]) begin + // 14 (eth header) + 20 (ipv4 header) + 8 (udp header) = 42B + tx_filtered_axis.tkeep = 64'h0000_03ff_ffff_ffff; + tx_filtered_axis.tlast = 1'b1; + tx_filter_dropping = 1'b1; + end else if (tx_udp_ipv6 & local_filter_config[25]) begin + // 14 (eth header) + 40 (ipv4 header) + 8 (udp header) = 62B + tx_filtered_axis.tkeep = 64'h3fff_ffff_ffff_ffff; + tx_filtered_axis.tlast = 1'b1; + tx_filter_dropping = 1'b1; + end else if (tx_tcp_ipv4 & local_filter_config[27]) begin + // 14 (eth header) + 20 (ipv4 header) + 20 (tcp header) = 54B + // note: TCP Options will also be ignored + tx_filtered_axis.tkeep = 64'h003f_ffff_ffff_ffff; + tx_filtered_axis.tlast = 1'b1; + tx_filter_dropping = 1'b1; + end else if (tx_rocev2_ipv4 & local_filter_config[31]) begin + // 14 (eth header) + 20 (ipv4 header) + 8 (udp header) + 12 (rocev2 bth) = 54B + // additional header? + if (tx_rocev2_ipv4_aeth) begin + // 54 + 4 = 58B + tx_filtered_axis.tkeep = 64'h03ff_ffff_ffff_ffff; + tx_filtered_axis.tlast = 1'b1; + tx_filter_dropping = 1'b1; + end else if (tx_rocev2_ipv4_reth) begin + // 54 + 16 = 70B + tx_header_remain_next = 64'h0000_0000_0000_003f; + end else begin + // 54B + tx_filtered_axis.tkeep = 64'h003f_ffff_ffff_ffff; + tx_filtered_axis.tlast = 1'b1; + tx_filter_dropping = 1'b1; + end + end + // long header (64 < len <= 128) logic + end else if (tx_header_remain > 64'b0 && tx_axis_net.tvalid && tx_axis_net.tready) begin + tx_header_remain_next = 64'b0; + tx_filtered_axis.tkeep = tx_header_remain; + tx_filtered_axis.tlast = 1'b1; + tx_filter_dropping = 1'b1; + end +end + +endmodule + + +// /** +// * @brief Timestamp Inserter +// * +// * Insert timestamp into packets +// */ +// module timestamp_inserter ( +// // TODO +// ); +// // TODO +// endmodule + + +/** + * @brief Packet Sniffer + * + * Capture packets on RX and TX, filter, insert timestamp, and merge results into one stream + */ +module packet_sniffer ( + /* Network stream */ + AXI4S.s rx_axis_net, // RX + AXI4S.s tx_axis_net, // TX + AXI4S.m rx_pass_axis_net, // RX pass-through + AXI4S.m tx_pass_axis_net, // TX pass-through + + // subject to future changes + /* Filtered stream */ + AXI4S.m rx_filtered_axis, + AXI4S.m tx_filtered_axis, + /* Filter configuration */ + metaIntf.s filter_config, + + input wire nclk, + input wire nresetn_r +); + +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) rx_filter_before_slice(); +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) tx_filter_before_slice(); +axis_reg_array #(.N_STAGES(3)) inst_slice_rx_filter (.aclk(nclk), .aresetn(nresetn_r), .s_axis(rx_filter_before_slice), .m_axis(rx_filtered_axis)); +axis_reg_array #(.N_STAGES(3)) inst_slice_tx_filter (.aclk(nclk), .aresetn(nresetn_r), .s_axis(tx_filter_before_slice), .m_axis(tx_filtered_axis)); + +assign filter_config.ready = 1'b1; +reg [63:0] filter_config_r; +always @(posedge nclk) begin + if (~nresetn_r) begin + filter_config_r <= 0; + end else begin + if (filter_config.valid && filter_config.ready) begin + filter_config_r <= filter_config.data; + end + end +end + +packet_filter packet_filter_inst ( + .rx_axis_net(rx_axis_net), + .tx_axis_net(tx_axis_net), + .rx_pass_axis_net(rx_pass_axis_net), + .tx_pass_axis_net(tx_pass_axis_net), + .rx_filtered_axis(rx_filter_before_slice), + .tx_filtered_axis(tx_filter_before_slice), + .local_filter_config(filter_config_r), + .nclk(nclk), + .nresetn_r(nresetn_r) +); + +endmodule diff --git a/hw/hdl/network/stack/network_stack.sv b/hw/hdl/network/stack/network_stack.sv index 61acc180..78d15f7a 100644 --- a/hw/hdl/network/stack/network_stack.sv +++ b/hw/hdl/network/stack/network_stack.sv @@ -104,6 +104,12 @@ module network_stack #( AXI4S.m m_axis_tcp_mem_wr, `endif +`ifdef EN_SNIFFER + AXI4S.m m_rx_sniffer, + AXI4S.m m_tx_sniffer, + metaIntf.s s_filter_config, +`endif + /* Network streams */ AXI4S.s s_axis_net, AXI4S.m m_axis_net @@ -120,10 +126,18 @@ always_ff @(posedge nclk) begin nresetn_r <= nresetn_r_int; end +// Packet Sniffer +// --------------------------------------------------------------------------------------------- +// RX +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_slice_to_sniffer(); +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_sniffer_to_ibh_slice(); +// TX +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_macmerger_to_sniffer_slice(); +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_sniffer_slice_to_sniffer(); // Ip handler // --------------------------------------------------------------------------------------------- -AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_slice_to_ibh(); +AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_ibh_slice_to_ibh(); AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_iph_to_arp_slice(); AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) axis_iph_to_icmp_slice(); @@ -297,12 +311,117 @@ vio_ip inst_vio_ip ( .probe_in1(local_mac_address) // 48 ); +/** + * Packet Sniffer + */ +axis_reg inst_sniffer_slice_0 (.aclk(nclk), .aresetn(nresetn_r), .s_axis(s_axis_net), .m_axis(axis_slice_to_sniffer)); + +`ifdef EN_SNIFFER +axis_reg_array inst_sniffer_slice_1 (.aclk(nclk), .aresetn(nresetn_r), .s_axis(axis_macmerger_to_sniffer_slice), .m_axis(axis_sniffer_slice_to_sniffer)); +`else +// assign axis_sniffer_slice_to_sniffer = axis_macmerger_to_sniffer_slice +assign axis_sniffer_slice_to_sniffer.tdata = axis_macmerger_to_sniffer_slice.tdata; +assign axis_sniffer_slice_to_sniffer.tvalid = axis_macmerger_to_sniffer_slice.tvalid; +assign axis_sniffer_slice_to_sniffer.tkeep = axis_macmerger_to_sniffer_slice.tkeep; +assign axis_sniffer_slice_to_sniffer.tlast = axis_macmerger_to_sniffer_slice.tlast; +assign axis_macmerger_to_sniffer_slice.tready = axis_sniffer_slice_to_sniffer.tready; +`endif + +`ifdef EN_SNIFFER +packet_sniffer packet_sniffer_inst ( + .rx_axis_net(axis_slice_to_sniffer), + .rx_pass_axis_net(axis_sniffer_to_ibh_slice), + .tx_axis_net(axis_sniffer_slice_to_sniffer), + .tx_pass_axis_net(m_axis_net), + .rx_filtered_axis(m_rx_sniffer), + .tx_filtered_axis(m_tx_sniffer), + .filter_config(s_filter_config), + .nclk(nclk), + .nresetn_r(nresetn_r) +); + +// ILA of IP handler +// ila_sniffer inst_ila_sniffer ( +// .clk(nclk), +// .probe0(axis_slice_to_sniffer.tvalid), // [0:0] +// .probe1(axis_slice_to_sniffer.tready), // [0:0] +// .probe2(axis_slice_to_sniffer.tdata), // [511:0] +// .probe3(axis_slice_to_sniffer.tkeep), // [63:0] +// .probe4(axis_slice_to_sniffer.tlast), // [0:0] + +// .probe5(axis_sniffer_to_ibh_slice.tvalid), +// .probe6(axis_sniffer_to_ibh_slice.tready), +// .probe7(axis_sniffer_to_ibh_slice.tdata), +// .probe8(axis_sniffer_to_ibh_slice.tkeep), +// .probe9(axis_sniffer_to_ibh_slice.tlast), + +// .probe10(axis_sniffer_slice_to_sniffer.tvalid), +// .probe11(axis_sniffer_slice_to_sniffer.tready), +// .probe12(axis_sniffer_slice_to_sniffer.tdata), +// .probe13(axis_sniffer_slice_to_sniffer.tkeep), +// .probe14(axis_sniffer_slice_to_sniffer.tlast), + +// .probe15(m_axis_net.tvalid), +// .probe16(m_axis_net.tready), +// .probe17(m_axis_net.tdata), +// .probe18(m_axis_net.tkeep), +// .probe19(m_axis_net.tlast), + +// .probe20(m_rx_sniffer.tvalid), +// .probe21(m_rx_sniffer.tready), +// .probe22(m_rx_sniffer.tdata), +// .probe23(m_rx_sniffer.tkeep), +// .probe24(m_rx_sniffer.tlast), + +// .probe25(m_tx_sniffer.tvalid), +// .probe26(m_tx_sniffer.tready), +// .probe27(m_tx_sniffer.tdata), +// .probe28(m_tx_sniffer.tkeep), +// .probe29(m_tx_sniffer.tlast), + +// .probe30(axis_ibh_slice_to_ibh.tvalid), +// .probe31(axis_ibh_slice_to_ibh.tready), +// .probe32(axis_ibh_slice_to_ibh.tdata), +// .probe33(axis_ibh_slice_to_ibh.tkeep), +// .probe34(axis_ibh_slice_to_ibh.tlast), + +// .probe35(axis_iph_to_icmp_slice.tvalid), +// .probe36(axis_iph_to_icmp_slice.tready), +// .probe37(axis_iph_to_icmp_slice.tdata), +// .probe38(axis_iph_to_icmp_slice.tkeep), +// .probe39(axis_iph_to_icmp_slice.tlast) +// ); +`else +// No packet sniffer, simply pass-through +// assign axis_sniffer_to_ibh_slice = axis_slice_to_sniffer +assign axis_sniffer_to_ibh_slice.tdata = axis_slice_to_sniffer.tdata; +assign axis_sniffer_to_ibh_slice.tvalid = axis_slice_to_sniffer.tvalid; +assign axis_sniffer_to_ibh_slice.tkeep = axis_slice_to_sniffer.tkeep; +assign axis_sniffer_to_ibh_slice.tlast = axis_slice_to_sniffer.tlast; +assign axis_slice_to_sniffer.tready = axis_sniffer_to_ibh_slice.tready; +// assign m_axis_net = axis_sniffer_slice_to_sniffer +assign m_axis_net.tdata = axis_sniffer_slice_to_sniffer.tdata; +assign m_axis_net.tvalid = axis_sniffer_slice_to_sniffer.tvalid; +assign m_axis_net.tkeep = axis_sniffer_slice_to_sniffer.tkeep; +assign m_axis_net.tlast = axis_sniffer_slice_to_sniffer.tlast; +assign axis_sniffer_slice_to_sniffer.tready = m_axis_net.tready; +`endif + /** * IP handler */ // In slice -axis_reg inst_slice_in (.aclk(nclk), .aresetn(nresetn_r), .s_axis(s_axis_net), .m_axis(axis_slice_to_ibh)); +`ifdef EN_SNIFFER +axis_reg_array inst_slice_in (.aclk(nclk), .aresetn(nresetn_r), .s_axis(axis_sniffer_to_ibh_slice), .m_axis(axis_ibh_slice_to_ibh)); +`else +// assign axis_ibh_slice_to_ibh = axis_sniffer_to_ibh_slice +assign axis_ibh_slice_to_ibh.tdata = axis_sniffer_to_ibh_slice.tdata; +assign axis_ibh_slice_to_ibh.tvalid = axis_sniffer_to_ibh_slice.tvalid; +assign axis_ibh_slice_to_ibh.tkeep = axis_sniffer_to_ibh_slice.tkeep; +assign axis_ibh_slice_to_ibh.tlast = axis_sniffer_to_ibh_slice.tlast; +assign axis_sniffer_to_ibh_slice.tready = axis_ibh_slice_to_ibh.tready; +`endif // IP handler ip_handler_ip ip_handler_inst ( @@ -348,11 +467,11 @@ ip_handler_ip ip_handler_inst ( .m_axis_roce_TKEEP(axis_iph_to_roce_slice.tkeep), .m_axis_roce_TLAST(axis_iph_to_roce_slice.tlast), - .s_axis_raw_TVALID(axis_slice_to_ibh.tvalid), - .s_axis_raw_TREADY(axis_slice_to_ibh.tready), - .s_axis_raw_TDATA(axis_slice_to_ibh.tdata), - .s_axis_raw_TKEEP(axis_slice_to_ibh.tkeep), - .s_axis_raw_TLAST(axis_slice_to_ibh.tlast), + .s_axis_raw_TVALID(axis_ibh_slice_to_ibh.tvalid), + .s_axis_raw_TREADY(axis_ibh_slice_to_ibh.tready), + .s_axis_raw_TDATA(axis_ibh_slice_to_ibh.tdata), + .s_axis_raw_TKEEP(axis_ibh_slice_to_ibh.tkeep), + .s_axis_raw_TLAST(axis_ibh_slice_to_ibh.tlast), `ifdef VITIS_HLS .myIpAddress(iph_ip_address), @@ -609,11 +728,11 @@ axis_interconnect_512_2to1 mac_merger ( .M00_AXIS_ACLK(nclk), // input M00_AXIS_ACLK .M00_AXIS_ARESETN(nresetn_r), // input M00_AXIS_ARESETN - .M00_AXIS_TVALID(m_axis_net.tvalid), // output M00_AXIS_TVALID - .M00_AXIS_TREADY(m_axis_net.tready), // input M00_AXIS_TREADY - .M00_AXIS_TDATA(m_axis_net.tdata), // output [63 : 0] M00_AXIS_TDATA - .M00_AXIS_TKEEP(m_axis_net.tkeep), // output [7 : 0] M00_AXIS_TKEEP - .M00_AXIS_TLAST(m_axis_net.tlast), // output M00_AXIS_TLAST + .M00_AXIS_TVALID(axis_macmerger_to_sniffer_slice.tvalid), // output M00_AXIS_TVALID + .M00_AXIS_TREADY(axis_macmerger_to_sniffer_slice.tready), // input M00_AXIS_TREADY + .M00_AXIS_TDATA(axis_macmerger_to_sniffer_slice.tdata), // output [63 : 0] M00_AXIS_TDATA + .M00_AXIS_TKEEP(axis_macmerger_to_sniffer_slice.tkeep), // output [7 : 0] M00_AXIS_TKEEP + .M00_AXIS_TLAST(axis_macmerger_to_sniffer_slice.tlast), // output M00_AXIS_TLAST .S00_ARB_REQ_SUPPRESS(1'b0), // input S00_ARB_REQ_SUPPRESS .S01_ARB_REQ_SUPPRESS(1'b0) // input S01_ARB_REQ_SUPPRESS //.S02_ARB_REQ_SUPPRESS(1'b0) // input S01_ARB_REQ_SUPPRESS diff --git a/hw/hdl/network/stack/network_top.sv b/hw/hdl/network/stack/network_top.sv index 919b5555..9c68d6ce 100644 --- a/hw/hdl/network/stack/network_top.sv +++ b/hw/hdl/network/stack/network_top.sv @@ -101,6 +101,12 @@ module network_top #( `endif +`ifdef EN_SNIFFER + AXI4S.m m_rx_sniffer, + AXI4S.m m_tx_sniffer, + metaIntf.s s_filter_config, +`endif + // Clocks input wire aclk, input wire aresetn, @@ -333,6 +339,12 @@ network_stack inst_network_stack ( .m_axis_tcp_mem_wr(axis_tcp_mem_wr_n_clk), `endif +`ifdef EN_SNIFFER + .m_rx_sniffer(m_rx_sniffer), + .m_tx_sniffer(m_tx_sniffer), + .s_filter_config(s_filter_config), +`endif + .nclk(n_clk), .nresetn(n_resetn) ); diff --git a/scripts/config.tcl.in b/scripts/config.tcl.in index ed9e328f..febbba34 100644 --- a/scripts/config.tcl.in +++ b/scripts/config.tcl.in @@ -37,6 +37,8 @@ set cfg(en_avx) ${EN_AVX} set cfg(en_wb) ${EN_WB} set cfg(en_rdma) ${EN_RDMA} set cfg(en_tcp) ${EN_TCP} +set cfg(en_sniffer) ${EN_SNIFFER} +set cfg(sniffer_vfpga_id) ${SNIFFER_VFPGA_ID} set cfg(en_net_0) ${EN_NET_0} set cfg(en_net_1) ${EN_NET_1} set cfg(en_net) ${EN_NET} diff --git a/scripts/export.cmake.in b/scripts/export.cmake.in index 93fe1092..43d28b9e 100644 --- a/scripts/export.cmake.in +++ b/scripts/export.cmake.in @@ -32,7 +32,8 @@ set(N_OUTSTANDING ${N_OUTSTANDING}) set(EN_AVX ${EN_AVX}) set(EN_WB ${EN_WB}) set(EN_RDMA ${EN_RDMA}) -set(EN_TCP} ${EN_TCP}) +set(EN_TCP ${EN_TCP}) +set(EN_SNIFFER ${EN_SNIFFER}) set(EN_NET_0 ${EN_NET_0}) set(EN_NET_1 ${EN_NET_1}) set(EN_NET ${EN_NET}) diff --git a/scripts/ip_inst/network_infrastructure.tcl b/scripts/ip_inst/network_infrastructure.tcl index b61a8627..4be2f39f 100644 --- a/scripts/ip_inst/network_infrastructure.tcl +++ b/scripts/ip_inst/network_infrastructure.tcl @@ -123,6 +123,14 @@ create_ip -name ethernet_frame_padding_512 -vendor ethz.systems.fpga -library hl create_ip -name axis_data_fifo -vendor xilinx.com -library ip -version 2.0 -module_name axis_data_fifo_512_used set_property -dict [list CONFIG.TDATA_NUM_BYTES {64} CONFIG.HAS_TKEEP {1} CONFIG.HAS_TLAST {1} CONFIG.HAS_WR_DATA_COUNT {1} CONFIG.Component_Name {axis_data_fifo_512_used}] [get_ips axis_data_fifo_512_used] +## +## Sniffer +## +# ILA Sniffer +# create_ip -name ila -vendor xilinx.com -library ip -version 6.2 -module_name ila_sniffer +# set_property -dict [list CONFIG.C_PROBE39_WIDTH {1} CONFIG.C_PROBE38_WIDTH {64} CONFIG.C_PROBE37_WIDTH {512} CONFIG.C_PROBE36_WIDTH {1} CONFIG.C_PROBE35_WIDTH {1} CONFIG.C_PROBE34_WIDTH {1} CONFIG.C_PROBE33_WIDTH {64} CONFIG.C_PROBE32_WIDTH {512} CONFIG.C_PROBE31_WIDTH {1} CONFIG.C_PROBE30_WIDTH {1} CONFIG.C_PROBE29_WIDTH {1} CONFIG.C_PROBE28_WIDTH {64} CONFIG.C_PROBE27_WIDTH {512} CONFIG.C_PROBE26_WIDTH {1} CONFIG.C_PROBE25_WIDTH {1} CONFIG.C_PROBE24_WIDTH {1} CONFIG.C_PROBE23_WIDTH {64} CONFIG.C_PROBE22_WIDTH {512} CONFIG.C_PROBE21_WIDTH {1} CONFIG.C_PROBE20_WIDTH {1} CONFIG.C_PROBE19_WIDTH {1} CONFIG.C_PROBE18_WIDTH {64} CONFIG.C_PROBE17_WIDTH {512} CONFIG.C_PROBE16_WIDTH {1} CONFIG.C_PROBE15_WIDTH {1} CONFIG.C_PROBE14_WIDTH {1} CONFIG.C_PROBE13_WIDTH {64} CONFIG.C_PROBE12_WIDTH {512} CONFIG.C_PROBE11_WIDTH {1} CONFIG.C_PROBE10_WIDTH {1} CONFIG.C_PROBE9_WIDTH {1} CONFIG.C_PROBE8_WIDTH {64} CONFIG.C_PROBE7_WIDTH {512} CONFIG.C_PROBE6_WIDTH {1} CONFIG.C_PROBE5_WIDTH {1} CONFIG.C_PROBE4_WIDTH {1} CONFIG.C_PROBE3_WIDTH {64} CONFIG.C_PROBE2_WIDTH {512} CONFIG.C_PROBE1_WIDTH {1} CONFIG.C_PROBE0_WIDTH {1} CONFIG.C_NUM_OF_PROBES {40} CONFIG.Component_Name {ila_sniffer} CONFIG.C_INPUT_PIPE_STAGES {1}] [get_ips ila_sniffer] + + ## ## RDMA ## diff --git a/scripts/wr_hdl/template_gen/dynamic_top_tmplt.txt b/scripts/wr_hdl/template_gen/dynamic_top_tmplt.txt index 08223f68..38bed3ab 100644 --- a/scripts/wr_hdl/template_gen/dynamic_top_tmplt.txt +++ b/scripts/wr_hdl/template_gen/dynamic_top_tmplt.txt @@ -98,6 +98,14 @@ module design_dynamic_top #( AXI4S.m m_axis_tcp, {% endif %} + +{% if cnfg.en_sniffer %} + // Sniffer + AXI4S.s s_rx_sniffer, + AXI4S.s s_tx_sniffer, + metaIntf.m m_filter_config, +{% endif %} + {% if cnfg.en_wb %} // Writeback metaIntf.m m_wback, @@ -1125,6 +1133,21 @@ module design_dynamic_top #( .axis_tcp_src_tlast (axis_tcp_out_ul[{{ i }}].tlast), .axis_tcp_src_tready (axis_tcp_out_ul[{{ i }}].tready), .axis_tcp_src_tvalid (axis_tcp_out_ul[{{ i }}].tvalid), +{% endif %} +{% if ( cnfg.en_sniffer and ( cnfg.sniffer_vfpga_id == i ) ) %} + .rx_sniffer_tdata(s_rx_sniffer.tdata), + .rx_sniffer_tkeep(s_rx_sniffer.tkeep), + .rx_sniffer_tlast(s_rx_sniffer.tlast), + .rx_sniffer_tready(s_rx_sniffer.tready), + .rx_sniffer_tvalid(s_rx_sniffer.tvalid), + .tx_sniffer_tdata(s_tx_sniffer.tdata), + .tx_sniffer_tkeep(s_tx_sniffer.tkeep), + .tx_sniffer_tlast(s_tx_sniffer.tlast), + .tx_sniffer_tready(s_tx_sniffer.tready), + .tx_sniffer_tvalid(s_tx_sniffer.tvalid), + .filter_config_data(m_filter_config.data), + .filter_config_ready(m_filter_config.ready), + .filter_config_valid(m_filter_config.valid), {% endif %} .S_BSCAN_drck(S_BSCAN_drck[{{ i }}]), .S_BSCAN_shift(S_BSCAN_shift[{{ i }}]), diff --git a/scripts/wr_hdl/template_gen/lynx_pkg_tmplt.txt b/scripts/wr_hdl/template_gen/lynx_pkg_tmplt.txt index 8d2bfd71..1863e534 100644 --- a/scripts/wr_hdl/template_gen/lynx_pkg_tmplt.txt +++ b/scripts/wr_hdl/template_gen/lynx_pkg_tmplt.txt @@ -56,6 +56,9 @@ {% if cnfg.en_tcp %} `define EN_TCP {% endif %} +{% if cnfg.en_sniffer %} +`define EN_SNIFFER +{% endif %} {% if cnfg.en_net_0 %} `define EN_NET_0 {% endif %} diff --git a/scripts/wr_hdl/template_gen/shell_top_tmplt.txt b/scripts/wr_hdl/template_gen/shell_top_tmplt.txt index 82eacb21..aaf93f77 100644 --- a/scripts/wr_hdl/template_gen/shell_top_tmplt.txt +++ b/scripts/wr_hdl/template_gen/shell_top_tmplt.txt @@ -713,6 +713,11 @@ module shell_top ( // DDR AXI mm tcp AXI4 axi_ddr_tcp (); +{% endif %} +{% if cnfg.en_sniffer %} + AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) rx_sniffer (); + AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) tx_sniffer (); + metaIntf #(.STYPE(logic[64-1:0])) filter_config (); {% endif %} network_top #( @@ -775,6 +780,11 @@ module shell_top ( .m_axis_tcp_rx(axis_tcp_rx), .s_ddr_offset_addr_tcp(ddr_offset_addr_tcp), .m_axi_tcp_ddr(axi_ddr_tcp), +{% endif %} +{% if cnfg.en_sniffer %} + .m_rx_sniffer(rx_sniffer), + .m_tx_sniffer(tx_sniffer), + .s_filter_config(filter_config), {% endif %} .aclk(aclk), .aresetn(aresetn), @@ -1316,6 +1326,11 @@ module shell_top ( .s_axis_tcp(axis_tcp_rx), .m_axis_tcp(axis_tcp_tx), {% endif %} +{% if cnfg.en_sniffer %} + .s_rx_sniffer(rx_sniffer), + .s_tx_sniffer(tx_sniffer), + .m_filter_config(filter_config), +{% endif %} {% if cnfg.en_wb %} .m_wback(wback), {% endif %} diff --git a/scripts/wr_hdl/template_gen/user_logic_tmplt.txt b/scripts/wr_hdl/template_gen/user_logic_tmplt.txt index 59238895..887481c9 100644 --- a/scripts/wr_hdl/template_gen/user_logic_tmplt.txt +++ b/scripts/wr_hdl/template_gen/user_logic_tmplt.txt @@ -83,6 +83,13 @@ module design_user_logic_c{{ c_cnfg }}_{{ c_reg }} ( AXI4SR.m axis_tcp_send [N_TCP_AXI], {% endif %} + +{% if ( cnfg.en_sniffer and ( cnfg.sniffer_vfpga_id == c_reg ) ) %} + AXI4S.s axis_rx_sniffer, + AXI4S.s axis_tx_sniffer, + metaIntf.m filter_config, +{% endif %} + // Clock and reset input wire aclk, input wire[0:0] aresetn diff --git a/scripts/wr_hdl/template_gen/user_wrapper_tmplt.txt b/scripts/wr_hdl/template_gen/user_wrapper_tmplt.txt index de505afb..87ef846a 100644 --- a/scripts/wr_hdl/template_gen/user_wrapper_tmplt.txt +++ b/scripts/wr_hdl/template_gen/user_wrapper_tmplt.txt @@ -176,6 +176,23 @@ module design_user_wrapper_{{ c_reg }} ( (* io_buffer_type = "none" *) output logic axis_tcp_src_tvalid, {% endif %} + +{% if ( cnfg.en_sniffer and ( cnfg.sniffer_vfpga_id == c_reg ) ) %} + (* io_buffer_type = "none" *) input logic[AXI_NET_BITS-1:0] rx_sniffer_tdata, + (* io_buffer_type = "none" *) input logic[AXI_NET_BITS/8-1:0] rx_sniffer_tkeep, + (* io_buffer_type = "none" *) input logic rx_sniffer_tlast, + (* io_buffer_type = "none" *) output logic rx_sniffer_tready, + (* io_buffer_type = "none" *) input logic rx_sniffer_tvalid, + (* io_buffer_type = "none" *) input logic[AXI_NET_BITS-1:0] tx_sniffer_tdata, + (* io_buffer_type = "none" *) input logic[AXI_NET_BITS/8-1:0] tx_sniffer_tkeep, + (* io_buffer_type = "none" *) input logic tx_sniffer_tlast, + (* io_buffer_type = "none" *) output logic tx_sniffer_tready, + (* io_buffer_type = "none" *) input logic tx_sniffer_tvalid, + (* io_buffer_type = "none" *) output logic[64-1:0] filter_config_data, + (* io_buffer_type = "none" *) input logic filter_config_ready, + (* io_buffer_type = "none" *) output logic filter_config_valid, +{% endif %} + // BSCAN (* io_buffer_type = "none" *) input logic S_BSCAN_drck, (* io_buffer_type = "none" *) input logic S_BSCAN_shift, @@ -411,6 +428,26 @@ module design_user_wrapper_{{ c_reg }} ( {% endif %} +{% if ( cnfg.en_sniffer and ( cnfg.sniffer_vfpga_id == c_reg ) ) %} + AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) rx_sniffer (); + AXI4S #(.AXI4S_DATA_BITS(AXI_NET_BITS)) tx_sniffer (); + metaIntf #(.STYPE(logic[64-1:0])) filter_config (); + + assign rx_sniffer.tdata = rx_sniffer_tdata; + assign rx_sniffer.tkeep = rx_sniffer_tkeep; + assign rx_sniffer.tlast = rx_sniffer_tlast; + assign rx_sniffer.tready = rx_sniffer_tready; + assign rx_sniffer.tvalid = rx_sniffer_tvalid; + assign tx_sniffer.tdata = tx_sniffer_tdata; + assign tx_sniffer.tkeep = tx_sniffer_tkeep; + assign tx_sniffer.tlast = tx_sniffer_tlast; + assign tx_sniffer.tready = tx_sniffer_tready; + assign tx_sniffer.tvalid = tx_sniffer_tvalid; + assign filter_config.data = filter_config_data; + assign filter_config.ready = filter_config_ready; + assign filter_config.valid = filter_config_valid; +{% endif %} + {% if cnfg.en_user_reg %} // // REG STAGE @@ -1026,6 +1063,11 @@ module design_user_wrapper_{{ c_reg }} ( {% if cnfg.en_tcp %} .axis_tcp_recv(axis_tcp_recv_int), .axis_tcp_send(axis_tcp_send_int), + {% endif %} + {% if ( cnfg.en_sniffer and ( cnfg.sniffer_vfpga_id == c_reg ) ) %} + .axis_rx_sniffer(rx_sniffer), + .axis_tx_sniffer(tx_sniffer), + .filter_config(filter_config), {% endif %} .aclk(aclk), .aresetn(aresetn)