247 lines
7.7 KiB
Verilog
247 lines
7.7 KiB
Verilog
//
|
|
// 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
|