diff options
Diffstat (limited to 'fpga/usrp3/lib/packet_proc/eth_dispatch.v')
-rw-r--r-- | fpga/usrp3/lib/packet_proc/eth_dispatch.v | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/packet_proc/eth_dispatch.v b/fpga/usrp3/lib/packet_proc/eth_dispatch.v new file mode 100644 index 000000000..b600a1533 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/eth_dispatch.v @@ -0,0 +1,573 @@ + +// Ethernet dispatcher +// Incoming ethernet packets are examined and sent to the correct destination +// There are 3 destinations, ZPU, other ethernet port (out), and vita router +// Packets going to the vita router will have the ethernet/ip/udp headers stripped off. +// +// To make things simpler, we start out by sending all packets to zpu and out port. +// By the end of the eth/ip/udp headers, we can determine where the correct destination is. +// If the correct destination is vita, we send an error indication on the zpu and out ports, +// which will cause the axi_packet_gate to drop those packets, and send the vita frame to +// the vita port. +// +// If at the end of the headers we determine the packet should go to zpu, then we send an +// error indication on the out port, the rest of the packet to zpu and nothing on vita. +// If it should go to out, we send the error indication to zpu, the rest of the packet to out, +// and nothing on vita. +// +// Downstream we should have adequate fifo space, otherwise we could get backed up here. +// +// No tuser bits sent to vita, as vita assumes there are no errors and that occupancy is +// indicated by the length field of the vita header. + +// +// Rules for forwarding: +// +// Ethernet Broadcast (Dst MAC = ff:ff:ff:ff:ff:ff). Forward to both ZPU and XO MAC. +// ? Ethernet Multicast (Dst MAC = USRP_NEXT_HOP). Forward only to ZPU. +// ? Ethernet Multicast (Dst MAC = Unknown). Forward only to XO. +// Ethernet Unicast (Dst MAC = Unknown). Forward only to XO. +// Ethernet Unicast (Dst MAC = local). Look deeper...... +// IP Broadcast. Forward to both ZPU and XO MAC. (Should be coverd by Eth broadcast) +// IP Multicast. ? Unknow Action. +// IP Unicast (Dst IP = local). Look deeper.... +// UDP (Port = Listed) and its a VRLP packet. Forward only to VITA Radio Core. +// UDP (Port = Unknown). Forward only to ZPU. +// +// + +module eth_dispatch + #(parameter BASE=0) + ( + // Clocking and reset interface + input clk, + input reset, + input clear, + // Setting register interface + input set_stb, + input [15:0] set_addr, + input [31:0] set_data, + // Input 68bit AXI-Stream interface (from MAC) + input [63:0] in_tdata, + input [3:0] in_tuser, + input in_tlast, + input in_tvalid, + output in_tready, + // Output AXI-STream interface to VITA Radio Core + output [63:0] vita_tdata, + output [3:0] vita_tuser, + output vita_tlast, + output vita_tvalid, + input vita_tready, + // Output AXI-Stream interface to ZPU + output [63:0] zpu_tdata, + output [3:0] zpu_tuser, + output zpu_tlast, + output zpu_tvalid, + input zpu_tready, + // Output AXI-Stream interface to cross-over MAC + output [63:0] xo_tdata, + output [3:0] xo_tuser, + output xo_tlast, + output xo_tvalid, + input xo_tready, + // Debug + output [31:0] debug + ); + + // + // State machine declarations + // + reg [2:0] state; + + localparam WAIT_PACKET = 0; + localparam READ_HEADER = 1; + localparam FORWARD_ZPU = 2; + localparam FORWARD_ZPU_AND_XO = 3; + localparam FORWARD_XO = 4; + localparam FORWARD_RADIO_CORE = 5; + localparam DROP_PACKET = 6; + localparam CLASSIFY_PACKET = 7; + + + // + // Small RAM stores packet header during parsing. + // + localparam HEADER_RAM_SIZE = 9; + (*ram_style="distributed"*) + reg [68:0] header_ram [HEADER_RAM_SIZE-1:0]; + reg [3:0] header_ram_addr; + reg drop_this_packet; + + wire header_done = (header_ram_addr == HEADER_RAM_SIZE-1); + reg fwd_input; + + // + reg [63:0] in_tdata_reg; + + // + wire out_tvalid; + wire out_tready; + wire out_tlast; + wire [3:0] out_tuser; + wire [63:0] out_tdata; + + + + // + // Output AXI-STream interface to VITA Radio Core + wire [63:0] vita_pre_tdata; + wire [3:0] vita_pre_tuser; + wire vita_pre_tlast; + wire vita_pre_tvalid; + wire vita_pre_tready; + // Output AXI-Stream interface to ZPU + wire [63:0] zpu_pre_tdata; + wire [3:0] zpu_pre_tuser; + wire zpu_pre_tlast; + wire zpu_pre_tvalid; + wire zpu_pre_tready; + // Output AXI-Stream interface to cross-over MAC + wire [63:0] xo_pre_tdata; + wire [3:0] xo_pre_tuser; + wire xo_pre_tlast; + wire xo_pre_tvalid; + wire xo_pre_tready; + + // + // Packet Parse Flags + // + reg is_eth_dst_addr; + reg is_eth_broadcast; + reg is_eth_type_ipv4; + reg is_ipv4_dst_addr; + reg is_ipv4_proto_udp; + reg is_ipv4_proto_icmp; + reg [1:0] is_udp_dst_ports; + reg is_icmp_no_fwd; + reg is_chdr; + + + + + + // + // Settings regs + // + + wire [47:0] my_mac; + + setting_reg #(.my_addr(BASE), .awidth(16), .width(32)) sr_my_mac_lsb + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(my_mac[31:0]),.changed()); + + setting_reg #(.my_addr(BASE+1), .awidth(16), .width(16)) sr_my_mac_msb + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(my_mac[47:32]),.changed()); + + wire [31:0] my_ip; + + setting_reg #(.my_addr(BASE+2), .awidth(16), .width(32)) sr_my_ip + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out(my_ip[31:0]),.changed()); + + wire [15:0] my_port0, my_port1; + + setting_reg #(.my_addr(BASE+3), .awidth(16), .width(32)) sr_udp_port + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({my_port1[15:0],my_port0[15:0]}),.changed()); + + wire forward_ndest, forward_bcast; + setting_reg #(.my_addr(BASE+4), .awidth(16), .width(2)) sr_forward_ctrl + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({forward_ndest, forward_bcast}),.changed()); + + wire [7:0] my_icmp_type, my_icmp_code; + setting_reg #(.my_addr(BASE+5), .awidth(16), .width(16)) sr_icmp_ctrl + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({my_icmp_type, my_icmp_code}),.changed()); + + assign debug = + { + 1'b0, state, //4 + 1'b0, in_tvalid, in_tready, in_tlast, //4 + 1'b0, is_eth_dst_addr, is_eth_broadcast, is_eth_type_ipv4, + is_ipv4_dst_addr, is_ipv4_proto_udp, is_udp_dst_ports, //8 + header_ram_addr[3:0], //4 + 4'b0, 8'b0 + }; + + + // + // Packet Forwarding State machine. + // + + always @(posedge clk) + if (reset || clear) begin + state <= WAIT_PACKET; + header_ram_addr <= 0; + drop_this_packet <= 0; + fwd_input <= 0; + end else begin + // Defaults. + drop_this_packet <= 0; + + case(state) + // + // Wait for start of a packet + // IJB: Add protection for a premature EOF here + // + WAIT_PACKET: begin + if (in_tvalid && in_tready) begin + header_ram[header_ram_addr] <= {in_tlast,in_tuser,in_tdata}; + header_ram_addr <= header_ram_addr + 1; + state <= READ_HEADER; + end + fwd_input <= 0; + end + // + // Continue to read full packet header into RAM. + // + READ_HEADER: begin + if (in_tvalid && in_tready) begin + header_ram[header_ram_addr] <= {in_tlast,in_tuser,in_tdata}; + // Have we reached end of fields we parse in header or got a short packet? + if (header_done || in_tlast) begin + // Make decision about where this packet is forwarded to. + state <= CLASSIFY_PACKET; + end // if (header_done || in_tlast) + else begin + header_ram_addr <= header_ram_addr + 1; + state <= READ_HEADER; + end // else: !if(header_done || in_tlast) + end // if (in_tvalid && in_tready) + end // case: READ_HEADER + + // + // Classify Packet + // + CLASSIFY_PACKET: begin + // Make decision about where this packet is forwarded to. + if (is_eth_type_ipv4 && is_ipv4_proto_icmp && is_icmp_no_fwd) begin + header_ram_addr <= 0; + state <= FORWARD_ZPU; + end else if (is_eth_broadcast) begin + header_ram_addr <= 0; + state <= forward_bcast? FORWARD_ZPU_AND_XO : FORWARD_ZPU; + end else if (!is_eth_dst_addr) begin + header_ram_addr <= 0; + state <= forward_ndest? FORWARD_XO : DROP_PACKET; + end else if ((is_udp_dst_ports != 0) && is_chdr) begin + header_ram_addr <= 6; // Jump to CHDR + state <= FORWARD_RADIO_CORE; + end else if (drop_this_packet) begin + header_ram_addr <= HEADER_RAM_SIZE-1; + state <= DROP_PACKET; + end else begin + header_ram_addr <= 0; + state <= FORWARD_ZPU; + end + end // case: CLASSIFY_PACKET + + // + // Forward this packet only to local ZPU + // + FORWARD_ZPU: begin + if (out_tvalid && out_tready) begin + if (out_tlast) begin + state <= WAIT_PACKET; + end + if (header_done) fwd_input <= 1; + header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1; + end + end + // + // Forward this packet to both local ZPU and XO + // + FORWARD_ZPU_AND_XO: begin + if (out_tvalid && out_tready) begin + if (out_tlast) begin + state <= WAIT_PACKET; + end + if (header_done) fwd_input <= 1; + header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1; + end + end + // + // Forward this packet to XO only + // + FORWARD_XO: begin + if (out_tvalid && out_tready) begin + if (out_tlast) begin + state <= WAIT_PACKET; + end + if (header_done) fwd_input <= 1; + header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1; + end + end + // + // Forward this packet to the Radio Core only + // + FORWARD_RADIO_CORE: begin + if (out_tvalid && out_tready) begin + if (out_tlast) begin + state <= WAIT_PACKET; + end + if (header_done) fwd_input <= 1; + header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1; + end + end + // + // Drop this packet on the ground + // + DROP_PACKET: begin + if (out_tvalid && out_tready) begin + if (out_tlast) begin + state <= WAIT_PACKET; + end + if (header_done) fwd_input <= 1; + header_ram_addr <= out_tlast? 4'b0 : header_ram_addr + 1; + end + end + endcase // case (state) + end // else: !if(reset || clear) + + // + // Classifier State machine. + // Deep packet inspection during header ingress. + // + always @(posedge clk) + if (reset || clear) begin + is_eth_dst_addr <= 1'b0; + is_eth_broadcast <= 1'b0; + is_eth_type_ipv4 <= 1'b0; + is_ipv4_dst_addr <= 1'b0; + is_ipv4_proto_udp <= 1'b0; + is_ipv4_proto_icmp <= 1'b0; + is_udp_dst_ports <= 0; + is_icmp_no_fwd <= 0; + is_chdr <= 1'b0; + + // space_in_fifo <= 0; + // is_there_fifo_space <= 1; + // packet_length <= 0; + end else if (in_tvalid && in_tready) begin // if (reset || clear) + in_tdata_reg <= in_tdata; + + case (header_ram_addr) + // Pipelined, so nothing to look at first cycle. + // Reset all the flags here. + 0: begin + is_eth_dst_addr <= 1'b0; + is_eth_broadcast <= 1'b0; + is_eth_type_ipv4 <= 1'b0; + is_ipv4_dst_addr <= 1'b0; + is_ipv4_proto_udp <= 1'b0; + is_ipv4_proto_icmp <= 1'b0; + is_udp_dst_ports <= 0; + is_icmp_no_fwd <= 0; + is_chdr <= 1'b0; + end + 1: begin + // Look at upper 16bits of MAC Dst Addr. + if (in_tdata_reg[15:0] == 16'hFFFF) + is_eth_broadcast <= 1'b1; + if (in_tdata_reg[15:0] == my_mac[47:32]) + is_eth_dst_addr <= 1'b1; + end + 2: begin + // Look at lower 32bits of MAC Dst Addr. + if (is_eth_broadcast && (in_tdata_reg[63:32] == 32'hFFFFFFFF)) + is_eth_broadcast <= 1'b1; + else + is_eth_broadcast <= 1'b0; + if (is_eth_dst_addr && (in_tdata_reg[63:32] == my_mac[31:0])) + is_eth_dst_addr <= 1'b1; + else + is_eth_dst_addr <= 1'b0; + end // case: 2 + 3: begin + // Look at Ethertype + if (in_tdata_reg[47:32] == 16'h0800) + is_eth_type_ipv4 <= 1'b1; + // Extract Packet Length + // ADD THIS HERE. + end + 4: begin + // Look at protocol enapsulated by IPv4 + if ((in_tdata_reg[23:16] == 8'h11) && is_eth_type_ipv4) + is_ipv4_proto_udp <= 1'b1; + if ((in_tdata_reg[23:16] == 8'h01) && is_eth_type_ipv4) + is_ipv4_proto_icmp <= 1'b1; + end + 5: begin + // Look at IP DST Address. + if ((in_tdata_reg[31:0] == my_ip[31:0]) && is_eth_type_ipv4) + is_ipv4_dst_addr <= 1'b1; + end + 6: begin + // Look at UDP dest port + if ((in_tdata_reg[47:32] == my_port0[15:0]) && is_ipv4_proto_udp) + is_udp_dst_ports[0] <= 1'b1; + if ((in_tdata_reg[47:32] == my_port1[15:0]) && is_ipv4_proto_udp) + is_udp_dst_ports[1] <= 1'b1; + // Look at ICMP type and code + if (in_tdata_reg[63:48] == {my_icmp_type, my_icmp_code} && is_ipv4_proto_icmp) + is_icmp_no_fwd <= 1'b1; + end + 7: begin + // Look for a possible CHDR header string + if (in_tdata_reg[63:32] != 32'h0) + is_chdr <= 1'b1; + end + 8: begin + // Check VRT Stream ID + // ADD THIS HERE. + end + endcase // case (header_ram_addr) + end // if (in_tvalid && in_tready) + + + // + // Output (Egress) Interface muxing + // + assign out_tready = + (state == DROP_PACKET) || + ((state == FORWARD_RADIO_CORE) && vita_pre_tready) || + ((state == FORWARD_XO) && xo_pre_tready) || + ((state == FORWARD_ZPU) && zpu_pre_tready) || + ((state == FORWARD_ZPU_AND_XO) && zpu_pre_tready && xo_pre_tready); + + assign out_tvalid = ((state == FORWARD_RADIO_CORE) || + (state == FORWARD_XO) || + (state == FORWARD_ZPU) || + (state == FORWARD_ZPU_AND_XO) || + (state == DROP_PACKET)) && (!fwd_input || in_tvalid); + + assign {out_tlast,out_tuser,out_tdata} = fwd_input ? {in_tlast,in_tuser,in_tdata} : header_ram[header_ram_addr]; + + assign in_tready = (state == WAIT_PACKET) || + (state == READ_HEADER) || + (state == DROP_PACKET) || + (out_tready && fwd_input); + + + // Because we can forward to both the ZPU and XO FIFO's concurrently + // we have to make sure both can accept data in the same cycle. + // This makes it possible for either destination to block the other. + assign xo_pre_tvalid = out_tvalid && + ((state == FORWARD_XO) || + ((state == FORWARD_ZPU_AND_XO) && zpu_pre_tready)); + assign zpu_pre_tvalid = out_tvalid && + ((state == FORWARD_ZPU) || + ((state == FORWARD_ZPU_AND_XO) && xo_pre_tready)); + assign vita_pre_tvalid = out_tvalid && (state == FORWARD_RADIO_CORE); + + assign {zpu_pre_tuser,zpu_pre_tdata} = ((state == FORWARD_ZPU_AND_XO) || (state == FORWARD_ZPU)) ? + {out_tuser,out_tdata} : 0; + + assign {xo_pre_tuser,xo_pre_tdata} = ((state == FORWARD_ZPU_AND_XO) || (state == FORWARD_XO)) ? + {out_tuser,out_tdata} : 0; + + assign {vita_pre_tuser,vita_pre_tdata} = (state == FORWARD_RADIO_CORE) ? {out_tuser,out_tdata} : 0; + + assign zpu_pre_tlast = out_tlast && ((state == FORWARD_ZPU) || (state == FORWARD_ZPU_AND_XO)); + + assign xo_pre_tlast = out_tlast && ((state == FORWARD_XO) || (state == FORWARD_ZPU_AND_XO)); + + assign vita_pre_tlast = out_tlast && (state == FORWARD_RADIO_CORE); + + // + // Egress FIFO's (Large) + // + axi_fifo #(.WIDTH(69),.SIZE(10)) + axi_fifo_zpu ( + .clk(clk), + .reset(reset), + .clear(clear), + .i_tdata({zpu_pre_tlast,zpu_pre_tuser,zpu_pre_tdata}), + .i_tvalid(zpu_pre_tvalid), + .i_tready(zpu_pre_tready), + .o_tdata({zpu_tlast,zpu_tuser,zpu_tdata}), + .o_tvalid(zpu_tvalid), + .o_tready(zpu_tready), + .space(), + .occupied() + ); + + axi_fifo #(.WIDTH(69),.SIZE(10)) + axi_fifo_xo ( + .clk(clk), + .reset(reset), + .clear(clear), + .i_tdata({xo_pre_tlast,xo_pre_tuser,xo_pre_tdata}), + .i_tvalid(xo_pre_tvalid), + .i_tready(xo_pre_tready), + .o_tdata({xo_tlast,xo_tuser,xo_tdata}), + .o_tvalid(xo_tvalid), + .o_tready(xo_tready), + .space(), + .occupied() + ); + + axi_fifo #(.WIDTH(69),.SIZE(10)) + axi_fifo_vita ( + .clk(clk), + .reset(reset), + .clear(clear), + .i_tdata({vita_pre_tlast,vita_pre_tuser,vita_pre_tdata}), + .i_tvalid(vita_pre_tvalid), + .i_tready(vita_pre_tready), + .o_tdata({vita_tlast,vita_tuser,vita_tdata}), + .o_tvalid(vita_tvalid), + .o_tready(vita_tready), + .space(), + .occupied() + ); + + +/* -----\/----- EXCLUDED -----\/----- + + wire vready, zready, oready; + wire vvalid, zvalid, ovalid; + + reg [2:0] ed_state; + localparam ED_IDLE = 3'd0; + localparam ED_IN_HDR = 3'd1; + localparam ED_VITA = 3'd2; + localparam ED_ZPU = 3'd3; + localparam ED_OUT = 3'd4; + localparam ED_DROP = 3'd5; + -----/\----- EXCLUDED -----/\----- */ + + // for now, send everything to zpu + /* + always @(posedge clk) + if(reset | clear) + ed_state <= ED_IDLE; + else + case(ed_state) + ED_IDLE: + if(vready & zready & oready & in_tvalid) + ; + endcase // case (ed_state) + */ + +/* -----\/----- EXCLUDED -----\/----- + axi_packet_gate #(.WIDTH(64), .SIZE(10)) vita_gate + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata(in_tdata), .i_tlast(), .i_terror(), .i_tvalid(1'b0), .i_tready(vready), + .o_tdata(vita_tdata), .o_tlast(vita_tlast), .o_tvalid(vita_tvalid), .o_tready(vita_tready)); + + axi_packet_gate #(.WIDTH(68), .SIZE(10)) zpu_gate + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({in_tuser,in_tdata}), .i_tlast(in_tlast), .i_terror(in_tuser[3]), .i_tvalid(in_tvalid), .i_tready(in_tready), + .o_tdata({zpu_tuser,zpu_tdata}), .o_tlast(zpu_tlast), .o_tvalid(zpu_tvalid), .o_tready(zpu_tready)); + + axi_packet_gate #(.WIDTH(68), .SIZE(10)) out_gate + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({in_tuser,in_tdata}), .i_tlast(), .i_terror(), .i_tvalid(1'b0), .i_tready(oready), + .o_tdata({out_tuser,out_tdata}), .o_tlast(out_tlast), .o_tvalid(out_tvalid), .o_tready(out_tready)); + -----/\----- EXCLUDED -----/\----- */ + +endmodule // eth_dispatch |