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 | 
