817 lines
28 KiB
Verilog
817 lines
28 KiB
Verilog
|
|
//
|
|
// There are various obligations put on this code not present in regular BRAM based FIFO's
|
|
//
|
|
// 1) Bursts are way more efficient, use local small FIFO's to interact with DRAM
|
|
// 2) Never cross a 4KByte address boundry within a single transaction, this is an AXI4 rule.
|
|
// 3) 2^SIZE must be greater than 4KB so that the 4KByte page protection also deals with FIFO wrap corner case.
|
|
//
|
|
module axi_dram_fifo
|
|
// NOTE: SIZE is log2 of size of FIFO buffer in bytes. i.e 13 for 8KBytes which is 1kx64
|
|
#(parameter BASE=0, SIZE=16, TIMEOUT=64)
|
|
(
|
|
input bus_clk,
|
|
input bus_reset,
|
|
input clear,
|
|
input dram_clk,
|
|
input dram_reset,
|
|
//
|
|
// AXI Write address channel
|
|
//
|
|
output [0 : 0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
|
|
output [31 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
|
|
output [7 : 0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
|
|
output [2 : 0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
|
output [1 : 0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
|
|
output [0 : 0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
|
|
output [3 : 0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
|
|
output [2 : 0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
|
output [3 : 0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction
|
|
output [3 : 0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
|
|
output [0 : 0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
|
|
output m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
|
|
input m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
|
|
//
|
|
// AXI Write data channel.
|
|
//
|
|
output [63 : 0] m_axi_wdata, // Write data
|
|
output [7 : 0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
|
|
output m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
|
|
output [0 : 0] m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
|
|
output m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
|
|
input m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
|
|
//
|
|
// AXI Write response channel signals
|
|
//
|
|
input [0 : 0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
|
|
input [1 : 0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
|
|
input [0 : 0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
|
|
input m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
|
|
output m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
|
|
//
|
|
// AXI Read address channel
|
|
//
|
|
output [0 : 0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals
|
|
output [31 : 0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
|
|
output [7 : 0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
|
|
output [2 : 0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
|
|
output [1 : 0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
|
|
output [0 : 0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
|
|
output [3 : 0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
|
|
output [2 : 0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
|
output [3 : 0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
|
|
output [3 : 0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
|
|
output [0 : 0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
|
|
output m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
|
|
input m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
|
|
//
|
|
// AXI Read data channel
|
|
//
|
|
input [0 : 0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals
|
|
input [63 : 0] m_axi_rdata, // Read data.
|
|
input [1 : 0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
|
|
input m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
|
|
input [0 : 0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
|
|
input m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
|
|
output m_axi_rready, // Read ready. This signal indicates that the master can accept the read data and response
|
|
//
|
|
// CHDR friendly AXI stream input
|
|
//
|
|
input [63:0] i_tdata,
|
|
input i_tlast,
|
|
input i_tvalid,
|
|
output i_tready,
|
|
//
|
|
// CHDR friendly AXI Stream output
|
|
//
|
|
output [63:0] o_tdata,
|
|
output o_tlast,
|
|
output o_tvalid,
|
|
input o_tready,
|
|
//
|
|
//
|
|
//
|
|
input [15:0] supress_threshold,
|
|
input supress_enable,
|
|
//
|
|
// Debug Bus
|
|
//
|
|
output [197:0] debug
|
|
);
|
|
|
|
//
|
|
// We are only solving for width 64bits here, since it's our standard CHDR quanta
|
|
//
|
|
localparam WIDTH=64;
|
|
|
|
//
|
|
// Input side declarations
|
|
//
|
|
localparam INPUT_IDLE = 0;
|
|
localparam INPUT1 = 1;
|
|
localparam INPUT2 = 2;
|
|
localparam INPUT3 = 3;
|
|
localparam INPUT4 = 4;
|
|
localparam INPUT5 = 5;
|
|
localparam INPUT6 = 6;
|
|
|
|
reg [2:0] input_state;
|
|
reg input_timeout_triggered;
|
|
reg input_timeout_reset;
|
|
reg [8:0] input_timeout_count;
|
|
reg [31:0] write_addr;
|
|
reg write_ctrl_valid;
|
|
wire write_ctrl_ready;
|
|
reg [7:0] write_count;
|
|
reg update_write;
|
|
wire [63:0] write_data;
|
|
wire write_data_valid;
|
|
wire write_data_ready;
|
|
|
|
//
|
|
// Output side declarations
|
|
//
|
|
localparam OUTPUT_IDLE = 0;
|
|
localparam OUTPUT1 = 1;
|
|
localparam OUTPUT2 = 2;
|
|
localparam OUTPUT3 = 3;
|
|
localparam OUTPUT4 = 4;
|
|
localparam OUTPUT5 = 5;
|
|
localparam OUTPUT6 = 6;
|
|
|
|
reg [2:0] output_state;
|
|
reg output_timeout_triggered;
|
|
reg output_timeout_reset;
|
|
reg [8:0] output_timeout_count;
|
|
reg [31:0] read_addr;
|
|
reg read_ctrl_valid;
|
|
wire read_ctrl_ready;
|
|
reg [7:0] read_count;
|
|
reg update_read;
|
|
wire [63:0] read_data;
|
|
wire read_data_valid;
|
|
wire read_data_ready;
|
|
|
|
// Track main FIFO active size.
|
|
reg [SIZE-3:0] space, occupied;
|
|
wire [11:0] input_page_boundry, output_page_boundry;
|
|
|
|
|
|
//
|
|
// Buffer input in FIFO's. Embeded tlast signal using ESCape code.
|
|
//
|
|
wire [WIDTH-1:0] i_tdata_i0;
|
|
wire i_tvalid_i0, i_tready_i0, i_tlast_i0;
|
|
|
|
wire [WIDTH-1:0] i_tdata_i1;
|
|
wire i_tvalid_i1, i_tready_i1;
|
|
|
|
wire [WIDTH-1:0] i_tdata_i2;
|
|
wire i_tvalid_i2, i_tready_i2;
|
|
|
|
wire [WIDTH-1:0] i_tdata_input;
|
|
wire i_tvalid_input, i_tready_input;
|
|
wire [15:0] space_input, occupied_input;
|
|
reg [15:0] space_input_reg;
|
|
reg supress_reads;
|
|
|
|
///////////////////////////
|
|
// DEBUG
|
|
///////////////////////////
|
|
wire [31:0] debug_axi_dma_master;
|
|
|
|
//assign debug = {18'h0, input_state[2:0], output_state[2:0], debug_axi_dma_master[7:0]};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
wire write_in, read_in, empty_in, full_in;
|
|
assign i_tready = ~full_in;
|
|
assign write_in = i_tvalid & i_tready;
|
|
assign i_tvalid_i0 = ~empty_in;
|
|
assign read_in = i_tvalid_i0 & i_tready_i0;
|
|
wire [6:0] discard_i0;
|
|
|
|
fifo_short_2clk fifo_short_2clk_i0
|
|
(.rst(bus_reset),
|
|
.wr_clk(bus_clk),
|
|
.din({7'h0,i_tlast,i_tdata}), // input [71 : 0] din
|
|
.wr_en(write_in), // input wr_en
|
|
.full(full_in), // output full
|
|
.wr_data_count(), // output [9 : 0] wr_data_count
|
|
|
|
.rd_clk(dram_clk), // input rd_clk
|
|
.dout({discard_i0,i_tlast_i0,i_tdata_i0}), // output [71 : 0] dout
|
|
.rd_en(read_in), // input rd_en
|
|
.empty(empty_in), // output empty
|
|
.rd_data_count() // output [9 : 0] rd_data_count
|
|
);
|
|
|
|
axi_embed_tlast axi_embed_tlast_i
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(i_tdata_i0),
|
|
.i_tlast(i_tlast_i0),
|
|
.i_tvalid(i_tvalid_i0),
|
|
.i_tready(i_tready_i0),
|
|
//
|
|
.o_tdata(i_tdata_i1),
|
|
.o_tvalid(i_tvalid_i1),
|
|
.o_tready(i_tready_i1)
|
|
);
|
|
|
|
|
|
axi_fast_fifo #(.WIDTH(WIDTH)) fast_fifo_i0
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(i_tdata_i1),
|
|
.i_tvalid(i_tvalid_i1),
|
|
.i_tready(i_tready_i1),
|
|
//
|
|
.o_tdata(i_tdata_i2),
|
|
.o_tvalid(i_tvalid_i2),
|
|
.o_tready(i_tready_i2)
|
|
);
|
|
|
|
axi_fifo #(.WIDTH(WIDTH),.SIZE(12)) fifo_i1
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(i_tdata_i2),
|
|
.i_tvalid(i_tvalid_i2),
|
|
.i_tready(i_tready_i2),
|
|
//
|
|
.o_tdata(i_tdata_input),
|
|
.o_tvalid(i_tvalid_input),
|
|
.o_tready(i_tready_input),
|
|
//
|
|
.space(space_input),
|
|
.occupied(occupied_input)
|
|
);
|
|
|
|
//
|
|
// Monitor occupied_input to deduce when DRAM FIFO is running short of bandwidth and there is a danger of backpressure
|
|
// passing upstream of the DRAM FIFO.
|
|
// In this situation supress read requests to the DRAM FIFO so that more bandwidth is available to writes.
|
|
//
|
|
|
|
|
|
always @(posedge dram_clk)
|
|
begin
|
|
space_input_reg <= space_input;
|
|
if ((space_input_reg < supress_threshold[15:0]) && supress_enable)
|
|
supress_reads <= 1'b1;
|
|
else
|
|
supress_reads <= 1'b0;
|
|
end
|
|
|
|
//
|
|
// Buffer output in 32entry FIFO's. Extract embeded tlast signal.
|
|
//
|
|
wire [WIDTH-1:0] o_tdata_output;
|
|
wire o_tvalid_output, o_tready_output;
|
|
wire [15:0] space_output, occupied_output;
|
|
|
|
wire [WIDTH-1:0] o_tdata_i0;
|
|
wire o_tvalid_i0, o_tready_i0;
|
|
|
|
wire [WIDTH-1:0] o_tdata_i1;
|
|
wire o_tvalid_i1, o_tready_i1, o_tlast_i1;
|
|
|
|
wire [WIDTH-1:0] o_tdata_i2;
|
|
wire o_tvalid_i2, o_tready_i2, o_tlast_i2;
|
|
|
|
wire [WIDTH-1:0] o_tdata_i3;
|
|
wire o_tvalid_i3, o_tready_i3, o_tlast_i3;
|
|
|
|
wire checksum_error;
|
|
|
|
|
|
axi_fifo #(.WIDTH(WIDTH),.SIZE(9)) fifo_i2
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(o_tdata_output),
|
|
.i_tvalid(o_tvalid_output),
|
|
.i_tready(o_tready_output),
|
|
//
|
|
.o_tdata(o_tdata_i0),
|
|
.o_tvalid(o_tvalid_i0),
|
|
.o_tready(o_tready_i0),
|
|
//
|
|
.space(space_output),
|
|
.occupied(occupied_output)
|
|
);
|
|
|
|
// Place FLops straight after SRAM read access for timing.
|
|
axi_fast_fifo #(.WIDTH(WIDTH)) fast_fifo_i1
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(o_tdata_i0),
|
|
.i_tvalid(o_tvalid_i0),
|
|
.i_tready(o_tready_i0),
|
|
//
|
|
.o_tdata(o_tdata_i1),
|
|
.o_tvalid(o_tvalid_i1),
|
|
.o_tready(o_tready_i1 && ~supress_reads)
|
|
);
|
|
|
|
// More pipeline flops to meet timing
|
|
axi_fast_fifo #(.WIDTH(WIDTH)) fast_fifo_i2
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(o_tdata_i1),
|
|
.i_tvalid(o_tvalid_i1 && ~supress_reads),
|
|
.i_tready(o_tready_i1),
|
|
//
|
|
.o_tdata(o_tdata_i2),
|
|
.o_tvalid(o_tvalid_i2),
|
|
.o_tready(o_tready_i2)
|
|
);
|
|
|
|
axi_fast_extract_tlast axi_fast_extract_tlast_i0
|
|
(
|
|
.clk(dram_clk),
|
|
.reset(dram_reset),
|
|
.clear(clear),
|
|
//
|
|
.i_tdata(o_tdata_i2),
|
|
.i_tvalid(o_tvalid_i2),
|
|
.i_tready(o_tready_i2),
|
|
//
|
|
.o_tdata(o_tdata_i3),
|
|
.o_tlast(o_tlast_i3),
|
|
.o_tvalid(o_tvalid_i3),
|
|
.o_tready(o_tready_i3)
|
|
//
|
|
// .checksum_error_reg(checksum_error)
|
|
);
|
|
|
|
|
|
wire write_out, read_out, empty_out, full_out;
|
|
assign o_tready_i3 = ~full_out;
|
|
assign write_out = o_tvalid_i3 & o_tready_i3;
|
|
assign o_tvalid = ~empty_out;
|
|
assign read_out = o_tvalid & o_tready;
|
|
wire [6:0] discard_i1;
|
|
|
|
fifo_short_2clk fifo_short_2clk_i1
|
|
(
|
|
.rst(bus_reset),
|
|
.wr_clk(dram_clk),
|
|
.din({7'h0,o_tlast_i3,o_tdata_i3}), // input [71 : 0] din
|
|
.wr_en(write_out), // input wr_en
|
|
.full(full_out), // output full
|
|
.wr_data_count(), // output [9 : 0] wr_data_count
|
|
|
|
.rd_clk(bus_clk), // input rd_clk
|
|
.dout({discard_i1,o_tlast,o_tdata}), // output [71 : 0] dout
|
|
.rd_en(read_out), // input rd_en
|
|
.empty(empty_out), // output empty
|
|
.rd_data_count() // output [9 : 0] rd_data_count
|
|
);
|
|
|
|
//
|
|
// Simple input timeout counter for now.
|
|
// Timeout count only increments when there is some data waiting to be written.
|
|
//
|
|
always @(posedge dram_clk)
|
|
if (dram_reset | clear) begin
|
|
input_timeout_count <= 0;
|
|
input_timeout_triggered <= 1'b0;
|
|
end else if (input_timeout_reset) begin
|
|
input_timeout_count <= 0;
|
|
input_timeout_triggered <= 1'b0;
|
|
end else if (input_timeout_count == TIMEOUT) begin
|
|
input_timeout_triggered <= 1'b1;
|
|
end else if (input_state == INPUT_IDLE) begin
|
|
input_timeout_count <= input_timeout_count + (occupied_input != 0);
|
|
end
|
|
|
|
|
|
//
|
|
// Wait for 16 entries in input FIFO to trigger DRAM write burst.
|
|
// Timeout can also trigger burst so fragments of data are not left to rot in the input FIFO.
|
|
// Also if enough data is present in the input FIFO to complete a burst upto the edge
|
|
// of a 4KByte page then immediately start the burst.
|
|
//
|
|
always @(posedge dram_clk)
|
|
if (dram_reset | clear) begin
|
|
input_state <= INPUT_IDLE;
|
|
write_addr[31:SIZE] <= BASE >> SIZE;
|
|
write_addr[SIZE-1:0] <= 0;
|
|
input_timeout_reset <= 1'b0;
|
|
write_ctrl_valid <= 1'b0;
|
|
write_count <= 8'd0;
|
|
update_write <= 1'b0;
|
|
end else
|
|
case (input_state)
|
|
//
|
|
// INPUT_IDLE.
|
|
// To start an input transfer to DRAM need:
|
|
// 1) Space in the DRAM FIFO
|
|
// and either
|
|
// 2) 256 entrys in the input FIFO
|
|
// or
|
|
// 3) Timeout waiting for more data.
|
|
//
|
|
INPUT_IDLE: begin
|
|
write_ctrl_valid <= 1'b0;
|
|
update_write <= 1'b0;
|
|
if (space > 255) begin // Space in the DRAM FIFO
|
|
if (occupied_input > 255) begin // 256 or more entrys in input FIFO
|
|
input_state <= INPUT1;
|
|
input_timeout_reset <= 1'b1;
|
|
end else if (input_timeout_triggered) begin // input FIFO timeout waiting for new data.
|
|
input_state <= INPUT2;
|
|
input_timeout_reset <= 1'b1;
|
|
end else begin
|
|
input_timeout_reset <= 1'b0;
|
|
input_state <= INPUT_IDLE;
|
|
end
|
|
end else begin
|
|
input_timeout_reset <= 1'b0;
|
|
input_state <= INPUT_IDLE;
|
|
end
|
|
end
|
|
//
|
|
// INPUT1.
|
|
// Caused by input FIFO reaching 256 entries.
|
|
// Request write burst of lesser of:
|
|
// 1) Entrys until page boundry crossed
|
|
// 2) 256.
|
|
//
|
|
INPUT1: begin
|
|
write_count <= (input_page_boundry < 255) ? input_page_boundry[7:0] : 8'd255;
|
|
write_ctrl_valid <= 1'b1;
|
|
if (write_ctrl_ready)
|
|
input_state <= INPUT4; // Pre-emptive ACK
|
|
else
|
|
input_state <= INPUT3; // Wait for ACK
|
|
end
|
|
//
|
|
// INPUT2.
|
|
// Caused by timeout of input FIFO. (occupied_input was implicitly less than 256 last cycle)
|
|
// Request write burst of lesser of:
|
|
// 1) Entries until page boundry crossed
|
|
// 2) Entries in input FIFO
|
|
//
|
|
INPUT2: begin
|
|
write_count <= (input_page_boundry < ({3'h0,occupied_input[8:0]} - 12'd1)) ? input_page_boundry[7:0] : (occupied_input[8:0] - 7'd1);
|
|
write_ctrl_valid <= 1'b1;
|
|
if (write_ctrl_ready)
|
|
input_state <= INPUT4; // Pre-emptive ACK
|
|
else
|
|
input_state <= INPUT3; // Wait for ACK
|
|
end
|
|
//
|
|
// INPUT3.
|
|
// Wait in this state for AXI4_DMA engine to accept transaction.
|
|
//
|
|
INPUT3: begin
|
|
if (write_ctrl_ready) begin
|
|
write_ctrl_valid <= 1'b0;
|
|
input_state <= INPUT4; // ACK
|
|
end else begin
|
|
write_ctrl_valid <= 1'b1;
|
|
input_state <= INPUT3; // Wait for ACK
|
|
end
|
|
end
|
|
//
|
|
// INPUT4.
|
|
// Wait here until write_ctrl_ready_deasserts.
|
|
// This is important as the next time it asserts we know that a write response was receieved.
|
|
INPUT4: begin
|
|
write_ctrl_valid <= 1'b0;
|
|
if (!write_ctrl_ready)
|
|
input_state <= INPUT5; // Move on
|
|
else
|
|
input_state <= INPUT4; // Wait for deassert
|
|
end
|
|
//
|
|
// INPUT5.
|
|
// Transaction has been accepted by AXI4 DMA engine. Now we wait for the re-assertion
|
|
// of write_ctrl_ready which signals that the AXI4 DMA engine has receieved a response
|
|
// for the whole write transaction and we assume that this means it is commited to DRAM.
|
|
// We are now free to update write_addr pointer and go back to idle state.
|
|
//
|
|
INPUT5: begin
|
|
write_ctrl_valid <= 1'b0;
|
|
if (write_ctrl_ready) begin
|
|
write_addr[SIZE-1:0] <= write_addr[SIZE-1:0] + ((write_count + 1) << 3);
|
|
input_state <= INPUT6;
|
|
update_write <= 1'b1;
|
|
end else begin
|
|
input_state <= INPUT5;
|
|
end
|
|
end
|
|
//
|
|
// INPUT6:
|
|
// Need to let space update before looking if there's more to do.
|
|
//
|
|
INPUT6: begin
|
|
input_state <= INPUT_IDLE;
|
|
update_write <= 1'b0;
|
|
end
|
|
// Ass covering.
|
|
default: input_state <= INPUT_IDLE;
|
|
|
|
endcase // case(input_state)
|
|
|
|
|
|
//
|
|
// Simple output timeout counter for now
|
|
//
|
|
always @(posedge dram_clk)
|
|
if (dram_reset | clear) begin
|
|
output_timeout_count <= 0;
|
|
output_timeout_triggered <= 1'b0;
|
|
end else if (output_timeout_reset) begin
|
|
output_timeout_count <= 0;
|
|
output_timeout_triggered <= 1'b0;
|
|
end else if (output_timeout_count == TIMEOUT) begin
|
|
output_timeout_triggered <= 1'b1;
|
|
end else if (output_state == OUTPUT_IDLE) begin
|
|
output_timeout_count <= output_timeout_count + (occupied != 0 );
|
|
end
|
|
|
|
|
|
//
|
|
// Wait for 64 entries in main FIFO to trigger DRAM read burst.
|
|
// Timeout can also trigger burst so fragments of data are not left to rot in the main FIFO.
|
|
// Also if enough data is present in the main FIFO to complete a burst upto the edge
|
|
// of a 4KByte page then immediately start the burst.
|
|
//
|
|
always @(posedge dram_clk)
|
|
if (dram_reset | clear) begin
|
|
output_state <= OUTPUT_IDLE;
|
|
read_addr[31:SIZE] <= BASE >> SIZE;
|
|
read_addr[SIZE-1:0] <= 0;
|
|
output_timeout_reset <= 1'b0;
|
|
read_ctrl_valid <= 1'b0;
|
|
read_count <= 8'd0;
|
|
update_read <= 1'b0;
|
|
end else
|
|
case (output_state)
|
|
//
|
|
// OUTPUT_IDLE.
|
|
// To start an output tranfer from DRAM
|
|
// 1) Space in the small output FIFO
|
|
// and either
|
|
// 2) 256 entrys in the DRAM FIFO
|
|
// or
|
|
// 3) Timeout waiting for more data.
|
|
//
|
|
OUTPUT_IDLE: begin
|
|
read_ctrl_valid <= 1'b0;
|
|
update_read <= 1'b0;
|
|
if (space_output > 255) begin // Space in the output FIFO.
|
|
if (occupied > 255) begin // 64 or more entrys in main FIFO
|
|
output_state <= OUTPUT1;
|
|
output_timeout_reset <= 1'b1;
|
|
end else if (output_timeout_triggered) begin // output FIFO timeout waiting for new data.
|
|
output_state <= OUTPUT2;
|
|
output_timeout_reset <= 1'b1;
|
|
end else begin
|
|
output_timeout_reset <= 1'b0;
|
|
output_state <= OUTPUT_IDLE;
|
|
end
|
|
end else begin
|
|
output_timeout_reset <= 1'b0;
|
|
output_state <= OUTPUT_IDLE;
|
|
end
|
|
end // case: OUTPUT_IDLE
|
|
//
|
|
// OUTPUT1.
|
|
// Caused by main FIFO reaching 256 entries.
|
|
// Request read burst of lesser of lesser of:
|
|
// 1) Entrys until page boundry crossed
|
|
// 2) 256.
|
|
//
|
|
OUTPUT1: begin
|
|
read_count <= (output_page_boundry < 255) ? output_page_boundry : 8'd255;
|
|
read_ctrl_valid <= 1'b1;
|
|
if (read_ctrl_ready)
|
|
output_state <= OUTPUT4; // Pre-emptive ACK
|
|
else
|
|
output_state <= OUTPUT3; // Wait for ACK
|
|
end
|
|
//
|
|
// OUTPUT2.
|
|
// Caused by timeout of main FIFO
|
|
// Request read burst of lesser of:
|
|
// 1) Entries until page boundry crossed
|
|
// 2) Entries in main FIFO
|
|
//
|
|
OUTPUT2: begin
|
|
read_count <= (output_page_boundry < (occupied - 1)) ? output_page_boundry : (occupied - 1);
|
|
read_ctrl_valid <= 1'b1;
|
|
if (read_ctrl_ready)
|
|
output_state <= OUTPUT4; // Pre-emptive ACK
|
|
else
|
|
output_state <= OUTPUT3; // Wait for ACK
|
|
end
|
|
//
|
|
// OUTPUT3.
|
|
// Wait in this state for AXI4_DMA engine to accept transaction.
|
|
//
|
|
OUTPUT3: begin
|
|
if (read_ctrl_ready) begin
|
|
read_ctrl_valid <= 1'b0;
|
|
output_state <= OUTPUT4; // ACK
|
|
end else begin
|
|
read_ctrl_valid <= 1'b1;
|
|
output_state <= OUTPUT3; // Wait for ACK
|
|
end
|
|
end
|
|
//
|
|
// OUTPUT4.
|
|
// Wait here unitl read_ctrl_ready_deasserts.
|
|
// This is important as the next time it asserts we know that a read response was receieved.
|
|
OUTPUT4: begin
|
|
read_ctrl_valid <= 1'b0;
|
|
if (!read_ctrl_ready)
|
|
output_state <= OUTPUT5; // Move on
|
|
else
|
|
output_state <= OUTPUT4; // Wait for deassert
|
|
end
|
|
//
|
|
// OUTPUT5.
|
|
// Transaction has been accepted by AXI4 DMA engine. Now we wait for the re-assertion
|
|
// of read_ctrl_ready which signals that the AXI4 DMA engine has receieved a last signal and good response
|
|
// for the whole read transaction.
|
|
// We are now free to update read_addr pointer and go back to idle state.
|
|
//
|
|
OUTPUT5: begin
|
|
read_ctrl_valid <= 1'b0;
|
|
if (read_ctrl_ready) begin
|
|
read_addr[SIZE-1:0] <= read_addr[SIZE-1:0] + ((read_count + 1) << 3);
|
|
output_state <= OUTPUT6;
|
|
update_read <= 1'b1;
|
|
|
|
end else begin
|
|
output_state <= OUTPUT5;
|
|
end
|
|
end // case: OUTPUT5
|
|
//
|
|
// OUTPUT6.
|
|
// Need to get occupied value updated before checking if there's more to do.
|
|
//
|
|
OUTPUT6: begin
|
|
update_read <= 1'b0;
|
|
output_state <= OUTPUT_IDLE;
|
|
end
|
|
// Ass covering.
|
|
default: output_state <= OUTPUT_IDLE;
|
|
|
|
endcase // case(output_state)
|
|
|
|
//
|
|
// Calculate number of entries remaining until next 4KB page boundry is crossed minus 1.
|
|
// Note, units of calculation are 64bit wide words. Address is always 64bit alligned.
|
|
//
|
|
assign input_page_boundry = {write_addr[31:12],9'h1ff} - write_addr[31:3];
|
|
assign output_page_boundry = {read_addr[31:12],9'h1ff} - read_addr[31:3];
|
|
|
|
//
|
|
// Count number of used entries in main DRAM FIFO.
|
|
// Note that this is expressed in units of 64bit wide words.
|
|
//
|
|
always @(posedge dram_clk)
|
|
if (dram_reset | clear)
|
|
occupied <= 0;
|
|
else
|
|
occupied <= occupied + (update_write ? write_count + 1 : 0) - (update_read ? read_count + 1 : 0);
|
|
|
|
always @(posedge dram_clk)
|
|
if (dram_reset | clear)
|
|
space <= (1 << SIZE-3) - 'd64; // Subtract 64 from space to make allowance for read/write reordering in DRAM controller confuing pointer math.
|
|
else
|
|
space <= space - (update_write ? write_count + 1 : 0) + (update_read ? read_count + 1 : 0);
|
|
|
|
//
|
|
// Instamce of axi_dma_master
|
|
//
|
|
|
|
|
|
axi_dma_master axi_dma_master_i
|
|
(
|
|
.aclk(dram_clk), // input aclk
|
|
.areset(dram_reset | clear), // input aresetn
|
|
// Write control
|
|
.m_axi_awid(m_axi_awid), // input [0 : 0] m_axi_awid
|
|
.m_axi_awaddr(m_axi_awaddr), // input [31 : 0] m_axi_awaddr
|
|
.m_axi_awlen(m_axi_awlen), // input [7 : 0] m_axi_awlen
|
|
.m_axi_awsize(m_axi_awsize), // input [2 : 0] m_axi_awsize
|
|
.m_axi_awburst(m_axi_awburst), // input [1 : 0] m_axi_awburst
|
|
.m_axi_awvalid(m_axi_awvalid), // input m_axi_awvalid
|
|
.m_axi_awready(m_axi_awready), // output m_axi_awready
|
|
.m_axi_awlock(m_axi_awlock),
|
|
.m_axi_awcache(m_axi_awcache),
|
|
.m_axi_awprot(m_axi_awprot),
|
|
.m_axi_awqos(m_axi_awqos),
|
|
.m_axi_awregion(m_axi_awregion),
|
|
.m_axi_awuser(m_axi_awuser),
|
|
// Write Data
|
|
.m_axi_wdata(m_axi_wdata), // input [63 : 0] m_axi_wdata
|
|
.m_axi_wstrb(m_axi_wstrb), // input [7 : 0] m_axi_wstrb
|
|
.m_axi_wlast(m_axi_wlast), // input m_axi_wlast
|
|
.m_axi_wvalid(m_axi_wvalid), // input m_axi_wvalid
|
|
.m_axi_wready(m_axi_wready), // output m_axi_wready
|
|
.m_axi_wuser(),
|
|
// Write Response
|
|
.m_axi_bid(m_axi_bid), // output [0 : 0] m_axi_bid
|
|
.m_axi_bresp(m_axi_bresp), // output [1 : 0] m_axi_bresp
|
|
.m_axi_bvalid(m_axi_bvalid), // output m_axi_bvalid
|
|
.m_axi_bready(m_axi_bready), // input m_axi_bready
|
|
.m_axi_buser(),
|
|
// Read Control
|
|
.m_axi_arid(m_axi_arid), // input [0 : 0] m_axi_arid
|
|
.m_axi_araddr(m_axi_araddr), // input [31 : 0] m_axi_araddr
|
|
.m_axi_arlen(m_axi_arlen), // input [7 : 0] m_axi_arlen
|
|
.m_axi_arsize(m_axi_arsize), // input [2 : 0] m_axi_arsize
|
|
.m_axi_arburst(m_axi_arburst), // input [1 : 0] m_axi_arburst
|
|
.m_axi_arvalid(m_axi_arvalid), // input m_axi_arvalid
|
|
.m_axi_arready(m_axi_arready), // output m_axi_arready
|
|
.m_axi_arlock(m_axi_arlock),
|
|
.m_axi_arcache(m_axi_arcache),
|
|
.m_axi_arprot(m_axi_arprot),
|
|
.m_axi_arqos(m_axi_arqos),
|
|
.m_axi_arregion(m_axi_arregion),
|
|
.m_axi_aruser(m_axi_aruser),
|
|
// Read Data
|
|
.m_axi_rid(m_axi_rid), // output [0 : 0] m_axi_rid
|
|
.m_axi_rdata(m_axi_rdata), // output [63 : 0] m_axi_rdata
|
|
.m_axi_rresp(m_axi_rresp), // output [1 : 0] m_axi_rresp
|
|
.m_axi_rlast(m_axi_rlast), // output m_axi_rlast
|
|
.m_axi_rvalid(m_axi_rvalid), // output m_axi_rvalid
|
|
.m_axi_rready(m_axi_rready), // input m_axi_rready
|
|
.m_axi_ruser(),
|
|
//
|
|
// DMA interface for Write transaction
|
|
//
|
|
.write_addr(write_addr), // Byte address for start of write transaction (should be 64bit alligned)
|
|
.write_count(write_count), // Count of 64bit words to write.
|
|
.write_ctrl_valid(write_ctrl_valid),
|
|
.write_ctrl_ready(write_ctrl_ready),
|
|
.write_data(i_tdata_input),
|
|
.write_data_valid(i_tvalid_input),
|
|
.write_data_ready(i_tready_input),
|
|
//
|
|
// DMA interface for Read
|
|
//
|
|
.read_addr(read_addr), // Byte address for start of read transaction (should be 64bit alligned)
|
|
.read_count(read_count), // Count of 64bit words to read.
|
|
.read_ctrl_valid(read_ctrl_valid),
|
|
.read_ctrl_ready(read_ctrl_ready),
|
|
.read_data(o_tdata_output),
|
|
.read_data_valid(o_tvalid_output),
|
|
.read_data_ready(o_tready_output),
|
|
//
|
|
// Debug
|
|
//
|
|
.debug(debug_axi_dma_master)
|
|
);
|
|
|
|
//
|
|
// Debug
|
|
//
|
|
assign debug = { checksum_error,
|
|
/*debug_axi_dma_master[7:0]*/
|
|
input_timeout_triggered, // 195
|
|
input_state[2:0], // 194-192
|
|
output_timeout_triggered, // 191
|
|
output_state[2:0], // 190-188
|
|
space_output[15:0], // 187-172
|
|
occupied[21:0], // 171-150
|
|
occupied_input[15:0], // 149-134
|
|
|
|
i_tvalid_i0, // 133
|
|
i_tready_i0, // 132
|
|
i_tlast_i0, // 131
|
|
i_tdata_i0[63:0],// 130-67
|
|
o_tvalid_i1, // 66
|
|
o_tready_i1, // 65
|
|
o_tlast_i1, // 64
|
|
o_tdata_i1[63:0] // 63-0
|
|
};
|
|
|
|
|
|
endmodule // axi_dram_fifo
|
|
|