diff options
Diffstat (limited to 'fpga/usrp3/lib/wishbone/axi_stream_to_wb.v')
-rw-r--r-- | fpga/usrp3/lib/wishbone/axi_stream_to_wb.v | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/wishbone/axi_stream_to_wb.v b/fpga/usrp3/lib/wishbone/axi_stream_to_wb.v new file mode 100644 index 000000000..a71daf063 --- /dev/null +++ b/fpga/usrp3/lib/wishbone/axi_stream_to_wb.v @@ -0,0 +1,246 @@ +// +// Copyright 2012 Ettus Research LLC +// + + +// AXI stream to/from wishbone +// Input is an axi stream which wites into a BRAM. +// Output is an axi stream which reads from a BRAM. +// This RAM can also be accessed from a wishbone interface. + +// From the wishbone interface we need to be able to: + +// Ask the module if a completed packet is available. +// Read number of bytes/lines in the BRAM. +// Release the completed packet. + +// Ask the module if an outgoing slot is available. +// Write number of bytes/lines in the BRAM. +// Release the completed packet. + +module axi_stream_to_wb +#( + parameter AWIDTH = 13, //WB addr width and buffering size in bytes + parameter UWIDTH = 4, //stream user width + parameter CTRL_ADDR = 0 //ctrl/status register +) +( + //-- the wishbone interface + input clk_i, input rst_i, + input we_i, input stb_i, input cyc_i, output reg ack_o, + input [AWIDTH-1:0] adr_i, input [31:0] dat_i, output [31:0] dat_o, + + //-- the axi stream interface input + input [63:0] rx_tdata, + input [3:0] rx_tuser, + input rx_tlast, + input rx_tvalid, + output rx_tready, + + //-- the axi stream interface output + output [63:0] tx_tdata, + output [3:0] tx_tuser, + output tx_tlast, + output tx_tvalid, + input tx_tready, + + output [31:0] debug_rx, + output [31:0] debug_tx +); + + //drive the ack signal + always @(posedge clk_i) begin + if (rst_i) ack_o <= 0; + else ack_o <= stb_i & ~ack_o; + end + + //control registers, status + reg [AWIDTH-1:0] tx_bytes, rx_bytes; + reg tx_error, rx_error; + wire rx_state_flag, tx_state_flag; + reg rx_proc_flag, tx_proc_flag; + + //assign status + wire [31:0] status; + assign status[31] = rx_state_flag; + assign status[30] = tx_state_flag; + assign status[29] = rx_error; + assign status[AWIDTH-1:0] = rx_bytes; + + // Create some piplining to break timing paths. + reg ctrl_addressed; + always @(posedge clk_i) + if (rst_i) + ctrl_addressed <= 1'b0; + else if(adr_i == CTRL_ADDR) + ctrl_addressed <= 1'b1; + else + ctrl_addressed <= 1'b0; + + //assign control + always @(posedge clk_i) begin + if (rst_i) begin + rx_proc_flag <= 0; + tx_proc_flag <= 0; + tx_error <= 0; + tx_bytes <= 0; + end + else if (we_i && ack_o && ctrl_addressed) begin + rx_proc_flag <= dat_i[31]; + tx_proc_flag <= dat_i[30]; + tx_error <= dat_i[29]; + tx_bytes <= dat_i[AWIDTH-1:0]; + end + end + + //------------------------------------------------------------------ + //-- block ram interface between wb and input stream + //------------------------------------------------------------------ + reg [AWIDTH-4:0] rx_counter; + wire [63:0] rx_bram_data64; + ram_2port #(.DWIDTH(64), .AWIDTH(AWIDTH-3)) input_stream_bram + ( + .clka(clk_i), .ena(rx_tready), .wea(rx_tvalid), + .addra(rx_counter), .dia(rx_tdata), .doa(), + .clkb(clk_i), .enb(stb_i), .web(1'b0), + .addrb(adr_i[AWIDTH-1:3]), .dib({64{1'b1}}), .dob(rx_bram_data64) + ); + + //select the data source, status, or upper/lower 32 from bram + assign dat_o = ctrl_addressed ? status : ((!adr_i[2])? rx_bram_data64[63:32]: rx_bram_data64[31:0]); + + //------------------------------------------------------------------ + //-- block ram interface between wb and output stream + //------------------------------------------------------------------ + reg [AWIDTH-4:0] tx_counter; + wire enb_out; + wire [63:0] tx_bram_data64; + ram_2port #(.DWIDTH(64), .AWIDTH(AWIDTH-3)) output_stream_bram + ( + .clka(clk_i), .ena(enb_out), .wea(1'b0), + .addra(tx_counter), .dia({64{1'b1}}), .doa(tx_tdata), + .clkb(clk_i), .enb(stb_i), .web(we_i && adr_i[2]), + .addrb(adr_i[AWIDTH-1:3]), .dib(tx_bram_data64), .dob() + ); + + //write 64 bit chunks, so register the lower write + reg [31:0] dat_i_reg; + always @(posedge clk_i) begin + if (we_i && stb_i && !adr_i[2]) dat_i_reg <= dat_i; + end + assign tx_bram_data64 = {dat_i_reg, dat_i}; + + //------------------------------------------------------------------ + //-- state machine to drive input stream + //------------------------------------------------------------------ + localparam RX_STATE_READY = 0; //waits for proc flag 0 + localparam RX_STATE_WRITE = 1; //writes stream to bram + localparam RX_STATE_RELEASE = 2; //waits for proc to flag 1 + reg [1:0] rx_state; + + always @(posedge clk_i) begin + if (rst_i) begin + rx_state <= RX_STATE_READY; + rx_counter <= 0; + rx_error <= 0; + rx_bytes <= 0; + end + else case (rx_state) + + RX_STATE_READY: begin + if (!rx_proc_flag) rx_state <= RX_STATE_WRITE; + rx_counter <= 0; + end + + RX_STATE_WRITE: begin + if (rx_tready && rx_tvalid) begin + rx_counter <= rx_counter + 1'b1; + if (rx_tlast) begin + rx_state <= RX_STATE_RELEASE; + rx_bytes <= {rx_counter + 1'b1, rx_tuser[2:0]}; + rx_error <= rx_tuser[3]; + end + end + end + + RX_STATE_RELEASE: begin + if (rx_proc_flag) rx_state <= RX_STATE_READY; + rx_counter <= 0; + end + + default: rx_state <= RX_STATE_READY; + endcase //rx_state + end + + //flag tells the processor when it can grab some input buffer + assign rx_state_flag = (rx_state == RX_STATE_RELEASE); + + //always ready to accept input data in the write state + assign rx_tready = (rx_state == RX_STATE_WRITE); + + //------------------------------------------------------------------ + //-- state machine to drive output stream + //------------------------------------------------------------------ + localparam TX_STATE_READY = 0; //waits for proc flag 0 + localparam TX_STATE_WRITE = 1; //writes bram to stream + localparam TX_STATE_RELEASE = 2; //waits for proc to flag 1 + reg [1:0] tx_state; + + always @(posedge clk_i) begin + if (rst_i) begin + tx_state <= TX_STATE_READY; + tx_counter <= 0; + end + else case (tx_state) + + TX_STATE_READY: begin + if (tx_proc_flag) begin + tx_state <= TX_STATE_WRITE; + tx_counter <= 1; + end + else tx_counter <= 0; + end + + TX_STATE_WRITE: begin + if (tx_tready && tx_tvalid) begin + tx_counter <= tx_counter + 1'b1; + if (tx_tlast) begin + tx_state <= TX_STATE_RELEASE; + end + end + end + + TX_STATE_RELEASE: begin + if (!tx_proc_flag) tx_state <= TX_STATE_READY; + tx_counter <= 0; + end + + default: tx_state <= TX_STATE_READY; + endcase //tx_state + end + + //flag tells the processor when it can grab available out buffer + assign tx_state_flag = (tx_state == TX_STATE_READY); + + //the output user bus assignment (non-zero only at end) + assign tx_tuser = (tx_tlast)? {tx_error, tx_bytes[2:0]} : 4'b0; + + //end of frame signal + assign tx_tlast = (tx_counter == tx_bytes[AWIDTH-1:3]); + + //output is always valid in state write + assign tx_tvalid = (tx_state == TX_STATE_WRITE); + + //enable the read so we can pre-read due to read 1 cycle delay + assign enb_out = (tx_state == TX_STATE_WRITE)? (tx_tvalid && tx_tready) : 1'b1; + + assign debug_rx = { + rx_state, rx_tlast, rx_tvalid, rx_tready, rx_tuser[2:0], //8 + rx_proc_flag, rx_state_flag, rx_tdata[21:0] //24 + }; + assign debug_tx = { + tx_state, tx_tlast, tx_tvalid, tx_tready, tx_tuser[2:0], //8 + tx_proc_flag, tx_state_flag, tx_tdata[21:0] //24 + }; + +endmodule //axi_stream_to_wb |