diff options
Diffstat (limited to 'fpga/usrp3/lib/fifo')
-rw-r--r-- | fpga/usrp3/lib/fifo/Makefile.srcs | 20 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_demux4.v | 77 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_demux8.v | 60 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo.v | 169 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo32_to_fifo64.v | 38 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo64_to_fifo32.v | 31 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo_2clk.v | 88 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo_32_64_tb.v | 114 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo_short.v | 110 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo_tb.v | 211 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_loopback.v | 71 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_mux4.v | 112 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_mux8.v | 56 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_packet_gate.v | 78 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_packet_gate_tb.v | 106 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/monitor_axi_fifo.v | 123 | ||||
-rw-r--r-- | fpga/usrp3/lib/fifo/shortfifo.v | 92 |
17 files changed, 1556 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/fifo/Makefile.srcs b/fpga/usrp3/lib/fifo/Makefile.srcs new file mode 100644 index 000000000..ffe2484f8 --- /dev/null +++ b/fpga/usrp3/lib/fifo/Makefile.srcs @@ -0,0 +1,20 @@ +# +# Copyright 2012-2013 Ettus Research LLC +# + +################################################## +# FIFO Sources +################################################## +FIFO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/fifo/, \ +axi_mux4.v \ +axi_mux8.v \ +axi_demux4.v \ +axi_demux8.v \ +axi_fifo_short.v \ +axi_packet_gate.v \ +axi_fifo.v \ +axi_fifo64_to_fifo32.v \ +axi_fifo32_to_fifo64.v \ +axi_fifo_2clk.v \ +axi_loopback.v \ +)) diff --git a/fpga/usrp3/lib/fifo/axi_demux4.v b/fpga/usrp3/lib/fifo/axi_demux4.v new file mode 100644 index 000000000..c613b064d --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_demux4.v @@ -0,0 +1,77 @@ + +// Copyright 2012 Ettus Research LLC +// axi_demux -- takes one AXI stream, sends to one of 4 output channels +// Choice of output channel is by external logic based on first line of packet ("header" port) +// If compressed vita data, this line contains vita header and streamid. + +module axi_demux4 + #(parameter ACTIVE_CHAN = 4'b1111, // ACTIVE_CHAN is a map of connected outputs + parameter WIDTH = 64, + parameter BUFFER=0) + (input clk, input reset, input clear, + output [WIDTH-1:0] header, input [1:0] dest, + input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, + output [WIDTH-1:0] o0_tdata, output o0_tlast, output o0_tvalid, input o0_tready, + output [WIDTH-1:0] o1_tdata, output o1_tlast, output o1_tvalid, input o1_tready, + output [WIDTH-1:0] o2_tdata, output o2_tlast, output o2_tvalid, input o2_tready, + output [WIDTH-1:0] o3_tdata, output o3_tlast, output o3_tvalid, input o3_tready); + + wire [WIDTH-1:0] i_tdata_int; + wire i_tlast_int, i_tvalid_int, i_tready_int; + + generate + if(BUFFER == 0) + begin + assign i_tdata_int = i_tdata; + assign i_tlast_int = i_tlast; + assign i_tvalid_int = i_tvalid; + assign i_tready = i_tready_int; + end + else + axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short + (.clk(clk), .reset(reset), .clear(clear), + .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), + .space(), .occupied()); + endgenerate + + reg [3:0] dm_state; + localparam DM_IDLE = 4'b0000; + localparam DM_0 = 4'b0001; + localparam DM_1 = 4'b0010; + localparam DM_2 = 4'b0100; + localparam DM_3 = 4'b1000; + + assign header = i_tdata_int; + + always @(posedge clk) + if(reset | clear) + dm_state <= DM_IDLE; + else + case (dm_state) + DM_IDLE : + if(i_tvalid_int) + case(dest) + 2'b00 : dm_state <= DM_0; + 2'b01 : dm_state <= DM_1; + 2'b10 : dm_state <= DM_2; + 2'b11 : dm_state <= DM_3; + endcase // case (i_tdata[1:0]) + + DM_0, DM_1, DM_2, DM_3 : + if(i_tvalid_int & i_tready_int & i_tlast_int) + dm_state <= DM_IDLE; + + default : + dm_state <= DM_IDLE; + endcase // case (dm_state) + + assign {o3_tvalid, o2_tvalid, o1_tvalid, o0_tvalid} = dm_state & {4{i_tvalid_int}}; + assign i_tready_int = |(dm_state & ({o3_tready, o2_tready, o1_tready, o0_tready} | ~ACTIVE_CHAN)); + + assign {o0_tlast, o0_tdata} = {i_tlast_int, i_tdata_int}; + assign {o1_tlast, o1_tdata} = {i_tlast_int, i_tdata_int}; + assign {o2_tlast, o2_tdata} = {i_tlast_int, i_tdata_int}; + assign {o3_tlast, o3_tdata} = {i_tlast_int, i_tdata_int}; + +endmodule // axi_demux4 diff --git a/fpga/usrp3/lib/fifo/axi_demux8.v b/fpga/usrp3/lib/fifo/axi_demux8.v new file mode 100644 index 000000000..9aa51674d --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_demux8.v @@ -0,0 +1,60 @@ + +// Copyright 2012 Ettus Research LLC +// axi_demux -- takes one AXI stream, sends to one of 8 output channels +// Choice of output channel is by external logic based on first line of packet ("header" port) +// If compressed vita data, this line contains vita header and streamid. + +module axi_demux8 #( + parameter ACTIVE_CHAN = 8'b11111111, // ACTIVE_CHAN is a map of connected outputs + parameter WIDTH = 64, + parameter BUFFER=0 +) ( + input clk, input reset, input clear, + output [WIDTH-1:0] header, input [2:0] dest, + input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, + output [WIDTH-1:0] o0_tdata, output o0_tlast, output o0_tvalid, input o0_tready, + output [WIDTH-1:0] o1_tdata, output o1_tlast, output o1_tvalid, input o1_tready, + output [WIDTH-1:0] o2_tdata, output o2_tlast, output o2_tvalid, input o2_tready, + output [WIDTH-1:0] o3_tdata, output o3_tlast, output o3_tvalid, input o3_tready, + output [WIDTH-1:0] o4_tdata, output o4_tlast, output o4_tvalid, input o4_tready, + output [WIDTH-1:0] o5_tdata, output o5_tlast, output o5_tvalid, input o5_tready, + output [WIDTH-1:0] o6_tdata, output o6_tlast, output o6_tvalid, input o6_tready, + output [WIDTH-1:0] o7_tdata, output o7_tlast, output o7_tvalid, input o7_tready +); + + wire [WIDTH-1:0] i_tdata_int0, i_tdata_int1; + wire i_tlast_int0, i_tlast_int1; + wire i_tvalid_int0, i_tvalid_int1; + wire i_tready_int0, i_tready_int1; + + axi_demux4 #(.ACTIVE_CHAN({2'b00, (|(ACTIVE_CHAN[7:4])), (|(ACTIVE_CHAN[3:0]))}), .WIDTH(WIDTH), .BUFFER(BUFFER)) demux2 ( + .clk(clk), .reset(reset), .clear(clear), + .header(header), .dest({1'b0, dest[2]}), + .i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o0_tdata(i_tdata_int0), .o0_tlast(i_tlast_int0), .o0_tvalid(i_tvalid_int0), .o0_tready(i_tready_int0), + .o1_tdata(i_tdata_int1), .o1_tlast(i_tlast_int1), .o1_tvalid(i_tvalid_int1), .o1_tready(i_tready_int1), + .o2_tdata(), .o2_tlast(), .o2_tvalid(), .o2_tready(1'b0), + .o3_tdata(), .o3_tlast(), .o3_tvalid(), .o3_tready(1'b0) + ); + + axi_demux4 #(.ACTIVE_CHAN(ACTIVE_CHAN[3:0]), .WIDTH(WIDTH), .BUFFER(0)) demux4_int0 ( + .clk(clk), .reset(reset), .clear(clear), + .header(), .dest(dest[1:0]), + .i_tdata(i_tdata_int0), .i_tlast(i_tlast_int0), .i_tvalid(i_tvalid_int0), .i_tready(i_tready_int0), + .o0_tdata(o0_tdata), .o0_tlast(o0_tlast), .o0_tvalid(o0_tvalid), .o0_tready(o0_tready), + .o1_tdata(o1_tdata), .o1_tlast(o1_tlast), .o1_tvalid(o1_tvalid), .o1_tready(o1_tready), + .o2_tdata(o2_tdata), .o2_tlast(o2_tlast), .o2_tvalid(o2_tvalid), .o2_tready(o2_tready), + .o3_tdata(o3_tdata), .o3_tlast(o3_tlast), .o3_tvalid(o3_tvalid), .o3_tready(o3_tready) + ); + + axi_demux4 #(.ACTIVE_CHAN(ACTIVE_CHAN[7:4]), .WIDTH(WIDTH), .BUFFER(0)) demux4_int1 ( + .clk(clk), .reset(reset), .clear(clear), + .header(), .dest(dest[1:0]), + .i_tdata(i_tdata_int1), .i_tlast(i_tlast_int1), .i_tvalid(i_tvalid_int1), .i_tready(i_tready_int1), + .o0_tdata(o4_tdata), .o0_tlast(o4_tlast), .o0_tvalid(o4_tvalid), .o0_tready(o4_tready), + .o1_tdata(o5_tdata), .o1_tlast(o5_tlast), .o1_tvalid(o5_tvalid), .o1_tready(o5_tready), + .o2_tdata(o6_tdata), .o2_tlast(o6_tlast), .o2_tvalid(o6_tvalid), .o2_tready(o6_tready), + .o3_tdata(o7_tdata), .o3_tlast(o7_tlast), .o3_tvalid(o7_tvalid), .o3_tready(o7_tready) + ); + +endmodule // axi_demux4 diff --git a/fpga/usrp3/lib/fifo/axi_fifo.v b/fpga/usrp3/lib/fifo/axi_fifo.v new file mode 100644 index 000000000..2f42fc181 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo.v @@ -0,0 +1,169 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// + + + +// Block RAM AXI fifo + +// Special case SIZE <= 5 uses a short fifo + +module axi_fifo + #(parameter WIDTH=32, SIZE=9) + (input clk, input reset, input clear, + input [WIDTH-1:0] i_tdata, + input i_tvalid, + output i_tready, + output [WIDTH-1:0] o_tdata, + output o_tvalid, + input o_tready, + + output reg [15:0] space, + output reg [15:0] occupied); + + generate + if(SIZE<=5) begin + wire [5:0] space_short, occupied_short; + axi_fifo_short #(.WIDTH(WIDTH)) fifo_short + ( + .clk(clk), .reset(reset), .clear(clear), + .i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready), + .space(space_short), .occupied(occupied_short) + ); + always @* space <= {10'b0, space_short}; + always @* occupied <= {10'b0, occupied_short}; + end + else begin + + wire write = i_tvalid & i_tready; + wire read = o_tvalid & o_tready; + wire full, empty; + + assign i_tready = ~full; + assign o_tvalid = ~empty; + + // Read side states + localparam EMPTY = 0; + localparam PRE_READ = 1; + localparam READING = 2; + + reg [SIZE-1:0] wr_addr, rd_addr; + reg [1:0] read_state; + + reg empty_reg, full_reg; + always @(posedge clk) + if(reset) + wr_addr <= 0; + else if(clear) + wr_addr <= 0; + else if(write) + wr_addr <= wr_addr + 1; + + ram_2port #(.DWIDTH(WIDTH),.AWIDTH(SIZE)) + ram (.clka(clk), + .ena(1'b1), + .wea(write), + .addra(wr_addr), + .dia(i_tdata), + .doa(), + + .clkb(clk), + .enb((read_state==PRE_READ)|read), + .web(1'b0), + .addrb(rd_addr), + .dib({WIDTH{1'b1}}), + .dob(o_tdata)); + + always @(posedge clk) + if(reset) + begin + read_state <= EMPTY; + rd_addr <= 0; + empty_reg <= 1; + end + else + if(clear) + begin + read_state <= EMPTY; + rd_addr <= 0; + empty_reg <= 1; + end + else + case(read_state) + EMPTY : + if(write) + begin + //rd_addr <= wr_addr; + read_state <= PRE_READ; + end + PRE_READ : + begin + read_state <= READING; + empty_reg <= 0; + rd_addr <= rd_addr + 1; + end + + READING : + if(read) + if(rd_addr == wr_addr) + begin + empty_reg <= 1; + if(write) + read_state <= PRE_READ; + else + read_state <= EMPTY; + end + else + rd_addr <= rd_addr + 1; + endcase // case(read_state) + + wire [SIZE-1:0] dont_write_past_me = rd_addr - 2; + wire becoming_full = wr_addr == dont_write_past_me; + + always @(posedge clk) + if(reset) + full_reg <= 0; + else if(clear) + full_reg <= 0; + else if(read & ~write) + full_reg <= 0; + //else if(write & ~read & (wr_addr == (rd_addr-3))) + else if(write & ~read & becoming_full) + full_reg <= 1; + + //assign empty = (read_state != READING); + assign empty = empty_reg; + + // assign full = ((rd_addr - 1) == wr_addr); + assign full = full_reg; + + ////////////////////////////////////////////// + // space and occupied are for diagnostics only + // not guaranteed exact + + localparam NUMLINES = (1<<SIZE); + always @(posedge clk) + if(reset) + space <= NUMLINES; + else if(clear) + space <= NUMLINES; + else if(read & ~write) + space <= space + 16'b1; + else if(write & ~read) + space <= space - 16'b1; + + always @(posedge clk) + if(reset) + occupied <= 16'b0; + else if(clear) + occupied <= 16'b0; + else if(read & ~write) + occupied <= occupied - 16'b1; + else if(write & ~read) + occupied <= occupied + 16'b1; + + end + endgenerate + +endmodule // fifo_long diff --git a/fpga/usrp3/lib/fifo/axi_fifo32_to_fifo64.v b/fpga/usrp3/lib/fifo/axi_fifo32_to_fifo64.v new file mode 100644 index 000000000..1df69d449 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo32_to_fifo64.v @@ -0,0 +1,38 @@ + +module axi_fifo32_to_fifo64 + (input clk, input reset, input clear, + input [31:0] i_tdata, input [1:0] i_tuser, input i_tlast, input i_tvalid, output i_tready, + output [63:0] o_tdata, output [2:0] o_tuser, output o_tlast, output o_tvalid, input o_tready + ); + + reg [31:0] holding; + + reg state; + + always @(posedge clk) + if(reset | clear) + state <= 0; + else + if(i_tvalid & i_tready) + case(state) + 0 : if(~i_tlast) state <= 1'b1; + 1 : state <= 1'b0; + default : state <= 1'b0; + endcase // case (state) + + always @(posedge clk) + if(i_tvalid & i_tready) + holding <= i_tdata; + + assign i_tready = (state == 0 && !i_tlast)? 1'b1 : o_tready; + assign o_tvalid = (state == 0 && !i_tlast)? 1'b0 : i_tvalid; + + assign o_tdata = (state == 0) ? {i_tdata, 32'h0} : { holding, i_tdata }; + assign o_tlast = i_tlast; + + wire [2:0] occ_in = (i_tuser == 0) ? 3'd4 : {1'b0, i_tuser}; + wire [2:0] occ_out = (state == 0) ? occ_in : (occ_in + 3'd4); + + assign o_tuser = ~o_tlast ? 3'd0 : occ_out; + +endmodule // axi_fifo32_to_fifo64 diff --git a/fpga/usrp3/lib/fifo/axi_fifo64_to_fifo32.v b/fpga/usrp3/lib/fifo/axi_fifo64_to_fifo32.v new file mode 100644 index 000000000..46b021600 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo64_to_fifo32.v @@ -0,0 +1,31 @@ + +module axi_fifo64_to_fifo32 + (input clk, input reset, input clear, + input [63:0] i_tdata, input [2:0] i_tuser, input i_tlast, input i_tvalid, output i_tready, + output [31:0] o_tdata, output [1:0] o_tuser, output o_tlast, output o_tvalid, input o_tready + ); + + wire short_last = i_tlast & ((i_tuser == 3'd1) | (i_tuser == 3'd2) | (i_tuser == 3'd3) | (i_tuser == 3'd4)); + + reg state; + always @(posedge clk) + if(reset | clear) + state <= 1'b0; + else + if(i_tvalid & o_tready) + case(state) + 1'b0 : + if(~short_last) + state <= 1'b1; + 1'b1 : + state <= 1'b0; + endcase // case (state) + + assign o_tdata = (state == 0) ? i_tdata[63:32] : i_tdata[31:0]; + assign o_tuser = o_tlast ? i_tuser[1:0] : 2'd0; + assign o_tlast = i_tlast & ((state == 1'b1) | short_last); + + assign o_tvalid = i_tvalid; + assign i_tready = o_tready & ((state == 1'b1) | short_last); + +endmodule // axi_fifo64_to_fifo32 diff --git a/fpga/usrp3/lib/fifo/axi_fifo_2clk.v b/fpga/usrp3/lib/fifo/axi_fifo_2clk.v new file mode 100644 index 000000000..f9f7beb88 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo_2clk.v @@ -0,0 +1,88 @@ +// +// Copyright 2013 Ettus Research LLC +// + + +// Special case SIZE <= 5 uses a short fifo + +module axi_fifo_2clk + #(parameter WIDTH=69, SIZE=9) + (input reset, + input i_aclk, + input [WIDTH-1:0] i_tdata, + input i_tvalid, + output i_tready, + input o_aclk, + output [WIDTH-1:0] o_tdata, + output o_tvalid, + input o_tready); + + wire write, read, empty, full; + assign i_tready = ~full; + assign write = i_tvalid & i_tready; + + wire [71:0] tdata_int; + wire tvalid_int, tready_int; + assign tvalid_int = ~empty; + assign read = tvalid_int & tready_int; + + wire [71:0] wr_data; + assign wr_data[WIDTH-1:0] = i_tdata; + wire [71:0] rd_data; + assign tdata_int = rd_data[WIDTH-1:0]; + + generate + if(WIDTH<72) begin + assign wr_data[71:WIDTH] = 0; + end + endgenerate + + generate + if(SIZE<=5) + fifo_short_2clk fifo_short_2clk + (.rst(reset), + .wr_clk(i_aclk), + .din(wr_data), // input [71 : 0] din + .wr_en(write), // input wr_en + .full(full), // output full + .wr_data_count(), // output [9 : 0] wr_data_count + + .rd_clk(o_aclk), // input rd_clk + .dout(rd_data), // output [71 : 0] dout + .rd_en(read), // input rd_en + .empty(empty), // output empty + .rd_data_count() // output [9 : 0] rd_data_count + ); + else + fifo_4k_2clk fifo_4k_2clk + (.rst(reset), + .wr_clk(i_aclk), + .din(wr_data), // input [71 : 0] din + .wr_en(write), // input wr_en + .full(full), // output full + .wr_data_count(), // output [9 : 0] wr_data_count + + .rd_clk(o_aclk), // input rd_clk + .dout(rd_data), // output [71 : 0] dout + .rd_en(read), // input rd_en + .empty(empty), // output empty + .rd_data_count() // output [9 : 0] rd_data_count + ); + endgenerate + + generate + if(SIZE>9) + axi_fifo #(.WIDTH(WIDTH), .SIZE(SIZE)) fifo_1clk + (.clk(o_aclk), .reset(reset), .clear(1'b0), + .i_tdata(tdata_int), .i_tvalid(tvalid_int), .i_tready(tready_int), + .o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready), + .space(), .occupied()); + else + begin + assign o_tdata = tdata_int; + assign o_tvalid = tvalid_int; + assign tready_int = o_tready; + end + endgenerate + +endmodule // axi_fifo_2clk diff --git a/fpga/usrp3/lib/fifo/axi_fifo_32_64_tb.v b/fpga/usrp3/lib/fifo/axi_fifo_32_64_tb.v new file mode 100644 index 000000000..9b104f1d9 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo_32_64_tb.v @@ -0,0 +1,114 @@ +`timescale 1ns/1ps + +module axi_fifo_32_64_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("axi_fifo_32_64_tb.vcd"); + initial $dumpvars(0,axi_fifo_32_64_tb); + + task send_packet; + input [63:0] data_start; + input [2:0] user; + input [31:0] len; + + begin + @(posedge clk); + {i_tuser, i_tlast, i_tdata} <= { 3'd0, 1'b0, data_start }; + repeat(len-1) + begin + i_tvalid <= 1; + @(posedge clk); + i_tdata <= i_tdata + 64'h0000_0002_0000_0002; + end + i_tuser <= user; + i_tlast <= 1; + @(posedge clk); + i_tvalid <= 1'b0; + @(posedge clk); + end + endtask // send_packet + + initial + begin + #1000 reset = 0; + #200000; + $finish; + end + + reg [63:0] i_tdata; + reg [2:0] i_tuser; + reg i_tlast; + reg i_tvalid; + wire i_tready; + + wire [63:0] i_tdata_int; + wire [2:0] i_tuser_int; + wire i_tlast_int, i_tvalid_int, i_tready_int; + + wire [63:0] o_tdata; + wire [31:0] o_tdata_int, o_tdata_int2; + wire [2:0] o_tuser; + wire [1:0] o_tuser_int, o_tuser_int2; + wire o_tlast, o_tlast_int, o_tvalid, o_tvalid_int, o_tready, o_tready_int; + wire o_tlast_int2, o_tvalid_int2, o_tready_int2; + + localparam RPT_COUNT = 16; + + initial + begin + i_tvalid <= 0; + + while(reset) + @(posedge clk); + @(posedge clk); + + send_packet(64'hA0000000_A0000001, 3'd7, 4); + @(posedge clk); + end // initial begin + + axi_fifo #(.WIDTH(68), .SIZE(10)) fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({i_tlast,i_tuser,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata({i_tlast_int,i_tuser_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int)); + + axi_fifo64_to_fifo32 dut + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata(i_tdata_int), .i_tuser(i_tuser_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int), + .o_tdata(o_tdata_int), .o_tuser(o_tuser_int), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int)); + + /* + axi_fifo #(.WIDTH(35), .SIZE(10)) fifo_middle + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({o_tlast_int,o_tuser_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int), + .o_tdata({o_tlast_int2,o_tuser_int2,o_tdata_int2}), .o_tvalid(o_tvalid_int2), .o_tready(o_tready_int2)); +*/ + assign o_tdata_int2 = o_tdata_int; + assign o_tlast_int2 = o_tlast_int; + assign o_tuser_int2 = o_tuser_int; + assign o_tvalid_int2 = o_tvalid_int; + assign o_tready_int = o_tready_int2; + + axi_fifo32_to_fifo64 dut2 + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata(o_tdata_int2), .i_tuser(o_tuser_int2), .i_tlast(o_tlast_int2), .i_tvalid(o_tvalid_int2), .i_tready(o_tready_int2), + .o_tdata(o_tdata), .o_tuser(o_tuser), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready)); + + assign o_tready = 1'b1; + + always @(posedge clk) + if(i_tvalid & i_tready) + $display("IN: TUSER %x\tTLAST %x\tTDATA %x", i_tuser, i_tlast, i_tdata); + + always @(posedge clk) + if(o_tvalid_int & o_tready_int) + $display("\t\t\t\t\t\tMIDDLE: TUSER %x\tTLAST %x\tTDATA %x", o_tuser_int, o_tlast_int, o_tdata_int); + + always @(posedge clk) + if(o_tvalid & o_tready) + $display("\t\t\t\t\t\t\t\t\t\t\tOUT: TUSER %x\tTLAST %x\tTDATA %x", o_tuser, o_tlast, o_tdata); + +endmodule // axi_fifo_32_64_tb diff --git a/fpga/usrp3/lib/fifo/axi_fifo_short.v b/fpga/usrp3/lib/fifo/axi_fifo_short.v new file mode 100644 index 000000000..e13993199 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo_short.v @@ -0,0 +1,110 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// +// 32 word FIFO with AXI4-STREAM interface. +// +// NOTE: This module uses the SRLC32E primitive explicitly and as such +// can only be used with Xilinx technology of the VIRTEX-6/SPARTAN-6/SIERIES-7 or newer. +// + +module axi_fifo_short + #(parameter WIDTH=32) + ( + input clk, + input reset, + input clear, + input [WIDTH-1:0] i_tdata, + input i_tvalid, + output i_tready, + output [WIDTH-1:0] o_tdata, + output o_tvalid, + input o_tready, + + output reg [5:0] space, + output reg [5:0] occupied + ); + + reg full, empty; + wire write = i_tvalid & i_tready; + wire read = o_tready & o_tvalid; + + assign i_tready = ~full; + assign o_tvalid = ~empty; + + reg [4:0] a; + genvar i; + + generate + for (i=0;i<WIDTH;i=i+1) + begin : gen_srlc32e + SRLC32E + srlc32e(.Q(o_tdata[i]), .Q31(), + .A(a), //.A0(a[0]),.A1(a[1]),.A2(a[2]),.A3(a[3]),.A4(a[4]), + .CE(write),.CLK(clk),.D(i_tdata[i])); + end + endgenerate + + always @(posedge clk) + if(reset) + begin + a <= 0; + empty <= 1; + full <= 0; + end + else if(clear) + begin + a <= 0; + empty <= 1; + full<= 0; + end + else if(read & ~write) + begin + full <= 0; + if(a==0) + empty <= 1; + else + a <= a - 1; + end + else if(write & ~read) + begin + empty <= 0; + if(~empty) + a <= a + 1; + if(a == 30) + full <= 1; + end + + // NOTE will fail if you write into a full fifo or read from an empty one + + ////////////////////////////////////////////////////////////// + // space and occupied are used for diagnostics, not + // guaranteed correct + + //assign space = full ? 0 : empty ? 16 : 15-a; + //assign occupied = empty ? 0 : full ? 16 : a+1; + + always @(posedge clk) + if(reset) + space <= 6'd32; + else if(clear) + space <= 6'd32; + else if(read & ~write) + space <= space + 6'd1; + else if(write & ~read) + space <= space - 6'd1; + + always @(posedge clk) + if(reset) + occupied <= 6'd0; + else if(clear) + occupied <= 6'd0; + else if(read & ~write) + occupied <= occupied - 6'd1; + else if(write & ~read) + occupied <= occupied + 6'd1; + +endmodule // axi_fifo_short + diff --git a/fpga/usrp3/lib/fifo/axi_fifo_tb.v b/fpga/usrp3/lib/fifo/axi_fifo_tb.v new file mode 100644 index 000000000..cb0e26ac5 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_fifo_tb.v @@ -0,0 +1,211 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// + + +module axi_fifo_tb(); + + reg clk, reset; + reg read_flag, write_flag; + + reg error; + reg [7:0] i_tdata, o_tdata_ref; + wire [7:0] o_tdata; + reg i_tvalid, o_tready; + wire o_tvalid, i_tready; + wire [15:0] space, occupied; + + always + #100 clk = ~clk; + + initial clk = 0; + + + axi_fifo + #( + .WIDTH(8), + .SIZE(8) + ) + 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_tvalid(o_tvalid), + .o_tready(o_tready), + .space(space), + .occupied(occupied) + ); + + + task write; + begin + write_flag <= 1; + i_tvalid <= 1'b1; + #1; + while (i_tready != 1'b1) + @(posedge clk); + #1; + @(posedge clk); + write_flag <= 0; + i_tvalid <= 1'b0; + i_tdata <= i_tdata + 8'h1; + end + endtask // write + + task read; + begin + read_flag <= 1; + o_tready <= 1'b1; + #1; + while (o_tvalid != 1'b1) + @(posedge clk); + #1; + @(posedge clk); + read_flag <= 0; + o_tready <= 1'b0; + if (o_tdata_ref != o_tdata) begin + $display("ERROR: Expected %d, got %d, at time %d",o_tdata_ref,o_tdata,$time); + error <= 1'b1; + end else + error <= 1'b0; + o_tdata_ref = o_tdata_ref + 8'h1; + end + endtask // read + + initial + begin + reset <= 1'b0; + error <= 1'b0; + i_tdata <= 8'b00; + o_tdata_ref <= 8'b00; + i_tvalid <= 1'b0; + o_tready <= 1'b0; + read_flag <= 0; + write_flag <= 0; + + repeat(10) @(posedge clk); + reset <= 1'b1; + repeat(10) @(posedge clk); + reset <= 1'b0; + @(posedge clk); + @(negedge clk); + + // FIFO Should be empty now, check avail space + if (space != 16'd256) + begin $display("ERROR: FIFO is empty, space should read 256 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd0) + begin $display("ERROR: FIFO is empty, occupied should read 0 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b1) + begin $display("ERROR: FIFO is empty, o_tvalid should be 0 at time %d",$time); error <= 1; end + @(posedge clk); + // Push 1 item onto FIFO, check fullness updates accordingly + write(); + @(posedge clk); + @(negedge clk); + if (space != 16'd255) + begin $display("ERROR: FIFO space should read 255 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd1) + begin $display("ERROR: FIFO occupied should read 1 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b0) + begin $display("ERROR: FIFO is not empty, o_tvalid should be 1 at time %d",$time); error <= 1; end + // Pop FIFO once, check it goes back empty OK. + @(posedge clk); + read(); + @(posedge clk); + @(negedge clk); + if (space != 16'd256) + begin $display("ERROR: FIFO is empty, space should read 256 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd0) + begin $display("ERROR: FIFO is empty, occupied should read 0 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b1) + begin $display("ERROR: FIFO is empty, o_tvalid should be 0 at time %d",$time); error <= 1; end + // Push FIFO 255 times and see if it goes full incorrectly + repeat(255) begin + @(posedge clk); + write(); + end + @(posedge clk); + @(negedge clk); + if (space != 16'd1) + begin $display("ERROR: FIFO is nearly full, space should read 1 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd255) + begin $display("ERROR: FIFO is nearly full, occupied should read 255 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b0) + begin $display("ERROR: FIFO is nearly full, o_tvalid should be 1 at time %d",$time); error <= 1; end + if (i_tready == 1'b0) + begin $display("ERROR: FIFO is nearly full, i_tready should be 1 at time %d",$time); error <= 1; end + // Push FIFO one more time, now it should be full + @(posedge clk); + write(); + @(posedge clk); + @(negedge clk); + if (space != 16'd0) + begin $display("ERROR: FIFO is full, space should read 0 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd256) + begin $display("ERROR: FIFO is full, occupied should read 256 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b0) + begin $display("ERROR: FIFO is full, o_tvalid should be 1 at time %d",$time); error <= 1; end + if (i_tready == 1'b1) + begin $display("ERROR: FIFO is full, i_tready should be 0 at time %d",$time); error <= 1; end + // POP FIFO once, check it went nonfull. + @(posedge clk); + read(); + @(posedge clk); + @(negedge clk); + if (space != 16'd1) + begin $display("ERROR: FIFO is nearly full, space should read 1 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd255) + begin $display("ERROR: FIFO is nearly full, occupied should read 255 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b0) + begin $display("ERROR: FIFO is nearly full, o_tvalid should be 1 at time %d",$time); error <= 1; end + if (i_tready == 1'b0) + begin $display("ERROR: FIFO is nearly full, i_tready should be 1 at time %d",$time); error <= 1; end + // Take FIFO to empty state + repeat(255) begin + @(posedge clk); + read(); + end + @(posedge clk); + @(negedge clk); + if (space != 16'd256) + begin $display("ERROR: FIFO is empty, space should read 256 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd0) + begin $display("ERROR: FIFO is empty, occupied should read 0 not %d at time %d",occupied,$time); error <= 1; end + if (o_tvalid == 1'b1) + begin $display("ERROR: FIFO is empty, o_tvalid should be 0 at time %d",$time); error <= 1; end + // Push 1 item onto FIFO + @(posedge clk); + write(); + @(posedge clk); + // Now write twice as fast as we read, and write 256 times, which should leave, 129 elements in FIFO. + fork + repeat(256) begin + write(); + @(posedge clk); + end + repeat(128) begin + read(); + @(posedge clk); + @(posedge clk); + end + join + @(posedge clk); + if (space != 16'd127) + begin $display("ERROR: FIFO space should read 127 not %d at time %d",space,$time); error <= 1; end + if (occupied != 16'd129) + begin $display("ERROR: FIFO occupied should read 129 not %d at time %d",occupied,$time); error <= 1; end + + + + // + // END + // + repeat(10) @(posedge clk); + $finish; + end // initial begin + +endmodule // axi_fifo_tb diff --git a/fpga/usrp3/lib/fifo/axi_loopback.v b/fpga/usrp3/lib/fifo/axi_loopback.v new file mode 100644 index 000000000..aea9ae22c --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_loopback.v @@ -0,0 +1,71 @@ +// Copyright 2012 Ettus Research LLC + +// +// axi_loopback.v +// +// Loopback all data assuming it's in CHDR format, and swap SRC/DST in the SID in the process +// thus reflecting it back to it's origin...in theory! +// + +module axi_loopback + ( + input clk, + input reset, + // Input AXIS + input [WIDTH-1:0] i_tdata, + input i_tlast, + input i_tvalid, + output i_tready, + // Output AXIS + output [WIDTH-1:0] o_tdata, + output o_tlast, + output o_tvalid, + input o_tready + ); + + + localparam WIDTH=64; + + wire [WIDTH-1:0] fifoin_tdata,fifoout_tdata,dmux_tdata; + wire fifoin_tlast,dmux_tlast; + wire fifoin_tvalid,dmux_tvalid; + wire fifoin_tready,dmux_tready; + + // Since most real endpoints go via Demux4 place one in here to look for bugs. + axi_demux4 #(.ACTIVE_CHAN(4'b0001), .WIDTH(WIDTH)) demux + (.clk(clk), .reset(reset), .clear(1'b0), + .header(), .dest(2'b00), + .i_tdata(i_tdata), .i_tlast(i_tlast), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o0_tdata(dmux_tdata), .o0_tlast(dmux_tlast), .o0_tvalid(dmux_tvalid), .o0_tready(dmux_tready), + .o1_tdata(), .o1_tlast(), .o1_tvalid(), .o1_tready(1'b1), + .o2_tdata(), .o2_tlast(), .o2_tvalid(), .o2_tready(1'b1), + .o3_tdata(), .o3_tlast(), .o3_tvalid(), .o3_tready(1'b1)); + + axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short1 + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({dmux_tlast,dmux_tdata}), .i_tvalid(dmux_tvalid), .i_tready(dmux_tready), + .o_tdata({fifoin_tlast,fifoin_tdata}), .o_tvalid(fifoin_tvalid), .o_tready(fifoin_tready), + .space(), .occupied()); + + reg header; + always @(posedge clk) begin + if(reset) begin + header <= 1'b1; + end else if (header) begin + if(fifoin_tvalid & fifoin_tready & ~fifoin_tlast) header <= 1'b0; + end else begin + if(fifoin_tvalid & fifoin_tready & fifoin_tlast) header <= 1'b1; + end + end + + assign fifoout_tdata = header ? + {fifoin_tdata[63:32] ,fifoin_tdata[15:0],fifoin_tdata[31:16]} : + fifoin_tdata; + + axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short2 + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({fifoin_tlast,fifoout_tdata}), .i_tvalid(fifoin_tvalid), .i_tready(fifoin_tready), + .o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready), + .space(), .occupied()); + +endmodule // axi_loopback diff --git a/fpga/usrp3/lib/fifo/axi_mux4.v b/fpga/usrp3/lib/fifo/axi_mux4.v new file mode 100644 index 000000000..91bfe43a9 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_mux4.v @@ -0,0 +1,112 @@ + +// Copyright 2012 Ettus Research LLC +// axi_mux -- takes 4 64-bit AXI stream, merges them to 1 output channel +// Round-robin if PRIO=0, priority if PRIO=1 (lower number ports get priority) +// Bubble cycles are inserted after each packet in PRIO mode, or on wraparound in Round Robin mode + +module axi_mux4 + #(parameter PRIO=0, + parameter WIDTH=64, + parameter BUFFER=0) + (input clk, input reset, input clear, + input [WIDTH-1:0] i0_tdata, input i0_tlast, input i0_tvalid, output i0_tready, + input [WIDTH-1:0] i1_tdata, input i1_tlast, input i1_tvalid, output i1_tready, + input [WIDTH-1:0] i2_tdata, input i2_tlast, input i2_tvalid, output i2_tready, + input [WIDTH-1:0] i3_tdata, input i3_tlast, input i3_tvalid, output i3_tready, + output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready); + + wire [WIDTH-1:0] o_tdata_int; + wire o_tlast_int, o_tvalid_int, o_tready_int; + + reg [3:0] mx_state; + localparam MX_IDLE = 4'b0000; + localparam MX_0 = 4'b0001; + localparam MX_1 = 4'b0010; + localparam MX_2 = 4'b0100; + localparam MX_3 = 4'b1000; + + always @(posedge clk) + if(reset | clear) + mx_state <= MX_IDLE; + else + case (mx_state) + MX_IDLE : + if(i0_tvalid) + mx_state <= MX_0; + else if(i1_tvalid) + mx_state <= MX_1; + else if(i2_tvalid) + mx_state <= MX_2; + else if(i3_tvalid) + mx_state <= MX_3; + + MX_0 : + if(o_tready_int & o_tvalid_int & o_tlast_int) + if(PRIO) + mx_state <= MX_IDLE; + else if(i1_tvalid) + mx_state <= MX_1; + else if(i2_tvalid) + mx_state <= MX_2; + else if(i3_tvalid) + mx_state <= MX_3; + else + mx_state <= MX_IDLE; + + MX_1 : + if(o_tready_int & o_tvalid_int & o_tlast_int) + if(PRIO) + mx_state <= MX_IDLE; + else if(i2_tvalid) + mx_state <= MX_2; + else if(i3_tvalid) + mx_state <= MX_3; + else + mx_state <= MX_IDLE; + + MX_2 : + if(o_tready_int & o_tvalid_int & o_tlast_int) + if(PRIO) + mx_state <= MX_IDLE; + else if(i3_tvalid) + mx_state <= MX_3; + else + mx_state <= MX_IDLE; + + MX_3 : + if(o_tready_int & o_tvalid_int & o_tlast_int) + if(PRIO) + mx_state <= MX_IDLE; + else + mx_state <= MX_IDLE; + + default : + mx_state <= MX_IDLE; + endcase // case (mx_state) + + assign {i3_tready, i2_tready, i1_tready, i0_tready} = mx_state & {4{o_tready_int}}; + + assign o_tvalid_int = |(mx_state & ({i3_tvalid, i2_tvalid, i1_tvalid, i0_tvalid})); + + assign {o_tlast_int, o_tdata_int} = mx_state[3] ? {i3_tlast, i3_tdata} : + mx_state[2] ? {i2_tlast, i2_tdata} : + mx_state[1] ? {i1_tlast, i1_tdata} : + {i0_tlast, i0_tdata}; + + generate + if(BUFFER == 0) + begin + assign o_tdata = o_tdata_int; + assign o_tlast = o_tlast_int; + assign o_tvalid = o_tvalid_int; + assign o_tready_int = o_tready; + end + else + axi_fifo_short #(.WIDTH(WIDTH+1)) axi_fifo_short + (.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()); + endgenerate + +endmodule // axi__mux4 diff --git a/fpga/usrp3/lib/fifo/axi_mux8.v b/fpga/usrp3/lib/fifo/axi_mux8.v new file mode 100644 index 000000000..5e620d3aa --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_mux8.v @@ -0,0 +1,56 @@ + +// Copyright 2012 Ettus Research LLC +// axi_mux -- takes 8 64-bit AXI stream, merges them to 1 output channel +// Round-robin if PRIO=0, priority if PRIO=1 (lower number ports get priority) +// Bubble cycles are inserted after each packet in PRIO mode, or on wraparound in Round Robin mode + +module axi_mux8 #( + parameter PRIO=0, + parameter WIDTH=64, + parameter BUFFER=0 +) ( + input clk, input reset, input clear, + input [WIDTH-1:0] i0_tdata, input i0_tlast, input i0_tvalid, output i0_tready, + input [WIDTH-1:0] i1_tdata, input i1_tlast, input i1_tvalid, output i1_tready, + input [WIDTH-1:0] i2_tdata, input i2_tlast, input i2_tvalid, output i2_tready, + input [WIDTH-1:0] i3_tdata, input i3_tlast, input i3_tvalid, output i3_tready, + input [WIDTH-1:0] i4_tdata, input i4_tlast, input i4_tvalid, output i4_tready, + input [WIDTH-1:0] i5_tdata, input i5_tlast, input i5_tvalid, output i5_tready, + input [WIDTH-1:0] i6_tdata, input i6_tlast, input i6_tvalid, output i6_tready, + input [WIDTH-1:0] i7_tdata, input i7_tlast, input i7_tvalid, output i7_tready, + output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready +); + + wire [WIDTH-1:0] o_tdata_int0, o_tdata_int1; + wire o_tlast_int0, o_tlast_int1; + wire o_tvalid_int0, o_tvalid_int1; + wire o_tready_int0, o_tready_int1; + + axi_mux4 #(.PRIO(PRIO), .WIDTH(WIDTH), .BUFFER(0)) mux4_int0 ( + .clk(clk), .reset(reset), .clear(clear), + .i0_tdata(i0_tdata), .i0_tlast(i0_tlast), .i0_tvalid(i0_tvalid), .i0_tready(i0_tready), + .i1_tdata(i1_tdata), .i1_tlast(i1_tlast), .i1_tvalid(i1_tvalid), .i1_tready(i1_tready), + .i2_tdata(i2_tdata), .i2_tlast(i2_tlast), .i2_tvalid(i2_tvalid), .i2_tready(i2_tready), + .i3_tdata(i3_tdata), .i3_tlast(i3_tlast), .i3_tvalid(i3_tvalid), .i3_tready(i3_tready), + .o_tdata(o_tdata_int0), .o_tlast(o_tlast_int0), .o_tvalid(o_tvalid_int0), .o_tready(o_tready_int0) + ); + + axi_mux4 #(.PRIO(PRIO), .WIDTH(WIDTH), .BUFFER(0)) mux4_int1 ( + .clk(clk), .reset(reset), .clear(clear), + .i0_tdata(i4_tdata), .i0_tlast(i4_tlast), .i0_tvalid(i4_tvalid), .i0_tready(i4_tready), + .i1_tdata(i5_tdata), .i1_tlast(i5_tlast), .i1_tvalid(i5_tvalid), .i1_tready(i5_tready), + .i2_tdata(i6_tdata), .i2_tlast(i6_tlast), .i2_tvalid(i6_tvalid), .i2_tready(i6_tready), + .i3_tdata(i7_tdata), .i3_tlast(i7_tlast), .i3_tvalid(i7_tvalid), .i3_tready(i7_tready), + .o_tdata(o_tdata_int1), .o_tlast(o_tlast_int1), .o_tvalid(o_tvalid_int1), .o_tready(o_tready_int1) + ); + + axi_mux4 #(.PRIO(PRIO), .WIDTH(WIDTH), .BUFFER(BUFFER)) mux2 ( + .clk(clk), .reset(reset), .clear(clear), + .i0_tdata(o_tdata_int0), .i0_tlast(o_tlast_int0), .i0_tvalid(o_tvalid_int0), .i0_tready(o_tready_int0), + .i1_tdata(o_tdata_int1), .i1_tlast(o_tlast_int1), .i1_tvalid(o_tvalid_int1), .i1_tready(o_tready_int1), + .i2_tdata(0), .i2_tlast(1'b0), .i2_tvalid(1'b0), .i2_tready(), + .i3_tdata(0), .i3_tlast(1'b0), .i3_tvalid(1'b0), .i3_tready(), + .o_tdata(o_tdata), .o_tlast(o_tlast), .o_tvalid(o_tvalid), .o_tready(o_tready) + ); + +endmodule // axi_mux8 diff --git a/fpga/usrp3/lib/fifo/axi_packet_gate.v b/fpga/usrp3/lib/fifo/axi_packet_gate.v new file mode 100644 index 000000000..6d10a8cf4 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_packet_gate.v @@ -0,0 +1,78 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// Hold packets in fifo until they are complete. This prevents slowly-built packets +// from clogging up the downstream. This block will hold up to 255 packets. +// Will permanently block if a single packet is bigger than the fifo. +// Will also drop any packet with an error signalled on the last line. +// This is useful after an ethernet interface to drop packets with bad CRCs. + +module axi_packet_gate + #(parameter WIDTH=68, + parameter SIZE=10) + (input clk, + input reset, + input clear, + input [WIDTH-1:0] i_tdata, + input i_tlast, + input i_terror, + input i_tvalid, + output i_tready, + output [WIDTH-1:0] o_tdata, + output o_tlast, + output o_tvalid, + input o_tready + ); + + reg [7:0] num_packets; + reg dump; + + wire o_tvalid_int, o_tready_int, i_tvalid_int, i_tready_int; + + assign i_tvalid_int = (~dump & (num_packets != 8'hFF)) ? i_tvalid : 1'b0; + assign i_tready = (~dump & (num_packets != 8'hFF)) ? i_tready_int : 1'b0; + + assign o_tvalid = (num_packets != 8'h0) ? o_tvalid_int : 1'b0; + assign o_tready_int = (num_packets != 8'h0) ? o_tready : 1'b0; + + wire last_in = i_tvalid_int & i_tready_int & i_tlast; + wire last_out = o_tvalid_int & o_tready_int & o_tlast; + + always @(posedge clk) + if(reset | clear) + begin + num_packets <= 8'd0; + dump <= 1'b0; + end + else + if(dump) + if(num_packets != 8'd0) + if(last_out) + num_packets <= num_packets - 8'd1; + else + ; + else + dump <= 1'b0; + else + if(last_in) + if(i_terror) + begin + dump <= 1'b1; + if(last_out) + num_packets <= num_packets - 8'd1; + end + else if(~last_out) + num_packets <= num_packets + 8'd1; + else + ; + else if(last_out) + num_packets <= num_packets - 8'd1; + + axi_fifo #(.SIZE(SIZE), .WIDTH(WIDTH+1)) axi_fifo + (.clk(clk), .reset(reset), .clear(clear | (dump & (num_packets == 8'd0))), + .i_tdata({i_tlast,i_tdata}), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int), + .o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int)); + +endmodule // axi_packet_gate diff --git a/fpga/usrp3/lib/fifo/axi_packet_gate_tb.v b/fpga/usrp3/lib/fifo/axi_packet_gate_tb.v new file mode 100644 index 000000000..30f8c76e4 --- /dev/null +++ b/fpga/usrp3/lib/fifo/axi_packet_gate_tb.v @@ -0,0 +1,106 @@ +`timescale 1ns/1ps + +module axi_packet_gate_tb(); + + reg clk = 0; + reg reset = 1; + + always #10 clk = ~clk; + + initial $dumpfile("axi_packet_gate_tb.vcd"); + initial $dumpvars(0,axi_packet_gate_tb); + + task send_packet; + input [63:0] data_start; + input [2:0] user; + input [31:0] len; + input error; + + begin + // Send a packet + @(posedge clk); + {i_terror, i_tuser, i_tlast, i_tdata} <= { 1'b0, user, 1'b0, data_start }; + repeat(len-1) + begin + i_tvalid <= 1; + @(posedge clk); + i_tdata <= i_tdata + 1; + end + i_tlast <= 1; + i_terror <= error; + 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'hB0,3'd0, 16, 0); + 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; + + axi_fifo #(.WIDTH(69), .SIZE(10)) fifo + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({i_terror,i_tlast,i_tuser,i_tdata}), .i_tvalid(i_tvalid), .i_tready(i_tready), + .o_tdata({i_terror_int,i_tlast_int,i_tuser_int,i_tdata_int}), .o_tvalid(i_tvalid_int), .o_tready(i_tready_int)); + + axi_packet_gate #(.WIDTH(67), .SIZE(10)) dut + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({i_tuser_int,i_tdata_int}), .i_terror(i_terror_int), .i_tlast(i_tlast_int), .i_tvalid(i_tvalid_int), .i_tready(i_tready_int), + .o_tdata({o_tuser_int,o_tdata_int}), .o_tlast(o_tlast_int), .o_tvalid(o_tvalid_int), .o_tready(o_tready_int)); + + axi_fifo #(.WIDTH(68), .SIZE(10)) fifo_out + (.clk(clk), .reset(reset), .clear(1'b0), + .i_tdata({o_tlast_int,o_tuser_int,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)); + + always @(posedge clk) + if(o_tvalid & o_tready) + $display("TUSER %x\tTLAST %x\tTDATA %x",o_tuser,o_tlast, o_tdata); + +endmodule // axi_packet_gate_tb diff --git a/fpga/usrp3/lib/fifo/monitor_axi_fifo.v b/fpga/usrp3/lib/fifo/monitor_axi_fifo.v new file mode 100644 index 000000000..f5d73455f --- /dev/null +++ b/fpga/usrp3/lib/fifo/monitor_axi_fifo.v @@ -0,0 +1,123 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// +// This module is instantiated in parallel with a FIFO with AXI4-STREAM interfaces. +// It tracks how many complete packets are contained within the FIFO, and also indicates +// when the first word of a packet is presented on the FIFO outputs. +// + + +module monitor_axi_fifo + #( + parameter COUNT_BITS=8 + ) + ( + input clk, + input reset, + input clear, + // Monitored FIFO signals + input i_tvalid, + input i_tready, + input i_tlast, + input o_tvalid, + input o_tready, + input o_tlast, + // FIFO status outputs + output reg [COUNT_BITS-1:0] pkt_count, // Exact whole packet count + output pkt_present // Flags any whole packets present + + ); + + localparam WAIT_SOF = 0; + localparam WAIT_EOF = 1; + + + reg in_state, out_state; + reg pause_tx; + + // + // Count packets arriving into large FIFO + // + always @(posedge clk) + if (reset | clear) begin + in_state <= WAIT_SOF; + end else + case(in_state) + // + // After RESET or the EOF of previous packet, the first cycle with + // input valid and input ready asserted is the SOF. + // + WAIT_SOF: + if (i_tvalid && i_tready) begin + in_state <= WAIT_EOF; + end else begin + in_state <= WAIT_SOF; + end + // + // EOF is signalled by the assertion i_tlast whilst input valid and ready are asserted. + // + WAIT_EOF: + if (i_tlast && i_tvalid && i_tready) begin + in_state <= WAIT_SOF; + end else begin + in_state <= WAIT_EOF; + end + endcase // case(in_state) + + + + // + // Count packets leaving large FIFO + // + always @(posedge clk) + if (reset | clear) begin + out_state <= WAIT_SOF; + end else + case(out_state) + // + // After RESET or the EOF of previous packet, the first cycle with + // output valid and output ready asserted is the SOF. + // + WAIT_SOF: + if (o_tvalid && o_tready) begin + out_state <= WAIT_EOF; + end else begin + out_state <= WAIT_SOF; + end + // + // EOF is signalled by o_tlast asserted whilst output valid and ready asserted. + // + WAIT_EOF: + if (o_tlast && o_tvalid && o_tready) begin + out_state <= WAIT_SOF; + end else begin + out_state <= WAIT_EOF; + end + endcase // case(in_state) + + + // + // Count packets in FIFO. + // No protection on counter wrap, + // unclear how to gracefully deal with it. + // Perhaps generate Error IRQ so that S/W could clean up? + // Configure so that the pkt_count is ample for the application. + // + always @(posedge clk) + if (reset | clear) + pkt_count <= 0; + else if (((out_state==WAIT_EOF) && o_tlast && o_tvalid && o_tready ) && + ((in_state==WAIT_EOF) && i_tlast && i_tvalid && i_tready)) + pkt_count <= pkt_count; + else if ((out_state==WAIT_EOF) && o_tlast && o_tvalid && o_tready) + pkt_count <= pkt_count - 1; + else if ((in_state==WAIT_EOF) && i_tlast && i_tvalid && i_tready) + pkt_count <= pkt_count + 1; + + // Non-zero packet count indicates packet(s) present. + assign pkt_present = |pkt_count; + +endmodule // count_tx_packets diff --git a/fpga/usrp3/lib/fifo/shortfifo.v b/fpga/usrp3/lib/fifo/shortfifo.v new file mode 100644 index 000000000..fbf92e2ed --- /dev/null +++ b/fpga/usrp3/lib/fifo/shortfifo.v @@ -0,0 +1,92 @@ +// +// Copyright 2011 Ettus Research LLC +// + + + +module shortfifo + #(parameter WIDTH=32) + (input clk, input rst, + input [WIDTH-1:0] datain, + output [WIDTH-1:0] dataout, + input read, + input write, + input clear, + output reg full, + output reg empty, + output reg [4:0] space, + output reg [4:0] occupied); + + reg [3:0] a; + genvar i; + + generate + for (i=0;i<WIDTH;i=i+1) + begin : gen_srl16 + SRL16E + srl16e(.Q(dataout[i]), + .A0(a[0]),.A1(a[1]),.A2(a[2]),.A3(a[3]), + .CE(write),.CLK(clk),.D(datain[i])); + end + endgenerate + + always @(posedge clk) + if(rst) + begin + a <= 0; + empty <= 1; + full <= 0; + end + else if(clear) + begin + a <= 0; + empty <= 1; + full<= 0; + end + else if(read & ~write) + begin + full <= 0; + if(a==0) + empty <= 1; + else + a <= a - 1; + end + else if(write & ~read) + begin + empty <= 0; + if(~empty) + a <= a + 1; + if(a == 14) + full <= 1; + end + + // NOTE will fail if you write into a full fifo or read from an empty one + + ////////////////////////////////////////////////////////////// + // space and occupied are used for diagnostics, not + // guaranteed correct + + //assign space = full ? 0 : empty ? 16 : 15-a; + //assign occupied = empty ? 0 : full ? 16 : a+1; + + always @(posedge clk) + if(rst) + space <= 16; + else if(clear) + space <= 16; + else if(read & ~write) + space <= space + 1; + else if(write & ~read) + space <= space - 1; + + always @(posedge clk) + if(rst) + occupied <= 0; + else if(clear) + occupied <= 0; + else if(read & ~write) + occupied <= occupied - 1; + else if(write & ~read) + occupied <= occupied + 1; + +endmodule // shortfifo |