diff options
Diffstat (limited to 'fpga/usrp2/fifo')
39 files changed, 3627 insertions, 0 deletions
diff --git a/fpga/usrp2/fifo/.gitignore b/fpga/usrp2/fifo/.gitignore new file mode 100644 index 000000000..866f1faad --- /dev/null +++ b/fpga/usrp2/fifo/.gitignore @@ -0,0 +1,3 @@ +*.vcd +*.lxt +a.out diff --git a/fpga/usrp2/fifo/Makefile.srcs b/fpga/usrp2/fifo/Makefile.srcs new file mode 100644 index 000000000..31b1f505a --- /dev/null +++ b/fpga/usrp2/fifo/Makefile.srcs @@ -0,0 +1,37 @@ +# +# Copyright 2010 Ettus Research LLC +# + +################################################## +# FIFO Sources +################################################## +FIFO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../fifo/, \ +buffer_int.v \ +buffer_int2.v \ +buffer_pool.v \ +crossbar36.v \ +dsp_framer36.v \ +fifo_2clock.v \ +fifo_2clock_cascade.v \ +ll8_shortfifo.v \ +fifo_short.v \ +fifo_long.v \ +fifo_cascade.v \ +fifo36_to_ll8.v \ +ll8_to_fifo36.v \ +fifo19_to_ll8.v \ +ll8_to_fifo19.v \ +fifo36_to_fifo19.v \ +fifo19_to_fifo36.v \ +fifo36_mux.v \ +fifo36_demux.v \ +packet_router.v \ +splitter36.v \ +valve36.v \ +fifo_pacer.v \ +packet_dispatcher36_x3.v \ +packet_generator32.v \ +packet_generator.v \ +packet_verifier32.v \ +packet_verifier.v \ +)) diff --git a/fpga/usrp2/fifo/buffer_int.v b/fpga/usrp2/fifo/buffer_int.v new file mode 100644 index 000000000..49ded8c8d --- /dev/null +++ b/fpga/usrp2/fifo/buffer_int.v @@ -0,0 +1,167 @@ + +// FIFO Interface to the 2K buffer RAMs +// Read port is read-acknowledge +// FIXME do we want to be able to interleave reads and writes? + +module buffer_int + #(parameter BUF_NUM = 0, + parameter BUF_SIZE = 9) + (// Control Interface + input clk, + input rst, + input [31:0] ctrl_word, + input go, + output done, + output error, + output idle, + + // Buffer Interface + output en_o, + output we_o, + output reg [BUF_SIZE-1:0] addr_o, + output [31:0] dat_to_buf, + input [31:0] dat_from_buf, + + // Write FIFO Interface + input [31:0] wr_data_i, + input [3:0] wr_flags_i, + input wr_ready_i, + output wr_ready_o, + + // Read FIFO Interface + output [31:0] rd_data_o, + output [3:0] rd_flags_o, + output rd_ready_o, + input rd_ready_i + ); + + reg [31:0] ctrl_reg; + reg go_reg; + + always @(posedge clk) + go_reg <= go; + + always @(posedge clk) + if(rst) + ctrl_reg <= 0; + else + if(go & (ctrl_word[31:28] == BUF_NUM)) + ctrl_reg <= ctrl_word; + + wire [BUF_SIZE-1:0] firstline = ctrl_reg[BUF_SIZE-1:0]; + wire [BUF_SIZE-1:0] lastline = ctrl_reg[2*BUF_SIZE-1:BUF_SIZE]; + + wire read = ctrl_reg[22]; + wire write = ctrl_reg[23]; + wire clear = ctrl_reg[24]; + //wire [2:0] port = ctrl_reg[27:25]; // Ignored in this block + //wire [3:0] buff_num = ctrl_reg[31:28]; // Ignored here ? + + localparam IDLE = 3'd0; + localparam PRE_READ = 3'd1; + localparam READING = 3'd2; + localparam WRITING = 3'd3; + localparam ERROR = 3'd4; + localparam DONE = 3'd5; + + reg [2:0] state; + reg rd_sop, rd_eop; + wire wr_sop, wr_eop, wr_error; + reg [1:0] rd_occ; + wire [1:0] wr_occ; + + always @(posedge clk) + if(rst) + begin + state <= IDLE; + rd_sop <= 0; + rd_eop <= 0; + rd_occ <= 0; + end + else + if(clear) + begin + state <= IDLE; + rd_sop <= 0; + rd_eop <= 0; + rd_occ <= 0; + end + else + case(state) + IDLE : + if(go_reg & read) + begin + addr_o <= firstline; + state <= PRE_READ; + end + else if(go_reg & write) + begin + addr_o <= firstline; + state <= WRITING; + end + + PRE_READ : + begin + state <= READING; + addr_o <= addr_o + 1; + rd_occ <= 2'b00; + rd_sop <= 1; + rd_eop <= 0; + end + + READING : + if(rd_ready_i) + begin + rd_sop <= 0; + addr_o <= addr_o + 1; + if(addr_o == lastline) + begin + rd_eop <= 1; + // FIXME assign occ here + rd_occ <= 0; + end + else + rd_eop <= 0; + if(rd_eop) + state <= DONE; + end + + WRITING : + begin + if(wr_ready_i) + begin + addr_o <= addr_o + 1; + if(wr_error) + begin + state <= ERROR; + // Save OCC flags here + end + else if((addr_o == lastline)||wr_eop) + state <= DONE; + end // if (wr_ready_i) + end // case: WRITING + + endcase // case(state) + + assign dat_to_buf = wr_data_i; + assign rd_data_o = dat_from_buf; + + assign rd_flags_o = { rd_occ[1:0], rd_eop, rd_sop }; + assign rd_ready_o = (state == READING); + + assign wr_sop = wr_flags_i[0]; + assign wr_eop = wr_flags_i[1]; + assign wr_occ = wr_flags_i[3:2]; + assign wr_error = wr_sop & wr_eop; + assign wr_ready_o = (state == WRITING); + + assign we_o = (state == WRITING); + //assign we_o = (state == WRITING) && wr_ready_i; // always write to avoid timing issue + + assign en_o = ~((state==READING)& ~rd_ready_i); // FIXME potential critical path + + assign done = (state == DONE); + assign error = (state == ERROR); + assign idle = (state == IDLE); + +endmodule // buffer_int diff --git a/fpga/usrp2/fifo/buffer_int2.v b/fpga/usrp2/fifo/buffer_int2.v new file mode 100644 index 000000000..532980aa2 --- /dev/null +++ b/fpga/usrp2/fifo/buffer_int2.v @@ -0,0 +1,177 @@ + +// FIFO Interface to the 2K buffer RAMs +// Read port is read-acknowledge +// FIXME do we want to be able to interleave reads and writes? + +module buffer_int2 + #(parameter BASE = 0, + parameter BUF_SIZE = 9) + (input clk, input rst, + input set_stb, input [7:0] set_addr, input [31:0] set_data, + output [31:0] status, + + // Wishbone interface to RAM + input wb_clk_i, + input wb_rst_i, + input wb_we_i, + input wb_stb_i, + input [15:0] wb_adr_i, + input [31:0] wb_dat_i, + output [31:0] wb_dat_o, + output reg wb_ack_o, + + // Write FIFO Interface + input [35:0] wr_data_i, + input wr_ready_i, + output wr_ready_o, + + // Read FIFO Interface + output [35:0] rd_data_o, + output rd_ready_o, + input rd_ready_i + ); + + reg [15:0] rd_addr, wr_addr; // Handle pkt bigger than buffer + wire [15:0] rd_addr_next = rd_addr + 1; + reg [15:0] rd_length; + + wire [31:0] ctrl; + wire wr_done, wr_error, wr_idle; + wire rd_done, rd_error, rd_idle; + wire we, en, go; + + wire read = ctrl[3]; + wire rd_clear = ctrl[2]; + wire write = ctrl[1]; + wire wr_clear = ctrl[0]; + + reg [2:0] rd_state, wr_state; + reg rd_sop, rd_eop; + wire wr_sop, wr_eop; + reg [1:0] rd_occ; + wire [1:0] wr_occ; + + localparam IDLE = 3'd0; + localparam PRE_READ = 3'd1; + localparam READING = 3'd2; + localparam WRITING = 3'd3; + localparam ERROR = 3'd4; + localparam DONE = 3'd5; + + // read state machine + always @(posedge clk) + if(rst | (rd_clear & go)) + begin + rd_state <= IDLE; + rd_sop <= 0; + rd_eop <= 0; + rd_occ <= 0; + end + else + case(rd_state) + IDLE : + if(go & read) + begin + rd_addr <= 0; + rd_state <= PRE_READ; + rd_length <= ctrl[31:16]; + end + + PRE_READ : + begin + rd_state <= READING; + rd_addr <= rd_addr_next; + rd_occ <= 2'b00; + rd_sop <= 1; + rd_eop <= 0; + end + + READING : + if(rd_ready_i) + begin + rd_sop <= 0; + rd_addr <= rd_addr_next; + if(rd_addr_next == rd_length) + begin + rd_eop <= 1; + // FIXME assign occ here + rd_occ <= 0; + end + else + rd_eop <= 0; + if(rd_eop) + rd_state <= DONE; + end + + endcase // case(rd_state) + + // write state machine + always @(posedge clk) + if(rst | (wr_clear & go)) + wr_state <= IDLE; + else + case(wr_state) + IDLE : + if(go & write) + begin + wr_addr <= 0; + wr_state <= WRITING; + end + + WRITING : + if(wr_ready_i) + begin + wr_addr <= wr_addr + 1; + if(wr_sop & wr_eop) + wr_state <= ERROR; // Should save OCC flags here + else if(wr_eop) + wr_state <= DONE; + end // if (wr_ready_i) + endcase // case(wr_state) + + assign rd_data_o[35:32] = { rd_occ[1:0], rd_eop, rd_sop }; + assign rd_ready_o = (rd_state == READING); + + assign wr_sop = wr_data_i[32]; + assign wr_eop = wr_data_i[33]; + assign wr_occ = wr_data_i[35:34]; + assign wr_ready_o = (wr_state == WRITING); + + assign we = (wr_state == WRITING); // always write to avoid timing issue + assign en = ~((rd_state==READING)& ~rd_ready_i); // FIXME potential critical path + + assign rd_done = (rd_state == DONE); + assign wr_done = (wr_state == DONE); + assign rd_error = (rd_state == ERROR); + assign wr_error = (wr_state == ERROR); + assign rd_idle = (rd_state == IDLE); + assign wr_idle = (wr_state == IDLE); + + wire [BUF_SIZE-1:0] wr_addr_clip = (|wr_addr[15:BUF_SIZE]) ? {BUF_SIZE{1'b1}} : wr_addr[BUF_SIZE-1:0]; + + ram_2port #(.DWIDTH(32),.AWIDTH(BUF_SIZE)) buffer_in // CPU reads here + (.clka(wb_clk_i),.ena(wb_stb_i),.wea(1'b0), + .addra(wb_adr_i[BUF_SIZE+1:2]),.dia(0),.doa(wb_dat_o), + .clkb(clk),.enb(1'b1),.web(we), + .addrb(wr_addr_clip),.dib(wr_data_i[31:0]),.dob()); + + ram_2port #(.DWIDTH(32),.AWIDTH(BUF_SIZE)) buffer_out // CPU writes here + (.clka(wb_clk_i),.ena(wb_stb_i),.wea(wb_we_i), + .addra(wb_adr_i[BUF_SIZE+1:2]),.dia(wb_dat_i),.doa(), + .clkb(clk),.enb(en),.web(1'b0), + .addrb(rd_addr[BUF_SIZE-1:0]),.dib(0),.dob(rd_data_o[31:0])); + + always @(posedge wb_clk_i) + if(wb_rst_i) + wb_ack_o <= 0; + else + wb_ack_o <= wb_stb_i & ~wb_ack_o; + + setting_reg #(.my_addr(BASE)) + sreg(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(ctrl),.changed(go)); + + assign status = { wr_addr, + 8'b0,1'b0,rd_idle,rd_error,rd_done, 1'b0,wr_idle,wr_error,wr_done}; + +endmodule // buffer_int2 diff --git a/fpga/usrp2/fifo/buffer_int_tb.v b/fpga/usrp2/fifo/buffer_int_tb.v new file mode 100644 index 000000000..df54dcc0b --- /dev/null +++ b/fpga/usrp2/fifo/buffer_int_tb.v @@ -0,0 +1,418 @@ + +module buffer_int_tb (); + + reg clk = 0; + reg rst = 1; + + initial #100 rst = 0; + always #5 clk = ~clk; + + wire en, we; + wire [8:0] addr; + wire [31:0] fifo2buf, buf2fifo; + + wire [31:0] rd_data_o; + wire [3:0] rd_flags_o; + wire rd_sop_o, rd_eop_o; + reg rd_error_i = 0, rd_read_i = 0; + + reg [31:0] wr_data_i = 0; + wire [3:0] wr_flags_i; + reg wr_eop_i = 0, wr_sop_i = 0; + reg wr_write_i = 0; + wire wr_ready_o, wr_full_o; + + reg clear = 0, write = 0, read = 0; + reg [8:0] firstline = 0, lastline = 0; + wire [3:0] step = 1; + wire [31:0] ctrl_word = {4'b0,3'b0,clear,write,read,step,lastline,firstline}; + reg go = 0; + wire done, error; + + assign wr_flags_i = {2'b00, wr_eop_i, wr_sop_i}; + assign rd_sop_o = rd_flags_o[0]; + assign rd_eop_o = rd_flags_o[1]; + + buffer_int buffer_int + (.clk(clk),.rst(rst), + .ctrl_word(ctrl_word),.go(go), + .done(done),.error(error), + + // Buffer Interface + .en_o(en),.we_o(we),.addr_o(addr), + .dat_to_buf(fifo2buf),.dat_from_buf(buf2fifo), + + // Write FIFO Interface + .wr_data_i(wr_data_i), .wr_flags_i(wr_flags_i), .wr_write_i(wr_write_i), .wr_ready_o(wr_ready_o), + + // Read FIFO Interface + .rd_data_o(rd_data_o), .rd_flags_o(rd_flags_o), .rd_ready_o(rd_ready_o), .rd_read_i(rd_read_i) + ); + + reg ram_en = 0, ram_we = 0; + reg [8:0] ram_addr = 0; + reg [31:0] ram_data = 0; + + ram_2port #(.DWIDTH(32),.AWIDTH(9)) ram_2port + (.clka(clk), .ena(ram_en), .wea(ram_we), .addra(ram_addr), .dia(ram_data), .doa(), + .clkb(clk), .enb(en), .web(we), .addrb(addr), .dib(fifo2buf), .dob(buf2fifo) ); + + initial + begin + @(negedge rst); + @(posedge clk); + FillRAM; + + ResetBuffer; + SetBufferRead(5,10); + $display("Testing full read, no wait states."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(6,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(5,10); + $display("Testing full read, 2 wait states."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(6,2); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(5,10); + $display("Testing partial read, 0 wait states, then nothing after last."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(3,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(5,10); + $display("Testing partial read, 0 wait states, then done at same time as last."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(2,0); + ReadALine; + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(5,10); + $display("Testing partial read, 3 wait states, then error at same time as last."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(2,3); + rd_error_i <= 1; + ReadALine; + rd_error_i <= 0; + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(500,511); + $display("Testing full read, to the end of the buffer."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(12,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(0,511); + $display("Testing full read, start to end of the buffer."); + while(!rd_sop_o) + @(posedge clk); + ReadLines(512,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(505,3); + $display("Testing full read, wraparound"); + while(!rd_sop_o) + @(posedge clk); + ReadLines(11,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(10,15); + $display("Testing Full Write, no wait states"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(6,0,72); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(18,23); + $display("Testing Full Write, 1 wait states"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(6,1,101); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(27,40); + $display("Testing Partial Write, 0 wait states"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(6,0,201); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(45,200); + $display("Testing Partial Write, 0 wait states, then done and write simultaneously"); + while(!wr_ready_o) + @(posedge clk); + wr_sop_i <= 1; wr_eop_i <= 0; + WriteLines(6,0,301); + wr_sop_i <= 0; wr_eop_i <= 1; + WriteALine(400); + wr_sop_i <= 0; wr_eop_i <= 0; + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(55,200); + $display("Testing Partial Write, 0 wait states, then error"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(6,0,501); + wr_sop_i <= 1; wr_eop_i <= 1; + WriteALine(400); + @(posedge clk); + repeat (10) + @(posedge clk); + wr_sop_i <= 0; wr_eop_i <= 0; + + ResetBuffer; + SetBufferRead(0,82); + $display("Testing read after all the writes"); + while(!rd_sop_o) + @(posedge clk); + ReadLines(83,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(508,4); + $display("Testing wraparound write"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(9,0,601); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(506,10); + $display("Reading wraparound write"); + while(!rd_sop_o) + @(posedge clk); + ReadLines(17,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferWrite(0,511); + $display("Testing Whole Buffer write"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(512,0,1000); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(0,511); + $display("Reading Whole Buffer write"); + while(!rd_sop_o) + @(posedge clk); + ReadLines(512,0); + repeat (10) + @(posedge clk); + + /* + ResetBuffer; + SetBufferWrite(5,10); + $display("Testing Write Too Many"); + while(!wr_ready_o) + @(posedge clk); + WriteLines(12,0,2000); + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(0,15); + $display("Reading back Write Too Many"); + while(!rd_sop_o) + @(posedge clk); + ReadLines(16,0); + repeat (10) + @(posedge clk); + */ + ResetBuffer; + SetBufferWrite(15,20); + $display("Testing Write One Less Than Full"); + while(!wr_ready_o) + @(posedge clk); + wr_sop_i <= 1; wr_eop_i <= 0; + WriteALine(400); + wr_sop_i <= 0; wr_eop_i <= 0; + WriteLines(3,0,2000); + wr_sop_i <= 0; wr_eop_i <= 1; + WriteALine(400); + wr_sop_i <= 0; wr_eop_i <= 0; + repeat (10) + @(posedge clk); + + ResetBuffer; + SetBufferRead(13,22); + $display("Reading back Write One Less Than Full"); + while(!rd_sop_o) + @(posedge clk); + ReadLines(10,0); + repeat (10) + @(posedge clk); + + ResetBuffer; + repeat(100) + @(posedge clk); + $finish; + end + + always @(posedge clk) + if(rd_read_i == 1'd1) + $display("READ Buffer %d, rd_sop_o %d, rd_eop_o %d", rd_data_o, rd_sop_o, rd_eop_o); + + always @(posedge clk) + if(wr_write_i == 1'd1) + $display("WRITE Buffer %d, wr_ready_o %d, wr_full_o %d", wr_data_i, wr_ready_o, wr_full_o); + + initial begin + $dumpfile("buffer_int_tb.lxt"); + $dumpvars(0,buffer_int_tb); + end + + task FillRAM; + begin + ram_addr <= 0; + ram_data <= 0; + @(posedge clk); + ram_en <= 1; + ram_we <= 1; + @(posedge clk); + repeat (511) + begin + ram_addr <= ram_addr + 1; + ram_data <= ram_data + 1; + ram_en <= 1; + ram_we <= 1; + @(posedge clk); + end + ram_en <= 0; + ram_we <= 0; + @(posedge clk); + $display("Filled the RAM"); + end + endtask // FillRAM + + task ResetBuffer; + begin + clear <= 1; read <= 0; write <= 0; + go <= 1; + @(posedge clk); + go <= 0; + @(posedge clk); + $display("Buffer Reset"); + end + endtask // ClearBuffer + + task SetBufferWrite; + input [8:0] start; + input [8:0] stop; + begin + clear <= 0; read <= 0; write <= 1; + firstline <= start; + lastline <= stop; + go <= 1; + @(posedge clk); + go <= 0; + @(posedge clk); + $display("Buffer Set for Write"); + end + endtask // SetBufferWrite + + task SetBufferRead; + input [8:0] start; + input [8:0] stop; + begin + clear <= 0; read <= 1; write <= 0; + firstline <= start; + lastline <= stop; + go <= 1; + @(posedge clk); + go <= 0; + @(posedge clk); + $display("Buffer Set for Read"); + end + endtask // SetBufferRead + + task ReadALine; + begin + while(~rd_ready_o) + @(posedge clk); + #1 rd_read_i <= 1; + @(posedge clk); + rd_read_i <= 0; + end + endtask // ReadALine + + task ReadLines; + input [9:0] lines; + input [7:0] wait_states; + begin + $display("Read Lines: Number %d, Wait States %d",lines,wait_states); + repeat (lines) + begin + ReadALine; + repeat (wait_states) + @(posedge clk); + end + end + endtask // ReadLines + + task WriteALine; + input [31:0] value; + begin + while(~wr_ready_o) + @(posedge clk); + #1 wr_write_i <= 1; + wr_data_i <= value; + @(posedge clk); + wr_write_i <= 0; + end + endtask // WriteALine + + task WriteLines; + input [9:0] lines; + input [7:0] wait_states; + input [31:0] value; + begin + $display("Write Lines: Number %d, Wait States %d",lines,wait_states); + repeat(lines) + begin + value <= value + 1; + WriteALine(value); + repeat(wait_states) + @(posedge clk); + end + end + endtask // WriteLines + +endmodule // buffer_int_tb diff --git a/fpga/usrp2/fifo/buffer_pool.v b/fpga/usrp2/fifo/buffer_pool.v new file mode 100644 index 000000000..41ac1deb3 --- /dev/null +++ b/fpga/usrp2/fifo/buffer_pool.v @@ -0,0 +1,283 @@ + +// Buffer pool. Contains 8 buffers, each 2K (512 by 32). Each buffer +// is a dual-ported RAM. Port A on each of them is indirectly connected +// to the wishbone bus by a bridge. Port B may be connected any one of the +// 8 (4 rd, 4 wr) FIFO-like streaming interaces, or disconnected. The wishbone bus +// provides access to all 8 buffers, and also controls the connections +// between the ports and the buffers, allocating them as needed. + +// wb_adr is 16 bits -- +// bits 13:11 select which buffer +// bits 10:2 select line in buffer +// bits 1:0 are unused (32-bit access only) + +// BUF_SIZE is in address lines (i.e. log2 of number of lines). +// For S3 it should be 9 (512 words, 2KB) +// For V5 it should be at least 10 (1024 words, 4KB) or 11 (2048 words, 8KB) + +module buffer_pool + #(parameter BUF_SIZE = 9, + parameter SET_ADDR = 64) + (input wb_clk_i, + input wb_rst_i, + input wb_we_i, + input wb_stb_i, + input [15:0] wb_adr_i, + input [31:0] wb_dat_i, + output [31:0] wb_dat_o, + output reg wb_ack_o, + output wb_err_o, + output wb_rty_o, + + input stream_clk, + input stream_rst, + + input set_stb, input [7:0] set_addr, input [31:0] set_data, + output [31:0] status, + output sys_int_o, + + output [31:0] s0, output [31:0] s1, output [31:0] s2, output [31:0] s3, + output [31:0] s4, output [31:0] s5, output [31:0] s6, output [31:0] s7, + + // Write Interfaces + input [31:0] wr0_data_i, input [3:0] wr0_flags_i, input wr0_ready_i, output wr0_ready_o, + input [31:0] wr1_data_i, input [3:0] wr1_flags_i, input wr1_ready_i, output wr1_ready_o, + input [31:0] wr2_data_i, input [3:0] wr2_flags_i, input wr2_ready_i, output wr2_ready_o, + input [31:0] wr3_data_i, input [3:0] wr3_flags_i, input wr3_ready_i, output wr3_ready_o, + + // Read Interfaces + output [31:0] rd0_data_o, output [3:0] rd0_flags_o, output rd0_ready_o, input rd0_ready_i, + output [31:0] rd1_data_o, output [3:0] rd1_flags_o, output rd1_ready_o, input rd1_ready_i, + output [31:0] rd2_data_o, output [3:0] rd2_flags_o, output rd2_ready_o, input rd2_ready_i, + output [31:0] rd3_data_o, output [3:0] rd3_flags_o, output rd3_ready_o, input rd3_ready_i + ); + + wire [7:0] sel_a; + + wire [BUF_SIZE-1:0] buf_addra = wb_adr_i[BUF_SIZE+1:2]; // ignore address 1:0, 32-bit access only + wire [2:0] which_buf = wb_adr_i[BUF_SIZE+4:BUF_SIZE+2]; // address 15:14 selects the buffer pool + + decoder_3_8 dec(.sel(which_buf),.res(sel_a)); + + genvar i; + + wire go; + + reg [2:0] port[0:7]; + reg [3:0] read_src[0:3]; + reg [3:0] write_src[0:3]; + + wire [7:0] done; + wire [7:0] error; + wire [7:0] idle; + + wire [31:0] buf_doa[0:7]; + + wire [7:0] buf_enb; + wire [7:0] buf_web; + wire [BUF_SIZE-1:0] buf_addrb[0:7]; + wire [31:0] buf_dib[0:7]; + wire [31:0] buf_dob[0:7]; + + wire [31:0] wr_data_i[0:7]; + wire [3:0] wr_flags_i[0:7]; + wire [7:0] wr_ready_i; + wire [7:0] wr_ready_o; + + wire [31:0] rd_data_o[0:7]; + wire [3:0] rd_flags_o[0:7]; + wire [7:0] rd_ready_o; + wire [7:0] rd_ready_i; + + assign status = {8'd0,idle[7:0],error[7:0],done[7:0]}; + + assign s0 = buf_addrb[0]; + assign s1 = buf_addrb[1]; + assign s2 = buf_addrb[2]; + assign s3 = buf_addrb[3]; + assign s4 = buf_addrb[4]; + assign s5 = buf_addrb[5]; + assign s6 = buf_addrb[6]; + assign s7 = buf_addrb[7]; + + wire [31:0] fifo_ctrl; + setting_reg #(.my_addr(SET_ADDR)) + sreg(.clk(stream_clk),.rst(stream_rst),.strobe(set_stb),.addr(set_addr),.in(set_data), + .out(fifo_ctrl),.changed(go)); + + integer k; + always @(posedge stream_clk) + if(stream_rst) + for(k=0;k<8;k=k+1) + port[k] <= 4; // disabled + else + for(k=0;k<8;k=k+1) + if(go & (fifo_ctrl[31:28]==k)) + port[k] <= fifo_ctrl[27:25]; + + always @(posedge stream_clk) + if(stream_rst) + for(k=0;k<4;k=k+1) + read_src[k] <= 8; // disabled + else + for(k=0;k<4;k=k+1) + if(go & fifo_ctrl[22] & (fifo_ctrl[27:25]==k)) + read_src[k] <= fifo_ctrl[31:28]; + + always @(posedge stream_clk) + if(stream_rst) + for(k=0;k<4;k=k+1) + write_src[k] <= 8; // disabled + else + for(k=0;k<4;k=k+1) + if(go & fifo_ctrl[23] & (fifo_ctrl[27:25]==k)) + write_src[k] <= fifo_ctrl[31:28]; + + generate + for(i=0;i<8;i=i+1) + begin : gen_buffer + RAMB16_S36_S36 dpram + (.DOA(buf_doa[i]),.ADDRA(buf_addra),.CLKA(wb_clk_i),.DIA(wb_dat_i),.DIPA(4'h0), + .ENA(wb_stb_i & sel_a[i]),.SSRA(0),.WEA(wb_we_i), + .DOB(buf_dob[i]),.ADDRB(buf_addrb[i]),.CLKB(stream_clk),.DIB(buf_dib[i]),.DIPB(4'h0), + .ENB(buf_enb[i]),.SSRB(0),.WEB(buf_web[i]) ); + +/* + ram_2port #(.DWIDTH(32),.AWIDTH(BUF_SIZE)) buffer + (.clka(wb_clk_i),.ena(wb_stb_i & sel_a[i]),.wea(wb_we_i), + .addra(buf_addra),.dia(wb_dat_i),.doa(buf_doa[i]), + .clkb(stream_clk),.enb(buf_enb[i]),.web(buf_web[i]), + .addrb(buf_addrb[i]),.dib(buf_dib[i]),.dob(buf_dob[i])); + + */ + + buffer_int #(.BUF_NUM(i),.BUF_SIZE(BUF_SIZE)) buffer_int + (.clk(stream_clk),.rst(stream_rst), + .ctrl_word(fifo_ctrl),.go(go & (fifo_ctrl[31:28]==i)), + .done(done[i]),.error(error[i]),.idle(idle[i]), + .en_o(buf_enb[i]), + .we_o(buf_web[i]), + .addr_o(buf_addrb[i]), + .dat_to_buf(buf_dib[i]), + .dat_from_buf(buf_dob[i]), + .wr_data_i(wr_data_i[i]), + .wr_flags_i(wr_flags_i[i]), + .wr_ready_i(wr_ready_i[i]), + .wr_ready_o(wr_ready_o[i]), + .rd_data_o(rd_data_o[i]), + .rd_flags_o(rd_flags_o[i]), + .rd_ready_o(rd_ready_o[i]), + .rd_ready_i(rd_ready_i[i]) ); + mux4 #(.WIDTH(37)) + mux4_wr (.en(~port[i][2]),.sel(port[i][1:0]), + .i0({wr0_data_i,wr0_flags_i,wr0_ready_i}), + .i1({wr1_data_i,wr1_flags_i,wr1_ready_i}), + .i2({wr2_data_i,wr2_flags_i,wr2_ready_i}), + .i3({wr3_data_i,wr3_flags_i,wr3_ready_i}), + .o({wr_data_i[i],wr_flags_i[i],wr_ready_i[i]}) ); + mux4 #(.WIDTH(1)) + mux4_rd (.en(~port[i][2]),.sel(port[i][1:0]), + .i0(rd0_ready_i),.i1(rd1_ready_i),.i2(rd2_ready_i),.i3(rd3_ready_i), + .o(rd_ready_i[i])); + end // block: gen_buffer + endgenerate + + //---------------------------------------------------------------------- + // Wishbone Outputs + + // Use the following lines if ram output and mux can be made fast enough + + assign wb_err_o = 1'b0; // Unused for now + assign wb_rty_o = 1'b0; // Unused for now + + always @(posedge wb_clk_i) + wb_ack_o <= wb_stb_i & ~wb_ack_o; + assign wb_dat_o = buf_doa[which_buf]; + + // Use this if we can't make the RAM+MUX fast enough + // reg [31:0] wb_dat_o_reg; + // reg stb_d1; + + // always @(posedge wb_clk_i) + // begin + // wb_dat_o_reg <= buf_doa[which_buf]; + // stb_d1 <= wb_stb_i; + // wb_ack_o <= (stb_d1 & ~wb_ack_o) | (wb_we_i & wb_stb_i); + // end + //assign wb_dat_o = wb_dat_o_reg; + + mux8 #(.WIDTH(1)) + mux8_wr0(.en(~write_src[0][3]),.sel(write_src[0][2:0]), + .i0(wr_ready_o[0]), .i1(wr_ready_o[1]), .i2(wr_ready_o[2]), .i3(wr_ready_o[3]), + .i4(wr_ready_o[4]), .i5(wr_ready_o[5]), .i6(wr_ready_o[6]), .i7(wr_ready_o[7]), + .o(wr0_ready_o)); + + mux8 #(.WIDTH(1)) + mux8_wr1(.en(~write_src[1][3]),.sel(write_src[1][2:0]), + .i0(wr_ready_o[0]), .i1(wr_ready_o[1]), .i2(wr_ready_o[2]), .i3(wr_ready_o[3]), + .i4(wr_ready_o[4]), .i5(wr_ready_o[5]), .i6(wr_ready_o[6]), .i7(wr_ready_o[7]), + .o(wr1_ready_o)); + + mux8 #(.WIDTH(1)) + mux8_wr2(.en(~write_src[2][3]),.sel(write_src[2][2:0]), + .i0(wr_ready_o[0]), .i1(wr_ready_o[1]), .i2(wr_ready_o[2]), .i3(wr_ready_o[3]), + .i4(wr_ready_o[4]), .i5(wr_ready_o[5]), .i6(wr_ready_o[6]), .i7(wr_ready_o[7]), + .o(wr2_ready_o)); + + mux8 #(.WIDTH(1)) + mux8_wr3(.en(~write_src[3][3]),.sel(write_src[3][2:0]), + .i0(wr_ready_o[0]), .i1(wr_ready_o[1]), .i2(wr_ready_o[2]), .i3(wr_ready_o[3]), + .i4(wr_ready_o[4]), .i5(wr_ready_o[5]), .i6(wr_ready_o[6]), .i7(wr_ready_o[7]), + .o(wr3_ready_o)); + + mux8 #(.WIDTH(37)) + mux8_rd0(.en(~read_src[0][3]),.sel(read_src[0][2:0]), + .i0({rd_data_o[0],rd_flags_o[0],rd_ready_o[0]}), + .i1({rd_data_o[1],rd_flags_o[1],rd_ready_o[1]}), + .i2({rd_data_o[2],rd_flags_o[2],rd_ready_o[2]}), + .i3({rd_data_o[3],rd_flags_o[3],rd_ready_o[3]}), + .i4({rd_data_o[4],rd_flags_o[4],rd_ready_o[4]}), + .i5({rd_data_o[5],rd_flags_o[5],rd_ready_o[5]}), + .i6({rd_data_o[6],rd_flags_o[6],rd_ready_o[6]}), + .i7({rd_data_o[7],rd_flags_o[7],rd_ready_o[7]}), + .o({rd0_data_o,rd0_flags_o,rd0_ready_o})); + + mux8 #(.WIDTH(37)) + mux8_rd1(.en(~read_src[1][3]),.sel(read_src[1][2:0]), + .i0({rd_data_o[0],rd_flags_o[0],rd_ready_o[0]}), + .i1({rd_data_o[1],rd_flags_o[1],rd_ready_o[1]}), + .i2({rd_data_o[2],rd_flags_o[2],rd_ready_o[2]}), + .i3({rd_data_o[3],rd_flags_o[3],rd_ready_o[3]}), + .i4({rd_data_o[4],rd_flags_o[4],rd_ready_o[4]}), + .i5({rd_data_o[5],rd_flags_o[5],rd_ready_o[5]}), + .i6({rd_data_o[6],rd_flags_o[6],rd_ready_o[6]}), + .i7({rd_data_o[7],rd_flags_o[7],rd_ready_o[7]}), + .o({rd1_data_o,rd1_flags_o,rd1_ready_o})); + + mux8 #(.WIDTH(37)) + mux8_rd2(.en(~read_src[2][3]),.sel(read_src[2][2:0]), + .i0({rd_data_o[0],rd_flags_o[0],rd_ready_o[0]}), + .i1({rd_data_o[1],rd_flags_o[1],rd_ready_o[1]}), + .i2({rd_data_o[2],rd_flags_o[2],rd_ready_o[2]}), + .i3({rd_data_o[3],rd_flags_o[3],rd_ready_o[3]}), + .i4({rd_data_o[4],rd_flags_o[4],rd_ready_o[4]}), + .i5({rd_data_o[5],rd_flags_o[5],rd_ready_o[5]}), + .i6({rd_data_o[6],rd_flags_o[6],rd_ready_o[6]}), + .i7({rd_data_o[7],rd_flags_o[7],rd_ready_o[7]}), + .o({rd2_data_o,rd2_flags_o,rd2_ready_o})); + + mux8 #(.WIDTH(37)) + mux8_rd3(.en(~read_src[3][3]),.sel(read_src[3][2:0]), + .i0({rd_data_o[0],rd_flags_o[0],rd_ready_o[0]}), + .i1({rd_data_o[1],rd_flags_o[1],rd_ready_o[1]}), + .i2({rd_data_o[2],rd_flags_o[2],rd_ready_o[2]}), + .i3({rd_data_o[3],rd_flags_o[3],rd_ready_o[3]}), + .i4({rd_data_o[4],rd_flags_o[4],rd_ready_o[4]}), + .i5({rd_data_o[5],rd_flags_o[5],rd_ready_o[5]}), + .i6({rd_data_o[6],rd_flags_o[6],rd_ready_o[6]}), + .i7({rd_data_o[7],rd_flags_o[7],rd_ready_o[7]}), + .o({rd3_data_o,rd3_flags_o,rd3_ready_o})); + + assign sys_int_o = (|error) | (|done); + +endmodule // buffer_pool diff --git a/fpga/usrp2/fifo/buffer_pool_tb.v b/fpga/usrp2/fifo/buffer_pool_tb.v new file mode 100644 index 000000000..91a01d268 --- /dev/null +++ b/fpga/usrp2/fifo/buffer_pool_tb.v @@ -0,0 +1,58 @@ + +module buffer_pool_tb(); + + wire wb_clk_i; + wire wb_rst_i; + wire wb_we_i; + wire wb_stb_i; + wire [15:0] wb_adr_i; + wire [31:0] wb_dat_i; + wire [31:0] wb_dat_o; + wire wb_ack_o; + wire wb_err_o; + wire wb_rty_o; + + wire stream_clk, stream_rst; + + wire set_stb; + wire [7:0] set_addr; + wire [31:0] set_data; + + wire [31:0] wr0_data, wr1_data, wr2_data, wr3_data; + wire [31:0] rd0_data, rd1_data, rd2_data, rd3_data; + wire [3:0] wr0_flags, wr1_flags, wr2_flags, wr3_flags; + wire [3:0] rd0_flags, rd1_flags, rd2_flags, rd3_flags; + wire wr0_ready, wr1_ready, wr2_ready, wr3_ready; + wire rd0_ready, rd1_ready, rd2_ready, rd3_ready; + wire wr0_write, wr1_write, wr2_write, wr3_write; + wire rd0_read, rd1_read, rd2_read, rd3_read; + + buffer_pool dut + (.wb_clk_i(wb_clk_i), + .wb_rst_i(wb_rst_i), + .wb_we_i(wb_we_i), + .wb_stb_i(wb_stb_i), + .wb_adr_i(wb_adr_i), + .wb_dat_i(wb_dat_i), + .wb_dat_o(wb_dat_o), + .wb_ack_o(wb_ack_o), + .wb_err_o(wb_err_o), + .wb_rty_o(wb_rty_o), + + .stream_clk(stream_clk), + .stream_rst(stream_rst), + + .set_stb(set_stb),.set_addr(set_addr),.set_data(set_data), + + .wr0_data_i(wr0_data), .wr0_write_i(wr0_write), .wr0_flags_i(wr0_flags), .wr0_ready_o(wr0_ready), + .wr1_data_i(wr1_data), .wr1_write_i(wr1_write), .wr1_flags_i(wr1_flags), .wr1_ready_o(wr1_ready), + .wr2_data_i(wr2_data), .wr2_write_i(wr2_write), .wr2_flags_i(wr2_flags), .wr2_ready_o(wr2_ready), + .wr3_data_i(wr3_data), .wr3_write_i(wr3_write), .wr3_flags_i(wr3_flags), .wr3_ready_o(wr3_ready), + + .rd0_data_o(rd0_data), .rd0_read_i(rd0_read), .rd0_flags_o(rd0_flags), .rd0_ready_o(rd0_ready), + .rd1_data_o(rd1_data), .rd1_read_i(rd1_read), .rd1_flags_o(rd1_flags), .rd1_ready_o(rd1_ready), + .rd2_data_o(rd2_data), .rd2_read_i(rd2_read), .rd2_flags_o(rd2_flags), .rd2_ready_o(rd2_ready), + .rd3_data_o(rd3_data), .rd3_read_i(rd3_read), .rd3_flags_o(rd3_flags), .rd3_ready_o(rd3_ready) + ); + +endmodule // buffer_pool_tb diff --git a/fpga/usrp2/fifo/crossbar36.v b/fpga/usrp2/fifo/crossbar36.v new file mode 100644 index 000000000..2a046d8bf --- /dev/null +++ b/fpga/usrp2/fifo/crossbar36.v @@ -0,0 +1,42 @@ + + +module crossbar36 + (input clk, input reset, input clear, + input cross, + input [35:0] data0_i, input src0_rdy_i, output dst0_rdy_o, + input [35:0] data1_i, input src1_rdy_i, output dst1_rdy_o, + output [35:0] data0_o, output src0_rdy_o, input dst0_rdy_i, + output [35:0] data1_o, output src1_rdy_o, input dst1_rdy_i); + + reg cross_int, active0, active1; + wire active0_next = (src0_rdy_i & dst0_rdy_o)? ~data0_i[33] : active0; + wire active1_next = (src1_rdy_i & dst1_rdy_o)? ~data1_i[33] : active1; + + assign data0_o = cross_int ? data1_i : data0_i; + assign data1_o = cross_int ? data0_i : data1_i; + + assign src0_rdy_o = cross_int ? src1_rdy_i : src0_rdy_i; + assign src1_rdy_o = cross_int ? src0_rdy_i : src1_rdy_i; + + assign dst0_rdy_o = cross_int ? dst1_rdy_i : dst0_rdy_i; + assign dst1_rdy_o = cross_int ? dst0_rdy_i : dst1_rdy_i; + + always @(posedge clk) + if(reset | clear) + active0 <= 0; + else + active0 <= active0_next; + + always @(posedge clk) + if(reset | clear) + active1 <= 0; + else + active1 <= active1_next; + + always @(posedge clk) + if(reset | clear) + cross_int <= 0; + else if(~active0_next & ~active1_next) + cross_int <= cross; + +endmodule // crossbar36 diff --git a/fpga/usrp2/fifo/dsp_framer36.v b/fpga/usrp2/fifo/dsp_framer36.v new file mode 100644 index 000000000..c2ae8f96c --- /dev/null +++ b/fpga/usrp2/fifo/dsp_framer36.v @@ -0,0 +1,68 @@ + +// Frame DSP packets with a header line to be handled by the protocol machine + +module dsp_framer36 + #(parameter BUF_SIZE = 9, + parameter PORT_SEL = 0) + (input clk, input reset, input clear, + input [35:0] data_i, input src_rdy_i, output dst_rdy_o, + output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + + wire dfifo_in_dst_rdy, dfifo_in_src_rdy, dfifo_out_dst_rdy, dfifo_out_src_rdy; + wire tfifo_in_dst_rdy, tfifo_in_src_rdy, tfifo_out_dst_rdy, tfifo_out_src_rdy; + + wire do_xfer_in = dfifo_in_src_rdy & dfifo_in_dst_rdy; + wire do_xfer_out = src_rdy_o & dst_rdy_i; + + wire have_space = dfifo_in_dst_rdy & tfifo_in_dst_rdy; + reg [15:0] pkt_len_in, pkt_len_out; + wire [15:0] tfifo_data; + wire [35:0] dfifo_out_data; + + assign dst_rdy_o = have_space; + assign dfifo_in_src_rdy = src_rdy_i & have_space; + + fifo_cascade #(.WIDTH(36), .SIZE(BUF_SIZE)) dfifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(data_i), .src_rdy_i(dfifo_in_src_rdy), .dst_rdy_o(dfifo_in_dst_rdy), + .dataout(dfifo_out_data), .src_rdy_o(dfifo_out_src_rdy), .dst_rdy_i(dfifo_out_dst_rdy) ); + + fifo_short #(.WIDTH(16)) tfifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(pkt_len_in), .src_rdy_i(tfifo_in_src_rdy), .dst_rdy_o(tfifo_in_dst_rdy), + .dataout(tfifo_data), .src_rdy_o(tfifo_out_src_rdy), .dst_rdy_i(tfifo_out_dst_rdy), + .space(), .occupied() ); + + // FIXME won't handle single-line packets, will show wrong length + always @(posedge clk) + if(reset | clear) + pkt_len_in <= 0; + else if(do_xfer_in) + if(data_i[32]) // sof + pkt_len_in <= 2; // fixes off by one since number is stored before increment + else + pkt_len_in <= pkt_len_in + 1; + + assign tfifo_in_src_rdy = do_xfer_in & data_i[33]; // store length when at eof in + assign tfifo_out_dst_rdy = do_xfer_out & data_o[33]; // remove length from list at eof out + + always @(posedge clk) + if(reset | clear) + pkt_len_out <= 0; + else if(do_xfer_out) + if(dfifo_out_data[33]) // eof + pkt_len_out <= 0; + else + pkt_len_out <= pkt_len_out + 1; + + assign dfifo_out_dst_rdy = do_xfer_out & (pkt_len_out != 0); + + wire [1:0] port_sel_bits = PORT_SEL; + + assign data_o = (pkt_len_out == 0) ? {4'b0001, 13'b0, port_sel_bits, 1'b1, tfifo_data[13:0],2'b00} : + (pkt_len_out == 1) ? {4'b0000, dfifo_out_data[31:16],tfifo_data} : + {dfifo_out_data[35:33], 1'b0, dfifo_out_data[31:0] }; + + assign src_rdy_o = dfifo_out_src_rdy & tfifo_out_src_rdy; + +endmodule // dsp_framer36 diff --git a/fpga/usrp2/fifo/fifo19_to_fifo36.v b/fpga/usrp2/fifo/fifo19_to_fifo36.v new file mode 100644 index 000000000..502821435 --- /dev/null +++ b/fpga/usrp2/fifo/fifo19_to_fifo36.v @@ -0,0 +1,110 @@ + +// Parameter LE tells us if we are little-endian. +// Little-endian means send lower 16 bits first. +// Default is big endian (network order), send upper bits first. + +module fifo19_to_fifo36 + #(parameter LE=0) + (input clk, input reset, input clear, + input [18:0] f19_datain, + input f19_src_rdy_i, + output f19_dst_rdy_o, + + output [35:0] f36_dataout, + output f36_src_rdy_o, + input f36_dst_rdy_i, + output [31:0] debug + ); + + // Shortfifo on input to guarantee no deadlock + wire [18:0] f19_data_int; + wire f19_src_rdy_int, f19_dst_rdy_int; + + fifo_short #(.WIDTH(19)) head_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f19_datain), .src_rdy_i(f19_src_rdy_i), .dst_rdy_o(f19_dst_rdy_o), + .dataout(f19_data_int), .src_rdy_o(f19_src_rdy_int), .dst_rdy_i(f19_dst_rdy_int), + .space(),.occupied() ); + + // Actual f19 to f36 which could deadlock if not connected to shortfifos + reg f36_sof_int, f36_eof_int; + reg [1:0] f36_occ_int; + wire [35:0] f36_data_int; + wire f36_src_rdy_int, f36_dst_rdy_int; + + reg [1:0] state; + reg [15:0] dat0, dat1; + + wire f19_sof_int = f19_data_int[16]; + wire f19_eof_int = f19_data_int[17]; + wire f19_occ_int = f19_data_int[18]; + + wire xfer_out = f36_src_rdy_int & f36_dst_rdy_int; + + always @(posedge clk) + if(f19_src_rdy_int & ((state==0)|xfer_out)) + f36_sof_int <= f19_sof_int; + + always @(posedge clk) + if(f19_src_rdy_int & ((state != 2)|xfer_out)) + f36_eof_int <= f19_eof_int; + + always @(posedge clk) + if(reset) + begin + state <= 0; + f36_occ_int <= 0; + end + else + if(f19_src_rdy_int) + case(state) + 0 : + begin + dat0 <= f19_data_int; + if(f19_eof_int) + begin + state <= 2; + f36_occ_int <= f19_occ_int ? 2'b01 : 2'b10; + end + else + state <= 1; + end + 1 : + begin + dat1 <= f19_data_int; + state <= 2; + if(f19_eof_int) + f36_occ_int <= f19_occ_int ? 2'b11 : 2'b00; + end + 2 : + if(xfer_out) + begin + dat0 <= f19_data_int; + if(f19_eof_int) // remain in state 2 if we are at eof + f36_occ_int <= f19_occ_int ? 2'b01 : 2'b10; + else + state <= 1; + end + endcase // case(state) + else + if(xfer_out) + begin + state <= 0; + f36_occ_int <= 0; + end + + assign f19_dst_rdy_int = xfer_out | (state != 2); + assign f36_data_int = LE ? {f36_occ_int,f36_eof_int,f36_sof_int,dat1,dat0} : + {f36_occ_int,f36_eof_int,f36_sof_int,dat0,dat1}; + assign f36_src_rdy_int = (state == 2); + + assign debug = state; + + // Shortfifo on output to guarantee no deadlock + fifo_short #(.WIDTH(36)) tail_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f36_data_int), .src_rdy_i(f36_src_rdy_int), .dst_rdy_o(f36_dst_rdy_int), + .dataout(f36_dataout), .src_rdy_o(f36_src_rdy_o), .dst_rdy_i(f36_dst_rdy_i), + .space(),.occupied() ); + +endmodule // fifo19_to_fifo36 diff --git a/fpga/usrp2/fifo/fifo19_to_ll8.v b/fpga/usrp2/fifo/fifo19_to_ll8.v new file mode 100644 index 000000000..4707f7523 --- /dev/null +++ b/fpga/usrp2/fifo/fifo19_to_ll8.v @@ -0,0 +1,53 @@ + +module fifo19_to_ll8 + (input clk, input reset, input clear, + input [18:0] f19_data, + input f19_src_rdy_i, + output f19_dst_rdy_o, + + output reg [7:0] ll_data, + output ll_sof_n, + output ll_eof_n, + output ll_src_rdy_n, + input ll_dst_rdy_n); + + wire ll_sof, ll_eof, ll_src_rdy; + assign ll_sof_n = ~ll_sof; + assign ll_eof_n = ~ll_eof; + assign ll_src_rdy_n = ~ll_src_rdy; + wire ll_dst_rdy = ~ll_dst_rdy_n; + + wire f19_sof = f19_data[16]; + wire f19_eof = f19_data[17]; + wire f19_occ = f19_data[18]; + + wire advance, end_early; + reg state; + + always @(posedge clk) + if(reset) + state <= 0; + else + if(advance) + if(ll_eof) + state <= 0; + else + state <= state + 1; + + always @* + case(state) + 0 : ll_data = f19_data[15:8]; + 1 : ll_data = f19_data[7:0]; + default : ll_data = f19_data[15:8]; + endcase // case (state) + + assign ll_sof = (state==0) & f19_sof; + assign ll_eof = f19_eof & ((f19_occ==1)|(state==1)); + + assign ll_src_rdy = f19_src_rdy_i; + + assign advance = ll_src_rdy & ll_dst_rdy; + assign f19_dst_rdy_o = advance & ((state==1)|ll_eof); + +endmodule // fifo19_to_ll8 + diff --git a/fpga/usrp2/fifo/fifo36_demux.v b/fpga/usrp2/fifo/fifo36_demux.v new file mode 100644 index 000000000..a54759d4d --- /dev/null +++ b/fpga/usrp2/fifo/fifo36_demux.v @@ -0,0 +1,50 @@ + +// Demux packets from a fifo based on the contents of the first line +// If first line matches the parameter and mask, send to data1, otherwise send to data0 + +module fifo36_demux + #(parameter match_data = 0, + parameter match_mask = 0) + (input clk, input reset, input clear, + input [35:0] data_i, input src_rdy_i, output dst_rdy_o, + output [35:0] data0_o, output src0_rdy_o, input dst0_rdy_i, + output [35:0] data1_o, output src1_rdy_o, input dst1_rdy_i); + + localparam DMX_IDLE = 0; + localparam DMX_DATA0 = 1; + localparam DMX_DATA1 = 2; + + reg [1:0] state; + + wire match = |( (data_i ^ match_data) & match_mask ); + wire eof = data_i[33]; + + always @(posedge clk) + if(reset | clear) + state <= DMX_IDLE; + else + case(state) + DMX_IDLE : + if(src_rdy_i) + if(match) + state <= DMX_DATA1; + else + state <= DMX_DATA0; + DMX_DATA0 : + if(src_rdy_i & dst0_rdy_i & eof) + state <= DMX_IDLE; + DMX_DATA1 : + if(src_rdy_i & dst1_rdy_i & eof) + state <= DMX_IDLE; + default : + state <= DMX_IDLE; + endcase // case (state) + + assign dst_rdy_o = (state==DMX_IDLE) ? 0 : (state==DMX_DATA0) ? dst0_rdy_i : dst1_rdy_i; + assign src0_rdy_o = (state==DMX_DATA0) ? src_rdy_i : 0; + assign src1_rdy_o = (state==DMX_DATA1) ? src_rdy_i : 0; + + assign data0_o = data_i; + assign data1_o = data_i; + +endmodule // fifo36_demux diff --git a/fpga/usrp2/fifo/fifo36_mux.v b/fpga/usrp2/fifo/fifo36_mux.v new file mode 100644 index 000000000..7f0f803ff --- /dev/null +++ b/fpga/usrp2/fifo/fifo36_mux.v @@ -0,0 +1,77 @@ + +// Mux packets from multiple FIFO interfaces onto a single one. +// Can alternate or give priority to one port (port 0) +// In prio mode, port 1 will never get access if port 0 is always busy + +module fifo36_mux + #(parameter prio = 0) + (input clk, input reset, input clear, + input [35:0] data0_i, input src0_rdy_i, output dst0_rdy_o, + input [35:0] data1_i, input src1_rdy_i, output dst1_rdy_o, + output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + + wire [35:0] data0_int, data1_int; + wire src0_rdy_int, dst0_rdy_int, src1_rdy_int, dst1_rdy_int; + + fifo_short #(.WIDTH(36)) mux_fifo_in0 + (.clk(clk), .reset(reset), .clear(clear), + .datain(data0_i), .src_rdy_i(src0_rdy_i), .dst_rdy_o(dst0_rdy_o), + .dataout(data0_int), .src_rdy_o(src0_rdy_int), .dst_rdy_i(dst0_rdy_int)); + + fifo_short #(.WIDTH(36)) mux_fifo_in1 + (.clk(clk), .reset(reset), .clear(clear), + .datain(data1_i), .src_rdy_i(src1_rdy_i), .dst_rdy_o(dst1_rdy_o), + .dataout(data1_int), .src_rdy_o(src1_rdy_int), .dst_rdy_i(dst1_rdy_int)); + + localparam MUX_IDLE0 = 0; + localparam MUX_DATA0 = 1; + localparam MUX_IDLE1 = 2; + localparam MUX_DATA1 = 3; + + reg [1:0] state; + + wire eof0 = data0_int[33]; + wire eof1 = data1_int[33]; + + wire [35:0] data_int; + wire src_rdy_int, dst_rdy_int; + + always @(posedge clk) + if(reset | clear) + state <= MUX_IDLE0; + else + case(state) + MUX_IDLE0 : + if(src0_rdy_int) + state <= MUX_DATA0; + else if(src1_rdy_int) + state <= MUX_DATA1; + + MUX_DATA0 : + if(src0_rdy_int & dst_rdy_int & eof0) + state <= prio ? MUX_IDLE0 : MUX_IDLE1; + + MUX_IDLE1 : + if(src1_rdy_int) + state <= MUX_DATA1; + else if(src0_rdy_int) + state <= MUX_DATA0; + + MUX_DATA1 : + if(src1_rdy_int & dst_rdy_int & eof1) + state <= MUX_IDLE0; + + default : + state <= MUX_IDLE0; + endcase // case (state) + + assign dst0_rdy_int = (state==MUX_DATA0) ? dst_rdy_int : 0; + assign dst1_rdy_int = (state==MUX_DATA1) ? dst_rdy_int : 0; + assign src_rdy_int = (state==MUX_DATA0) ? src0_rdy_int : (state==MUX_DATA1) ? src1_rdy_int : 0; + assign data_int = (state==MUX_DATA0) ? data0_int : data1_int; + + fifo_short #(.WIDTH(36)) mux_fifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(data_int), .src_rdy_i(src_rdy_int), .dst_rdy_o(dst_rdy_int), + .dataout(data_o), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i)); +endmodule // fifo36_demux diff --git a/fpga/usrp2/fifo/fifo36_to_fifo19.v b/fpga/usrp2/fifo/fifo36_to_fifo19.v new file mode 100644 index 000000000..0e9b2d442 --- /dev/null +++ b/fpga/usrp2/fifo/fifo36_to_fifo19.v @@ -0,0 +1,63 @@ + +// Parameter LE tells us if we are little-endian. +// Little-endian means send lower 16 bits first. +// Default is big endian (network order), send upper bits first. + +module fifo36_to_fifo19 + #(parameter LE=0) + (input clk, input reset, input clear, + input [35:0] f36_datain, + input f36_src_rdy_i, + output f36_dst_rdy_o, + + output [18:0] f19_dataout, + output f19_src_rdy_o, + input f19_dst_rdy_i ); + + wire [18:0] f19_data_int; + wire f19_src_rdy_int, f19_dst_rdy_int; + wire [35:0] f36_data_int; + wire f36_src_rdy_int, f36_dst_rdy_int; + + // Shortfifo on input to guarantee no deadlock + fifo_short #(.WIDTH(36)) head_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f36_datain), .src_rdy_i(f36_src_rdy_i), .dst_rdy_o(f36_dst_rdy_o), + .dataout(f36_data_int), .src_rdy_o(f36_src_rdy_int), .dst_rdy_i(f36_dst_rdy_int), + .space(),.occupied() ); + + // Main fifo36_to_fifo19, needs shortfifos to guarantee no deadlock + wire [1:0] f36_occ_int = f36_data_int[35:34]; + wire f36_sof_int = f36_data_int[32]; + wire f36_eof_int = f36_data_int[33]; + + reg phase; + wire half_line = f36_eof_int & ((f36_occ_int==1)|(f36_occ_int==2)); + + assign f19_data_int[15:0] = (LE ^ phase) ? f36_data_int[15:0] : f36_data_int[31:16]; + assign f19_data_int[16] = phase ? 0 : f36_sof_int; + assign f19_data_int[17] = phase ? f36_eof_int : half_line; + assign f19_data_int[18] = f19_data_int[17] & ((f36_occ_int==1)|(f36_occ_int==3)); + + assign f19_src_rdy_int = f36_src_rdy_int; + assign f36_dst_rdy_int = (phase | half_line) & f19_dst_rdy_int; + + wire f19_xfer = f19_src_rdy_int & f19_dst_rdy_int; + wire f36_xfer = f36_src_rdy_int & f36_dst_rdy_int; + + always @(posedge clk) + if(reset) + phase <= 0; + else if(f36_xfer) + phase <= 0; + else if(f19_xfer) + phase <= 1; + + // Shortfifo on output to guarantee no deadlock + fifo_short #(.WIDTH(19)) tail_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f19_data_int), .src_rdy_i(f19_src_rdy_int), .dst_rdy_o(f19_dst_rdy_int), + .dataout(f19_dataout), .src_rdy_o(f19_src_rdy_o), .dst_rdy_i(f19_dst_rdy_i), + .space(),.occupied() ); + +endmodule // fifo36_to_fifo19 diff --git a/fpga/usrp2/fifo/fifo36_to_fifo72.v b/fpga/usrp2/fifo/fifo36_to_fifo72.v new file mode 100644 index 000000000..038eda9e9 --- /dev/null +++ b/fpga/usrp2/fifo/fifo36_to_fifo72.v @@ -0,0 +1,125 @@ + +// Parameter LE tells us if we are little-endian. +// Little-endian means send lower 16 bits first. +// Default is big endian (network order), send upper bits first. + +module fifo36_to_fifo72 + #(parameter LE=0) + (input clk, input reset, input clear, + input [35:0] f36_datain, + input f36_src_rdy_i, + output f36_dst_rdy_o, + + output [71:0] f72_dataout, + output f72_src_rdy_o, + input f72_dst_rdy_i, + output [31:0] debug + ); + + // Shortfifo on input to guarantee no deadlock + wire [35:0] f36_data_int; + wire f36_src_rdy_int, f36_dst_rdy_int; + + fifo_short #(.WIDTH(36)) head_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f36_datain), .src_rdy_i(f36_src_rdy_i), .dst_rdy_o(f36_dst_rdy_o), + .dataout(f36_data_int), .src_rdy_o(f36_src_rdy_int), .dst_rdy_i(f36_dst_rdy_int), + .space(),.occupied() ); + + // Actual f36 to f72 which could deadlock if not connected to shortfifos + reg f72_sof_int, f72_eof_int; + reg [2:0] f72_occ_int; + wire [71:0] f72_data_int; + wire f72_src_rdy_int, f72_dst_rdy_int; + + reg [1:0] state; + reg [31:0] dat0, dat1; + + wire f36_sof_int = f36_data_int[32]; + wire f36_eof_int = f36_data_int[33]; + wire [1:0] f36_occ_int = f36_data_int[35:34]; + + wire xfer_out = f72_src_rdy_int & f72_dst_rdy_int; + + always @(posedge clk) + if(f36_src_rdy_int & ((state==0)|xfer_out)) + f72_sof_int <= f36_sof_int; + + always @(posedge clk) + if(f36_src_rdy_int & ((state != 2)|xfer_out)) + f72_eof_int <= f36_eof_int; + + always @(posedge clk) + if(reset) + begin + state <= 0; + f72_occ_int <= 0; + end + else + if(f36_src_rdy_int) + case(state) + 0 : + begin + dat0 <= f36_data_int; + if(f36_eof_int) + begin + state <= 2; + case (f36_occ_int) + 0 : f72_occ_int <= 3'd4; + 1 : f72_occ_int <= 3'd1; + 2 : f72_occ_int <= 3'd2; + 3 : f72_occ_int <= 3'd3; + endcase // case (f36_occ_int) + end + else + state <= 1; + end + 1 : + begin + dat1 <= f36_data_int; + state <= 2; + if(f36_eof_int) + case (f36_occ_int) + 0 : f72_occ_int <= 3'd0; + 1 : f72_occ_int <= 3'd5; + 2 : f72_occ_int <= 3'd6; + 3 : f72_occ_int <= 3'd7; + endcase // case (f36_occ_int) + end + 2 : + if(xfer_out) + begin + dat0 <= f36_data_int; + if(f36_eof_int) // remain in state 2 if we are at eof + case (f36_occ_int) + 0 : f72_occ_int <= 3'd4; + 1 : f72_occ_int <= 3'd1; + 2 : f72_occ_int <= 3'd2; + 3 : f72_occ_int <= 3'd3; + endcase // case (f36_occ_int) + else + state <= 1; + end + endcase // case(state) + else + if(xfer_out) + begin + state <= 0; + f72_occ_int <= 0; + end + + assign f36_dst_rdy_int = xfer_out | (state != 2); + assign f72_data_int = LE ? {3'b000,f72_occ_int[2:0],f72_eof_int,f72_sof_int,dat1,dat0} : + {3'b000,f72_occ_int[2:0],f72_eof_int,f72_sof_int,dat0,dat1}; + assign f72_src_rdy_int = (state == 2); + + assign debug = state; + + // Shortfifo on output to guarantee no deadlock + fifo_short #(.WIDTH(72)) tail_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f72_data_int), .src_rdy_i(f72_src_rdy_int), .dst_rdy_o(f72_dst_rdy_int), + .dataout(f72_dataout), .src_rdy_o(f72_src_rdy_o), .dst_rdy_i(f72_dst_rdy_i), + .space(),.occupied() ); + +endmodule // fifo36_to_fifo72 diff --git a/fpga/usrp2/fifo/fifo36_to_ll8.v b/fpga/usrp2/fifo/fifo36_to_ll8.v new file mode 100644 index 000000000..390e49962 --- /dev/null +++ b/fpga/usrp2/fifo/fifo36_to_ll8.v @@ -0,0 +1,75 @@ + +module fifo36_to_ll8 + (input clk, input reset, input clear, + input [35:0] f36_data, + input f36_src_rdy_i, + output f36_dst_rdy_o, + + output [7:0] ll_data, + output ll_sof, + output ll_eof, + output ll_src_rdy, + input ll_dst_rdy, + + output [31:0] debug); + + // Shortfifo on input to guarantee no deadlock + wire [35:0] f36_data_int; + wire f36_src_rdy_int, f36_dst_rdy_int; + reg [7:0] ll_data_int; + wire ll_sof_int, ll_eof_int, ll_src_rdy_int, ll_dst_rdy_int; + + fifo_short #(.WIDTH(36)) head_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f36_data), .src_rdy_i(f36_src_rdy_i), .dst_rdy_o(f36_dst_rdy_o), + .dataout(f36_data_int), .src_rdy_o(f36_src_rdy_int), .dst_rdy_i(f36_dst_rdy_int), + .space(),.occupied() ); + + // Actual fifo36 to ll8, can deadlock if not connected to shortfifo + wire [1:0] f36_occ_int = f36_data_int[35:34]; + wire f36_sof_int = f36_data_int[32]; + wire f36_eof_int = f36_data_int[33]; + wire advance, end_early; + reg [1:0] state; + + assign debug = {29'b0,state}; + + always @(posedge clk) + if(reset) + state <= 0; + else + if(advance) + if(ll_eof_int) + state <= 0; + else + state <= state + 1; + + always @* + case(state) + 0 : ll_data_int = f36_data_int[31:24]; + 1 : ll_data_int = f36_data_int[23:16]; + 2 : ll_data_int = f36_data_int[15:8]; + 3 : ll_data_int = f36_data_int[7:0]; + default : ll_data_int = f36_data_int[31:24]; + endcase // case (state) + + assign ll_sof_int = (state==0) & f36_sof_int; + assign ll_eof_int = f36_eof_int & (((state==0)&(f36_occ_int==1)) | + ((state==1)&(f36_occ_int==2)) | + ((state==2)&(f36_occ_int==3)) | + (state==3)); + + assign ll_src_rdy_int = f36_src_rdy_int; + + assign advance = ll_src_rdy_int & ll_dst_rdy_int; + assign f36_dst_rdy_int= advance & ((state==3)|ll_eof_int); + + // Short FIFO on output to guarantee no deadlock + ll8_shortfifo tail_fifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(ll_data_int), .sof_i(ll_sof_int), .eof_i(ll_eof_int), + .error_i(0), .src_rdy_i(ll_src_rdy_int), .dst_rdy_o(ll_dst_rdy_int), + .dataout(ll_data), .sof_o(ll_sof), .eof_o(ll_eof), + .error_o(), .src_rdy_o(ll_src_rdy), .dst_rdy_i(ll_dst_rdy)); + +endmodule // fifo36_to_ll8 diff --git a/fpga/usrp2/fifo/fifo72_to_fifo36.v b/fpga/usrp2/fifo/fifo72_to_fifo36.v new file mode 100644 index 000000000..1b3bc3ab7 --- /dev/null +++ b/fpga/usrp2/fifo/fifo72_to_fifo36.v @@ -0,0 +1,63 @@ + +// Parameter LE tells us if we are little-endian. +// Little-endian means send lower 16 bits first. +// Default is big endian (network order), send upper bits first. + +module fifo72_to_fifo36 + #(parameter LE=0) + (input clk, input reset, input clear, + input [71:0] f72_datain, + input f72_src_rdy_i, + output f72_dst_rdy_o, + + output [35:0] f36_dataout, + output f36_src_rdy_o, + input f36_dst_rdy_i ); + + wire [35:0] f36_data_int; + wire f36_src_rdy_int, f36_dst_rdy_int; + wire [71:0] f72_data_int; + wire f72_src_rdy_int, f72_dst_rdy_int; + + // Shortfifo on input to guarantee no deadlock + fifo_short #(.WIDTH(72)) head_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f72_datain), .src_rdy_i(f72_src_rdy_i), .dst_rdy_o(f72_dst_rdy_o), + .dataout(f72_data_int), .src_rdy_o(f72_src_rdy_int), .dst_rdy_i(f72_dst_rdy_int), + .space(),.occupied() ); + + // Main fifo72_to_fifo36, needs shortfifos to guarantee no deadlock + wire [2:0] f72_occ_int = f72_data_int[68:66]; + wire f72_sof_int = f72_data_int[64]; + wire f72_eof_int = f72_data_int[65]; + + reg phase; + wire half_line = f72_eof_int & ( (f72_occ_int==1)|(f72_occ_int==2)|(f72_occ_int==3)|(f72_occ_int==4) ); + + assign f36_data_int[31:0] = (LE ^ phase) ? f72_data_int[31:0] : f72_data_int[63:32]; + assign f36_data_int[32] = phase ? 0 : f72_sof_int; + assign f36_data_int[33] = phase ? f72_eof_int : half_line; + assign f36_data_int[35:34] = f36_data_int[33] ? f72_occ_int[1:0] : 2'b00; + + assign f36_src_rdy_int = f72_src_rdy_int; + assign f72_dst_rdy_int = (phase | half_line) & f36_dst_rdy_int; + + wire f36_xfer = f36_src_rdy_int & f36_dst_rdy_int; + wire f72_xfer = f72_src_rdy_int & f72_dst_rdy_int; + + always @(posedge clk) + if(reset) + phase <= 0; + else if(f72_xfer) + phase <= 0; + else if(f36_xfer) + phase <= 1; + + // Shortfifo on output to guarantee no deadlock + fifo_short #(.WIDTH(36)) tail_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f36_data_int), .src_rdy_i(f36_src_rdy_int), .dst_rdy_o(f36_dst_rdy_int), + .dataout(f36_dataout), .src_rdy_o(f36_src_rdy_o), .dst_rdy_i(f36_dst_rdy_i), + .space(),.occupied() ); + +endmodule // fifo72_to_fifo36 diff --git a/fpga/usrp2/fifo/fifo_19to36_tb.v b/fpga/usrp2/fifo/fifo_19to36_tb.v new file mode 100644 index 000000000..c585392c3 --- /dev/null +++ b/fpga/usrp2/fifo/fifo_19to36_tb.v @@ -0,0 +1,82 @@ +module fifo_tb(); + + reg clk = 0; + reg rst = 1; + reg clear = 0; + initial #1000 rst = 0; + always #50 clk = ~clk; + + reg [18:0] f19a; + wire [18:0] f19b, f19c, f19d; + wire [35:0] f36a, f36b; + + reg f19a_sr = 0; + wire f19b_sr, f19c_sr, f19d_sr, f36a_sr, f36b_sr; + wire f19a_dr, f19b_dr, f19c_dr, f19d_dr, f36a_dr, f36b_dr; + + fifo_short #(.WIDTH(19)) fifo_short1 + (.clk(clk),.reset(rst),.clear(clear), + .datain(f19a),.src_rdy_i(f19a_sr),.dst_rdy_o(f19a_dr), + .dataout(f19b),.src_rdy_o(f19b_sr),.dst_rdy_i(f19b_dr) ); + + fifo19_to_fifo36 fifo19_to_fifo36 + (.clk(clk),.reset(rst),.clear(clear), + .f19_datain(f19b),.f19_src_rdy_i(f19b_sr),.f19_dst_rdy_o(f19b_dr), + .f36_dataout(f36a),.f36_src_rdy_o(f36a_sr),.f36_dst_rdy_i(f36a_dr) ); + + fifo_short #(.WIDTH(36)) fifo_short2 + (.clk(clk),.reset(rst),.clear(clear), + .datain(f36a),.src_rdy_i(f36a_sr),.dst_rdy_o(f36a_dr), + .dataout(f36b),.src_rdy_o(f36b_sr),.dst_rdy_i(f36b_dr) ); + + fifo36_to_fifo19 fifo36_to_fifo19 + (.clk(clk),.reset(rst),.clear(clear), + .f36_datain(f36b),.f36_src_rdy_i(f36b_sr),.f36_dst_rdy_o(f36b_dr), + .f19_dataout(f19c),.f19_src_rdy_o(f19c_sr),.f19_dst_rdy_i(f19c_dr) ); + + fifo_short #(.WIDTH(19)) fifo_short3 + (.clk(clk),.reset(rst),.clear(clear), + .datain(f19c),.src_rdy_i(f19c_sr),.dst_rdy_o(f19c_dr), + .dataout(f19d),.src_rdy_o(f19d_sr),.dst_rdy_i(f19d_dr) ); + + assign f19d_dr = 1; + + always @(posedge clk) + if(f19a_sr & f19a_dr) + $display("18IN: %h", f19a); + + always @(posedge clk) + if(f19d_sr & f19d_dr) + $display(" 18OUT: %h", f19d); + + always @(posedge clk) + if(f36b_sr & f36b_dr) + $display(" 36: %h", f36b); + + initial $dumpfile("fifo_tb.vcd"); + initial $dumpvars(0,fifo_tb); + + initial + begin + @(negedge rst); + @(posedge clk); + repeat (2) + begin + f19a <= 19'h1_AA01; + f19a_sr <= 1; + @(posedge clk); + f19a <= 19'h0_AA02; + repeat (4) + begin + @(posedge clk); + f19a <= f19a + 1; + end + f19a[18:16] <= 3'b010; + @(posedge clk); + f19a_sr <= 0; + f19a <= 19'h7_FFFF; + @(posedge clk); + end + #20000 $finish; + end +endmodule // longfifo_tb diff --git a/fpga/usrp2/fifo/fifo_2clock.v b/fpga/usrp2/fifo/fifo_2clock.v new file mode 100644 index 000000000..34c85ccb4 --- /dev/null +++ b/fpga/usrp2/fifo/fifo_2clock.v @@ -0,0 +1,117 @@ + +// FIXME ignores the AWIDTH (fifo size) parameter + +module fifo_2clock + #(parameter WIDTH=36, SIZE=6) + (input wclk, input [WIDTH-1:0] datain, input src_rdy_i, output dst_rdy_o, output [15:0] space, + input rclk, output [WIDTH-1:0] dataout, output src_rdy_o, input dst_rdy_i, output [15:0] occupied, + input arst); + + wire [SIZE:0] level_rclk, level_wclk; // xilinx adds an extra bit if you ask for accurate levels + wire full, empty, write, read; + + assign dst_rdy_o = ~full; + assign src_rdy_o = ~empty; + assign write = src_rdy_i & dst_rdy_o; + assign read = src_rdy_o & dst_rdy_i; + + generate + if(WIDTH==36) + if(SIZE==9) + fifo_xlnx_512x36_2clk fifo_xlnx_512x36_2clk + (.rst(arst), + .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk), + .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) ); + else if(SIZE==11) + fifo_xlnx_2Kx36_2clk fifo_xlnx_2Kx36_2clk + (.rst(arst), + .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk), + .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) ); + else if(SIZE==6) + fifo_xlnx_64x36_2clk fifo_xlnx_64x36_2clk + (.rst(arst), + .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk), + .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) ); + else + fifo_xlnx_512x36_2clk fifo_xlnx_512x36_2clk + (.rst(arst), + .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk), + .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) ); + else if((WIDTH==19)|(WIDTH==18)) + if(SIZE==4) + fifo_xlnx_16x19_2clk fifo_xlnx_16x19_2clk + (.rst(arst), + .wr_clk(wclk),.din(datain),.full(full),.wr_en(write),.wr_data_count(level_wclk), + .rd_clk(rclk),.dout(dataout),.empty(empty),.rd_en(read),.rd_data_count(level_rclk) ); + endgenerate + + assign occupied = {{(16-SIZE-1){1'b0}},level_rclk}; + assign space = ((1<<SIZE)+1)-level_wclk; + +endmodule // fifo_2clock + +/* +`else + // ISE sucks, so the following doesn't work properly + + reg [AWIDTH-1:0] wr_addr, rd_addr; + wire [AWIDTH-1:0] wr_addr_rclk, rd_addr_wclk; + wire [AWIDTH-1:0] next_rd_addr; + wire enb_read; + + // Write side management + wire [AWIDTH-1:0] next_wr_addr = wr_addr + 1; + always @(posedge wclk or posedge arst) + if(arst) + wr_addr <= 0; + else if(write) + wr_addr <= next_wr_addr; + assign full = (next_wr_addr == rd_addr_wclk); + + // RAM for data storage. Data out is registered, complicating the + // read side logic + ram_2port #(.DWIDTH(DWIDTH),.AWIDTH(AWIDTH)) mac_rx_ff_ram + (.clka(wclk),.ena(1'b1),.wea(write),.addra(wr_addr),.dia(datain),.doa(), + .clkb(rclk),.enb(enb_read),.web(1'b0),.addrb(next_rd_addr),.dib(0),.dob(dataout) ); + + // Read side management + reg data_valid; + assign empty = ~data_valid; + assign next_rd_addr = rd_addr + data_valid; + assign enb_read = read | ~data_valid; + + always @(posedge rclk or posedge arst) + if(arst) + rd_addr <= 0; + else if(read) + rd_addr <= rd_addr + 1; + + always @(posedge rclk or posedge arst) + if(arst) + data_valid <= 0; + else + if(read & (next_rd_addr == wr_addr_rclk)) + data_valid <= 0; + else if(next_rd_addr != wr_addr_rclk) + data_valid <= 1; + + // Send pointers across clock domains via gray code + gray_send #(.WIDTH(AWIDTH)) send_wr_addr + (.clk_in(wclk),.addr_in(wr_addr), + .clk_out(rclk),.addr_out(wr_addr_rclk) ); + + gray_send #(.WIDTH(AWIDTH)) send_rd_addr + (.clk_in(rclk),.addr_in(rd_addr), + .clk_out(wclk),.addr_out(rd_addr_wclk) ); + + // Generate fullness info, these are approximate and may be delayed + // and are only for higher-level flow control. + // Only full and empty are guaranteed exact. + always @(posedge wclk) + level_wclk <= wr_addr - rd_addr_wclk; + always @(posedge rclk) + level_rclk <= wr_addr_rclk - rd_addr; +`endif +endmodule // fifo_2clock + +*/ diff --git a/fpga/usrp2/fifo/fifo_2clock_cascade.v b/fpga/usrp2/fifo/fifo_2clock_cascade.v new file mode 100644 index 000000000..4e8c244c2 --- /dev/null +++ b/fpga/usrp2/fifo/fifo_2clock_cascade.v @@ -0,0 +1,41 @@ + +module fifo_2clock_cascade + #(parameter WIDTH=32, SIZE=9) + (input wclk, input [WIDTH-1:0] datain, input src_rdy_i, output dst_rdy_o, + output [15:0] space, output [15:0] short_space, + input rclk, output [WIDTH-1:0] dataout, output src_rdy_o, input dst_rdy_i, + output [15:0] occupied, output [15:0] short_occupied, + input arst); + + wire [WIDTH-1:0] data_int1, data_int2; + wire src_rdy_int1, src_rdy_int2, dst_rdy_int1, dst_rdy_int2; + wire [SIZE-1:0] level_wclk, level_rclk; + wire [4:0] s1_space, s1_occupied, s2_space, s2_occupied; + wire [15:0] l_space, l_occupied; + + fifo_short #(.WIDTH(WIDTH)) shortfifo + (.clk(wclk), .reset(arst), .clear(0), + .datain(datain), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o), + .dataout(data_int1), .src_rdy_o(src_rdy_int1), .dst_rdy_i(dst_rdy_int1), + .space(s1_space), .occupied(s1_occupied) ); + + fifo_2clock #(.WIDTH(WIDTH),.SIZE(SIZE)) fifo_2clock + (.wclk(wclk), .datain(data_int1), .src_rdy_i(src_rdy_int1), .dst_rdy_o(dst_rdy_int1), .space(l_space), + .rclk(rclk), .dataout(data_int2), .src_rdy_o(src_rdy_int2), .dst_rdy_i(dst_rdy_int2), .occupied(l_occupied), + .arst(arst) ); + + fifo_short #(.WIDTH(WIDTH)) shortfifo2 + (.clk(rclk), .reset(arst), .clear(0), + .datain(data_int2), .src_rdy_i(src_rdy_int2), .dst_rdy_o(dst_rdy_int2), + .dataout(dataout), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i), + .space(s2_space), .occupied(s2_occupied)); + + // Be conservative -- Only advertise space from input side of fifo, occupied from output side + assign space = {11'b0,s1_space} + l_space; + assign occupied = {11'b0,s2_occupied} + l_occupied; + + // For the fifo_extram, we only want to know the immediately adjacent space + assign short_space = {11'b0,s1_space}; + assign short_occupied = {11'b0,s2_occupied}; + +endmodule // fifo_2clock_cascade diff --git a/fpga/usrp2/fifo/fifo_cascade.v b/fpga/usrp2/fifo/fifo_cascade.v new file mode 100644 index 000000000..fdd8449bc --- /dev/null +++ b/fpga/usrp2/fifo/fifo_cascade.v @@ -0,0 +1,52 @@ + + +// This FIFO exists to provide an intermediate point for the data on its +// long trek from one RAM (in the buffer pool) to another (in the longfifo) +// The shortfifo is more flexible in its placement since it is based on +// distributed RAM + +// This one has the shortfifo on both the in and out sides. +module fifo_cascade + #(parameter WIDTH=32, SIZE=9) + (input clk, input reset, input clear, + input [WIDTH-1:0] datain, + input src_rdy_i, + output dst_rdy_o, + output [WIDTH-1:0] dataout, + output src_rdy_o, + input dst_rdy_i, + output [15:0] space, + output [15:0] occupied); + + wire [WIDTH-1:0] data_int, data_int2; + wire src_rdy_1, dst_rdy_1, src_rdy_2, dst_rdy_2; + + wire [4:0] s1_space, s1_occupied, s2_space, s2_occupied; + wire [15:0] l_space, l_occupied; + + fifo_short #(.WIDTH(WIDTH)) head_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(datain), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o), + .dataout(data_int), .src_rdy_o(src_rdy_1), .dst_rdy_i(dst_rdy_1), + .space(s1_space),.occupied(s1_occupied) ); + + fifo_long #(.WIDTH(WIDTH),.SIZE(SIZE)) middle_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(data_int), .src_rdy_i(src_rdy_1), .dst_rdy_o(dst_rdy_1), + .dataout(data_int2), .src_rdy_o(src_rdy_2), .dst_rdy_i(dst_rdy_2), + .space(l_space),.occupied(l_occupied) ); + + fifo_short #(.WIDTH(WIDTH)) tail_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(data_int2), .src_rdy_i(src_rdy_2), .dst_rdy_o(dst_rdy_2), + .dataout(dataout), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i), + .space(s2_space),.occupied(s2_occupied) ); + + assign space = {11'b0,s1_space} + {11'b0,s2_space} + l_space; + assign occupied = {11'b0,s1_occupied} + {11'b0,s2_occupied} + l_occupied; + +endmodule // cascadefifo2 + + + + diff --git a/fpga/usrp2/fifo/fifo_long.v b/fpga/usrp2/fifo/fifo_long.v new file mode 100644 index 000000000..0426779f6 --- /dev/null +++ b/fpga/usrp2/fifo/fifo_long.v @@ -0,0 +1,148 @@ + +// FIFO intended to be interchangeable with shortfifo, but +// based on block ram instead of SRL16's +// only one clock domain + +// Port A is write port, Port B is read port + +module fifo_long + #(parameter WIDTH=32, SIZE=9) + (input clk, input reset, input clear, + input [WIDTH-1:0] datain, + input src_rdy_i, + output dst_rdy_o, + output [WIDTH-1:0] dataout, + output src_rdy_o, + input dst_rdy_i, + + output reg [15:0] space, + output reg [15:0] occupied); + + wire write = src_rdy_i & dst_rdy_o; + wire read = dst_rdy_i & src_rdy_o; + wire full, empty; + + assign dst_rdy_o = ~full; + assign src_rdy_o = ~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(datain), + .doa(), + + .clkb(clk), + .enb((read_state==PRE_READ)|read), + .web(0), + .addrb(rd_addr), + .dib(0), + .dob(dataout)); + + 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 - 3; + 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)-2; + always @(posedge clk) + if(reset) + space <= NUMLINES; + else if(clear) + space <= NUMLINES; + else if(read & ~write) + space <= space + 1; + else if(write & ~read) + space <= space - 1; + + always @(posedge clk) + if(reset) + occupied <= 0; + else if(clear) + occupied <= 0; + else if(read & ~write) + occupied <= occupied - 1; + else if(write & ~read) + occupied <= occupied + 1; + +endmodule // fifo_long diff --git a/fpga/usrp2/fifo/fifo_pacer.v b/fpga/usrp2/fifo/fifo_pacer.v new file mode 100644 index 000000000..1bf03ab6e --- /dev/null +++ b/fpga/usrp2/fifo/fifo_pacer.v @@ -0,0 +1,24 @@ + + +module fifo_pacer + (input clk, + input reset, + input [7:0] rate, + input enable, + input src1_rdy_i, output dst1_rdy_o, + output src2_rdy_o, input dst2_rdy_i, + output underrun, overrun); + + wire strobe; + + cic_strober strober (.clock(clk), .reset(reset), .enable(enable), + .rate(rate), .strobe_fast(1), .strobe_slow(strobe)); + + wire all_ready = src1_rdy_i & dst2_rdy_i; + assign dst1_rdy_o = all_ready & strobe; + assign src2_rdy_o = dst1_rdy_o; + + assign underrun = strobe & ~src1_rdy_i; + assign overrun = strobe & ~dst2_rdy_i; + +endmodule // fifo_pacer diff --git a/fpga/usrp2/fifo/fifo_short.v b/fpga/usrp2/fifo/fifo_short.v new file mode 100644 index 000000000..53a7603c7 --- /dev/null +++ b/fpga/usrp2/fifo/fifo_short.v @@ -0,0 +1,95 @@ + +module fifo_short + #(parameter WIDTH=32) + (input clk, input reset, input clear, + input [WIDTH-1:0] datain, + input src_rdy_i, + output dst_rdy_o, + output [WIDTH-1:0] dataout, + output src_rdy_o, + input dst_rdy_i, + + output reg [4:0] space, + output reg [4:0] occupied); + + reg full, empty; + wire write = src_rdy_i & dst_rdy_o; + wire read = dst_rdy_i & src_rdy_o; + + assign dst_rdy_o = ~full; + assign src_rdy_o = ~empty; + + 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(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 == 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(reset) + 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(reset) + occupied <= 0; + else if(clear) + occupied <= 0; + else if(read & ~write) + occupied <= occupied - 1; + else if(write & ~read) + occupied <= occupied + 1; + +endmodule // fifo_short + diff --git a/fpga/usrp2/fifo/fifo_spec.txt b/fpga/usrp2/fifo/fifo_spec.txt new file mode 100644 index 000000000..133b9fa8e --- /dev/null +++ b/fpga/usrp2/fifo/fifo_spec.txt @@ -0,0 +1,36 @@ + + +FIFO and Buffer Interface Spec + +Buffer Interface Data Wires -- matches fifo36 + DATA[31:0] + FLAGS[3:0] + Bit 0 SOP + Bit 1 EOP + If SOP=1 && EOP=1, OCC contains error flags + Bits 3:2 OCC[1:0] --> 00 = all 4 bytes + 01 = 1 byte + 10 = 2 bytes + 11 = 3 bytes + +fifo36 --> {OCC[1:0],EOP,SOP,DATA[31:0]} + OCC same as buffer interface + +fifo19 --> {OCC,EOP,SOP,DATA[15:0]} + Doesn't fit well into BRAM, dist RAM ok + OCC = 1 means last word is half full + = 0 means last word is full + +fifo18 --> {EOP,SOP,DATA[15:0]} + No half-word capability? Should we drop sop instead? + +Control Wires - Data into FIFO + SRC_RDY_i Upstream has data for me + DST_RDY_o I have space + Transfer occurs if SRC_RDI_i && DST_RDY_o + +Control Wires - Data out of FIFO + SRC_RDY_o I have data for downstream + DST_RDY_i Downstream has space + Transfer occurs if SRC_RDI_o && DST_RDY_i + diff --git a/fpga/usrp2/fifo/fifo_tb.v b/fpga/usrp2/fifo/fifo_tb.v new file mode 100644 index 000000000..3e2862a70 --- /dev/null +++ b/fpga/usrp2/fifo/fifo_tb.v @@ -0,0 +1,55 @@ +module fifo_tb(); + + reg clk = 0; + reg rst = 1; + reg clear = 0; + initial #1000 rst = 0; + always #50 clk = ~clk; + + reg [31:0] f36_data = 0; + reg [1:0] f36_occ = 0; + reg f36_sof = 0, f36_eof = 0; + reg f36_src_rdy; + wire f36_dst_rdy; + + wire [7:0] ll_data; + wire ll_src_rdy, ll_dst_rdy, ll_sof, ll_eof; + wire [35:0] err_dat; + wire err_src_rdy, err_dst_rdy; + + fifo36_to_ll8 fifo36_to_ll8 + (.clk(clk),.reset(rst),.clear(clear), + .f36_data({f36_occ,f36_eof,f36_sof,f36_data}),.f36_src_rdy_i(f36_src_rdy),.f36_dst_rdy_o(f36_dst_rdy), + .ll_data(ll_data),.ll_sof(ll_sof),.ll_eof(ll_eof), + .ll_src_rdy(ll_src_rdy),.ll_dst_rdy(ll_dst_rdy)); + + assign ll_dst_rdy = 1; + + always @(posedge clk) + if(ll_src_rdy) + $display("LL: SOF %d, EOF %d, DAT %x",ll_sof,ll_eof,ll_data); + + initial $dumpfile("fifo_tb.vcd"); + initial $dumpvars(0,fifo_tb); + + initial + begin + @(negedge rst); + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge clk); + f36_src_rdy <= 1; + {f36_occ,f36_eof,f36_sof,f36_data} <= { 2'b00,1'b0,1'b1,32'h00010203}; + @(posedge clk); + {f36_occ,f36_eof,f36_sof,f36_data} <= { 2'b00,1'b0,1'b0,32'h04050607}; + @(posedge clk); + {f36_occ,f36_eof,f36_sof,f36_data} <= { 2'b00,1'b0,1'b0,32'h08090a0b}; + @(posedge clk); + {f36_occ,f36_eof,f36_sof,f36_data} <= { 2'b11,1'b1,1'b0,32'h0c0d0e0f}; + @(posedge clk); + f36_src_rdy <= 0; + end + + initial #4000 $finish; +endmodule // longfifo_tb diff --git a/fpga/usrp2/fifo/ll8_shortfifo.v b/fpga/usrp2/fifo/ll8_shortfifo.v new file mode 100644 index 000000000..39ada9a4f --- /dev/null +++ b/fpga/usrp2/fifo/ll8_shortfifo.v @@ -0,0 +1,13 @@ + + +module ll8_shortfifo + (input clk, input reset, input clear, + input [7:0] datain, input sof_i, input eof_i, input error_i, input src_rdy_i, output dst_rdy_o, + output [7:0] dataout, output sof_o, output eof_o, output error_o, output src_rdy_o, input dst_rdy_i); + + fifo_short #(.WIDTH(11)) fifo_short + (.clk(clk), .reset(reset), .clear(clear), + .datain({error_i,eof_i,sof_i,datain}), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o), + .dataout({error_o,eof_o,sof_o,dataout}), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i)); + +endmodule // ll8_shortfifo diff --git a/fpga/usrp2/fifo/ll8_to_fifo19.v b/fpga/usrp2/fifo/ll8_to_fifo19.v new file mode 100644 index 000000000..ac8ac19a6 --- /dev/null +++ b/fpga/usrp2/fifo/ll8_to_fifo19.v @@ -0,0 +1,87 @@ + +module ll8_to_fifo19 + (input clk, input reset, input clear, + input [7:0] ll_data, + input ll_sof, + input ll_eof, + input ll_src_rdy, + output ll_dst_rdy, + + output [18:0] f19_data, + output f19_src_rdy_o, + input f19_dst_rdy_i ); + + // Short FIFO on input to guarantee no deadlock + wire [7:0] ll_data_int; + wire ll_sof_int, ll_eof_int, ll_src_rdy_int, ll_dst_rdy_int; + + ll8_shortfifo head_fifo + (.clk(clk), .reset(reset), .clear(clear), + .datain(ll_data), .sof_i(ll_sof), .eof_i(ll_eof), + .error_i(0), .src_rdy_i(ll_src_rdy), .dst_rdy_o(ll_dst_rdy), + .dataout(ll_data_int), .sof_o(ll_sof_int), .eof_o(ll_eof_int), + .error_o(), .src_rdy_o(ll_src_rdy_int), .dst_rdy_i(ll_dst_rdy_int)); + + // Actual ll8_to_fifo19 which could deadlock if not connected to a shortfifo + localparam XFER_EMPTY = 0; + localparam XFER_HALF = 1; + localparam XFER_HALF_WRITE = 3; + + wire [18:0] f19_data_int; + wire f19_sof_int, f19_eof_int, f19_occ_int, f19_src_rdy_int, f19_dst_rdy_int; + + wire xfer_out = f19_src_rdy_int & f19_dst_rdy_int; + wire xfer_in = ll_src_rdy_int & ll_dst_rdy_int; + reg hold_sof; + + reg [1:0] state; + reg [7:0] hold_reg; + + always @(posedge clk) + if(ll_src_rdy_int & (state==XFER_EMPTY)) + hold_reg <= ll_data_int; + + always @(posedge clk) + if(ll_sof_int & (state==XFER_EMPTY)) + hold_sof <= 1; + else if(xfer_out) + hold_sof <= 0; + + always @(posedge clk) + if(reset | clear) + state <= XFER_EMPTY; + else + case(state) + XFER_EMPTY : + if(ll_src_rdy_int) + if(ll_eof_int) + state <= XFER_HALF_WRITE; + else + state <= XFER_HALF; + XFER_HALF : + if(ll_src_rdy_int & f19_dst_rdy_int) + state <= XFER_EMPTY; + XFER_HALF_WRITE : + if(f19_dst_rdy_int) + state <= XFER_EMPTY; + endcase // case (state) + + assign ll_dst_rdy_int = (state==XFER_EMPTY) | ((state==XFER_HALF)&f19_dst_rdy_int); + assign f19_src_rdy_int= (state==XFER_HALF_WRITE) | ((state==XFER_HALF)&ll_src_rdy_int); + + assign f19_sof_int = hold_sof | (ll_sof_int & (state==XFER_HALF)); + assign f19_eof_int = (state == XFER_HALF_WRITE) | ll_eof_int; + assign f19_occ_int = (state == XFER_HALF_WRITE); + + assign f19_data_int = {f19_occ_int,f19_eof_int,f19_sof_int,hold_reg,ll_data_int}; + + // Shortfifo on output to guarantee no deadlock + fifo_short #(.WIDTH(19)) tail_fifo + (.clk(clk),.reset(reset),.clear(clear), + .datain(f19_data_int), .src_rdy_i(f19_src_rdy_int), .dst_rdy_o(f19_dst_rdy_int), + .dataout(f19_data), .src_rdy_o(f19_src_rdy_o), .dst_rdy_i(f19_dst_rdy_i), + .space(),.occupied() ); + + +endmodule // ll8_to_fifo19 + diff --git a/fpga/usrp2/fifo/ll8_to_fifo36.v b/fpga/usrp2/fifo/ll8_to_fifo36.v new file mode 100644 index 000000000..108daa903 --- /dev/null +++ b/fpga/usrp2/fifo/ll8_to_fifo36.v @@ -0,0 +1,97 @@ + +module ll8_to_fifo36 + (input clk, input reset, input clear, + input [7:0] ll_data, + input ll_sof_n, + input ll_eof_n, + input ll_src_rdy_n, + output ll_dst_rdy_n, + + output [35:0] f36_data, + output f36_src_rdy_o, + input f36_dst_rdy_i ); + + wire f36_write = f36_src_rdy_o & f36_dst_rdy_i; + + // Why anybody would use active low in an FPGA is beyond me... + wire ll_sof = ~ll_sof_n; + wire ll_eof = ~ll_eof_n; + wire ll_src_rdy = ~ll_src_rdy_n; + wire ll_dst_rdy; + assign ll_dst_rdy_n = ~ll_dst_rdy; + + reg f36_sof, f36_eof; + reg [1:0] f36_occ; + + + reg [2:0] state; + reg [7:0] dat0, dat1, dat2, dat3; + + always @(posedge clk) + if(ll_src_rdy & ((state==0)|f36_write)) + f36_sof <= ll_sof; + + always @(posedge clk) + if(ll_src_rdy & ((state !=4)|f36_write)) + f36_eof <= ll_eof; + + always @(posedge clk) + if(ll_eof) + f36_occ <= state[1:0] + 1; + else + f36_occ <= 0; + + always @(posedge clk) + if(reset) + state <= 0; + else + if(ll_src_rdy) + case(state) + 0 : + if(ll_eof) + state <= 4; + else + state <= 1; + 1 : + if(ll_eof) + state <= 4; + else + state <= 2; + 2 : + if(ll_eof) + state <= 4; + else + state <= 3; + 3 : state <= 4; + 4 : + if(f36_dst_rdy_i) + if(ll_eof) + state <= 4; + else + state <= 1; + endcase // case(state) + else + if(f36_write) + state <= 0; + + always @(posedge clk) + if(ll_src_rdy & (state==3)) + dat3 <= ll_data; + + always @(posedge clk) + if(ll_src_rdy & (state==2)) + dat2 <= ll_data; + + always @(posedge clk) + if(ll_src_rdy & (state==1)) + dat1 <= ll_data; + + always @(posedge clk) + if(ll_src_rdy & ((state==0) | f36_write)) + dat0 <= ll_data; + + assign ll_dst_rdy = f36_dst_rdy_i | (state != 4); + assign f36_data = {f36_occ,f36_eof,f36_sof,dat0,dat1,dat2,dat3}; // FIXME endianess + assign f36_src_rdy_o = (state == 4); + +endmodule // ll8_to_fifo36 diff --git a/fpga/usrp2/fifo/packet32_tb.v b/fpga/usrp2/fifo/packet32_tb.v new file mode 100644 index 000000000..82bb09c29 --- /dev/null +++ b/fpga/usrp2/fifo/packet32_tb.v @@ -0,0 +1,27 @@ + + +module packet32_tb(); + + wire [35:0] data; + wire src_rdy, dst_rdy; + + wire clear = 0; + reg clk = 0; + reg reset = 1; + + always #10 clk <= ~clk; + initial #1000 reset <= 0; + + initial $dumpfile("packet32_tb.vcd"); + initial $dumpvars(0,packet32_tb); + + wire [31:0] total, crc_err, seq_err, len_err; + + packet_generator32 pkt_gen (.clk(clk), .reset(reset), .clear(clear), + .data_o(data), .src_rdy_o(src_rdy), .dst_rdy_i(dst_rdy)); + + packet_verifier32 pkt_ver (.clk(clk), .reset(reset), .clear(clear), + .data_i(data), .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), + .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +endmodule // packet32_tb diff --git a/fpga/usrp2/fifo/packet_dispatcher36_x3.v b/fpga/usrp2/fifo/packet_dispatcher36_x3.v new file mode 100644 index 000000000..fd762d061 --- /dev/null +++ b/fpga/usrp2/fifo/packet_dispatcher36_x3.v @@ -0,0 +1,270 @@ +// +// Copyright 2011 Ettus Research LLC +// +// Packet dispatcher with fifo36 interface and 3 outputs. +// +// The packet dispatcher expects 2-byte padded ethernet frames. +// The frames will be inspected at ethernet, IPv4, UDP, and VRT layers. +// Packets are dispatched into the following streams: +// * tx dsp stream +// * to cpu stream +// * to external stream +// * to both cpu and external +// +// The following registers are used for dispatcher control: +// * base + 0 = this ipv4 address (32 bits) +// * base + 1 = udp dst port (lower 16 bits) +// + +module packet_dispatcher36_x3 + #( + parameter BASE = 0 + ) + ( + //clocking and reset interface: + input clk, input rst, input clr, + + //setting register interface: + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + //input stream interfaces: + input [35:0] com_inp_data, input com_inp_valid, output com_inp_ready, + + //output stream interfaces: + output [35:0] ext_out_data, output ext_out_valid, input ext_out_ready, + output [35:0] dsp_out_data, output dsp_out_valid, input dsp_out_ready, + output [35:0] cpu_out_data, output cpu_out_valid, input cpu_out_ready + ); + + //setting register to program the IP address + wire [31:0] my_ip_addr; + setting_reg #(.my_addr(BASE+0)) sreg_ip_addr( + .clk(clk),.rst(rst), + .strobe(set_stb),.addr(set_addr),.in(set_data), + .out(my_ip_addr),.changed() + ); + + //setting register to program the UDP DSP port + wire [15:0] dsp_udp_port; + setting_reg #(.my_addr(BASE+1), .width(16)) sreg_data_port( + .clk(clk),.rst(rst), + .strobe(set_stb),.addr(set_addr),.in(set_data), + .out(dsp_udp_port),.changed() + ); + + //////////////////////////////////////////////////////////////////// + // Communication input inspector + // - inspect com input and send it to DSP, EXT, CPU, or BOTH + //////////////////////////////////////////////////////////////////// + localparam PD_STATE_READ_COM_PRE = 0; + localparam PD_STATE_READ_COM = 1; + localparam PD_STATE_WRITE_REGS = 2; + localparam PD_STATE_WRITE_LIVE = 3; + + localparam PD_DEST_DSP = 0; + localparam PD_DEST_EXT = 1; + localparam PD_DEST_CPU = 2; + localparam PD_DEST_BOF = 3; + + localparam PD_MAX_NUM_DREGS = 13; //padded_eth + ip + udp + seq + vrt_hdr + localparam PD_DREGS_DSP_OFFSET = 11; //offset to start dsp at + + //output inspector interfaces + wire [35:0] pd_out_dsp_data; + wire pd_out_dsp_valid; + wire pd_out_dsp_ready; + + wire [35:0] pd_out_ext_data; + wire pd_out_ext_valid; + wire pd_out_ext_ready; + + wire [35:0] pd_out_cpu_data; + wire pd_out_cpu_valid; + wire pd_out_cpu_ready; + + wire [35:0] pd_out_bof_data; + wire pd_out_bof_valid; + wire pd_out_bof_ready; + + reg [1:0] pd_state; + reg [1:0] pd_dest; + reg [3:0] pd_dreg_count; //data registers to buffer headers + wire [3:0] pd_dreg_count_next = pd_dreg_count + 1'b1; + wire pd_dreg_counter_done = (pd_dreg_count_next == PD_MAX_NUM_DREGS)? 1'b1 : 1'b0; + reg [35:0] pd_dregs [PD_MAX_NUM_DREGS-1:0]; + + //extract various packet components: + wire [47:0] pd_dregs_eth_dst_mac = {pd_dregs[0][15:0], pd_dregs[1][31:0]}; + wire [15:0] pd_dregs_eth_type = pd_dregs[3][15:0]; + wire [7:0] pd_dregs_ipv4_proto = pd_dregs[6][23:16]; + wire [31:0] pd_dregs_ipv4_dst_addr = pd_dregs[8][31:0]; + wire [15:0] pd_dregs_udp_dst_port = pd_dregs[9][15:0]; + wire [15:0] pd_dregs_vrt_size = com_inp_data[15:0]; + + //Inspector output flags special case: + //Inject SOF into flags at first DSP line. + wire [3:0] pd_out_flags = ( + (pd_dreg_count == PD_DREGS_DSP_OFFSET) && + (pd_dest == PD_DEST_DSP) + )? 4'b0001 : pd_dregs[pd_dreg_count][35:32]; + + //The communication inspector ouput data and valid signals: + //Mux between com input and data registers based on the state. + wire [35:0] pd_out_data = (pd_state == PD_STATE_WRITE_REGS)? + {pd_out_flags, pd_dregs[pd_dreg_count][31:0]} : com_inp_data + ; + wire pd_out_valid = + (pd_state == PD_STATE_WRITE_REGS)? 1'b1 : ( + (pd_state == PD_STATE_WRITE_LIVE)? com_inp_valid : ( + 1'b0)); + + //The communication inspector ouput ready signal: + //Mux between the various destination ready signals. + wire pd_out_ready = + (pd_dest == PD_DEST_DSP)? pd_out_dsp_ready : ( + (pd_dest == PD_DEST_EXT)? pd_out_ext_ready : ( + (pd_dest == PD_DEST_CPU)? pd_out_cpu_ready : ( + (pd_dest == PD_DEST_BOF)? pd_out_bof_ready : ( + 1'b0)))); + + //Always connected output data lines. + assign pd_out_dsp_data = pd_out_data; + assign pd_out_ext_data = pd_out_data; + assign pd_out_cpu_data = pd_out_data; + assign pd_out_bof_data = pd_out_data; + + //Destination output valid signals: + //Comes from inspector valid when destination is selected, and otherwise low. + assign pd_out_dsp_valid = (pd_dest == PD_DEST_DSP)? pd_out_valid : 1'b0; + assign pd_out_ext_valid = (pd_dest == PD_DEST_EXT)? pd_out_valid : 1'b0; + assign pd_out_cpu_valid = (pd_dest == PD_DEST_CPU)? pd_out_valid : 1'b0; + assign pd_out_bof_valid = (pd_dest == PD_DEST_BOF)? pd_out_valid : 1'b0; + + //The communication inspector ouput ready signal: + //Always ready when storing to data registers, + //comes from inspector ready output when live, + //and otherwise low. + assign com_inp_ready = + (pd_state == PD_STATE_READ_COM_PRE) ? 1'b1 : ( + (pd_state == PD_STATE_READ_COM) ? 1'b1 : ( + (pd_state == PD_STATE_WRITE_LIVE) ? pd_out_ready : ( + 1'b0))); + + always @(posedge clk) + if(rst | clr) begin + pd_state <= PD_STATE_READ_COM_PRE; + pd_dreg_count <= 0; + end + else begin + case(pd_state) + PD_STATE_READ_COM_PRE: begin + if (com_inp_ready & com_inp_valid & com_inp_data[32]) begin + pd_state <= PD_STATE_READ_COM; + pd_dreg_count <= pd_dreg_count_next; + pd_dregs[pd_dreg_count] <= com_inp_data; + end + end + + PD_STATE_READ_COM: begin + if (com_inp_ready & com_inp_valid) begin + pd_dregs[pd_dreg_count] <= com_inp_data; + if (pd_dreg_counter_done | com_inp_data[33]) begin + pd_state <= PD_STATE_WRITE_REGS; + pd_dreg_count <= 0; + + //---------- begin inspection decision -----------// + //EOF or bcast or not IPv4 or not UDP: + if ( + com_inp_data[33] || (pd_dregs_eth_dst_mac == 48'hffffffffffff) || + (pd_dregs_eth_type != 16'h800) || (pd_dregs_ipv4_proto != 8'h11) + ) begin + pd_dest <= PD_DEST_BOF; + end + + //not my IP address: + else if (pd_dregs_ipv4_dst_addr != my_ip_addr) begin + pd_dest <= PD_DEST_EXT; + end + + //UDP data port and VRT: + else if ((pd_dregs_udp_dst_port == dsp_udp_port) && (pd_dregs_vrt_size != 16'h0)) begin + pd_dest <= PD_DEST_DSP; + pd_dreg_count <= PD_DREGS_DSP_OFFSET; + end + + //other: + else begin + pd_dest <= PD_DEST_CPU; + end + //---------- end inspection decision -------------// + + end + else begin + pd_dreg_count <= pd_dreg_count_next; + end + end + end + + PD_STATE_WRITE_REGS: begin + if (pd_out_ready & pd_out_valid) begin + if (pd_out_data[33]) begin + pd_state <= PD_STATE_READ_COM_PRE; + pd_dreg_count <= 0; + end + else if (pd_dreg_counter_done) begin + pd_state <= PD_STATE_WRITE_LIVE; + pd_dreg_count <= 0; + end + else begin + pd_dreg_count <= pd_dreg_count_next; + end + end + end + + PD_STATE_WRITE_LIVE: begin + if (pd_out_ready & pd_out_valid & pd_out_data[33]) begin + pd_state <= PD_STATE_READ_COM_PRE; + end + end + + endcase //pd_state + end + + //connect this fast-path signals directly to the DSP out + assign dsp_out_data = pd_out_dsp_data; + assign dsp_out_valid = pd_out_dsp_valid; + assign pd_out_dsp_ready = dsp_out_ready; + + //////////////////////////////////////////////////////////////////// + // Splitter and output muxes for the bof packets + // - split the bof packets into two streams + // - mux split packets into cpu out and ext out + //////////////////////////////////////////////////////////////////// + + //dummy signals to join the the splitter and muxes below + wire [35:0] _split_to_ext_data, _split_to_cpu_data; + wire _split_to_ext_valid, _split_to_cpu_valid; + wire _split_to_ext_ready, _split_to_cpu_ready; + + splitter36 bof_out_splitter( + .clk(clk), .rst(rst), .clr(clr), + .inp_data(pd_out_bof_data), .inp_valid(pd_out_bof_valid), .inp_ready(pd_out_bof_ready), + .out0_data(_split_to_ext_data), .out0_valid(_split_to_ext_valid), .out0_ready(_split_to_ext_ready), + .out1_data(_split_to_cpu_data), .out1_valid(_split_to_cpu_valid), .out1_ready(_split_to_cpu_ready) + ); + + fifo36_mux ext_out_mux( + .clk(clk), .reset(rst), .clear(clr), + .data0_i(pd_out_ext_data), .src0_rdy_i(pd_out_ext_valid), .dst0_rdy_o(pd_out_ext_ready), + .data1_i(_split_to_ext_data), .src1_rdy_i(_split_to_ext_valid), .dst1_rdy_o(_split_to_ext_ready), + .data_o(ext_out_data), .src_rdy_o(ext_out_valid), .dst_rdy_i(ext_out_ready) + ); + + fifo36_mux cpu_out_mux( + .clk(clk), .reset(rst), .clear(clr), + .data0_i(pd_out_cpu_data), .src0_rdy_i(pd_out_cpu_valid), .dst0_rdy_o(pd_out_cpu_ready), + .data1_i(_split_to_cpu_data), .src1_rdy_i(_split_to_cpu_valid), .dst1_rdy_o(_split_to_cpu_ready), + .data_o(cpu_out_data), .src_rdy_o(cpu_out_valid), .dst_rdy_i(cpu_out_ready) + ); + +endmodule // packet_dispatcher36_x3 diff --git a/fpga/usrp2/fifo/packet_generator.v b/fpga/usrp2/fifo/packet_generator.v new file mode 100644 index 000000000..2ae911e24 --- /dev/null +++ b/fpga/usrp2/fifo/packet_generator.v @@ -0,0 +1,83 @@ + + +module packet_generator + (input clk, input reset, input clear, + output reg [7:0] data_o, output sof_o, output eof_o, + input [127:0] header, + output src_rdy_o, input dst_rdy_i); + + localparam len = 32'd2000; + + reg [31:0] state; + reg [31:0] seq; + reg [31:0] crc_out; + wire calc_crc = src_rdy_o & dst_rdy_i & ~(state[31:2] == 30'h3FFF_FFFF); + + + always @(posedge clk) + if(reset | clear) + seq <= 0; + else + if(eof_o & src_rdy_o & dst_rdy_i) + seq <= seq + 1; + + always @(posedge clk) + if(reset | clear) + state <= 0; + else + if(src_rdy_o & dst_rdy_i) + if(state == (len - 1)) + state <= 32'hFFFF_FFFC; + else + state <= state + 1; + + always @* + case(state) + 0 : data_o <= len[31:24]; + 1 : data_o <= len[23:16]; + 2 : data_o <= len[15:8]; + 3 : data_o <= len[7:0]; + 4 : data_o <= seq[31:24]; + 5 : data_o <= seq[23:16]; + 6 : data_o <= seq[15:8]; + 7 : data_o <= seq[7:0]; + 8 : data_o <= header[7:0]; + 9 : data_o <= header[15:8]; + 10 : data_o <= header[23:16]; + 11 : data_o <= header[31:24]; + 12 : data_o <= header[39:32]; + 13 : data_o <= header[47:40]; + 14 : data_o <= header[55:48]; + 15 : data_o <= header[63:56]; + 16 : data_o <= header[71:64]; + 17 : data_o <= header[79:72]; + 18 : data_o <= header[87:80]; + 19 : data_o <= header[95:88]; + 20 : data_o <= header[103:96]; + 21 : data_o <= header[111:104]; + 22 : data_o <= header[119:112]; + 23 : data_o <= header[127:120]; + + 32'hFFFF_FFFC : data_o <= crc_out[31:24]; + 32'hFFFF_FFFD : data_o <= crc_out[23:16]; + 32'hFFFF_FFFE : data_o <= crc_out[15:8]; + 32'hFFFF_FFFF : data_o <= crc_out[7:0]; + default : data_o <= state[7:0]; + endcase // case (state) + + assign src_rdy_o = 1; + assign sof_o = (state == 0); + assign eof_o = (state == 32'hFFFF_FFFF); + + wire clear_crc = eof_o & src_rdy_o & dst_rdy_i; + +// crc crc(.clk(clk), .reset(reset), .clear(clear_crc), .data(data_o), +// .calc(calc_crc), .crc_out(crc_out), .match()); + always @(posedge clk) + if(reset | clear | clear_crc) + crc_out <= 0; + else + if(calc_crc) + crc_out <= crc_out + data_o; + +endmodule // packet_generator diff --git a/fpga/usrp2/fifo/packet_generator32.v b/fpga/usrp2/fifo/packet_generator32.v new file mode 100644 index 000000000..1dc57191d --- /dev/null +++ b/fpga/usrp2/fifo/packet_generator32.v @@ -0,0 +1,23 @@ + + +module packet_generator32 + (input clk, input reset, input clear, + input [127:0] header, + output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + + wire [7:0] ll_data; + wire ll_sof, ll_eof, ll_src_rdy, ll_dst_rdy_n; + + packet_generator pkt_gen + (.clk(clk), .reset(reset), .clear(clear), + .data_o(ll_data), .sof_o(ll_sof), .eof_o(ll_eof), + .header(header), + .src_rdy_o(ll_src_rdy), .dst_rdy_i(~ll_dst_rdy_n)); + + ll8_to_fifo36 ll8_to_f36 + (.clk(clk), .reset(reset), .clear(clear), + .ll_data(ll_data), .ll_sof_n(~ll_sof), .ll_eof_n(~ll_eof), + .ll_src_rdy_n(~ll_src_rdy), .ll_dst_rdy_n(ll_dst_rdy_n), + .f36_data(data_o), .f36_src_rdy_o(src_rdy_o), .f36_dst_rdy_i(dst_rdy_i)); + +endmodule // packet_generator32 diff --git a/fpga/usrp2/fifo/packet_router.v b/fpga/usrp2/fifo/packet_router.v new file mode 100644 index 000000000..04c17b647 --- /dev/null +++ b/fpga/usrp2/fifo/packet_router.v @@ -0,0 +1,298 @@ +module packet_router + #( + parameter BUF_SIZE = 9, + parameter UDP_BASE = 0, + parameter CTRL_BASE = 0 + ) + ( + //wishbone interface for memory mapped CPU frames + input wb_clk_i, + input wb_rst_i, + input wb_we_i, + input wb_stb_i, + input [15:0] wb_adr_i, + input [31:0] wb_dat_i, + output [31:0] wb_dat_o, + output wb_ack_o, + output wb_err_o, + output wb_rty_o, + + //setting register interface + input set_stb, input [7:0] set_addr, input [31:0] set_data, + + input stream_clk, + input stream_rst, + input stream_clr, + + //output status register + output [31:0] status, + + output sys_int_o, //want an interrupt? + + output [31:0] debug, + + // Input Interfaces (in to router) + input [35:0] ser_inp_data, input ser_inp_valid, output ser_inp_ready, + input [35:0] dsp0_inp_data, input dsp0_inp_valid, output dsp0_inp_ready, + input [35:0] dsp1_inp_data, input dsp1_inp_valid, output dsp1_inp_ready, + input [35:0] eth_inp_data, input eth_inp_valid, output eth_inp_ready, + input [35:0] err_inp_data, input err_inp_valid, output err_inp_ready, + + // Output Interfaces (out of router) + output [35:0] ser_out_data, output ser_out_valid, input ser_out_ready, + output [35:0] dsp_out_data, output dsp_out_valid, input dsp_out_ready, + output [35:0] eth_out_data, output eth_out_valid, input eth_out_ready + ); + + assign wb_err_o = 1'b0; // Unused for now + assign wb_rty_o = 1'b0; // Unused for now + + //////////////////////////////////////////////////////////////////// + // CPU interface to this packet router + //////////////////////////////////////////////////////////////////// + wire [35:0] cpu_inp_data, cpu_out_data; + wire cpu_inp_valid, cpu_out_valid; + wire cpu_inp_ready, cpu_out_ready; + + //////////////////////////////////////////////////////////////////// + // Communication interfaces + //////////////////////////////////////////////////////////////////// + wire [35:0] com_inp_data, com_out_data, udp_out_data; + wire com_inp_valid, com_out_valid, udp_out_valid; + wire com_inp_ready, com_out_ready, udp_out_ready; + + //////////////////////////////////////////////////////////////////// + // Control signals (setting registers and status signals) + // - handshake lines for the CPU communication + // - setting registers to program the inspector + //////////////////////////////////////////////////////////////////// + + //setting register for mode control + wire [31:0] _sreg_mode_ctrl; + wire master_mode_flag; + + setting_reg #(.my_addr(CTRL_BASE+0), .width(1)) sreg_mode_ctrl( + .clk(stream_clk),.rst(stream_rst), + .strobe(set_stb),.addr(set_addr),.in(set_data), + .out(master_mode_flag),.changed() + ); + + //assign status output signals + wire [31:0] cpu_iface_status; + assign status = { + cpu_iface_status[31:9], master_mode_flag, cpu_iface_status[7:0] + }; + + //////////////////////////////////////////////////////////////////// + // Communication input source crossbar + // When in master mode: + // - serdes input -> comm output combiner + // - ethernet input -> comm input inspector + // When in slave mode: + // - serdes input -> comm input inspector + // - ethernet input -> null sink + //////////////////////////////////////////////////////////////////// + + //streaming signals from the crossbar to the combiner + wire [35:0] ext_inp_data; + wire ext_inp_valid; + wire ext_inp_ready; + + //dummy signals for valve/xbar below + wire [35:0] _eth_inp_data; + wire _eth_inp_valid; + wire _eth_inp_ready; + + // dummy signals to connect fifo_short + wire [35:0] _com_inp_data; + wire _com_inp_valid; + wire _com_inp_ready; + + valve36 eth_inp_valve ( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), .shutoff(~master_mode_flag), + .data_i(eth_inp_data), .src_rdy_i(eth_inp_valid), .dst_rdy_o(eth_inp_ready), + .data_o(_eth_inp_data), .src_rdy_o(_eth_inp_valid), .dst_rdy_i(_eth_inp_ready) + ); + + crossbar36 com_inp_xbar ( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), .cross(~master_mode_flag), + .data0_i(_eth_inp_data), .src0_rdy_i(_eth_inp_valid), .dst0_rdy_o(_eth_inp_ready), + .data1_i(ser_inp_data), .src1_rdy_i(ser_inp_valid), .dst1_rdy_o(ser_inp_ready), + .data0_o(_com_inp_data), .src0_rdy_o(_com_inp_valid), .dst0_rdy_i(_com_inp_ready), + .data1_o(ext_inp_data), .src1_rdy_o(ext_inp_valid), .dst1_rdy_i(ext_inp_ready) + ); + + // short fifo in the packet inspection path to help timing + fifo_short #(.WIDTH(36)) com_inp_fifo + (.clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .datain(_com_inp_data), .src_rdy_i(_com_inp_valid), .dst_rdy_o(_com_inp_ready), + .dataout(com_inp_data), .src_rdy_o(com_inp_valid), .dst_rdy_i(com_inp_ready), + .space(), .occupied() ); + + //////////////////////////////////////////////////////////////////// + // Communication output sink crossbar + // When in master mode: + // - comm output -> ethernet output + // - insp output -> serdes output + // When in slave mode: + // - com output -> serdes output + // - insp output -> null sink + //////////////////////////////////////////////////////////////////// + + //streaming signals from the inspector to the crossbar + wire [35:0] ext_out_data; + wire ext_out_valid; + wire ext_out_ready; + + //dummy signals for valve/xbar below + wire [35:0] _eth_out_data; + wire _eth_out_valid; + wire _eth_out_ready; + + crossbar36 com_out_xbar ( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), .cross(~master_mode_flag), + .data0_i(com_out_data), .src0_rdy_i(com_out_valid), .dst0_rdy_o(com_out_ready), + .data1_i(ext_out_data), .src1_rdy_i(ext_out_valid), .dst1_rdy_o(ext_out_ready), + .data0_o(_eth_out_data), .src0_rdy_o(_eth_out_valid), .dst0_rdy_i(_eth_out_ready), + .data1_o(ser_out_data), .src1_rdy_o(ser_out_valid), .dst1_rdy_i(ser_out_ready) + ); + + valve36 eth_out_valve ( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), .shutoff(~master_mode_flag), + .data_i(_eth_out_data), .src_rdy_i(_eth_out_valid), .dst_rdy_o(_eth_out_ready), + .data_o(eth_out_data), .src_rdy_o(eth_out_valid), .dst_rdy_i(eth_out_ready) + ); + + //////////////////////////////////////////////////////////////////// + // Communication output source combiner (feeds UDP proto machine) + // - DSP input + // - CPU input + // - ERR input + //////////////////////////////////////////////////////////////////// + + //dummy signals to join the the muxes below + wire [35:0] _combiner0_data, _combiner1_data; + wire _combiner0_valid, _combiner1_valid; + wire _combiner0_ready, _combiner1_ready; + + fifo36_mux #(.prio(0)) // No priority, fair sharing + _com_output_combiner0( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .data0_i(err_inp_data), .src0_rdy_i(err_inp_valid), .dst0_rdy_o(err_inp_ready), + .data1_i(cpu_inp_data), .src1_rdy_i(cpu_inp_valid), .dst1_rdy_o(cpu_inp_ready), + .data_o(_combiner0_data), .src_rdy_o(_combiner0_valid), .dst_rdy_i(_combiner0_ready) + ); + + fifo36_mux #(.prio(0)) // No priority, fair sharing + _com_output_combiner1( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .data0_i(dsp0_inp_data), .src0_rdy_i(dsp0_inp_valid), .dst0_rdy_o(dsp0_inp_ready), + .data1_i(dsp1_inp_data), .src1_rdy_i(dsp1_inp_valid), .dst1_rdy_o(dsp1_inp_ready), + .data_o(_combiner1_data), .src_rdy_o(_combiner1_valid), .dst_rdy_i(_combiner1_ready) + ); + + fifo36_mux #(.prio(1)) // Give priority to err/cpu over dsp + com_output_source( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .data0_i(_combiner0_data), .src0_rdy_i(_combiner0_valid), .dst0_rdy_o(_combiner0_ready), + .data1_i(_combiner1_data), .src1_rdy_i(_combiner1_valid), .dst1_rdy_o(_combiner1_ready), + .data_o(udp_out_data), .src_rdy_o(udp_out_valid), .dst_rdy_i(udp_out_ready) + ); + + //////////////////////////////////////////////////////////////////// + // Interface CPU to memory mapped wishbone + // - Uses 1 setting register + //////////////////////////////////////////////////////////////////// + buffer_int2 #(.BASE(CTRL_BASE+3), .BUF_SIZE(BUF_SIZE)) cpu_to_wb( + .clk(stream_clk), .rst(stream_rst | stream_clr), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .status(cpu_iface_status), + // Wishbone interface to RAM + .wb_clk_i(wb_clk_i), .wb_rst_i(wb_rst_i), + .wb_we_i(wb_we_i), .wb_stb_i(wb_stb_i), + .wb_adr_i(wb_adr_i), .wb_dat_i(wb_dat_i), + .wb_dat_o(wb_dat_o), .wb_ack_o(wb_ack_o), + // Write FIFO Interface (from PR and into WB) + .wr_data_i(cpu_out_data), + .wr_ready_i(cpu_out_valid), + .wr_ready_o(cpu_out_ready), + // Read FIFO Interface (from WB and into PR) + .rd_data_o(cpu_inp_data), + .rd_ready_o(cpu_inp_valid), + .rd_ready_i(cpu_inp_ready) + ); + + //////////////////////////////////////////////////////////////////// + // Packet Dispatcher + // - Uses 2 setting registers + // - provide buffering before cpu for random + small packet bursts + //////////////////////////////////////////////////////////////////// + wire [35:0] _cpu_out_data; + wire _cpu_out_valid; + wire _cpu_out_ready; + + packet_dispatcher36_x3 #(.BASE(CTRL_BASE+1)) packet_dispatcher( + .clk(stream_clk), .rst(stream_rst), .clr(stream_clr), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .com_inp_data(com_inp_data), .com_inp_valid(com_inp_valid), .com_inp_ready(com_inp_ready), + .ext_out_data(ext_out_data), .ext_out_valid(ext_out_valid), .ext_out_ready(ext_out_ready), + .dsp_out_data(dsp_out_data), .dsp_out_valid(dsp_out_valid), .dsp_out_ready(dsp_out_ready), + .cpu_out_data(_cpu_out_data), .cpu_out_valid(_cpu_out_valid), .cpu_out_ready(_cpu_out_ready) + ); + + fifo_cascade #(.WIDTH(36), .SIZE(9/*512 lines plenty for short pkts*/)) cpu_out_fifo ( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .datain(_cpu_out_data), .src_rdy_i(_cpu_out_valid), .dst_rdy_o(_cpu_out_ready), + .dataout(cpu_out_data), .src_rdy_o(cpu_out_valid), .dst_rdy_i(cpu_out_ready) + ); + + //////////////////////////////////////////////////////////////////// + // UDP TX Protocol machine + //////////////////////////////////////////////////////////////////// + + //dummy signals to connect the components below + wire [35:0] _com_out_data; + wire _com_out_valid, _com_out_ready; + + prot_eng_tx #(.BASE(UDP_BASE)) udp_prot_eng_tx + (.clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data), + .datain(udp_out_data), .src_rdy_i(udp_out_valid), .dst_rdy_o(udp_out_ready), + .dataout(_com_out_data), .src_rdy_o(_com_out_valid), .dst_rdy_i(_com_out_ready) ); + + fifo36_mux com_out_mux( + .clk(stream_clk), .reset(stream_rst), .clear(stream_clr), + .data0_i(ext_inp_data), .src0_rdy_i(ext_inp_valid), .dst0_rdy_o(ext_inp_ready), + .data1_i(_com_out_data), .src1_rdy_i(_com_out_valid), .dst1_rdy_o(_com_out_ready), + .data_o(com_out_data), .src_rdy_o(com_out_valid), .dst_rdy_i(com_out_ready) + ); + + //////////////////////////////////////////////////////////////////// + // Assign debugs + //////////////////////////////////////////////////////////////////// + + assign debug = { + //inputs to the router (12) + dsp0_inp_ready, dsp0_inp_valid, + dsp1_inp_ready, dsp1_inp_valid, + err_inp_ready, err_inp_valid, + ser_inp_ready, ser_inp_valid, + eth_inp_ready, eth_inp_valid, + cpu_inp_ready, cpu_inp_valid, + + //outputs from the router (8) + dsp_out_ready, dsp_out_valid, + ser_out_ready, ser_out_valid, + eth_out_ready, eth_out_valid, + cpu_out_ready, cpu_out_valid, + + //other interfaces (8) + ext_inp_ready, ext_inp_valid, + com_out_ready, com_out_valid, + ext_out_ready, ext_out_valid, + com_inp_ready, com_inp_valid, + + 4'b0 + }; + +endmodule // packet_router diff --git a/fpga/usrp2/fifo/packet_tb.v b/fpga/usrp2/fifo/packet_tb.v new file mode 100644 index 000000000..3c423d2ba --- /dev/null +++ b/fpga/usrp2/fifo/packet_tb.v @@ -0,0 +1,29 @@ + + +module packet_tb(); + + wire [7:0] data; + wire sof, eof, src_rdy, dst_rdy; + + wire clear = 0; + reg clk = 0; + reg reset = 1; + + always #10 clk <= ~clk; + initial #1000 reset <= 0; + + initial $dumpfile("packet_tb.vcd"); + initial $dumpvars(0,packet_tb); + + wire [31:0] total, crc_err, seq_err, len_err; + + packet_generator pkt_gen (.clk(clk), .reset(reset), .clear(clear), + .data_o(data), .sof_o(sof), .eof_o(eof), + .src_rdy_o(src_rdy), .dst_rdy_i(dst_rdy)); + + packet_verifier pkt_ver (.clk(clk), .reset(reset), .clear(clear), + .data_i(data), .sof_i(sof), .eof_i(eof), + .src_rdy_i(src_rdy), .dst_rdy_o(dst_rdy), + .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +endmodule // packet_tb diff --git a/fpga/usrp2/fifo/packet_verifier.v b/fpga/usrp2/fifo/packet_verifier.v new file mode 100644 index 000000000..21a4c136e --- /dev/null +++ b/fpga/usrp2/fifo/packet_verifier.v @@ -0,0 +1,61 @@ + + +// Packet format -- +// Line 1 -- Length, 32 bits +// Line 2 -- Sequence number, 32 bits +// Last line -- CRC, 32 bits + +module packet_verifier + (input clk, input reset, input clear, + input [7:0] data_i, input sof_i, input eof_i, input src_rdy_i, output dst_rdy_o, + + output reg [31:0] total, + output reg [31:0] crc_err, + output reg [31:0] seq_err, + output reg [31:0] len_err); + + reg [31:0] seq_num; + reg [31:0] length; + wire first_byte, last_byte; + reg second_byte, last_byte_d1; + wire match_crc; + wire calc_crc = src_rdy_i & dst_rdy_o; + + crc crc(.clk(clk), .reset(reset), .clear(last_byte_d1), .data(data_i), + .calc(calc_crc), .crc_out(), .match(match_crc)); + + assign first_byte = src_rdy_i & dst_rdy_o & sof_i; + assign last_byte = src_rdy_i & dst_rdy_o & eof_i; + assign dst_rdy_o = ~last_byte_d1; + + // stubs for now + wire match_seq = 1; + wire match_len = 1; + + always @(posedge clk) + if(reset | clear) + last_byte_d1 <= 0; + else + last_byte_d1 <= last_byte; + + always @(posedge clk) + if(reset | clear) + begin + total <= 0; + crc_err <= 0; + seq_err <= 0; + len_err <= 0; + end + else + if(last_byte_d1) + begin + total <= total + 1; + if(~match_crc) + crc_err <= crc_err + 1; + else if(~match_seq) + seq_err <= seq_err + 1; + else if(~match_len) + seq_err <= len_err + 1; + end + +endmodule // packet_verifier diff --git a/fpga/usrp2/fifo/packet_verifier32.v b/fpga/usrp2/fifo/packet_verifier32.v new file mode 100644 index 000000000..ec08e657d --- /dev/null +++ b/fpga/usrp2/fifo/packet_verifier32.v @@ -0,0 +1,23 @@ + + +module packet_verifier32 + (input clk, input reset, input clear, + input [35:0] data_i, input src_rdy_i, output dst_rdy_o, + output [31:0] total, output [31:0] crc_err, output [31:0] seq_err, output [31:0] len_err); + + wire [7:0] ll_data; + wire ll_sof, ll_eof, ll_src_rdy, ll_dst_rdy; + + fifo36_to_ll8 f36_to_ll8 + (.clk(clk), .reset(reset), .clear(clear), + .f36_data(data_i), .f36_src_rdy_i(src_rdy_i), .f36_dst_rdy_o(dst_rdy_o), + .ll_data(ll_data), .ll_sof(ll_sof), .ll_eof(ll_eof), + .ll_src_rdy(ll_src_rdy), .ll_dst_rdy(ll_dst_rdy)); + + packet_verifier pkt_ver + (.clk(clk), .reset(reset), .clear(clear), + .data_i(ll_data), .sof_i(ll_sof), .eof_i(ll_eof), + .src_rdy_i(ll_src_rdy), .dst_rdy_o(ll_dst_rdy), + .total(total), .crc_err(crc_err), .seq_err(seq_err), .len_err(len_err)); + +endmodule // packet_verifier32 diff --git a/fpga/usrp2/fifo/splitter36.v b/fpga/usrp2/fifo/splitter36.v new file mode 100644 index 000000000..ed998b4f5 --- /dev/null +++ b/fpga/usrp2/fifo/splitter36.v @@ -0,0 +1,68 @@ + +// Split packets from a fifo based interface so it goes out identically on two interfaces + +module splitter36 + ( + input clk, input rst, input clr, + input [35:0] inp_data, input inp_valid, output inp_ready, + output [35:0] out0_data, output out0_valid, input out0_ready, + output [35:0] out1_data, output out1_valid, input out1_ready + ); + + localparam STATE_COPY_BOTH = 0; + localparam STATE_COPY_ZERO = 1; + localparam STATE_COPY_ONE = 2; + + reg [1:0] state; + reg [35:0] data_reg; + + assign out0_data = (state == STATE_COPY_BOTH)? inp_data : data_reg; + assign out1_data = (state == STATE_COPY_BOTH)? inp_data : data_reg; + + assign out0_valid = + (state == STATE_COPY_BOTH)? inp_valid : ( + (state == STATE_COPY_ZERO)? 1'b1 : ( + 1'b0)); + + assign out1_valid = + (state == STATE_COPY_BOTH)? inp_valid : ( + (state == STATE_COPY_ONE)? 1'b1 : ( + 1'b0)); + + assign inp_ready = (state == STATE_COPY_BOTH)? (out0_ready | out1_ready) : 1'b0; + + always @(posedge clk) + if (rst | clr) begin + state <= STATE_COPY_BOTH; + end + else begin + case (state) + + STATE_COPY_BOTH: begin + if ((out0_valid & out0_ready) & ~(out1_valid & out1_ready)) begin + state <= STATE_COPY_ONE; + end + else if (~(out0_valid & out0_ready) & (out1_valid & out1_ready)) begin + state <= STATE_COPY_ZERO; + end + data_reg <= inp_data; + end + + STATE_COPY_ZERO: begin + if (out0_valid & out0_ready) begin + state <= STATE_COPY_BOTH; + end + end + + STATE_COPY_ONE: begin + if (out1_valid & out1_ready) begin + state <= STATE_COPY_BOTH; + end + end + + endcase //state + end + + + +endmodule //splitter36 diff --git a/fpga/usrp2/fifo/valve36.v b/fpga/usrp2/fifo/valve36.v new file mode 100644 index 000000000..d45eee497 --- /dev/null +++ b/fpga/usrp2/fifo/valve36.v @@ -0,0 +1,29 @@ + + +module valve36 + (input clk, input reset, input clear, + input shutoff, + input [35:0] data_i, input src_rdy_i, output dst_rdy_o, + output [35:0] data_o, output src_rdy_o, input dst_rdy_i); + + reg shutoff_int, active; + wire active_next = (src_rdy_i & dst_rdy_o)? ~data_i[33] : active; + + assign data_o = data_i; + + assign dst_rdy_o = shutoff_int ? 1'b1 : dst_rdy_i; + assign src_rdy_o = shutoff_int ? 1'b0 : src_rdy_i; + + always @(posedge clk) + if(reset | clear) + active <= 0; + else + active <= active_next; + + always @(posedge clk) + if(reset | clear) + shutoff_int <= 0; + else if(~active_next) + shutoff_int <= shutoff; + +endmodule // valve36 |