diff options
author | Ben Hilburn <ben.hilburn@ettus.com> | 2013-10-10 10:17:27 -0700 |
---|---|---|
committer | Ben Hilburn <ben.hilburn@ettus.com> | 2013-10-10 10:17:27 -0700 |
commit | 0df4b801a34697f2058b4a7b95e08d2a0576c9db (patch) | |
tree | be10e78d1a97c037a9e7492360a178d1873b9c09 /fpga/usrp3/lib/packet_proc | |
parent | 6e7bc850b66e8188718248b76b729c7cf9c89700 (diff) | |
download | uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.tar.gz uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.tar.bz2 uhd-0df4b801a34697f2058b4a7b95e08d2a0576c9db.zip |
Squashed B200 FPGA Source. Code from Josh Blum, Ian Buckley, and Matt Ettus.
Diffstat (limited to 'fpga/usrp3/lib/packet_proc')
22 files changed, 2883 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/packet_proc/.gitignore b/fpga/usrp3/lib/packet_proc/.gitignore new file mode 100644 index 000000000..ca543057c --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/.gitignore @@ -0,0 +1,3 @@ +vita.txt +xo.txt +zpu.txt diff --git a/fpga/usrp3/lib/packet_proc/Makefile.srcs b/fpga/usrp3/lib/packet_proc/Makefile.srcs new file mode 100644 index 000000000..078609514 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/Makefile.srcs @@ -0,0 +1,21 @@ +# +# Copyright 2013 Ettus Research LLC +# + +################################################## +# Packet Processing Sources +################################################## +PACKET_PROC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/packet_proc/, \ +eth_dispatch.v \ +ip_hdr_checksum.v \ +vrlp_eth_framer.v \ +chdr_eth_framer.v \ +eth_interface.v \ +vrlp_to_compressed_vita.v \ +compressed_vita_to_vrlp.v \ +source_flow_control.v \ +cvita_insert_tlast.v \ +cvita_dest_lookup.v \ +cvita_chunker.v \ +cvita_dechunker.v \ +)) diff --git a/fpga/usrp3/lib/packet_proc/chdr_eth_framer.v b/fpga/usrp3/lib/packet_proc/chdr_eth_framer.v new file mode 100644 index 000000000..ac56a7a32 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/chdr_eth_framer.v @@ -0,0 +1,136 @@ + +// chdr_eth_framer +// Takes a CHDR stream in and adds udp, ip, and ethernet framing +// Uses 8 setting reg addresses. First 4 are simple registers: +// BASE+0 : Upper 16 bits of ethernet src mac +// BASE+1 : Lower 32 bits of ethernet src mac +// BASE+2 : IP src address +// BASE+3 : UDP src port +// +// Next 4 control write ports on a RAM indexed by destination field of stream ID +// BASE+4 : Dest SID for next 3 regs +// BASE+5 : Dest IP +// BASE+6 : Dest UDP port, upper 16 bits of dest mac +// BASE+7 : Lower 32 bits of dest mac +// + +module chdr_eth_framer + #(parameter BASE=0) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready, + output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready, + output [31:0] debug ); + + localparam SR_AWIDTH = 8; + + reg [7:0] sid; + reg [15:0] chdr_len; + + reg [2:0] vef_state; + localparam VEF_IDLE = 3'd0; + localparam VEF_PAYLOAD = 3'd7; + + reg [63:0] tdata; + + always @(posedge clk) + if(reset | clear) + begin + vef_state <= VEF_IDLE; + sid <= 8'd0; + chdr_len <= 16'd0; + end + else + case(vef_state) + VEF_IDLE : + if(in_tvalid) + begin + vef_state <= 1; + sid <= in_tdata[7:0]; + chdr_len <= in_tdata[47:32]; + end + VEF_PAYLOAD : + if(in_tvalid & out_tready) + if(in_tlast) + vef_state <= VEF_IDLE; + default : + if(out_tready) + vef_state <= vef_state + 3'd1; + endcase // case (vef_state) + + assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0; + assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1; + assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0; + assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,chdr_len[2:0]} : 4'b0000; + assign out_tdata = tdata; + + wire [47:0] pad = 48'h0; + wire [47:0] mac_src, mac_dst; + wire [15:0] eth_type = 16'h0800; + wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */}; + wire [15:0] ip_len = (16'd28 + chdr_len); // 20 for IP, 8 for UDP + wire [15:0] ident = 16'h0; + wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 }; + wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ }; + wire [15:0] iphdr_checksum; + wire [31:0] ip_src, ip_dst; + wire [15:0] udp_src, udp_dst; + wire [15:0] udp_len = (16'd8 + chdr_len); + wire [15:0] udp_checksum = 16'h0; + + setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(mac_src[47:32]), .changed()); + + setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(mac_src[31:0]), .changed()); + + setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(ip_src), .changed()); + + setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(udp_src), .changed()); + + // Tables of MAC/IP/UDP addresses + wire [7:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses + + setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(8)) set_ram_addr + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(ram_addr), .changed()); + + ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_ip + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst)); + + ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_udpmac + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]})); + + ram_2port #(.DWIDTH(32), .AWIDTH(8)) ram_maclower + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[7:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0])); + + ip_hdr_checksum ip_hdr_checksum + (.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}), + .out(iphdr_checksum)); + + always @* + case(vef_state) + 1 : tdata <= { pad[47:0], mac_dst[47:32]}; + 2 : tdata <= { mac_dst[31:0], mac_src[47:16]}; + 3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] }; + 4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]}; + 5 : tdata <= { ip_src, ip_dst}; + 6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum}; + default : tdata <= in_tdata; + endcase // case (vef_state) + +endmodule // chdr_eth_framer diff --git a/fpga/usrp3/lib/packet_proc/compressed_vita_to_vrlp.v b/fpga/usrp3/lib/packet_proc/compressed_vita_to_vrlp.v new file mode 100644 index 000000000..11595f200 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/compressed_vita_to_vrlp.v @@ -0,0 +1,78 @@ + +module compressed_vita_to_vrlp + (input clk, input reset, input clear, + input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, + output [63:0] o_tdata, output [15:0] o_tuser, output o_tlast, output o_tvalid, input o_tready + ); + + wire [19:0] vrlp_size = 20'd3 + {4'b0000,i_tdata[47:32]}; + reg odd_len; + reg [2:0] cv2v_state; + + reg [63:0] o_tdata_int; + wire o_tlast_int, o_tvalid_int, o_tready_int; + + localparam CV2V_VRLP = 3'd0; // VRLP header + localparam CV2V_VRT_ECH = 3'd1; // Extension context header + localparam CV2V_VRT_IFH = 3'd2; // IF Data header + localparam CV2V_BODY = 3'd3; + localparam CV2V_VEND_ODD = 3'd4; + localparam CV2V_VEND_EVEN = 3'd4; + + always @(posedge clk) + if(reset | clear) + begin + cv2v_state <= CV2V_VRLP; + odd_len <= 1'b0; + end + else + case(cv2v_state) + CV2V_VRLP : + if(i_tvalid & o_tready_int) + begin + odd_len <= i_tdata[32]; + if(i_tdata[63]) + cv2v_state <= CV2V_VRT_ECH; + else + cv2v_state <= CV2V_VRT_IFH; + end + + CV2V_VRT_ECH, CV2V_VRT_IFH : + if(i_tvalid & o_tready_int) + cv2v_state <= CV2V_BODY; + + CV2V_BODY : + if(i_tlast & i_tvalid & o_tready_int) + if(odd_len) + cv2v_state <= CV2V_VRLP; + else + cv2v_state <= CV2V_VEND_EVEN; + + CV2V_VEND_EVEN : + if(o_tready_int) + cv2v_state <= CV2V_VRLP; + endcase // case (cv2v_state) + + assign i_tready = o_tready_int & (cv2v_state != CV2V_VRLP) & (cv2v_state != CV2V_VEND_EVEN); + assign o_tvalid_int = i_tvalid | (cv2v_state == CV2V_VEND_EVEN); + + always @* + case(cv2v_state) + CV2V_VRLP : o_tdata_int <= { 32'h5652_4c50 /*VRLP*/, i_tdata[59:48] /*seqnum*/, vrlp_size[19:0] }; + CV2V_VRT_ECH : o_tdata_int <= { 4'h5 /*type*/, 4'h0, 3'b00, i_tdata[61] /*time*/, i_tdata[51:48] /*seqnum*/, i_tdata[47:32] /*len*/, i_tdata[31:0] /*sid*/ }; + CV2V_VRT_IFH : o_tdata_int <= { 4'h1 /*type*/, 1'b0, i_tdata[62] /*TRL*/, 1'b0, i_tdata[60] /*eob*/, 3'b00, i_tdata[61] /*time*/, i_tdata[51:48] /*seqnum*/, i_tdata[47:32] /*len*/, i_tdata[31:0] /*sid*/ }; + CV2V_BODY : o_tdata_int <= (i_tlast & odd_len) ? { i_tdata[63:32], 32'h5645_4E44 /*VEND*/ } : i_tdata; + CV2V_VEND_EVEN : o_tdata_int <= { 32'h5645_4E44 /*VEND*/, 32'h0}; + default : o_tdata_int <= i_tdata; + endcase // case (cv2v_state) + + assign o_tlast_int = (cv2v_state == CV2V_VEND_EVEN) | ((cv2v_state == CV2V_BODY) & i_tlast & odd_len); + + // Short FIFO before output + axi_fifo_short #(.WIDTH(81)) axi_fifo_short + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({o_tlast_int, i_tdata[15:0], o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int), + .o_tdata({o_tlast, o_tuser, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready), + .space(), .occupied()); + +endmodule // compressed_vita_to_vrlp diff --git a/fpga/usrp3/lib/packet_proc/cvita_chunker.v b/fpga/usrp3/lib/packet_proc/cvita_chunker.v new file mode 100644 index 000000000..cbc34d00a --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_chunker.v @@ -0,0 +1,91 @@ +// +// Copyright 2013 Ettus Research LLC +// + +// Quantize cvita packets to a configurable quantum value. o_tlast and +// i_tready will be held off until the entire quantized packet is xferred. +// If quantum is changed, it is the responsibility of the client to clear +// this module. error is asserted if a packet is larger than the quantum +// error can be reset by asserting reset or clear. + +module cvita_chunker # ( + parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF +) ( + input clk, + input reset, + input clear, + input [15:0] frame_size, + + input [63:0] i_tdata, + input i_tlast, + input i_tvalid, + output i_tready, + + output [63:0] o_tdata, + output o_tlast, + output o_tvalid, + input o_tready, + + output error +); + + localparam ST_HEADER = 2'd0; + localparam ST_DATA = 2'd1; + localparam ST_PADDING = 2'd2; + localparam ST_ERROR = 2'd3; + + reg [1:0] state; + reg [15:0] frame_rem; + + wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7; + wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]}; + + always @(posedge clk) begin + if (reset | clear) begin + state <= ST_HEADER; + frame_rem <= 16'd0; + end else if (o_tvalid & o_tready) begin + case (state) + ST_HEADER: begin + if (axi_len > frame_size) + state <= ST_ERROR; + else if (i_tlast) + state <= ST_PADDING; + else + state <= ST_DATA; + + frame_rem <= frame_size - 16'd1; + end + + ST_DATA: begin + if (i_tlast) begin + state <= o_tlast ? ST_HEADER : ST_PADDING; + frame_rem <= o_tlast ? 16'd0 : (frame_rem - 16'd1); + end else begin + state <= ST_DATA; + frame_rem <= frame_rem - 16'd1; + end + end + + ST_PADDING: begin + if (o_tlast) begin + state <= ST_HEADER; + frame_rem <= 16'd0; + end else begin + state <= ST_PADDING; + frame_rem <= frame_rem - 16'd1; + end + end + endcase + end + end + + assign i_tready = o_tready & (state != ST_PADDING); + + assign o_tvalid = i_tvalid | (state == ST_PADDING); + assign o_tlast = (frame_rem != 0) ? (frame_rem == 16'd1) : (axi_len == 16'd1); + assign o_tdata = (state == ST_PADDING) ? PAD_VALUE : i_tdata; + + assign error = (state == ST_ERROR); + +endmodule // cvita_chunker diff --git a/fpga/usrp3/lib/packet_proc/cvita_chunker_tb.v b/fpga/usrp3/lib/packet_proc/cvita_chunker_tb.v new file mode 100644 index 000000000..08f46d72a --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_chunker_tb.v @@ -0,0 +1,187 @@ +// +// Copyright 2013 Ettus Research LLC +// + + +`timescale 500ps/1ps + +module cvita_chunker_tb(); + + // TB stimulus + reg clk = 0; + reg reset = 1; + reg clear = 0; + reg [15:0] quantum; + + // Check vars + reg [31:0] o_xfer_count = 0, i_xfer_count = 0; + reg [63:0] o_last_tdata = 0; + + + always #10 clk = ~clk; + + initial $dumpfile("cvita_chunker_tb.vcd"); + initial $dumpvars(0,cvita_chunker_tb); + + function check_result; + input [31:0] o_xfer_count_arg; + input [31:0] i_xfer_count_arg; + input [63:0] o_last_tdata_arg; + input error_arg; + begin + //Check vars + check_result = 1; + check_result = check_result & ((o_xfer_count_arg == o_xfer_count) !== 0); + check_result = check_result & ((i_xfer_count_arg == i_xfer_count) !== 0); + check_result = check_result & ((o_last_tdata_arg == o_last_tdata) !== 0); + check_result = check_result & ((error_arg == error) != 0); + + if (check_result) begin + $display ("... Passed"); + end else begin + $display ("... FAILED!!!"); + $display ("o_xfer_count = %d (Expected %d)",o_xfer_count,o_xfer_count_arg); + $display ("i_xfer_count = %d (Expected %d)",i_xfer_count,i_xfer_count_arg); + $display ("o_last_tdata = %h (Expected %h)",o_last_tdata,o_last_tdata_arg); + $display ("error = %d (Expected %d)",error,error_arg); + end + + //Reset vars + o_xfer_count = 0; + i_xfer_count = 0; + o_last_tdata = 64'h0; + end + endfunction + + task send_packet; + input [63:0] data_start; + input [31:0] len; + + begin + if(len < 9) begin + {i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] }; + i_tvalid <= 1; + @(posedge clk); + i_tvalid <= 0; + end else begin + {i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] }; + i_tvalid <= 1; + @(posedge clk); + repeat(((len-1)/8)-1) begin + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + @(posedge clk); + end + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + i_tlast <= 1; + @(posedge clk); + i_tvalid <= 0; + end // else: !if(len < 3) + end + endtask // send_packet + + task reset_quantum_atomic; + input [15:0] quant; + begin + quantum <= quant; + clear <= 1; + @(posedge clk); + clear <= 0; + @(posedge clk); + end + endtask // reset_quantum_atomic + + + initial begin + #100 reset = 0; + #200000; + $finish; + end + + reg [63:0] i_tdata; + reg i_tlast; + reg i_tvalid; + wire i_tready; + + wire [63:0] o_tdata; + wire o_tlast, o_tvalid, o_tready; + wire error; + + initial begin + quantum <= 256; + i_tvalid <= 0; + while(reset) @(posedge clk); + + $write ("Running test case: First packet after reset"); + send_packet(64'h00000001_00000000, 128); + while(o_tvalid) @(posedge clk); + check_result(256,16,64'hFFFFFFFF_FFFFFFFF,0); + + reset_quantum_atomic(8); + + $write ("Running test case: sizeof(packet) < quantum"); + send_packet(64'h00000001_00000000, 40); + while(o_tvalid) @(posedge clk); + check_result(8,5,64'hFFFFFFFF_FFFFFFFF,0); + + reset_quantum_atomic(5); + + $write ("Running test case: sizeof(packet) == quantum"); + send_packet(64'h00000001_00000000, 40); + while(o_tvalid) @(posedge clk); + check_result(5,5,64'h00000030_00000008,0); + + $write ("Running test case: sizeof(packet) == quantum - 64bits"); + send_packet(64'h00000001_00000000, 32); + while(o_tvalid) @(posedge clk); + check_result(5,4,64'hFFFFFFFF_FFFFFFFF,0); + + $write ("Running test case: sizeof(packet) == quantum + 64bits"); + send_packet(64'h00000001_00000000, 48); + while(o_tvalid) @(posedge clk); + check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,1); + + $write ("Running test case: Error reset"); + reset_quantum_atomic(8); + check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,0); + + $write ("Running test case: sizeof(packet) > quantum"); + send_packet(64'h00000001_00000000, 80); + while(o_tvalid) @(posedge clk); + check_result(32'hxxxxxxxx,32'hxxxxxxxx,64'hxxxxxxxx_xxxxxxxx,1); + + reset_quantum_atomic(8); + + $write ("Running test case: sizeof(packet) == 2"); + send_packet(64'h00000001_00000000, 8); + while(o_tvalid) @(posedge clk); + check_result(8,1,64'hFFFFFFFF_FFFFFFFF,0); + + $write ("Running test case: Multiple packets back-to-back"); + send_packet(64'h00000001_00000000, 40); + while(o_tvalid) @(posedge clk); + send_packet(64'h00000001_00000000, 16); + while(o_tvalid) @(posedge clk); + send_packet(64'h00000001_00000000, 64); + while(o_tvalid) @(posedge clk); + check_result(24,15,64'h0000004e0000000e,0); + + end // initial begin + + + cvita_chunker dut ( + .clk(clk), .reset(reset), .clear(clear), .frame_size(quantum), + .i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready), + .error(error)); + + assign o_tready = 1; + + always @(posedge clk) begin + if (o_tvalid & o_tready) begin + o_xfer_count <= o_xfer_count + 1; + o_last_tdata <= o_tdata; + end + if (i_tvalid & i_tready) i_xfer_count <= i_xfer_count + 1; + end + +endmodule // cvita_chunker_tb diff --git a/fpga/usrp3/lib/packet_proc/cvita_dechunker.v b/fpga/usrp3/lib/packet_proc/cvita_dechunker.v new file mode 100644 index 000000000..2ad873305 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_dechunker.v @@ -0,0 +1,90 @@ +// +// Copyright 2013 Ettus Research LLC +// + + +module cvita_dechunker # ( + parameter PAD_VALUE = 64'hFFFFFFFF_FFFFFFFF +) ( + input clk, + input reset, + input clear, + input [15:0] frame_size, + + input [63:0] i_tdata, + input i_tvalid, + output i_tready, + + output [63:0] o_tdata, + output o_tlast, + output o_tvalid, + input o_tready, + + output error +); + + localparam ST_HEADER = 2'd0; + localparam ST_DATA = 2'd1; + localparam ST_PADDING = 2'd2; + localparam ST_ERROR = 2'd3; + + reg [1:0] state; + reg [15:0] frame_rem, pkt_rem; + wire i_tlast; + + wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7; + wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]}; + + always @(posedge clk) begin + if (reset | clear) begin + state <= ST_HEADER; + frame_rem <= 16'd0; + pkt_rem <= 16'd0; + end else if (i_tvalid & i_tready) begin + case (state) + ST_HEADER: begin + if (axi_len > frame_size) + state <= ST_ERROR; + else if (~o_tlast) + state <= ST_DATA; + else + state <= ST_PADDING; + + frame_rem <= frame_size - 16'd1; + pkt_rem <= axi_len - 16'd1; + end + + ST_DATA: begin + if (o_tlast) begin + state <= i_tlast ? ST_HEADER : ST_PADDING; + pkt_rem <= 16'd0; + end else begin + state <= ST_DATA; + pkt_rem <= pkt_rem - 16'd1; + end + frame_rem <= frame_rem - 16'd1; + end + + ST_PADDING: begin + if (i_tlast) begin + state <= ST_HEADER; + frame_rem <= 16'd0; + end else begin + state <= ST_PADDING; + frame_rem <= frame_rem - 16'd1; + end + end + endcase + end + end + + assign i_tready = o_tready | (state == ST_PADDING); + assign i_tlast = (frame_rem == 16'd1); //Temp signal + + assign o_tvalid = i_tvalid & (state != ST_PADDING); + assign o_tlast = (pkt_rem != 0) ? (pkt_rem == 16'd1) : (axi_len == 16'd1); + assign o_tdata = i_tdata; + + assign error = (state == ST_ERROR); + +endmodule // cvita_dechunker diff --git a/fpga/usrp3/lib/packet_proc/cvita_dechunker_tb.v b/fpga/usrp3/lib/packet_proc/cvita_dechunker_tb.v new file mode 100644 index 000000000..198eb4c40 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_dechunker_tb.v @@ -0,0 +1,183 @@ +// +// Copyright 2013 Ettus Research LLC +// + + +`timescale 500ps/1ps + +module cvita_dechunker_tb(); + + // TB stimulus + reg clk = 0; + reg reset = 1; + reg clear = 0; + reg [15:0] quantum; + wire error; + + // Check vars + reg [31:0] o_xfer_count = 0, i_xfer_count = 0; + reg [63:0] o_last_tdata = 0; + + + always #10 clk = ~clk; + + initial $dumpfile("cvita_dechunker_tb.vcd"); + initial $dumpvars(0,cvita_dechunker_tb); + + function check_result; + input [31:0] o_xfer_count_arg; + input [31:0] i_xfer_count_arg; + input [63:0] o_last_tdata_arg; + input error_arg; + begin + //Check vars + check_result = 1; + check_result = check_result & ((o_xfer_count_arg == o_xfer_count) !== 0); + check_result = check_result & ((i_xfer_count_arg == i_xfer_count) !== 0); + check_result = check_result & ((o_last_tdata_arg == o_last_tdata) !== 0); + check_result = check_result & ((error_arg == error) != 0); + + if (check_result) begin + $display ("... Passed"); + end else begin + $display ("... FAILED!!!"); + $display ("o_xfer_count = %d (Expected %d)",o_xfer_count,o_xfer_count_arg); + $display ("i_xfer_count = %d (Expected %d)",i_xfer_count,i_xfer_count_arg); + $display ("o_last_tdata = %h (Expected %h)",o_last_tdata,o_last_tdata_arg); + $display ("error = %d (Expected %d)",error,error_arg); + end + + //Reset vars + o_xfer_count = 0; + i_xfer_count = 0; + o_last_tdata = 64'h0; + end + endfunction + + task send_packet; + input [63:0] data_start; + input [31:0] len; + input [31:0] quant; + + begin + if(quant < 2) begin + {i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] }; + i_tvalid <= 1; + @(posedge clk); + i_tvalid <= 0; + end else begin + {i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] }; + i_tvalid <= 1; + @(posedge clk); + repeat(quant - 2) begin + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + @(posedge clk); + end + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + i_tlast <= 1; + @(posedge clk); + i_tvalid <= 1'b0; + end // else: !if(len < 3) + end + endtask // send_packet + + task reset_quantum_atomic; + input [15:0] quant; + begin + quantum <= quant; + clear <= 1; + @(posedge clk); + clear <= 0; + @(posedge clk); + end + endtask // reset_quantum_atomic + + + initial begin + #100 reset = 0; + #200000; + $finish; + end + + reg [63:0] i_tdata; + reg i_tlast; + reg i_tvalid; + wire i_tready; + + wire [63:0] o_tdata; + wire o_tlast, o_tvalid, o_tready; + + initial begin + quantum <= 8; + i_tvalid <= 0; + while(reset) @(posedge clk); + + $write ("Running test case: First packet after reset"); + send_packet(64'h00000001_00000000, 32, 8); + @(posedge clk); + check_result(4,8,64'hxxxxxxxx_xxxxxx06, 0); + + reset_quantum_atomic(10); + + $write ("Running test case: sizeof(packet) < quantum"); + send_packet(64'h00000001_00000000, 64, 10); + @(posedge clk); + check_result(8,10,64'hxxxxxxxx_xxxxxx0e, 0); + + $write ("Running test case: sizeof(packet) == quantum"); + send_packet(64'h00000001_00000000, 80, 10); + @(posedge clk); + check_result(10,10,64'hxxxxxxxx_xxxxxx12, 0); + + $write ("Running test case: sizeof(packet) == quantum - 64bits"); + send_packet(64'h00000001_00000000, 72, 10); + @(posedge clk); + check_result(9,10,64'hxxxxxxxx_xxxxxx10, 0); + + $write ("Running test case: sizeof(packet) == quantum + 64bits"); + send_packet(64'h00000001_00000000, 88, 10); + @(posedge clk); + check_result(32'hxxxxxxxx,10,64'hxxxxxxxx_xxxxxxxx, 1); + + reset_quantum_atomic(10); + + $write ("Running test case: sizeof(packet) > quantum"); + send_packet(64'h00000001_00000000, 88, 10); + @(posedge clk); + check_result(32'hxxxxxxxx,10,64'hxxxxxxxx_xxxxxxxx, 1); + + reset_quantum_atomic(8); + + $write ("Running test case: sizeof(packet) == 2"); + send_packet(64'h00000001_00000000, 8, 8); + @(posedge clk); + check_result(1,8,64'hxxxxxxxx_xxxxxx00, 0); + + $write ("Running test case: Multiple packets"); + send_packet(64'h00000001_00000000, 8, 8); + send_packet(64'h00000001_00000000, 16, 8); + send_packet(64'h00000001_00000000, 24, 8); + send_packet(64'h00000001_00000000, 32, 8); + @(posedge clk); + check_result(10,32,64'hxxxxxxxx_xxxxxx06, 0); + + end // initial begin + + + cvita_dechunker dut ( + .clk(clk), .reset(reset), .clear(clear), .frame_size(quantum), + .i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready), + .error(error)); + + assign o_tready = 1; + + always @(posedge clk) begin + if (o_tvalid & o_tready) begin + o_xfer_count <= o_xfer_count + 1; + o_last_tdata <= o_tdata; + end + if (i_tvalid & i_tready) i_xfer_count <= i_xfer_count + 1; + end + +endmodule // cvita_dechunker_tb diff --git a/fpga/usrp3/lib/packet_proc/cvita_dest_lookup.v b/fpga/usrp3/lib/packet_proc/cvita_dest_lookup.v new file mode 100644 index 000000000..5951b127a --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_dest_lookup.v @@ -0,0 +1,48 @@ + +// Map the endpoint dest part of the SID in the CVITA header to a destination +// This destination (o_tdest) signal will be valid with o_tdata +// This only works with VALID CVITA frames + +module cvita_dest_lookup +#( + parameter DEST_WIDTH = 4 +) +( + input clk, input rst, + input set_stb, input [7:0] set_addr, input [DEST_WIDTH-1:0] set_data, + input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, + output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready, + output [DEST_WIDTH-1:0] o_tdest +); + + reg [7:0] endpoint; + ram_2port #(.DWIDTH(DEST_WIDTH), .AWIDTH(8)) dest_lut + ( + .clka(clk), .ena(1'b1), .wea(set_stb), .addra(set_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(endpoint), .dib(8'hff), .dob(o_tdest) + ); + + reg forward; + reg [1:0] count; + always @(posedge clk) begin + if (rst) begin + forward <= 1'b0; + count <= 2'b0; + end + else if (forward == 1'b0 && i_tvalid) begin + if (count == 2'b11) forward <= 1'b1; + endpoint <= i_tdata[23:16]; + count <= count + 1'b1; + end + else if (forward == 1'b1 && i_tvalid && i_tready && i_tlast) begin + forward <= 1'b0; + count <= 2'b0; + end + end + + assign o_tdata = i_tdata; + assign o_tlast = i_tlast; + assign o_tvalid = i_tvalid && forward; + assign i_tready = o_tready && forward; + +endmodule // cvita_dest_lookup diff --git a/fpga/usrp3/lib/packet_proc/cvita_insert_tlast.v b/fpga/usrp3/lib/packet_proc/cvita_insert_tlast.v new file mode 100644 index 000000000..8d5c4f981 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_insert_tlast.v @@ -0,0 +1,33 @@ + +// Insert tlast bit for fifos that don't support it. This only works with VALID CVITA frames +// A single partial or invalid frame will make this wrong FOREVER + +module cvita_insert_tlast + (input clk, input reset, input clear, + input [63:0] i_tdata, input i_tvalid, output i_tready, + output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready); + + assign o_tdata = i_tdata; + assign o_tvalid = i_tvalid; + assign i_tready = o_tready; + + wire [15:0] cvita_len_ceil = i_tdata[47:32] + 7; + wire [15:0] axi_len = {3'b000, cvita_len_ceil[15:3]}; + + reg [15:0] count; + + assign o_tlast = (count != 0) ? (count == 16'd1) : (axi_len == 16'd1); + + always @(posedge clk) + if(reset | clear) + begin + count <= 16'd0; + end + else + if(i_tready & i_tvalid) + if(count != 16'd0) + count <= count - 16'd1; + else + count <= axi_len - 16'd1; + +endmodule // cvita_insert_tlast diff --git a/fpga/usrp3/lib/packet_proc/cvita_insert_tlast_tb.v b/fpga/usrp3/lib/packet_proc/cvita_insert_tlast_tb.v new file mode 100644 index 000000000..6398c40a4 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/cvita_insert_tlast_tb.v @@ -0,0 +1,92 @@ +`timescale 1ns/1ps + +module cvita_insert_tlast_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("cvita_insert_tlast_tb.vcd"); + initial $dumpvars(0,cvita_insert_tlast_tb); + + task send_packet; + input [63:0] data_start; + input [31:0] len; + + begin + if(len < 9) + begin + {i_tlast, i_tdata} <= { 1'b1, data_start[63:48],len[15:0], data_start[31:0] }; + i_tvalid <= 1; + @(posedge clk); + i_tvalid <= 0; + end + else + begin + {i_tlast, i_tdata} <= { 1'b0, data_start[63:48],len[15:0], data_start[31:0] }; + i_tvalid <= 1; + @(posedge clk); + repeat(((len-2)/2)-1+len[0]) + begin + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + @(posedge clk); + end + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + i_tlast <= 1; + @(posedge clk); + i_tvalid <= 1'b0; + end // else: !if(len < 3) + end + endtask // send_packet + + initial + begin + #1000 reset = 0; + #200000; + $finish; + end + + reg [63:0] i_tdata; + reg i_tlast; + reg i_tvalid; + wire i_tready; + + wire [63:0] o_tdata; + wire o_tlast, o_tvalid, o_tready, o_tlast_regen; + + initial + begin + i_tvalid <= 0; + + while(reset) + @(posedge clk); + @(posedge clk); + + send_packet(64'hA0000000_A0000001, 24); + send_packet(64'hA0000000_A0000001, 20); + send_packet(64'hA0000000_A0000001, 16); + send_packet(64'hA0000000_A0000001, 12); + send_packet(64'hA0000000_A0000001, 8); + send_packet(64'hA0000000_A0000001, 4); + send_packet(64'hA0000000_A0000001, 4); + send_packet(64'hA0000000_A0000001, 8); + send_packet(64'hA0000000_A0000001, 12); + end // initial begin + + cvita_insert_tlast dut + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata(o_tdata), .o_tlast(o_tlast_regen), .o_tvalid(o_tvalid), .o_tready(o_tready)); + + assign o_tready = 1; + + + always @(posedge clk) + if(o_tvalid & o_tready) + begin + $display ("TLAST %x\t TLAST_REGEN %x",i_tlast, o_tlast_regen); + if(i_tlast != o_tlast_regen) + $display("ERROR!!!!!!"); + end +endmodule // cvita_insert_tlast_tb 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 diff --git a/fpga/usrp3/lib/packet_proc/eth_dispatch_tb.v b/fpga/usrp3/lib/packet_proc/eth_dispatch_tb.v new file mode 100644 index 000000000..ecc7d1025 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/eth_dispatch_tb.v @@ -0,0 +1,311 @@ +// +// Copyright 2012 Ettus Research LLC +// + +`timescale 1 ps / 1 ps + +module eth_dispatch_tb(); + + + // Clocking and reset interface + reg clk; + reg reset; + reg clear; + // Setting register interface + reg set_stb; + reg [15:0] set_addr; + reg [31:0] set_data; + // Input 68bit AXI-Stream interface (from MAC) + wire [63:0] in_tdata; + wire [3:0] in_tuser; + wire in_tlast; + wire in_tvalid; + wire in_tready; + // Output AXI-Stream interface to VITA Radio Core + wire [63:0] vita_tdata; + wire [3:0] vita_tuser; + wire vita_tlast; + wire vita_tvalid; + wire vita_tready; + // Output AXI-Stream interface to ZPU + wire [63:0] zpu_tdata; + wire [3:0] zpu_tuser; + wire zpu_tlast; + wire zpu_tvalid; + wire zpu_tready; + // Output AXI-Stream interface to cross-over MAC + wire [63:0] xo_tdata; + wire [3:0] xo_tuser; + wire xo_tlast; + wire xo_tvalid; + wire xo_tready; + + reg [63:0] data_in; + reg [3:0] user_in; + reg valid_in; + wire ready_in; + reg last_in; + + eth_dispatch + #(.BASE(0)) + eth_dispatch_i + ( + // Clocking and reset interface + .clk(clk), + .reset(reset), + .clear(clear), + // Setting register interface + .set_stb(set_stb), + .set_addr(set_addr), + .set_data(set_data), + // Input 68bit AXI-Stream interface (from MAC) + .in_tdata(in_tdata), + .in_tuser(in_tuser), + .in_tlast(in_tlast), + .in_tvalid(in_tvalid), + .in_tready(in_tready), + // Output AXI-STream interface to VITA Radio Core + .vita_tdata(vita_tdata), + .vita_tuser(vita_tuser), + .vita_tlast(vita_tlast), + .vita_tvalid(vita_tvalid), + .vita_tready(vita_tready), + // Output AXI-Stream interface to ZPU + .zpu_tdata(zpu_tdata), + .zpu_tuser(zpu_tuser), + .zpu_tlast(zpu_tlast), + .zpu_tvalid(zpu_tvalid), + .zpu_tready(zpu_tready), + // Output AXI-Stream interface to cross-over MAC + .xo_tdata(xo_tdata), + .xo_tuser(xo_tuser), + .xo_tlast(xo_tlast), + .xo_tvalid(xo_tvalid), + .xo_tready(xo_tready) + ); + + // + // Define Clocks + // + initial begin + clk = 1'b1; + end + + // 125MHz clock + always #4000 clk = ~clk; + + // + // Good starting state + // + initial begin + reset <= 0; + clear <= 0; + set_stb <= 0; + set_addr <= 0; + set_data <= 0; + data_in <= 0; + user_in <= 0; + valid_in <= 0; + last_in <= 0; + + end + + + + // + // Task Libaray + // + task write_setting_bus; + input [15:0] address; + input [31:0] data; + + begin + + @(negedge clk); + set_stb = 1'b0; + set_addr = 16'h0; + set_data = 32'h0; + @(negedge clk); + set_stb = 1'b1; + set_addr = address; + set_data = data; + @(negedge clk); + set_stb = 1'b0; + set_addr = 16'h0; + set_data = 32'h0; + + end + endtask // write_setting_bus + + + task enqueue_line; + input last; + input [2:0] keep; + input [63:0] data; + begin + data_in <= {keep, data}; + last_in <= last; + valid_in <= 1; + while (~ready_in) begin + @(negedge clk); + end + @(negedge clk); + data_in <= 0; + last_in <= 0; + valid_in <= 0; + end + endtask // enqueue_line + + task enqueue_arp_req; + input [47:0] src_mac; + input [31:0] src_ip; + input [47:0] dst_mac; + input [31:0] dst_ip; + + begin + @(negedge clk); + // Line 0 + enqueue_line( 0, 3'b0, {48'h0,16'hffff}); + // Line 1 - Eth + enqueue_line( 0, 3'b0, {32'hffffffff,src_mac[47:16]}); + // Line 2 - Eth+ARP (HTYPE = 1, PTYPE = 0x0800) + enqueue_line( 0, 3'b0, {src_mac[15:0],16'h0806,16'h0001,16'h0800}); + // Line 3 - HLEN=6, PLEN=4 OPER=1 + enqueue_line( 0, 3'b0, {8'h06,8'h04,16'h0001,src_mac[47:16]}); + // Line 4 - ARP + enqueue_line( 0, 3'b0, {src_mac[15:0],src_ip[31:0],dst_mac[47:32]}); + // Line 5 - ARP + enqueue_line( 1, 3'b0, {dst_mac[31:0],dst_ip[31:0]}); + end + endtask // enqueue_arp_req + + + + reg [11:0] frame_count =12'h0; + + task enqueue_vita_pkt; + // We assume that we always have SID and TSF fields. + input [47:0] mac; + input [31:0] ip; + input [15:0] udp; + input [15:0] vita_size; + input [63:0] vita_tsf; + input [31:0] vita_sid; + + + integer i; + reg [15:0] j; + reg [19:0] vrl_size; + reg [15:0] udp_size; + reg [15:0] ip_size; + + + begin + vrl_size = vita_size + 3; + udp_size = vrl_size*4 + 8; + ip_size = udp_size + 20; + @(negedge clk); + // Line 0 + enqueue_line( 0, 3'b0, {48'h0,mac[47:32]}); + // Line 1 - Eth + enqueue_line( 0, 3'b0, {mac[31:0],32'h11223344}); + // Line 2 - Eth+IP + enqueue_line( 0, 3'b0, {16'h5566,16'h0800,16'h0000,ip_size}); + // Line 3 - IP + enqueue_line( 0, 3'b0, 'h11<<16); + // Line 4 - IP + enqueue_line( 0, 3'b0, {32'h09080706, ip}); + // Line 5 - UDP + enqueue_line( 0, 3'b0, {16'h1234, udp, udp_size, 16'h0}); + // Line 6 - VRL + enqueue_line( 0, 3'b0, {"VRLP",frame_count,vrl_size}); + // Line 7 - VRT + enqueue_line( 0, 3'b0, {16'b0001000000010000, vita_size,vita_sid}); //vita hdr + SID + enqueue_line( 0, 3'b0, vita_tsf); + j = 0; + + for (i = 6; i < vita_size; i = i + 2) begin + enqueue_line( 0 , 3'b0, {j,j+16'h1,j+16'h2,j+16'h3}); + j = j + 4; + end + + if (i-vita_size==0) // 2x32words to finish VITA packet. + enqueue_line( 1, 3'b0, {j,j+16'h1,j+16'h2,j+16'h3}); + else // 1x32bit word to finish VITA packet + enqueue_line( 1, 3'h4, {j,j+16'h1,j+16'h2,j+16'h3}); + end + + endtask // enqueue_packet + + + // + // Simulation specific testbench is included here + // +`include "simulation_script.v" + + + // + // Input FIFO + // + axi_fifo_short + #(.WIDTH(69)) axi_fifo_short_in + ( + .clk(clk), + .reset(reset), + .clear(clear), + .o_tdata({in_tlast,in_tuser,in_tdata}), + .o_tvalid(in_tvalid), + .o_tready(in_tready), + .i_tdata({last_in,user_in,data_in}), + .i_tvalid(valid_in), + .i_tready(ready_in), + .space(), + .occupied() + ); + + // + // Output Sinks + // + axi_probe_tb + #(.FILENAME("zpu.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001)) axi_probe_tb_zpu + ( + .clk(clk), + .reset(reset), + .clear(clear), + .tdata(zpu_tdata), + .tvalid(zpu_tvalid), + .tready(zpu_tready), + .tlast(zpu_tlast) + ); + + assign zpu_tready = 1'b1; + + axi_probe_tb + #(.FILENAME("xo.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001)) axi_probe_tb_xo + ( + .clk(clk), + .reset(reset), + .clear(clear), + .tdata(xo_tdata), + .tvalid(xo_tvalid), + .tready(xo_tready), + .tlast(xo_tlast) + ); + + assign xo_tready = 1'b1; + + axi_probe_tb + #(.FILENAME("vita.txt"),.VITA_PORT0(60000),.VITA_PORT1(60001),.START_AT_VRL(1)) axi_probe_tb_vita + ( + .clk(clk), + .reset(reset), + .clear(clear), + .tdata(vita_tdata), + .tvalid(vita_tvalid), + .tready(vita_tready), + .tlast(vita_tlast) + ); + + assign vita_tready = 1'b1; + +endmodule // eth_dispatch_tb diff --git a/fpga/usrp3/lib/packet_proc/eth_interface.v b/fpga/usrp3/lib/packet_proc/eth_interface.v new file mode 100644 index 000000000..083badfea --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/eth_interface.v @@ -0,0 +1,113 @@ + +// Adapts from internal VITA to ethernet packets. Also handles ZPU and ethernet crossover interfaces. + +module eth_interface + #(parameter BASE=0, + parameter XO_FIFOSIZE=10, + parameter ZPU_FIFOSIZE=10, + parameter VITA_FIFOSIZE=10, + parameter ETHOUT_FIFOSIZE=10) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + // Eth ports + output [63:0] eth_tx_tdata, output [3:0] eth_tx_tuser, output eth_tx_tlast, output eth_tx_tvalid, input eth_tx_tready, + input [63:0] eth_rx_tdata, input [3:0] eth_rx_tuser, input eth_rx_tlast, input eth_rx_tvalid, output eth_rx_tready, + // Vita router interface + output [63:0] e2v_tdata, output e2v_tlast, output e2v_tvalid, input e2v_tready, + input [63:0] v2e_tdata, input v2e_tlast, input v2e_tvalid, output v2e_tready, + // Ethernet crossover + output [63:0] xo_tdata, output [3:0] xo_tuser, output xo_tlast, output xo_tvalid, input xo_tready, + input [63:0] xi_tdata, input [3:0] xi_tuser, input xi_tlast, input xi_tvalid, output xi_tready, + // ZPU + output [63:0] e2z_tdata, output [3:0] e2z_tuser, output e2z_tlast, output e2z_tvalid, input e2z_tready, + input [63:0] z2e_tdata, input [3:0] z2e_tuser, input z2e_tlast, input z2e_tvalid, output z2e_tready, + + output [31:0] debug + ); + + wire [63:0] v2ef_tdata; + wire [3:0] v2ef_tuser; + wire v2ef_tlast, v2ef_tvalid, v2ef_tready; + + // ////////////////////////////////////////////////////////////// + // Incoming Ethernet path + // Includes FIFO on the output going to ZPU + + wire [63:0] epg_tdata_int; + wire [3:0] epg_tuser_int; + wire epg_tlast_int, epg_tvalid_int, epg_tready_int; + + axi_packet_gate #(.WIDTH(68), .SIZE(10)) packet_gater //holds 8K pkts + (.clk(clk), .reset(reset), .clear(clear), + + .i_tdata({eth_rx_tuser, eth_rx_tdata}), .i_tlast(eth_rx_tlast), + .i_terror(eth_rx_tuser[3]), //top bit of user bus is error + .i_tvalid(eth_rx_tvalid), .i_tready(eth_rx_tready), + + .o_tdata({epg_tuser_int, epg_tdata_int}), .o_tlast(epg_tlast_int), + .o_tvalid(epg_tvalid_int), .o_tready(epg_tready_int)); + + wire [63:0] e2z_tdata_int; + wire [3:0] e2z_tuser_int; + wire e2z_tlast_int, e2z_tvalid_int, e2z_tready_int; + + eth_dispatch #(.BASE(BASE+8)) eth_dispatch + (.clk(clk), .reset(reset), .clear(clear), + .set_stb(set_stb), .set_addr(set_addr) , .set_data(set_data), + .in_tdata(epg_tdata_int), .in_tuser(epg_tuser_int), .in_tlast(epg_tlast_int), .in_tvalid(epg_tvalid_int), .in_tready(epg_tready_int), + .vita_tdata(e2v_tdata), .vita_tlast(e2v_tlast), .vita_tvalid(e2v_tvalid), .vita_tready(e2v_tready), + .zpu_tdata(e2z_tdata_int), .zpu_tuser(e2z_tuser_int), .zpu_tlast(e2z_tlast_int), .zpu_tvalid(e2z_tvalid_int), .zpu_tready(e2z_tready_int), + .xo_tdata(xo_tdata), .xo_tuser(xo_tuser), .xo_tlast(xo_tlast), .xo_tvalid(xo_tvalid), .xo_tready(xo_tready), // to other eth port + .debug(debug)); + + axi_fifo #(.WIDTH(69),.SIZE(ZPU_FIFOSIZE)) zpu_fifo + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({e2z_tlast_int,e2z_tuser_int,e2z_tdata_int}), .i_tvalid(e2z_tvalid_int), .i_tready(e2z_tready_int), + .o_tdata({e2z_tlast,e2z_tuser,e2z_tdata}), .o_tvalid(e2z_tvalid), .o_tready(e2z_tready)); + + // ////////////////////////////////////////////////////////////// + // Outgoing Ethernet path + // Includes FIFOs on path from VITA router, from ethernet crossover, and on the overall output + + wire [63:0] eth_tx_tdata_int; + wire [3:0] eth_tx_tuser_int; + wire eth_tx_tlast_int, eth_tx_tvalid_int, eth_tx_tready_int; + + wire [63:0] xi_tdata_int; + wire [3:0] xi_tuser_int; + wire xi_tlast_int, xi_tvalid_int, xi_tready_int; + + wire [63:0] v2e_tdata_int; + wire v2e_tlast_int, v2e_tvalid_int, v2e_tready_int; + + axi_fifo #(.WIDTH(65),.SIZE(VITA_FIFOSIZE)) vitaout_fifo + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({v2e_tlast,v2e_tdata}), .i_tvalid(v2e_tvalid), .i_tready(v2e_tready), + .o_tdata({v2e_tlast_int,v2e_tdata_int}), .o_tvalid(v2e_tvalid_int), .o_tready(v2e_tready_int)); + + chdr_eth_framer #(.BASE(BASE)) my_eth_framer + (.clk(clk), .reset(reset), .clear(clear), + .set_stb(set_stb), .set_addr(set_addr) , .set_data(set_data), + .in_tdata(v2e_tdata_int), .in_tlast(v2e_tlast_int), .in_tvalid(v2e_tvalid_int), .in_tready(v2e_tready_int), + .out_tdata(v2ef_tdata), .out_tuser(v2ef_tuser), .out_tlast(v2ef_tlast), .out_tvalid(v2ef_tvalid), .out_tready(v2ef_tready), + .debug()); + + axi_fifo #(.WIDTH(69),.SIZE(XO_FIFOSIZE)) xo_fifo + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({xi_tlast,xi_tuser,xi_tdata}), .i_tvalid(xi_tvalid), .i_tready(xi_tready), + .o_tdata({xi_tlast_int,xi_tuser_int,xi_tdata_int}), .o_tvalid(xi_tvalid_int), .o_tready(xi_tready_int)); + + axi_mux4 #(.PRIO(0), .WIDTH(68)) eth_mux + (.clk(clk), .reset(reset), .clear(clear), + .i0_tdata({z2e_tuser,z2e_tdata}), .i0_tlast(z2e_tlast), .i0_tvalid(z2e_tvalid), .i0_tready(z2e_tready), + .i1_tdata({v2ef_tuser,v2ef_tdata}), .i1_tlast(v2ef_tlast), .i1_tvalid(v2ef_tvalid), .i1_tready(v2ef_tready), + .i2_tdata({xi_tuser_int,xi_tdata_int}), .i2_tlast(xi_tlast_int), .i2_tvalid(xi_tvalid_int), .i2_tready(xi_tready_int), + .i3_tdata(), .i3_tlast(), .i3_tvalid(1'b0), .i3_tready(), + .o_tdata({eth_tx_tuser_int,eth_tx_tdata_int}), .o_tlast(eth_tx_tlast_int), .o_tvalid(eth_tx_tvalid_int), .o_tready(eth_tx_tready_int)); + + axi_fifo #(.WIDTH(69),.SIZE(ETHOUT_FIFOSIZE)) ethout_fifo + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({eth_tx_tlast_int,eth_tx_tuser_int,eth_tx_tdata_int}), .i_tvalid(eth_tx_tvalid_int), .i_tready(eth_tx_tready_int), + .o_tdata({eth_tx_tlast,eth_tx_tuser,eth_tx_tdata}), .o_tvalid(eth_tx_tvalid), .o_tready(eth_tx_tready)); + +endmodule // eth_interface diff --git a/fpga/usrp3/lib/packet_proc/ip_hdr_checksum.v b/fpga/usrp3/lib/packet_proc/ip_hdr_checksum.v new file mode 100644 index 000000000..869378aba --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/ip_hdr_checksum.v @@ -0,0 +1,24 @@ + +// Compute IP header checksum. 2 cycles of latency. +module ip_hdr_checksum + (input clk, input [159:0] in, output reg [15:0] out); + + wire [18:0] padded [0:9]; + reg [18:0] sum_a, sum_b; + + genvar i; + generate + for(i=0 ; i<10 ; i=i+1) + assign padded[i] = {3'b000,in[i*16+15:i*16]}; + endgenerate + + always @(posedge clk) sum_a = padded[0] + padded[1] + padded[2] + padded[3] + padded[4]; + always @(posedge clk) sum_b = padded[5] + padded[6] + padded[7] + padded[8] + padded[9]; + + wire [18:0] sum = sum_a + sum_b; + + always @(posedge clk) + out <= ~(sum[15:0] + {13'd0,sum[18:16]}); + + +endmodule // ip_hdr_checksum diff --git a/fpga/usrp3/lib/packet_proc/ip_hdr_checksum_tb.v b/fpga/usrp3/lib/packet_proc/ip_hdr_checksum_tb.v new file mode 100644 index 000000000..8d9ccf948 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/ip_hdr_checksum_tb.v @@ -0,0 +1,38 @@ + +module ip_hdr_checksum_tb(); + + initial $dumpfile("ip_hdr_checksum_tb.vcd"); + initial $dumpvars(0,ip_hdr_checksum_tb); + + reg clk; + + wire [159:0] in = { + 16'h4500, + 16'h0030, + 16'h4422, + 16'h4000, + 16'h8006, + 16'h0000, + 16'h8c7c, + 16'h19ac, + 16'hae24, + 16'h1e2b + }; + + wire [15:0] out; + ip_hdr_checksum ip_hdr_checksum + (.clk(clk), + .in(in), + .out(out)); + + initial + begin + clk <= 0; + #100 clk <= 1; + #100 clk <= 0; + #100 clk <= 1; + #100 $display("Computed 0x%x, should be 0x442e", out); + #100 $finish; + end + +endmodule // ip_hdr_checksum_tb diff --git a/fpga/usrp3/lib/packet_proc/source_flow_control.v b/fpga/usrp3/lib/packet_proc/source_flow_control.v new file mode 100644 index 000000000..d26b848c9 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/source_flow_control.v @@ -0,0 +1,127 @@ + +// source_flow_control.v +// +// This block passes the in_* AXI port to the out_* AXI port only when it has +// enough flow control credits. Data is held when there are not enough credits. +// Credits are replenished with extension context packets which update the +// last_consumed packet register. Max credits are controlled by settings regs. +// The 2nd line of the packet contains the sequence number in the low 12 bits. +// These packets should not have a time value, but if they do it will be ignored. + +module source_flow_control + #(parameter BASE=0) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input [63:0] fc_tdata, input fc_tlast, input fc_tvalid, output fc_tready, + input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready, + output [63:0] out_tdata, output out_tlast, output out_tvalid, input out_tready); + + reg [31:0] last_seqnum_consumed; + wire [31:0] window_size; + wire [31:0] go_until_seqnum = last_seqnum_consumed + window_size + 1; + reg [31:0] current_seqnum; + wire window_reset; + wire window_enable; + wire setting_changed; + + setting_reg #(.my_addr(BASE)) sr_window_size + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(window_size),.changed(setting_changed)); + + setting_reg #(.my_addr(BASE+1), .width(1)) sr_window_enable + (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(window_enable),.changed(window_reset)); + + reg go; + reg [1:0] sfc_state; + + + localparam SFC_HEAD = 2'd0; + localparam SFC_TIME = 2'd1; + localparam SFC_BODY = 2'd2; + localparam SFC_DUMP = 2'd3; + + always @(posedge clk) + if(reset | clear | window_reset) + begin + last_seqnum_consumed <= 32'hFFFFFFFF; + sfc_state <= SFC_HEAD; + end + else + if(fc_tvalid & fc_tready) + case(sfc_state) + SFC_HEAD : + if(fc_tlast) + sfc_state <= SFC_HEAD; // Error. CHDR packet with only a header is an error. + else if(~fc_tdata[63]) // Is this NOT an extension context packet? + sfc_state <= SFC_DUMP; // Error. Only extension context packets should come in on this interface. + else if(fc_tdata[61]) // Does this packet have time? + sfc_state <= SFC_TIME; + else + sfc_state <= SFC_BODY; + + SFC_TIME : + if(fc_tlast) + sfc_state <= SFC_HEAD; // Error, CHDR packet with only header and time is an error. + else + sfc_state <= SFC_BODY; + + SFC_BODY : + begin + last_seqnum_consumed <= fc_tdata[31:0]; // Sequence number is in lower 32bits. + if(fc_tlast) + sfc_state <= SFC_HEAD; + else + sfc_state <= SFC_DUMP; // Error. Not expecting any more data in a CHDR packet. + end + + SFC_DUMP : // shouldn't ever need to be here, this is an error condition + if(fc_tlast) + sfc_state <= SFC_HEAD; + endcase // case (sfc_state) + + assign fc_tready = 1'b1; // Always consume FC -- FIXME Even if we are getting reset? + assign out_tdata = in_tdata; // CHDR data flows through combinatorially. + assign out_tlast = in_tlast; + assign in_tready = go ? out_tready : 1'b0; + assign out_tvalid = go ? in_tvalid : 1'b0; + + // + // Each time we recieve the end of an IF data packet increment the current_seqnum. + // We bravely assume that no packets go missing...or at least that they will be detected elsewhere + // and then handled appropriately. + // The SEQNUM needs to be initialized every time we start a new stream. In new_rx_framer this is done + // as a side effect of writing a new SID value to the setting reg. + // + // By incrementing current_seqnum on the last signal we get the nice effect that packet flow is + // always suspended between packets rather than within a packet. + // + always @(posedge clk) + if(reset | clear | window_reset) + current_seqnum <= 0; + else if (in_tvalid && in_tready && in_tlast) + current_seqnum <= current_seqnum + 1; + + always @(posedge clk) + if(reset | clear) + go <= 1'b0; + else + if(~window_enable) + go <= 1'b1; + else + case(go) + 1'b0 : + // This test assumes the host is well behaved in sending good numbers for packets consumed + // and that current_seqnum increments always by 1 only. + // This way wraps are dealt with without a large logic penalty. + if (in_tvalid & (go_until_seqnum - current_seqnum != 0)) + // if(in_tvalid & (go_until_seqnum > current_seqnum)) // FIXME will need to handle wrap of 32-bit seqnum + go <= 1'b1; + + 1'b1 : + if(in_tvalid & in_tready & in_tlast) + go <= 1'b0; + endcase // case (go) + + +endmodule // source_flow_control diff --git a/fpga/usrp3/lib/packet_proc/source_flow_control_tb.v b/fpga/usrp3/lib/packet_proc/source_flow_control_tb.v new file mode 100644 index 000000000..f51d17f97 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/source_flow_control_tb.v @@ -0,0 +1,249 @@ +`timescale 1ns/1ps + +module source_flow_control_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("source_flow_control_tb.vcd"); + initial $dumpvars(0,source_flow_control_tb); + + initial + begin + #1000 reset = 0; + #20000; + $finish; + end + + reg [63:0] tdata; + wire [63:0] tdata_int; + reg tlast; + wire tlast_int; + reg tvalid = 1'b0; + wire tvalid_int; + wire tready, tready_int; + + reg [63:0] fc_tdata; + reg fc_tlast, fc_tvalid; + wire fc_tready; + + wire [63:0] out_tdata; + wire out_tlast, out_tready, out_tvalid; + + wire [15:0] occ_in, occ_out; + reg set_stb = 0; + reg [7:0] set_addr; + reg [31:0] set_data; + + + task send_fc_packet; + input [31:0] seqnum; + input [31:0] sid; + input always_go; + + begin + @(posedge clk); + fc_tlast <= 1'b0; + fc_tdata <= { 1'b1, 1'b0, 1'b0, 1'b0, 12'hABC, 16'd4, sid }; + fc_tvalid <= 1; + @(posedge clk); + fc_tlast <= 1'b1; + //fc_tdata <= { 52'h0,seqnum }; + fc_tdata <= { 31'h0,always_go, seqnum }; + @(posedge clk); + fc_tvalid <= 0; + @(posedge clk); + end + endtask // send_packet + + task send_packet; + input ec; + input timed; + input [11:0] seqnum; + input [31:0] sid; + input [63:0] vtime; + input [15:0] addr; + input [31:0] data; + + begin + // Send a packet + @(posedge clk); + tlast <= 1'b0; + tdata <= { ec, 1'b0, timed, 1'b0, seqnum, timed ? 16'd6 : 16'd4, sid }; + tvalid <= 1; + @(posedge clk); + if(timed) + begin + tdata <= vtime; + @(posedge clk); + end + tlast <= 1'b1; + tdata <= { 16'h0, addr, data }; + @(posedge clk); + tlast <= 1'b0; + tvalid <= 0; + @(posedge clk); + end + endtask // send_packet + + initial + begin + tvalid <= 1'b0; + while(reset) + @(posedge clk); + @(posedge clk); + // Set flow control window to be 2 + set_stb <= 1; + set_addr <= 0; + set_data <= 2; + @(posedge clk); + set_stb <= 0; + // ExtContext. Time. Seq=0, SID=DEAD_6789, Time=10 + send_packet(1'b1,1'b1,12'h0,32'hDEAD_6789,64'h10,16'hB,32'hF00D_1234); + send_packet(1'b1,1'b1,12'h1,32'hDEAD_6789,64'h20,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h2,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h3,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h4,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h5,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h6,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h7,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h8,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + #500; + // Consumed 2 packets + send_fc_packet(32'd1,32'h3,1'b0); + #300; + // Consumed 1 packet + send_fc_packet(32'd2,32'h3,1'b0); + #500; + // Consumed 2 packets + send_fc_packet(32'd4,32'h3,1'b0); + #400; + // Send same SEQ ID again to test it causes no changes. + send_fc_packet(32'd4,32'h3,1'b0); + #300; + // Consumed 1 packet + send_fc_packet(32'd5,32'h3,1'b0); + #500; + // Consumed 2 packets + send_fc_packet(32'd7,32'h3,1'b0); + #500; + send_packet(1'b1,1'b1,12'h9,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'hA,32'hDEAD_6789,64'h30,16'hC,32'hABCD_4321); + #300; + // Consumed 1 packet + send_fc_packet(32'd8,32'h3,1'b0); + // + // Now force internal sequence count to close to wrap value to test corner case + // + #100; + source_flow_control.current_seqnum <= 32'hFFFF_FFFC; + #100; + send_fc_packet(32'hFFFF_FFFA,32'h3,1'b0); + #100; + send_packet(1'b1,1'b1,12'hFFC,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + #200; + send_packet(1'b1,1'b1,12'hFFD,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'hFFE,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'hFFF,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h000,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h001,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h002,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + #200; + // Consumed 2 packets + send_fc_packet(32'hFFFF_FFFC,32'h3,1'b0); + #200; + // Consumed 2 packets + send_fc_packet(32'hFFFF_FFFE,32'h3,1'b0); + send_packet(1'b1,1'b1,12'h003,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h004,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + #200; + // Consumed 2 packets + send_fc_packet(32'h0,32'h3,1'b0); + #200; + // Consumed 2 packets + send_fc_packet(32'h2,32'h3,1'b0); + #500; + // + // Again force internal sequence count to close to wrap value to test new corner case + // + #100; + source_flow_control.current_seqnum <= 32'hFFFF_FFFC; + #100; + send_fc_packet(32'hFFFF_FFFA,32'h3,1'b0); + #100; + send_packet(1'b1,1'b1,12'hFFC,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + #200; + send_packet(1'b1,1'b1,12'hFFD,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'hFFE,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'hFFF,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h000,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h001,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h002,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + #200; + // Consumed 1 packets + send_fc_packet(32'hFFFF_FFFB,32'h3,1'b0); + #200; + // Consumed 1 packets + send_fc_packet(32'hFFFF_FFFC,32'h3,1'b0); + send_packet(1'b1,1'b1,12'h003,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + send_packet(1'b1,1'b1,12'h004,32'hDEAD_6789,64'h40,16'hC,32'hABCD_4321); + #200; + // Consumed 1 packets + send_fc_packet(32'hFFFF_FFFD,32'h3,1'b0); + #200; + // Consumed 1 packets + send_fc_packet(32'hFFFF_FFFE,32'h3,1'b0); + #200; + // Consumed 1 packets + send_fc_packet(32'hFFFF_FFFF,32'h3,1'b0); + #200; + // Consumed 1 packets + send_fc_packet(32'h0,32'h3,1'b0); + #200; + // Consumed 1 packets + send_fc_packet(32'h1,32'h3,1'b0); + #200; + // Consumed 1 packets + send_fc_packet(32'h2,32'h3,1'b0); + #500; + + + + + end + + axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_in + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({tlast,tdata}), .i_tvalid(tvalid), .i_tready(tready), + .o_tdata({tlast_int,tdata_int}), .o_tvalid(tvalid_int), .o_tready(tready_int), + .occupied(occ_in)); + + source_flow_control source_flow_control + (.clk(clk), .reset(reset), .clear(1'b0), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .fc_tdata(fc_tdata), .fc_tlast(fc_tlast), .fc_tvalid(fc_tvalid), .fc_tready(fc_tready), + .in_tdata(tdata_int), .in_tlast(tlast_int), .in_tvalid(tvalid_int), .in_tready(tready_int), + .out_tdata(out_tdata), .out_tlast(out_tlast), .out_tvalid(out_tvalid), .out_tready(out_tready) + ); + + wire [63:0] dump_tdata; + wire dump_tlast, dump_tvalid, dump_tready; + + axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_out + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({out_tlast,out_tdata}), .i_tvalid(out_tvalid), .i_tready(out_tready), + .o_tdata({dump_tlast,dump_tdata}), .o_tvalid(dump_tvalid), .o_tready(dump_tready), + .occupied(occ_out)); + + assign dump_tready = 0; + + always @(posedge clk) + if(out_tvalid & out_tready) + begin + $display("%x",out_tdata); + if(out_tlast) + $display("TLAST"); + end +endmodule // source_flow_control_tb diff --git a/fpga/usrp3/lib/packet_proc/vita_eth_framer.v b/fpga/usrp3/lib/packet_proc/vita_eth_framer.v new file mode 100644 index 000000000..a5e02e899 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/vita_eth_framer.v @@ -0,0 +1,137 @@ + +// vita_eth_framer +// Takes a vita stream in and adds udp, ip, and ethernet framing +// Uses 8 setting reg addresses. First 4 are simple registers: +// BASE+0 : Upper 16 bits of ethernet src mac +// BASE+1 : Lower 32 bits of ethernet src mac +// BASE+2 : IP src address +// BASE+3 : UDP src port +// +// Next 4 control write ports on a RAM indexed by destination field of stream ID +// BASE+4 : Dest SID for next 3 regs +// BASE+5 : Dest IP +// BASE+6 : Dest UDP port, upper 16 bits of dest mac +// BASE+7 : Lower 32 bits of dest mac + +module vita_eth_framer + #(parameter BASE=0) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input [63:0] in_tdata, input in_tlast, input in_tvalid, output in_tready, + output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready, + output [31:0] debug ); + + localparam SR_AWIDTH = 8; + + reg [31:0] sid; + reg [15:0] vita_len; + + reg [2:0] vef_state; + localparam VEF_IDLE = 3'd0; + localparam VEF_PAYLOAD = 3'd7; + + reg [63:0] tdata; + + always @(posedge clk) + if(reset | clear) + begin + vef_state <= VEF_IDLE; + sid <= 32'd0; + vita_len <= 16'd0; + end + else + case(vef_state) + VEF_IDLE : + if(in_tvalid) + begin + vef_state <= 1; + sid <= in_tdata[31:0]; + vita_len <= in_tdata[47:32]; + end + VEF_PAYLOAD : + if(in_tvalid & out_tready) + if(in_tlast) + vef_state <= VEF_IDLE; + default : + if(out_tready) + vef_state <= vef_state + 3'd1; + endcase // case (vef_state) + + assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0; + assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1; + assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0; + assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,vita_len[0],2'b00} : 4'b0000; + assign out_tdata = tdata; + + wire [15:0] vita_len_in_bytes = {vita_len[13:0],2'b00}; // Vita length is in 32-bit words + + wire [47:0] pad = 48'h0; + wire [47:0] mac_src, mac_dst; + wire [15:0] eth_type = 16'h0800; + wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */}; + wire [15:0] ip_len = (16'd28 + vita_len_in_bytes); // 20 for IP, 8 for UDP + wire [15:0] ident = 16'h0; + wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 }; + wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ }; + wire [15:0] iphdr_checksum; + wire [31:0] ip_src, ip_dst; + wire [15:0] udp_src, udp_dst; + wire [15:0] udp_len = (16'd8 + vita_len_in_bytes); + wire [15:0] udp_checksum = 16'h0; + + setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(mac_src[47:32]), .changed()); + + setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(mac_src[31:0]), .changed()); + + setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(ip_src), .changed()); + + setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(udp_src), .changed()); + + // Tables of MAC/IP/UDP addresses + wire [8:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses + + setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(9)) set_ram_addr + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(ram_addr), .changed()); + + ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_ip + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst)); + + ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_udpmac + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]})); + + ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_maclower + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0])); + + ip_hdr_checksum ip_hdr_checksum + (.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}), + .out(iphdr_checksum)); + + always @* + case(vef_state) + 1 : tdata <= { pad[47:0], mac_dst[47:32]}; + 2 : tdata <= { mac_dst[31:0], mac_src[47:16]}; + 3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] }; + 4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]}; + 5 : tdata <= { ip_src, ip_dst}; + 6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum}; + default : tdata <= in_tdata; + endcase // case (vef_state) + +endmodule // vita_eth_framer diff --git a/fpga/usrp3/lib/packet_proc/vrlp_eth_framer.v b/fpga/usrp3/lib/packet_proc/vrlp_eth_framer.v new file mode 100644 index 000000000..5962b9ba1 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/vrlp_eth_framer.v @@ -0,0 +1,139 @@ + +// vrlp_eth_framer +// Takes a vrlp stream in and adds udp, ip, and ethernet framing +// Uses 8 setting reg addresses. First 4 are simple registers: +// BASE+0 : Upper 16 bits of ethernet src mac +// BASE+1 : Lower 32 bits of ethernet src mac +// BASE+2 : IP src address +// BASE+3 : UDP src port +// +// Next 4 control write ports on a RAM indexed by destination field of stream ID +// BASE+4 : Dest SID for next 3 regs +// BASE+5 : Dest IP +// BASE+6 : Dest UDP port, upper 16 bits of dest mac +// BASE+7 : Lower 32 bits of dest mac +// +// in_tuser holds streamid + +module vrlp_eth_framer + #(parameter BASE=0) + (input clk, input reset, input clear, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + input [63:0] in_tdata, input [15:0] in_tuser, input in_tlast, input in_tvalid, output in_tready, + output [63:0] out_tdata, output [3:0] out_tuser, output out_tlast, output out_tvalid, input out_tready, + output [31:0] debug ); + + localparam SR_AWIDTH = 8; + + reg [15:0] sid; + reg [15:0] vrlp_len; + + reg [2:0] vef_state; + localparam VEF_IDLE = 3'd0; + localparam VEF_PAYLOAD = 3'd7; + + reg [63:0] tdata; + + always @(posedge clk) + if(reset | clear) + begin + vef_state <= VEF_IDLE; + sid <= 16'd0; + vrlp_len <= 16'd0; + end + else + case(vef_state) + VEF_IDLE : + if(in_tvalid) + begin + vef_state <= 1; + sid <= in_tuser[15:0]; + vrlp_len <= in_tdata[15:0]; // modified for VRLP header + end + VEF_PAYLOAD : + if(in_tvalid & out_tready) + if(in_tlast) + vef_state <= VEF_IDLE; + default : + if(out_tready) + vef_state <= vef_state + 3'd1; + endcase // case (vef_state) + + wire [15:0] vrlp_len_in_bytes = {vrlp_len[13:0],2'b00}; // Vrlp length is in 32-bit words + + assign in_tready = (vef_state == VEF_PAYLOAD) ? out_tready : 1'b0; + assign out_tvalid = (vef_state == VEF_PAYLOAD) ? in_tvalid : (vef_state == VEF_IDLE) ? 1'b0 : 1'b1; + assign out_tlast = (vef_state == VEF_PAYLOAD) ? in_tlast : 1'b0; + assign out_tuser = ((vef_state == VEF_PAYLOAD) & in_tlast) ? {1'b0,vrlp_len_in_bytes[2:0]} : 4'b0000; + assign out_tdata = tdata; + + wire [47:0] pad = 48'h0; + wire [47:0] mac_src, mac_dst; + wire [15:0] eth_type = 16'h0800; + wire [15:0] misc_ip = { 4'd4 /* IPv4 */, 4'd5 /* IP HDR Len */, 8'h00 /* DSCP and ECN */}; + wire [15:0] ip_len = (16'd28 + vrlp_len_in_bytes); // 20 for IP, 8 for UDP + wire [15:0] ident = 16'h0; + wire [15:0] flag_frag = { 3'b010 /* don't fragment */, 13'h0 }; + wire [15:0] ttl_prot = { 8'h10 /* TTL */, 8'h11 /* UDP */ }; + wire [15:0] iphdr_checksum; + wire [31:0] ip_src, ip_dst; + wire [15:0] udp_src, udp_dst; + wire [15:0] udp_len = (16'd8 + vrlp_len_in_bytes); + wire [15:0] udp_checksum = 16'h0; + + setting_reg #(.my_addr(BASE), .awidth(SR_AWIDTH), .width(16)) set_mac_upper + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(mac_src[47:32]), .changed()); + + setting_reg #(.my_addr(BASE+1), .awidth(SR_AWIDTH), .width(32)) set_mac_lower + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(mac_src[31:0]), .changed()); + + setting_reg #(.my_addr(BASE+2), .awidth(SR_AWIDTH), .width(32)) set_ip + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(ip_src), .changed()); + + setting_reg #(.my_addr(BASE+3), .awidth(SR_AWIDTH), .width(16)) set_udp + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(udp_src), .changed()); + + // Tables of MAC/IP/UDP addresses + wire [8:0] ram_addr; // FIXME we could skip this part if we had wider SR addresses + + setting_reg #(.my_addr(BASE+4), .awidth(SR_AWIDTH), .width(9)) set_ram_addr + (.clk(clk), .rst(reset), + .strobe(set_stb), .addr(set_addr), .in(set_data), + .out(ram_addr), .changed()); + + ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_ip + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+5)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(ip_dst)); + + ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_udpmac + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+6)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob({udp_dst,mac_dst[47:32]})); + + ram_2port #(.DWIDTH(32), .AWIDTH(9)) ram_maclower + (.clka(clk), .ena(1'b1), .wea(set_stb & (set_addr == BASE+7)), .addra(ram_addr), .dia(set_data), .doa(), + .clkb(clk), .enb(1'b1), .web(1'b0), .addrb(sid[8:0]), .dib(32'hFFFF_FFFF), .dob(mac_dst[31:0])); + + ip_hdr_checksum ip_hdr_checksum + (.clk(clk), .in({misc_ip,ip_len,ident,flag_frag,ttl_prot,16'd0,ip_src,ip_dst}), + .out(iphdr_checksum)); + + always @* + case(vef_state) + 1 : tdata <= { pad[47:0], mac_dst[47:32]}; + 2 : tdata <= { mac_dst[31:0], mac_src[47:16]}; + 3 : tdata <= { mac_src[15:0], eth_type[15:0], misc_ip[15:0], ip_len[15:0] }; + 4 : tdata <= { ident[15:0], flag_frag[15:0], ttl_prot[15:0], iphdr_checksum[15:0]}; + 5 : tdata <= { ip_src, ip_dst}; + 6 : tdata <= { udp_src, udp_dst, udp_len, udp_checksum}; + default : tdata <= in_tdata; + endcase // case (vef_state) + +endmodule // vrlp_eth_framer diff --git a/fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita.v b/fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita.v new file mode 100644 index 000000000..04da73616 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita.v @@ -0,0 +1,90 @@ + +module vrlp_to_compressed_vita + (input clk, input reset, input clear, + input [63:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, + output [63:0] o_tdata, output o_tlast, output o_tvalid, input o_tready); + + wire [63:0] o_tdata_int; + wire o_tlast_int, o_tvalid_int, o_tready_int; + + reg [1:0] v2cv_state; + reg [11:0] seqnum; + reg trim_line; + + localparam V2CV_VRLP = 2'd0; + localparam V2CV_VRTH = 2'd1; + localparam V2CV_BODY = 2'd2; + localparam V2CV_DUMP = 2'd3; + + wire is_ec = i_tdata[63:60] == 4'h5; + wire has_trailer = i_tdata[58] & ~is_ec; + wire has_time = |i_tdata[53:52]; + wire eob = i_tdata[56] & ~is_ec; + wire [15:0] len = i_tdata[47:32]; + wire [31:0] sid = i_tdata[31:0]; + + wire [63:0] compressed_hdr = { is_ec, has_trailer, has_time, eob, seqnum, len, sid }; + wire bad_vita = |i_tdata[55:54] /* has secs */ | i_tdata[59] /* has class */ | ( {i_tdata[63],i_tdata[61:60]} != 3'b001 ); + reg [15:0] len_reg; + wire [16:0] vita_words32 = i_tdata[16:0]-17'd4; + assign trim_now = 0; + + always @(posedge clk) + if(reset | clear) + begin + v2cv_state <= V2CV_VRLP; + seqnum <= 12'd0; + trim_line <= 1'b0; + len_reg <= 16'd0; + end + else + case(v2cv_state) + V2CV_VRLP : + if(i_tvalid) + begin + seqnum <= i_tdata[31:20]; + trim_line <= i_tdata[0]; + len_reg <= vita_words32[16:1]; + if(~i_tlast) + v2cv_state <= V2CV_VRTH; + end + + V2CV_VRTH : + if(i_tvalid & o_tready_int) + begin + len_reg <= len_reg - 16'd1; + if(i_tlast) + v2cv_state <= V2CV_VRLP; + else if(bad_vita) + v2cv_state <= V2CV_DUMP; + else + v2cv_state <= V2CV_BODY; + end + + V2CV_BODY : + if(i_tvalid & o_tready_int) + begin + len_reg <= len_reg - 16'd1; + if(i_tlast) + v2cv_state <= V2CV_VRLP; + else if(len_reg == 16'd0) + v2cv_state <= V2CV_DUMP; + end + V2CV_DUMP : + if(i_tvalid) + if(i_tlast) + v2cv_state <= V2CV_VRLP; + endcase // case (v2cv_state) + + assign o_tdata_int = (v2cv_state == V2CV_VRTH) ? compressed_hdr : i_tdata; + assign o_tlast_int = i_tlast | (len_reg == 16'd0); + assign o_tvalid_int = i_tvalid && (((v2cv_state == V2CV_VRTH) && !bad_vita) || (v2cv_state == V2CV_BODY)); + assign i_tready = o_tready_int | (v2cv_state == V2CV_VRLP) | (v2cv_state == V2CV_DUMP); + + axi_fifo_short #(.WIDTH(65)) short_fifo + (.clk(clk), .reset(reset), .clear(clear), + .i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int), + .o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready), + .space(), .occupied()); + +endmodule // vrlp_to_compressed_vita diff --git a/fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita_tb.v b/fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita_tb.v new file mode 100644 index 000000000..167c0b014 --- /dev/null +++ b/fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita_tb.v @@ -0,0 +1,120 @@ +`timescale 1ns/1ps + +module vrlp_to_compressed_vita_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("vrlp_to_compressed_vita_tb.vcd"); + initial $dumpvars(0,vrlp_to_compressed_vita_tb); + + task send_packet; + input [63:0] data_start; + input [31:0] len; + + begin + // Send a packet + @(posedge clk); + {i_tlast, i_tdata} <= { 1'b0, data_start }; + i_tvalid <= 1; + @(posedge clk); + i_tdata <= 64'hAAAA_BBBB_CCCC_0000; + + repeat(len-2) + begin + i_tvalid <= 1; + @(posedge clk); + i_tdata <= i_tdata + 1; + end + i_tlast <= 1; + i_tdata <= i_tdata + 1; + @(posedge clk); + i_tvalid <= 1'b0; + + @(posedge clk); + end + endtask // send_packet + + + initial + begin + #1000 reset = 0; + #200000; + $finish; + end + + wire [63:0] o_tdata; + reg [63:0] i_tdata; + wire [2:0] o_tuser; + reg [2:0] i_tuser; + reg i_tlast; + wire o_tlast; + wire o_tvalid, i_tready; + reg i_tvalid, o_tready; + reg i_terror; + + localparam RPT_COUNT = 16; + + initial + begin + i_tvalid <= 0; + o_tready <= 0; + + while(reset) + @(posedge clk); + @(posedge clk); + + //send_packet(64'hA0,3'd0, 16, 0); + send_packet(64'hAABC_0008_DEAD_BEEF, 4); + send_packet(64'h7DEF_0008_8765_4321, 4); + send_packet(64'hAABC_0007_F00D_1234, 4); + send_packet(64'h7DEF_0007_ABCD_4321, 4); + o_tready <= 1; + //send_packet(64'hC0,3'd0, 16, 1); + //send_packet(64'hD0,3'd0, 16, 0); + //send_packet(64'hE0,3'd0, 16, 0); + //send_packet(64'hF0,3'd0, 16, 0); + + @(posedge clk); + + end // initial begin + + wire i_terror_int, i_tlast_int, i_tready_int, i_tvalid_int; + wire [2:0] i_tuser_int; + wire [63:0] i_tdata_int; + wire o_tlast_int, o_tready_int, o_tvalid_int; + wire [2:0] o_tuser_int; + wire [63:0] o_tdata_int; + wire [63:0] vrlp_tdata; + wire vrlp_tlast, vrlp_tvalid, vrlp_tready; + + axi_fifo #(.WIDTH(65), .SIZE(10)) fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata({i_tlast_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int)); + + compressed_vita_to_vrlp dut0 + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata(i_tdata_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int), + .o_tdata(vrlp_tdata), .o_tlast(vrlp_tlast), .o_tvalid(vrlp_tvalid), .o_tready(vrlp_tready)); + + vrlp_to_compressed_vita dut1 + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata(vrlp_tdata), .i_tlast(vrlp_tlast), .i_tvalid(vrlp_tvalid), .i_tready(vrlp_tready), + .o_tdata(o_tdata_int), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int)); + + axi_fifo #(.WIDTH(65), .SIZE(10)) fifo_out + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int), + .o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready)); + + always @(posedge clk) + if(o_tvalid & o_tready) + begin + $display("%x",o_tdata); + if(o_tlast) + $display("======EOF========"); + end +endmodule // vrlp_to_compressed_vita_tb |