aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/packet_proc
diff options
context:
space:
mode:
authorBen Hilburn <ben.hilburn@ettus.com>2013-10-10 10:17:27 -0700
committerBen Hilburn <ben.hilburn@ettus.com>2013-10-10 10:17:27 -0700
commit0df4b801a34697f2058b4a7b95e08d2a0576c9db (patch)
treebe10e78d1a97c037a9e7492360a178d1873b9c09 /fpga/usrp3/lib/packet_proc
parent6e7bc850b66e8188718248b76b729c7cf9c89700 (diff)
downloaduhd-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')
-rw-r--r--fpga/usrp3/lib/packet_proc/.gitignore3
-rw-r--r--fpga/usrp3/lib/packet_proc/Makefile.srcs21
-rw-r--r--fpga/usrp3/lib/packet_proc/chdr_eth_framer.v136
-rw-r--r--fpga/usrp3/lib/packet_proc/compressed_vita_to_vrlp.v78
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_chunker.v91
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_chunker_tb.v187
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_dechunker.v90
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_dechunker_tb.v183
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_dest_lookup.v48
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_insert_tlast.v33
-rw-r--r--fpga/usrp3/lib/packet_proc/cvita_insert_tlast_tb.v92
-rw-r--r--fpga/usrp3/lib/packet_proc/eth_dispatch.v573
-rw-r--r--fpga/usrp3/lib/packet_proc/eth_dispatch_tb.v311
-rw-r--r--fpga/usrp3/lib/packet_proc/eth_interface.v113
-rw-r--r--fpga/usrp3/lib/packet_proc/ip_hdr_checksum.v24
-rw-r--r--fpga/usrp3/lib/packet_proc/ip_hdr_checksum_tb.v38
-rw-r--r--fpga/usrp3/lib/packet_proc/source_flow_control.v127
-rw-r--r--fpga/usrp3/lib/packet_proc/source_flow_control_tb.v249
-rw-r--r--fpga/usrp3/lib/packet_proc/vita_eth_framer.v137
-rw-r--r--fpga/usrp3/lib/packet_proc/vrlp_eth_framer.v139
-rw-r--r--fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita.v90
-rw-r--r--fpga/usrp3/lib/packet_proc/vrlp_to_compressed_vita_tb.v120
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