Pushing the bulk of UHD-3.7.0 code.
Original-commit: ff1546f8137f7f92bb250f685561b0c34cc0e053
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Copyright 2012-2013 Ettus Research LLC
|
||||
#
|
||||
|
||||
##################################################
|
||||
# FIFO Sources
|
||||
##################################################
|
||||
AXI_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/axi/, \
|
||||
axi_dma_master.v \
|
||||
axi_dram_fifo.v \
|
||||
axi_embed_tlast.v \
|
||||
axi_extract_tlast.v \
|
||||
axi_chdr_test_pattern.v \
|
||||
axi_fast_fifo.v \
|
||||
axi_fast_extract_tlast.v \
|
||||
axi_chdr_header_trigger.v \
|
||||
))
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
// Copyright 2014 Ettus Research LLC
|
||||
|
||||
|
||||
module axi_chdr_header_trigger
|
||||
#(
|
||||
parameter WIDTH=64,
|
||||
parameter SID=0
|
||||
)
|
||||
(input clk, input reset, input clear,
|
||||
input [WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, input i_tready,
|
||||
output trigger
|
||||
);
|
||||
|
||||
|
||||
reg state;
|
||||
localparam IDLE = 0;
|
||||
localparam RUN = 1;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if(reset | clear)
|
||||
state <= IDLE;
|
||||
else
|
||||
case (state)
|
||||
IDLE :
|
||||
if(i_tvalid && i_tready)
|
||||
state <= RUN;
|
||||
|
||||
RUN :
|
||||
if(i_tready && i_tvalid && i_tlast)
|
||||
state <= IDLE;
|
||||
|
||||
default :
|
||||
state <= IDLE;
|
||||
endcase // case (state)
|
||||
|
||||
assign trigger = i_tvalid && i_tready && (state == IDLE) && (i_tdata[15:0] != SID);
|
||||
|
||||
endmodule // axi_chdr_header_trigger
|
||||
@@ -0,0 +1,314 @@
|
||||
//
|
||||
// Synthesizable test pattern generators and checkers
|
||||
// for CHDR that can be used to test transparent blocks
|
||||
// (FIFOs, switches, etc)
|
||||
//
|
||||
|
||||
//`define MTU 8192
|
||||
`define MTU 1536
|
||||
|
||||
module axi_chdr_test_pattern
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
|
||||
//
|
||||
// CHDR friendly AXI stream input
|
||||
//
|
||||
output reg [63:0] i_tdata,
|
||||
output reg i_tlast,
|
||||
output reg i_tvalid,
|
||||
input wire i_tready,
|
||||
//
|
||||
// CHDR friendly AXI Stream output
|
||||
//
|
||||
input wire [63:0] o_tdata,
|
||||
input wire o_tlast,
|
||||
input wire o_tvalid,
|
||||
output reg o_tready,
|
||||
//
|
||||
// Test flags
|
||||
//
|
||||
input start,
|
||||
input [15:0] control,
|
||||
output reg fail,
|
||||
output reg done
|
||||
);
|
||||
|
||||
wire [7:0] bist_rx_delay = control[7:0];
|
||||
wire [7:0] bist_tx_delay = control[15:8];
|
||||
|
||||
|
||||
reg [15:0] tx_count, rx_count;
|
||||
reg [15:0] tx_data, rx_data;
|
||||
reg [7:0] tx_delay, rx_delay;
|
||||
|
||||
|
||||
localparam TX_IDLE = 0;
|
||||
localparam TX_START = 1;
|
||||
localparam TX_ACTIVE = 2;
|
||||
localparam TX_GAP = 3;
|
||||
localparam TX_DONE = 4;
|
||||
localparam TX_WAIT = 5;
|
||||
|
||||
localparam RX_IDLE = 0;
|
||||
localparam RX_ACTIVE = 1;
|
||||
localparam RX_FAIL = 2;
|
||||
localparam RX_DONE = 3;
|
||||
localparam RX_WAIT = 4;
|
||||
|
||||
reg [2:0] tx_state, rx_state;
|
||||
|
||||
//
|
||||
// Transmitter
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset)
|
||||
begin
|
||||
tx_delay <= 0;
|
||||
tx_count <= 8;
|
||||
tx_data <= 0;
|
||||
i_tdata <= 64'h0;
|
||||
i_tlast <= 1'b0;
|
||||
i_tvalid <= 1'b0;
|
||||
tx_state <= TX_IDLE;
|
||||
end
|
||||
else
|
||||
begin
|
||||
case(tx_state)
|
||||
TX_IDLE: begin
|
||||
tx_delay <= 0;
|
||||
i_tdata <= 64'h0;
|
||||
i_tlast <= 1'b0;
|
||||
i_tvalid <= 1'b0;
|
||||
tx_data <= 0;
|
||||
tx_count <= 4;
|
||||
// Run whilst start asserted.
|
||||
if (start) begin
|
||||
tx_state <= TX_START;
|
||||
// ....Go back to initialized state if start deasserted.
|
||||
end else begin
|
||||
tx_state <= TX_IDLE;
|
||||
end
|
||||
end // case: TX_IDLE
|
||||
|
||||
//
|
||||
// START signal is asserted.
|
||||
// Now need to start transmiting a packet.
|
||||
//
|
||||
TX_START: begin
|
||||
// At the next clock edge drive first beat of new packet onto HDR bus.
|
||||
i_tlast <= 1'b0;
|
||||
i_tvalid <= 1'b1;
|
||||
tx_data <= tx_data + 4;
|
||||
// i_tdata <= {tx_data,tx_data+16'd1,tx_data+16'd2,tx_data+16'd3};
|
||||
i_tdata <= {4{(tx_data[2]?16'hffff:16'h0000)^tx_data[15:0]}};
|
||||
tx_state <= TX_ACTIVE;
|
||||
|
||||
end
|
||||
|
||||
//
|
||||
// Valid data is (already) being driven onto the CHDR bus.
|
||||
// i_tlast may also be driven asserted if current data count has reached EOP.
|
||||
// Watch i_tready to see when it's consumed.
|
||||
// When packets are consumed increment data counter or transition state if
|
||||
// EOP has sucsesfully concluded.
|
||||
//
|
||||
TX_ACTIVE: begin
|
||||
i_tvalid <= 1'b1; // Always assert tvalid
|
||||
if (i_tready) begin
|
||||
|
||||
// i_tdata <= {tx_data,tx_data+16'd1,tx_data+16'd2,tx_data+16'd3};
|
||||
i_tdata <= {4{(tx_data[2]?16'hffff:16'h0000)^tx_data[15:0]}};
|
||||
// Will this next beat be the last in a packet?
|
||||
if (tx_data == tx_count) begin
|
||||
tx_data <= 0;
|
||||
i_tlast <= 1'b1;
|
||||
tx_state <= TX_GAP;
|
||||
end else begin
|
||||
tx_data <= tx_data + 4;
|
||||
i_tlast <= 1'b0;
|
||||
tx_state <= TX_ACTIVE;
|
||||
end
|
||||
end else begin
|
||||
// Keep driving all CHDR bus signals as-is until i_tready is asserted.
|
||||
tx_state <= TX_ACTIVE;
|
||||
end
|
||||
end // case: TX_ACTIVE
|
||||
//
|
||||
// Force an inter-packet gap between packets in a BIST sequence where tvalid is driven low.
|
||||
// As we leave this state check if all packets in BIST sequence have been generated yet,
|
||||
// and if so go to done state.
|
||||
//
|
||||
TX_GAP: begin
|
||||
if (i_tready) begin
|
||||
i_tvalid <= 1'b0;
|
||||
i_tdata <= 64'h0;
|
||||
i_tlast <= 1'b0;
|
||||
tx_count <= tx_count + 4;
|
||||
|
||||
if (tx_count < `MTU) begin
|
||||
tx_state <= TX_WAIT;
|
||||
tx_delay <= bist_tx_delay;
|
||||
end else
|
||||
tx_state <= TX_DONE;
|
||||
end else begin // if (i_tready)
|
||||
tx_state <= TX_GAP;
|
||||
end
|
||||
end // case: TX_GAP
|
||||
//
|
||||
// Simulate inter packet gap in real UHD system
|
||||
TX_WAIT: begin
|
||||
if (tx_delay == 0)
|
||||
tx_state <= TX_START;
|
||||
else begin
|
||||
tx_delay <= tx_delay - 1;
|
||||
tx_state <= TX_WAIT;
|
||||
end
|
||||
end
|
||||
|
||||
//
|
||||
// Complete test pattern BIST sequence has been transmitted. Sit in this
|
||||
// state indefinately if START is taken low, which re-inits the whole BIST solution.
|
||||
//
|
||||
TX_DONE: begin
|
||||
if (!start) begin
|
||||
tx_state <= TX_DONE;
|
||||
end else begin
|
||||
tx_state <= TX_IDLE;
|
||||
end
|
||||
i_tvalid <= 1'b0;
|
||||
i_tdata <= 64'd0;
|
||||
i_tlast <= 1'b0;
|
||||
|
||||
end
|
||||
endcase // case (tx_state)
|
||||
end
|
||||
|
||||
//
|
||||
// Receiver
|
||||
//
|
||||
always @(posedge clk)
|
||||
if (reset)
|
||||
begin
|
||||
rx_delay <= 0;
|
||||
rx_count <= 0;
|
||||
rx_data <= 0;
|
||||
o_tready <= 1'b0;
|
||||
rx_state <= RX_IDLE;
|
||||
fail <= 1'b0;
|
||||
done <= 1'b0;
|
||||
|
||||
end
|
||||
else begin
|
||||
case (rx_state)
|
||||
RX_IDLE: begin
|
||||
rx_delay <= 0;
|
||||
o_tready <= 1'b0;
|
||||
rx_data <= 0;
|
||||
rx_count <= 4;
|
||||
fail <= 1'b0;
|
||||
done <= 1'b0;
|
||||
// Not accepting data whilst Idle,
|
||||
// switch to active when packet arrives
|
||||
if (o_tvalid) begin
|
||||
o_tready <= 1'b1;
|
||||
rx_state <= RX_ACTIVE;
|
||||
end else
|
||||
rx_state <= RX_IDLE;
|
||||
end
|
||||
|
||||
RX_ACTIVE: begin
|
||||
o_tready <= 1'b1;
|
||||
if (o_tvalid)
|
||||
// if (o_tdata != {rx_data,rx_data+16'd1,rx_data+16'd2,rx_data+16'd3})
|
||||
if (o_tdata != {4{(rx_data[2]?16'hffff:16'h0000)^rx_data[15:0]}})
|
||||
begin
|
||||
$display("o_tdata: %x != expected: %x @ time: %d",o_tdata,
|
||||
// {rx_data,rx_data+16'd1,rx_data+16'd2,rx_data+16'd3},
|
||||
{4{(rx_data[2]?16'hffff:16'h0000)^rx_data[15:0]}},
|
||||
$time);
|
||||
rx_state <= RX_FAIL;
|
||||
end
|
||||
else
|
||||
// Should last be asserted?
|
||||
if (rx_data == rx_count)
|
||||
// ...last not asserted when it should be!
|
||||
if (~(o_tlast===1)) begin
|
||||
$display("o_tlast not asserted when it should be @ time: %d",$time);
|
||||
rx_state <= RX_FAIL;
|
||||
end else begin
|
||||
// End of packet, set up to RX next
|
||||
rx_data <= 0;
|
||||
rx_count <= rx_count + 4;
|
||||
rx_delay <= bist_rx_delay;
|
||||
if (rx_count == `MTU) begin
|
||||
rx_state <= RX_DONE;
|
||||
end else begin
|
||||
rx_state <= RX_WAIT;
|
||||
end
|
||||
o_tready <= 1'b0;
|
||||
end
|
||||
else
|
||||
// ...last asserted when it should not be!
|
||||
if (~(o_tlast===0)) begin
|
||||
$display("o_tlast asserted when it should not be @ time: %d",$time);
|
||||
rx_state <= RX_FAIL;
|
||||
end else begin
|
||||
// Still in packet body
|
||||
rx_data <= rx_data + 4;
|
||||
rx_delay <= bist_rx_delay;
|
||||
rx_state <= RX_WAIT;
|
||||
o_tready <= 1'b0;
|
||||
end
|
||||
else
|
||||
// Nothing to do this cycle
|
||||
rx_state <= RX_ACTIVE;
|
||||
end // case: RX_ACTIVE
|
||||
|
||||
// To simulate the radio consuming samples at a steady rate set by the decimation
|
||||
// have a programable delay here
|
||||
RX_WAIT: begin
|
||||
if (rx_delay == 0) begin
|
||||
rx_state <= RX_ACTIVE;
|
||||
o_tready <= 1'b1;
|
||||
end else begin
|
||||
rx_delay <= rx_delay - 1;
|
||||
rx_state <= RX_WAIT;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
RX_FAIL: begin
|
||||
o_tready <= 1'b0;
|
||||
done <= 1'b1;
|
||||
fail <= 1'b1;
|
||||
// If start is deasserted allow BIST logic to reset and rearm
|
||||
if (start)
|
||||
rx_state <= RX_FAIL;
|
||||
else
|
||||
rx_state <= RX_IDLE;
|
||||
|
||||
end
|
||||
|
||||
RX_DONE: begin
|
||||
o_tready <= 1'b0;
|
||||
done <= 1'b1;
|
||||
fail <= 1'b0;
|
||||
// If start is asserted allow BIST logic to reset, rearm & restart
|
||||
if (!start)
|
||||
rx_state <= RX_DONE;
|
||||
else
|
||||
rx_state <= RX_IDLE;
|
||||
|
||||
end
|
||||
|
||||
endcase // case (rx_state)
|
||||
end
|
||||
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// AXI4 Burst enumeration
|
||||
//
|
||||
`define AXI4_BURST_FIXED 2'b00
|
||||
`define AXI4_BURST_INCR 2'b01
|
||||
`define AXI4_BURST_WRAP 2'b10
|
||||
`define AXI4_BURST_RSVD 2'b11
|
||||
//
|
||||
// AXI4 response code enumeration
|
||||
//
|
||||
`define AXI4_RESP_OKAY 2'b00
|
||||
`define AXI4_RESP_EXOKAY 2'b01
|
||||
`define AXI4_RESP_SLVERR 2'b10
|
||||
`define AXI4_RESP_DECERR 2'b11
|
||||
//
|
||||
// AXI4 lock enumeration
|
||||
//
|
||||
`define AXI4_LOCK_NORMAL 1'b0
|
||||
`define AXI4_LOCK_EXCLUSIVE 1'b1
|
||||
//
|
||||
// AXI4 memory attrubutes
|
||||
//
|
||||
`define AXI4_CACHE_ALLOCATE 4'h8
|
||||
`define AXI4_CACHE_OTHER_ALLOCATE 4'h4
|
||||
`define AXI4_CACHE_MODIFIABLE 4'h2
|
||||
`define AXI4_CACHE_BUFFERABLE 4'h1
|
||||
//
|
||||
// AXI4 PROT attributes
|
||||
//
|
||||
`define AXI4_PROT_PRIVILEDGED 3'h1
|
||||
`define AXI4_PROT_NON_SECURE 3'h2
|
||||
`define AXI4_PROT_INSTRUCTION 3'h4
|
||||
|
||||
|
||||
@@ -0,0 +1,538 @@
|
||||
|
||||
`include "axi_defs.v"
|
||||
|
||||
`define DEBUG if (1)
|
||||
|
||||
module axi_dma_master
|
||||
(
|
||||
input aclk, // Global AXI clock
|
||||
input areset, // Global AXI 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 reg [31 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
|
||||
output reg [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 reg 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 reg 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 reg 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 reg [31 : 0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
|
||||
output reg [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 reg 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
|
||||
//
|
||||
// DMA interface for Write transaction
|
||||
//
|
||||
input [31:0] write_addr, // Byte address for start of write transaction (should be 64bit alligned)
|
||||
input [7:0] write_count, // Count of 64bit words to write. (minus one)
|
||||
input write_ctrl_valid,
|
||||
output reg write_ctrl_ready,
|
||||
input [63:0] write_data,
|
||||
input write_data_valid,
|
||||
output write_data_ready,
|
||||
//
|
||||
// DMA interface for Read
|
||||
//
|
||||
input [31:0] read_addr, // Byte address for start of read transaction (should be 64bit alligned)
|
||||
input [7:0] read_count, // Count of 64bit words to read.
|
||||
input read_ctrl_valid,
|
||||
output reg read_ctrl_ready,
|
||||
output [63:0] read_data,
|
||||
output read_data_valid,
|
||||
input read_data_ready,
|
||||
//
|
||||
// Debug Bus
|
||||
//
|
||||
output [31:0] debug
|
||||
|
||||
);
|
||||
|
||||
|
||||
localparam AW_IDLE = 0;
|
||||
localparam WAIT_AWREADY = 1;
|
||||
localparam WAIT_BVALID = 2;
|
||||
localparam AW_ERROR = 3;
|
||||
|
||||
reg [1:0] write_addr_state;
|
||||
reg [7:0] write_data_count; // Count write transfers.
|
||||
reg enable_data_write;
|
||||
|
||||
localparam DW_IDLE = 0;
|
||||
localparam DW_RUN = 1;
|
||||
localparam DW_LAST = 2;
|
||||
|
||||
reg [1:0] write_data_state;
|
||||
|
||||
localparam AR_IDLE = 0;
|
||||
localparam WAIT_ARREADY = 1;
|
||||
localparam WAIT_READ_DONE = 2;
|
||||
localparam AR_ERROR = 3;
|
||||
|
||||
reg [1:0] read_addr_state;
|
||||
|
||||
localparam DR_IDLE = 0;
|
||||
localparam DR_RUN = 1;
|
||||
localparam DR_WAIT_ERROR = 2;
|
||||
localparam DR_ERROR = 3;
|
||||
|
||||
reg [1:0] read_data_state;
|
||||
reg [7:0] read_data_count;
|
||||
reg enable_data_read;
|
||||
|
||||
///////////////////////////
|
||||
// DEBUG
|
||||
///////////////////////////
|
||||
assign debug= {24'h0,write_addr_state[1:0],write_data_state[1:0],read_addr_state[1:0],read_data_state[1:0]};
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Write address channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
assign m_axi_awid = 1'b0;
|
||||
assign m_axi_awsize = 3'h3; // 8 bytes.
|
||||
assign m_axi_awburst = `AXI4_BURST_INCR;
|
||||
assign m_axi_awlock = `AXI4_LOCK_NORMAL;
|
||||
assign m_axi_awcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
|
||||
assign m_axi_awprot = `AXI4_PROT_NON_SECURE;
|
||||
assign m_axi_awqos = 4'h0;
|
||||
assign m_axi_awregion = 4'h0;
|
||||
assign m_axi_awuser = 1'b0;
|
||||
|
||||
|
||||
//
|
||||
// AXI Write address state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
write_addr_state <= AW_IDLE;
|
||||
m_axi_awaddr[31:0] <= 32'h0;
|
||||
m_axi_awlen[7:0] <= 8'h0;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
m_axi_bready <= 1'b0;
|
||||
end else
|
||||
case (write_addr_state)
|
||||
//
|
||||
// AW_IDLE
|
||||
// We are ready to accept a new write transaction.
|
||||
//
|
||||
AW_IDLE: begin
|
||||
// Premptively accept new write transaction since we are idle.
|
||||
write_ctrl_ready <= 1'b1;
|
||||
// No need to be waiting for a response while idle.
|
||||
m_axi_bready <= 1'b0;
|
||||
// If we are offered a new transaction then.....
|
||||
if (write_ctrl_valid) begin
|
||||
// Drive all the relevent AXI4 write address channel signals next cycle.
|
||||
m_axi_awaddr[31:0] <= write_addr[31:0];
|
||||
m_axi_awlen[7:0] <= {write_count};
|
||||
m_axi_awvalid <= 1'b1;
|
||||
// If the AXI4 write channel is pre-emptively accepting the transaction...
|
||||
if (m_axi_awready == 1'b1) begin
|
||||
// ...go straight to looking for a transaction response...
|
||||
`DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",write_addr[31:0],write_count,$time);
|
||||
write_addr_state <= WAIT_BVALID;
|
||||
m_axi_bready <= 1'b1;
|
||||
end else begin
|
||||
// ...otherwise wait to get the transaction accepted.
|
||||
write_addr_state <= WAIT_AWREADY;
|
||||
end
|
||||
end
|
||||
end
|
||||
//
|
||||
// WAIT_AWREADY
|
||||
// Waiting for AXI4 slave to accept new write transaction.
|
||||
//
|
||||
WAIT_AWREADY: begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
// If the AXI4 write channel is accepting the transaction...
|
||||
if (m_axi_awready == 1'b1) begin
|
||||
// ...go to looking for a transaction response...
|
||||
write_addr_state <= WAIT_BVALID;
|
||||
m_axi_bready <= 1'b1;
|
||||
`DEBUG $display("WRITE TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_awaddr[31:0],m_axi_awlen[7:0],$time);
|
||||
end else begin
|
||||
// ...otherwise wait to get the trasaction accepted.
|
||||
write_addr_state <= WAIT_AWREADY;
|
||||
end
|
||||
end // case: WAIT_AWREADY
|
||||
//
|
||||
// WAIT_BVALID
|
||||
// Write transaction has been accepted, now waiting for a response to signal it's sucsesful.
|
||||
// Ignoring ID tag for the moment
|
||||
//
|
||||
WAIT_BVALID: begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
// Wait for response channel to signal how write transaction went down....
|
||||
if (m_axi_bvalid == 1'b1) begin
|
||||
if ((m_axi_bresp == `AXI4_RESP_OKAY) || (m_axi_bresp == `AXI4_RESP_EXOKAY)) begin
|
||||
// ....it went well, we are ready to start something new.
|
||||
write_addr_state <= AW_IDLE;
|
||||
m_axi_bready <= 1'b0;
|
||||
write_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
|
||||
end else if ((m_axi_bresp == `AXI4_RESP_SLVERR) || (m_axi_bresp == `AXI4_RESP_DECERR)) begin
|
||||
// ....things got ugly, retreat to an error stat and wait for intervention.
|
||||
write_addr_state <= AW_ERROR;
|
||||
m_axi_bready <= 1'b0;
|
||||
end
|
||||
end else begin
|
||||
write_addr_state <= WAIT_BVALID;
|
||||
m_axi_bready <= 1'b1;
|
||||
end
|
||||
end // case: WAIT_BVALID
|
||||
//
|
||||
// AW_ERROR
|
||||
// Something bad happened, going to need external intervention to restore a safe state.
|
||||
//
|
||||
AW_ERROR: begin
|
||||
write_ctrl_ready <= 1'b0;
|
||||
write_addr_state <= AW_ERROR;
|
||||
m_axi_awaddr[31:0] <= 32'h0;
|
||||
m_axi_awlen[7:0] <= 8'h0;
|
||||
m_axi_awvalid <= 1'b0;
|
||||
m_axi_bready <= 1'b0;
|
||||
end
|
||||
endcase // case(write_addr_state)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Write data channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
assign m_axi_wstrb = 8'hff;
|
||||
assign m_axi_wuser = 1'b0;
|
||||
|
||||
//
|
||||
// AXI Write data state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
write_data_state <= AW_IDLE;
|
||||
write_data_count <= 1;
|
||||
enable_data_write <= 1'b0;
|
||||
m_axi_wlast <= 1'b0;
|
||||
|
||||
end else
|
||||
case (write_data_state)
|
||||
//
|
||||
// DW_IDLE
|
||||
// Sit in this state until presented with the control details of a new write transaction.
|
||||
//
|
||||
DW_IDLE: begin
|
||||
write_data_count <= 1;
|
||||
m_axi_wlast <= 1'b0;
|
||||
|
||||
if (write_ctrl_valid && write_ctrl_ready) begin
|
||||
enable_data_write <= 1'b1;
|
||||
if (write_count[7:0] == 8'h0) begin
|
||||
// Single transfer transaction
|
||||
write_data_state <= DW_LAST;
|
||||
m_axi_wlast <= 1'b1;
|
||||
end else begin
|
||||
write_data_state <= DW_RUN;
|
||||
end
|
||||
end else begin
|
||||
write_data_state <= DW_IDLE;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DW_RUN
|
||||
//
|
||||
DW_RUN : begin
|
||||
enable_data_write <= 1'b1;
|
||||
m_axi_wlast <= 1'b0;
|
||||
|
||||
if (write_data_valid && m_axi_wready) begin
|
||||
// Single write transfer
|
||||
write_data_count <= write_data_count + 1;
|
||||
|
||||
if (write_data_count == m_axi_awlen[7:0]) begin
|
||||
write_data_state <= DW_LAST;
|
||||
m_axi_wlast <= 1'b1;
|
||||
end else begin
|
||||
write_data_state <= DW_RUN;
|
||||
end
|
||||
end else begin
|
||||
write_data_state <= DW_RUN;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DW_LAST
|
||||
//
|
||||
DW_LAST: begin
|
||||
if (write_data_valid && m_axi_wready) begin
|
||||
enable_data_write <= 1'b0;
|
||||
write_data_state <= DW_IDLE;
|
||||
m_axi_wlast <= 1'b0;
|
||||
end else begin
|
||||
enable_data_write <= 1'b1;
|
||||
write_data_state <= DW_LAST;
|
||||
m_axi_wlast <= 1'b1;
|
||||
end
|
||||
end // case: DW_LAST
|
||||
//
|
||||
default:
|
||||
write_data_state <= DW_IDLE;
|
||||
|
||||
endcase // case(write_data_state)
|
||||
|
||||
|
||||
assign m_axi_wdata = write_data;
|
||||
assign m_axi_wvalid = enable_data_write && write_data_valid;
|
||||
assign write_data_ready = enable_data_write && m_axi_wready;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Read address channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
assign m_axi_arid = 1'b0;
|
||||
assign m_axi_arsize = 3'h3; // 8 bytes
|
||||
assign m_axi_arburst = `AXI4_BURST_INCR;
|
||||
assign m_axi_arlock = `AXI4_LOCK_NORMAL;
|
||||
assign m_axi_arcache = `AXI4_CACHE_ALLOCATE | `AXI4_CACHE_OTHER_ALLOCATE | `AXI4_CACHE_MODIFIABLE | `AXI4_CACHE_BUFFERABLE;
|
||||
assign m_axi_arprot = `AXI4_PROT_NON_SECURE;
|
||||
assign m_axi_arqos = 4'h0;
|
||||
assign m_axi_arregion = 4'h0;
|
||||
assign m_axi_aruser = 1'b0;
|
||||
|
||||
|
||||
//
|
||||
// AXI Read address state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
read_addr_state <= AR_IDLE;
|
||||
m_axi_araddr[31:0] <= 32'h0;
|
||||
m_axi_arlen[7:0] <= 8'h0;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
end else
|
||||
case (read_addr_state)
|
||||
//
|
||||
// AR_IDLE
|
||||
// We are ready to accept a new read transaction.
|
||||
//
|
||||
AR_IDLE: begin
|
||||
// Premptively accept new read transaction since we are idle.
|
||||
read_ctrl_ready <= 1'b1;
|
||||
// If we are offered a new transaction then.....
|
||||
if (read_ctrl_valid) begin
|
||||
// Drive all the relevent AXI4 read address channel signals next cycle.
|
||||
m_axi_araddr[31:0] <= read_addr[31:0];
|
||||
m_axi_arlen[7:0] <= {read_count};
|
||||
m_axi_arvalid <= 1'b1;
|
||||
// If the AXI4 read channel is pre-emptively accepting the transaction...
|
||||
if (m_axi_arready == 1'b1) begin
|
||||
// ...go straight to looking for the transaction to complete
|
||||
`DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",read_addr[31:0],read_count,$time);
|
||||
read_addr_state <= WAIT_READ_DONE;
|
||||
end else begin
|
||||
// ...otherwise wait to get the transaction accepted.
|
||||
read_addr_state <= WAIT_ARREADY;
|
||||
end
|
||||
end
|
||||
end
|
||||
//
|
||||
// WAIT_ARREADY
|
||||
// Waiting for AXI4 slave to accept new read transaction.
|
||||
//
|
||||
WAIT_ARREADY: begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
// If the AXI4 read channel is accepting the transaction...
|
||||
if (m_axi_arready == 1'b1) begin
|
||||
// ...go to looking for the transaction to complete...
|
||||
read_addr_state <= WAIT_READ_DONE;
|
||||
`DEBUG $display("READ TRANSACTION: ADDR: %x LEN: %x @ time %d",m_axi_araddr[31:0],m_axi_arlen[7:0],$time);
|
||||
end else begin
|
||||
// ...otherwise wait to get the trasaction accepted.
|
||||
read_addr_state <= WAIT_ARREADY;
|
||||
end
|
||||
end // case: WAIT_ARREADY
|
||||
//
|
||||
// WAIT_READ_DONE
|
||||
// Read transaction has been accepted, now waiting for the data transfer to complete
|
||||
// Ignoring ID tag for the moment
|
||||
//
|
||||
WAIT_READ_DONE: begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
// Wait for read transaction to complete
|
||||
if (read_data_state == DR_IDLE) begin
|
||||
// ....it went well, we are ready to start something new.
|
||||
read_addr_state <= AR_IDLE;
|
||||
read_ctrl_ready <= 1'b1; // Ready to run again as soon as we hit idle.
|
||||
end else if (read_data_state == DR_ERROR) begin
|
||||
// ....things got ugly, retreat to an error stat and wait for intervention.
|
||||
read_addr_state <= AR_ERROR;
|
||||
end else begin
|
||||
read_addr_state <= WAIT_READ_DONE;
|
||||
end
|
||||
end // case: WAIT_BVALID
|
||||
//
|
||||
// AR_ERROR
|
||||
// Something bad happened, going to need external intervention to restore a safe state.
|
||||
//
|
||||
AR_ERROR: begin
|
||||
read_ctrl_ready <= 1'b0;
|
||||
read_addr_state <= AR_ERROR;
|
||||
m_axi_araddr[31:0] <= 32'h0;
|
||||
m_axi_arlen[7:0] <= 8'h0;
|
||||
m_axi_arvalid <= 1'b0;
|
||||
end
|
||||
endcase // case(read_addr_state)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// AXI Read data channel
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//
|
||||
// AXI Read data state machine
|
||||
//
|
||||
always @(posedge aclk)
|
||||
if (areset) begin
|
||||
read_data_state <= AR_IDLE;
|
||||
read_data_count <= 0;
|
||||
enable_data_read <= 1'b0;
|
||||
|
||||
end else
|
||||
case (read_data_state)
|
||||
//
|
||||
// DR_IDLE
|
||||
// Sit in this state until presented with the control details of a new read transaction.
|
||||
//
|
||||
DR_IDLE: begin
|
||||
read_data_count <= 0;
|
||||
|
||||
if (read_ctrl_valid && read_ctrl_ready) begin
|
||||
enable_data_read <= 1'b1;
|
||||
read_data_state <= DR_RUN;
|
||||
end else begin
|
||||
read_data_state <= DR_IDLE;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DR_RUN
|
||||
// Sit here counting read transfers. If any have error's shift to error state.
|
||||
//
|
||||
DR_RUN : begin
|
||||
enable_data_read <= 1'b1;
|
||||
|
||||
if (read_data_ready && m_axi_rvalid) begin
|
||||
// Single read transfer
|
||||
read_data_count <= read_data_count + 1;
|
||||
if ((m_axi_rresp == `AXI4_RESP_SLVERR) || (m_axi_rresp == `AXI4_RESP_DECERR)) begin
|
||||
if (m_axi_rlast) begin
|
||||
read_data_state <= DR_ERROR;
|
||||
end else begin
|
||||
read_data_state <= DR_WAIT_ERROR;
|
||||
end
|
||||
end else if (m_axi_rlast) begin // Implicitly good response signalled this transfer.
|
||||
if (read_data_count == m_axi_arlen[7:0]) begin
|
||||
read_data_state <= DR_IDLE;
|
||||
end else begin
|
||||
read_data_state <= DR_ERROR;
|
||||
end
|
||||
end else begin
|
||||
read_data_state <= DR_RUN;
|
||||
end
|
||||
end else begin
|
||||
read_data_state <= DR_RUN;
|
||||
end
|
||||
end
|
||||
//
|
||||
// DR_WAIT_ERROR
|
||||
// Something bad happened, wait for last signalled in this burst
|
||||
//
|
||||
DR_WAIT_ERROR: begin
|
||||
if (read_data_ready && m_axi_rvalid && m_axi_rlast) begin
|
||||
enable_data_read <= 1'b0;
|
||||
read_data_state <= DR_ERROR;
|
||||
end else begin
|
||||
enable_data_read <= 1'b1;
|
||||
read_data_state <= DR_WAIT_ERROR;
|
||||
end
|
||||
end // case: DR_WAIT_ERROR
|
||||
//
|
||||
// DR_ERROR
|
||||
// Something bad happened, going to need external intervention to restore a safe state.
|
||||
//
|
||||
DR_ERROR: begin
|
||||
enable_data_read <= 1'b0;
|
||||
read_data_state <= DR_ERROR;
|
||||
end // case: DR_ERROR
|
||||
|
||||
|
||||
endcase // case(read_data_state)
|
||||
|
||||
|
||||
assign read_data = m_axi_rdata;
|
||||
assign m_axi_rready = enable_data_read && read_data_ready;
|
||||
assign read_data_valid = enable_data_read && m_axi_rvalid;
|
||||
|
||||
endmodule // axi_dma_master
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
module axi_dma_master_tb;
|
||||
|
||||
|
||||
|
||||
wire aclk; // Global AXI clock
|
||||
wire aresetn; // Global AXI reset, active low.
|
||||
//
|
||||
// AXI Write address channel
|
||||
//
|
||||
wire [0 : 0] m_axi_awid; // Write address ID. This signal is the identification tag for the write address signals
|
||||
wire [31 : 0] m_axi_awaddr; // Write address. The write address gives the address of the first transfer in a write burst
|
||||
wire [7 : 0] m_axi_awlen; // Burst length. The burst length gives the exact number of transfers in a burst.
|
||||
wire [2 : 0] m_axi_awsize; // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
wire [1 : 0] m_axi_awburst; // Burst type. The burst type and the size information, determine how the address is calculated
|
||||
wire [0 : 0] m_axi_awlock; // Lock type. Provides additional information about the atomic characteristics of the transfer.
|
||||
wire [3 : 0] m_axi_awcache; // Memory type. This signal indicates how transactions are required to progress
|
||||
wire [2 : 0] m_axi_awprot; // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
wire [3 : 0] m_axi_awqos; // Quality of Service, QoS. The QoS identifier sent for each write transaction
|
||||
wire [3 : 0] m_axi_awregion; // Region identifier. Permits a single physical interface on a slave to be re-used.
|
||||
wire [0 : 0] m_axi_awuser; // User signal. Optional User-defined signal in the write address channel.
|
||||
wire m_axi_awvalid; // Write address valid. This signal indicates that the channel is signaling valid write addr
|
||||
wire m_axi_awready; // Write address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Write data channel.
|
||||
//
|
||||
wire [63 : 0] m_axi_wdata; // Write data
|
||||
wire [7 : 0] m_axi_wstrb; // Write strobes. This signal indicates which byte lanes hold valid data.
|
||||
wire m_axi_wlast; // Write last. This signal indicates the last transfer in a write burst
|
||||
wire [0 : 0] m_axi_wuser; // User signal. Optional User-defined signal in the write data channel.
|
||||
wire m_axi_wvalid; // Write valid. This signal indicates that valid write data and strobes are available.
|
||||
wire m_axi_wready; // Write ready. This signal indicates that the slave can accept the write data.
|
||||
//
|
||||
// AXI Write response channel signals
|
||||
//
|
||||
wire [0 : 0] m_axi_bid; // Response ID tag. This signal is the ID tag of the write response.
|
||||
wire [1 : 0] m_axi_bresp; // Write response. This signal indicates the status of the write transaction.
|
||||
wire [0 : 0] m_axi_buser; // User signal. Optional User-defined signal in the write response channel.
|
||||
wire m_axi_bvalid; // Write response valid. This signal indicates that the channel is signaling a valid response
|
||||
wire m_axi_bready; // Response ready. This signal indicates that the master can accept a write response
|
||||
//
|
||||
// AXI Read address channel
|
||||
//
|
||||
wire [0 : 0] m_axi_arid; // Read address ID. This signal is the identification tag for the read address group of signals
|
||||
wire [31 : 0] m_axi_araddr; // Read address. The read address gives the address of the first transfer in a read burst
|
||||
wire [7 : 0] m_axi_arlen; // Burst length. This signal indicates the exact number of transfers in a burst.
|
||||
wire [2 : 0] m_axi_arsize; // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
wire [1 : 0] m_axi_arburst; // Burst type. The burst type and the size information determine how the address for each transfer
|
||||
wire [0 : 0] m_axi_arlock; // Lock type. This signal provides additional information about the atomic characteristics
|
||||
wire [3 : 0] m_axi_arcache; // Memory type. This signal indicates how transactions are required to progress
|
||||
wire [2 : 0] m_axi_arprot; // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
wire [3 : 0] m_axi_arqos; // Quality of Service, QoS. QoS identifier sent for each read transaction.
|
||||
wire [3 : 0] m_axi_arregion; // Region identifier. Permits a single physical interface on a slave to be re-used
|
||||
wire [0 : 0] m_axi_aruser; // User signal. Optional User-defined signal in the read address channel.
|
||||
wire m_axi_arvalid; // Read address valid. This signal indicates that the channel is signaling valid read addr
|
||||
wire m_axi_arready; // Read address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Read data channel
|
||||
//
|
||||
wire [0 : 0] m_axi_rid; // Read ID tag. This signal is the identification tag for the read data group of signals
|
||||
wire [63 : 0] m_axi_rdata; // Read data.
|
||||
wire [1 : 0] m_axi_rresp; // Read response. This signal indicates the status of the read transfer
|
||||
wire m_axi_rlast; // Read last. This signal indicates the last transfer in a read burst.
|
||||
wire [0 : 0] m_axi_ruser; // User signal. Optional User-defined signal in the read data channel.
|
||||
wire m_axi_rvalid; // Read valid. This signal indicates that the channel is signaling the required read data.
|
||||
wire m_axi_rready; // Read ready. This signal indicates that the master can accept the read data and response
|
||||
//
|
||||
// DMA interface for Write transaction
|
||||
//
|
||||
wire [31:0] write_addr; // Byte address for start of write transaction (should be 64bit alligned)
|
||||
wire [3:0] write_count; // Count of 64 words to write.
|
||||
wire write_ctrl_valid;
|
||||
wire write_ctrl_ready;
|
||||
wire [63:0] write_data;
|
||||
wire write_data_valid;
|
||||
wire write_data_ready;
|
||||
//
|
||||
// DMA interface for Read
|
||||
//
|
||||
wire [31:0] read_addr; // Byte address for start of read transaction (should be 64bit alligned)
|
||||
wire [3:0] read_count; // Count of 64 words to read.
|
||||
wire read_ctrl_valid;
|
||||
wire read_ctrl_ready;
|
||||
wire [63:0] read_data;
|
||||
wire read_data_valid;
|
||||
wire read_data_ready;
|
||||
|
||||
|
||||
|
||||
axi_dma_master axi_dma_master_i1
|
||||
(
|
||||
.aclk(s_aclk), // input s_aclk
|
||||
.aresetn(s_aresetn), // input s_aresetn
|
||||
//
|
||||
.s_axi_awid(s_axi_awid), // input [0 : 0] s_axi_awid
|
||||
.s_axi_awaddr(s_axi_awaddr), // input [31 : 0] s_axi_awaddr
|
||||
.s_axi_awlen(s_axi_awlen), // input [7 : 0] s_axi_awlen
|
||||
.s_axi_awsize(s_axi_awsize), // input [2 : 0] s_axi_awsize
|
||||
.s_axi_awburst(s_axi_awburst), // input [1 : 0] s_axi_awburst
|
||||
.s_axi_awvalid(s_axi_awvalid), // input s_axi_awvalid
|
||||
.s_axi_awready(s_axi_awready), // output s_axi_awready
|
||||
//
|
||||
.s_axi_wdata(s_axi_wdata), // input [63 : 0] s_axi_wdata
|
||||
.s_axi_wstrb(s_axi_wstrb), // input [7 : 0] s_axi_wstrb
|
||||
.s_axi_wlast(s_axi_wlast), // input s_axi_wlast
|
||||
.s_axi_wvalid(s_axi_wvalid), // input s_axi_wvalid
|
||||
.s_axi_wready(s_axi_wready), // output s_axi_wready
|
||||
//
|
||||
.s_axi_bid(s_axi_bid), // output [0 : 0] s_axi_bid
|
||||
.s_axi_bresp(s_axi_bresp), // output [1 : 0] s_axi_bresp
|
||||
.s_axi_bvalid(s_axi_bvalid), // output s_axi_bvalid
|
||||
.s_axi_bready(s_axi_bready), // input s_axi_bready
|
||||
//
|
||||
.s_axi_arid(s_axi_arid), // input [0 : 0] s_axi_arid
|
||||
.s_axi_araddr(s_axi_araddr), // input [31 : 0] s_axi_araddr
|
||||
.s_axi_arlen(s_axi_arlen), // input [7 : 0] s_axi_arlen
|
||||
.s_axi_arsize(s_axi_arsize), // input [2 : 0] s_axi_arsize
|
||||
.s_axi_arburst(s_axi_arburst), // input [1 : 0] s_axi_arburst
|
||||
.s_axi_arvalid(s_axi_arvalid), // input s_axi_arvalid
|
||||
.s_axi_arready(s_axi_arready), // output s_axi_arready
|
||||
//
|
||||
.s_axi_rid(s_axi_rid), // output [0 : 0] s_axi_rid
|
||||
.s_axi_rdata(s_axi_rdata), // output [63 : 0] s_axi_rdata
|
||||
.s_axi_rresp(s_axi_rresp), // output [1 : 0] s_axi_rresp
|
||||
.s_axi_rlast(s_axi_rlast), // output s_axi_rlast
|
||||
.s_axi_rvalid(s_axi_rvalid), // output s_axi_rvalid
|
||||
.s_axi_rready(s_axi_rready) // input s_axi_rready
|
||||
);
|
||||
|
||||
|
||||
axi4_bram_1kx64 axi4_bram_1kx64_i1
|
||||
(
|
||||
.s_aclk(s_aclk), // input s_aclk
|
||||
.s_aresetn(s_aresetn), // input s_aresetn
|
||||
.s_axi_awid(s_axi_awid), // input [0 : 0] s_axi_awid
|
||||
.s_axi_awaddr(s_axi_awaddr), // input [31 : 0] s_axi_awaddr
|
||||
.s_axi_awlen(s_axi_awlen), // input [7 : 0] s_axi_awlen
|
||||
.s_axi_awsize(s_axi_awsize), // input [2 : 0] s_axi_awsize
|
||||
.s_axi_awburst(s_axi_awburst), // input [1 : 0] s_axi_awburst
|
||||
.s_axi_awvalid(s_axi_awvalid), // input s_axi_awvalid
|
||||
.s_axi_awready(s_axi_awready), // output s_axi_awready
|
||||
.s_axi_wdata(s_axi_wdata), // input [63 : 0] s_axi_wdata
|
||||
.s_axi_wstrb(s_axi_wstrb), // input [7 : 0] s_axi_wstrb
|
||||
.s_axi_wlast(s_axi_wlast), // input s_axi_wlast
|
||||
.s_axi_wvalid(s_axi_wvalid), // input s_axi_wvalid
|
||||
.s_axi_wready(s_axi_wready), // output s_axi_wready
|
||||
.s_axi_bid(s_axi_bid), // output [0 : 0] s_axi_bid
|
||||
.s_axi_bresp(s_axi_bresp), // output [1 : 0] s_axi_bresp
|
||||
.s_axi_bvalid(s_axi_bvalid), // output s_axi_bvalid
|
||||
.s_axi_bready(s_axi_bready), // input s_axi_bready
|
||||
.s_axi_arid(s_axi_arid), // input [0 : 0] s_axi_arid
|
||||
.s_axi_araddr(s_axi_araddr), // input [31 : 0] s_axi_araddr
|
||||
.s_axi_arlen(s_axi_arlen), // input [7 : 0] s_axi_arlen
|
||||
.s_axi_arsize(s_axi_arsize), // input [2 : 0] s_axi_arsize
|
||||
.s_axi_arburst(s_axi_arburst), // input [1 : 0] s_axi_arburst
|
||||
.s_axi_arvalid(s_axi_arvalid), // input s_axi_arvalid
|
||||
.s_axi_arready(s_axi_arready), // output s_axi_arready
|
||||
.s_axi_rid(s_axi_rid), // output [0 : 0] s_axi_rid
|
||||
.s_axi_rdata(s_axi_rdata), // output [63 : 0] s_axi_rdata
|
||||
.s_axi_rresp(s_axi_rresp), // output [1 : 0] s_axi_rresp
|
||||
.s_axi_rlast(s_axi_rlast), // output s_axi_rlast
|
||||
.s_axi_rvalid(s_axi_rvalid), // output s_axi_rvalid
|
||||
.s_axi_rready(s_axi_rready) // input s_axi_rready
|
||||
);
|
||||
|
||||
endmodule // axi_dma_master_tb
|
||||
@@ -0,0 +1,816 @@
|
||||
|
||||
//
|
||||
// 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
|
||||
|
||||
@@ -0,0 +1,421 @@
|
||||
module axi_dram_fifo_tb;
|
||||
|
||||
|
||||
|
||||
reg clk; // Global AXI clock
|
||||
reg reset; // Global reset, active high.
|
||||
reg clear;
|
||||
wire aresetn; // Global AXI reset, active low.
|
||||
//
|
||||
// AXI Write address channel
|
||||
//
|
||||
wire [0 : 0] axi_awid; // Write address ID. This signal is the identification tag for the write address signals
|
||||
wire [31 : 0] axi_awaddr; // Write address. The write address gives the address of the first transfer in a write burst
|
||||
wire [7 : 0] axi_awlen; // Burst length. The burst length gives the exact number of transfers in a burst.
|
||||
wire [2 : 0] axi_awsize; // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
wire [1 : 0] axi_awburst; // Burst type. The burst type and the size information, determine how the address is calculated
|
||||
wire [0 : 0] axi_awlock; // Lock type. Provides additional information about the atomic characteristics of the transfer.
|
||||
wire [3 : 0] axi_awcache; // Memory type. This signal indicates how transactions are required to progress
|
||||
wire [2 : 0] axi_awprot; // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
wire [3 : 0] axi_awqos; // Quality of Service, QoS. The QoS identifier sent for each write transaction
|
||||
wire [3 : 0] axi_awregion; // Region identifier. Permits a single physical interface on a slave to be re-used.
|
||||
wire [0 : 0] axi_awuser; // User signal. Optional User-defined signal in the write address channel.
|
||||
wire axi_awvalid; // Write address valid. This signal indicates that the channel is signaling valid write addr
|
||||
wire axi_awready; // Write address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Write data channel.
|
||||
//
|
||||
wire [63 : 0] axi_wdata; // Write data
|
||||
wire [7 : 0] axi_wstrb; // Write strobes. This signal indicates which byte lanes hold valid data.
|
||||
wire axi_wlast; // Write last. This signal indicates the last transfer in a write burst
|
||||
wire [0 : 0] axi_wuser; // User signal. Optional User-defined signal in the write data channel.
|
||||
wire axi_wvalid; // Write valid. This signal indicates that valid write data and strobes are available.
|
||||
wire axi_wready; // Write ready. This signal indicates that the slave can accept the write data.
|
||||
//
|
||||
// AXI Write response channel signals
|
||||
//
|
||||
wire [0 : 0] axi_bid; // Response ID tag. This signal is the ID tag of the write response.
|
||||
wire [1 : 0] axi_bresp; // Write response. This signal indicates the status of the write transaction.
|
||||
wire [0 : 0] axi_buser; // User signal. Optional User-defined signal in the write response channel.
|
||||
wire axi_bvalid; // Write response valid. This signal indicates that the channel is signaling a valid response
|
||||
wire axi_bready; // Response ready. This signal indicates that the master can accept a write response
|
||||
//
|
||||
// AXI Read address channel
|
||||
//
|
||||
wire [0 : 0] axi_arid; // Read address ID. This signal is the identification tag for the read address group of signals
|
||||
wire [31 : 0] axi_araddr; // Read address. The read address gives the address of the first transfer in a read burst
|
||||
wire [7 : 0] axi_arlen; // Burst length. This signal indicates the exact number of transfers in a burst.
|
||||
wire [2 : 0] axi_arsize; // Burst size. This signal indicates the size of each transfer in the burst.
|
||||
wire [1 : 0] axi_arburst; // Burst type. The burst type and the size information determine how the address for each transfer
|
||||
wire [0 : 0] axi_arlock; // Lock type. This signal provides additional information about the atomic characteristics
|
||||
wire [3 : 0] axi_arcache; // Memory type. This signal indicates how transactions are required to progress
|
||||
wire [2 : 0] axi_arprot; // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
wire [3 : 0] axi_arqos; // Quality of Service, QoS. QoS identifier sent for each read transaction.
|
||||
wire [3 : 0] axi_arregion; // Region identifier. Permits a single physical interface on a slave to be re-used
|
||||
wire [0 : 0] axi_aruser; // User signal. Optional User-defined signal in the read address channel.
|
||||
wire axi_arvalid; // Read address valid. This signal indicates that the channel is signaling valid read addr
|
||||
wire axi_arready; // Read address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Read data channel
|
||||
//
|
||||
wire [0 : 0] axi_rid; // Read ID tag. This signal is the identification tag for the read data group of signals
|
||||
wire [63 : 0] axi_rdata; // Read data.
|
||||
wire [1 : 0] axi_rresp; // Read response. This signal indicates the status of the read transfer
|
||||
wire axi_rlast; // Read last. This signal indicates the last transfer in a read burst.
|
||||
wire [0 : 0] axi_ruser; // User signal. Optional User-defined signal in the read data channel.
|
||||
wire axi_rvalid; // Read valid. This signal indicates that the channel is signaling the required read data.
|
||||
wire axi_rready; // Read ready. This signal indicates that the master can accept the read data and response
|
||||
|
||||
//
|
||||
// CHDR friendly AXI stream input
|
||||
//
|
||||
wire [63:0] i_tdata;
|
||||
wire i_tlast;
|
||||
wire i_tvalid;
|
||||
wire i_tready;
|
||||
//
|
||||
// CHDR friendly AXI Stream output
|
||||
//
|
||||
wire [63:0] o_tdata;
|
||||
wire o_tlast;
|
||||
wire o_tvalid;
|
||||
wire o_tready;
|
||||
|
||||
//
|
||||
// These registers optionaly used
|
||||
// to drive nets through procedural assignments in test bench.
|
||||
// These drivers default to tri-stated.
|
||||
//
|
||||
|
||||
reg [63:0] i_tdata_r;
|
||||
reg i_tlast_r;
|
||||
reg i_tvalid_r;
|
||||
reg o_tready_r;
|
||||
|
||||
assign i_tdata = i_tdata_r;
|
||||
assign i_tlast = i_tlast_r;
|
||||
assign i_tvalid = i_tvalid_r;
|
||||
assign o_tready = o_tready_r;
|
||||
|
||||
initial
|
||||
begin
|
||||
i_tdata_r <= 64'hzzzz_zzzz_zzzz_zzzz;
|
||||
i_tlast_r <= 1'bz;
|
||||
i_tvalid_r <= 1'bz;
|
||||
o_tready_r <= 1'bz;
|
||||
end
|
||||
|
||||
|
||||
|
||||
axi_dram_fifo
|
||||
#(.SIZE(13))
|
||||
axi_dram_fifo_i1
|
||||
(
|
||||
.bus_clk(clk), // input s_aclk
|
||||
.bus_reset(reset), // input s_aresetn
|
||||
.clear(clear),
|
||||
.dram_clk(clk), // input s_aclk
|
||||
.dram_reset(reset), // input s_aresetn
|
||||
// Write control
|
||||
.m_axi_awid(axi_awid), // input [0 : 0] s_axi_awid
|
||||
.m_axi_awaddr(axi_awaddr), // input [31 : 0] s_axi_awaddr
|
||||
.m_axi_awlen(axi_awlen), // input [7 : 0] s_axi_awlen
|
||||
.m_axi_awsize(axi_awsize), // input [2 : 0] s_axi_awsize
|
||||
.m_axi_awburst(axi_awburst), // input [1 : 0] s_axi_awburst
|
||||
.m_axi_awvalid(axi_awvalid), // input s_axi_awvalid
|
||||
.m_axi_awready(axi_awready), // output s_axi_awready
|
||||
.m_axi_awlock(),
|
||||
.m_axi_awcache(),
|
||||
.m_axi_awprot(),
|
||||
.m_axi_awqos(),
|
||||
.m_axi_awregion(),
|
||||
.m_axi_awuser(),
|
||||
// Write Data
|
||||
.m_axi_wdata(axi_wdata), // input [63 : 0] s_axi_wdata
|
||||
.m_axi_wstrb(axi_wstrb), // input [7 : 0] s_axi_wstrb
|
||||
.m_axi_wlast(axi_wlast), // input s_axi_wlast
|
||||
.m_axi_wvalid(axi_wvalid), // input s_axi_wvalid
|
||||
.m_axi_wready(axi_wready), // output s_axi_wready
|
||||
.m_axi_wuser(),
|
||||
// Write Response
|
||||
.m_axi_bid(axi_bid), // output [0 : 0] s_axi_bid
|
||||
.m_axi_bresp(axi_bresp), // output [1 : 0] s_axi_bresp
|
||||
.m_axi_bvalid(axi_bvalid), // output s_axi_bvalid
|
||||
.m_axi_bready(axi_bready), // input s_axi_bready
|
||||
.m_axi_buser(),
|
||||
// Read Control
|
||||
.m_axi_arid(axi_arid), // input [0 : 0] s_axi_arid
|
||||
.m_axi_araddr(axi_araddr), // input [31 : 0] s_axi_araddr
|
||||
.m_axi_arlen(axi_arlen), // input [7 : 0] s_axi_arlen
|
||||
.m_axi_arsize(axi_arsize), // input [2 : 0] s_axi_arsize
|
||||
.m_axi_arburst(axi_arburst), // input [1 : 0] s_axi_arburst
|
||||
.m_axi_arvalid(axi_arvalid), // input s_axi_arvalid
|
||||
.m_axi_arready(axi_arready), // output s_axi_arready
|
||||
.m_axi_arlock(),
|
||||
.m_axi_arcache(),
|
||||
.m_axi_arprot(),
|
||||
.m_axi_arqos(),
|
||||
.m_axi_arregion(),
|
||||
.m_axi_aruser(),
|
||||
// Read Data
|
||||
.m_axi_rid(axi_rid), // output [0 : 0] s_axi_rid
|
||||
.m_axi_rdata(axi_rdata), // output [63 : 0] s_axi_rdata
|
||||
.m_axi_rresp(axi_rresp), // output [1 : 0] s_axi_rresp
|
||||
.m_axi_rlast(axi_rlast), // output s_axi_rlast
|
||||
.m_axi_rvalid(axi_rvalid), // output s_axi_rvalid
|
||||
.m_axi_rready(axi_rready), // input s_axi_rready
|
||||
.m_axi_ruser(),
|
||||
// CHDR in
|
||||
.i_tdata(i_tdata),
|
||||
.i_tlast(i_tlast),
|
||||
.i_tvalid(i_tvalid),
|
||||
.i_tready(i_tready),
|
||||
// CHDR out
|
||||
.o_tdata(o_tdata),
|
||||
.o_tlast(o_tlast),
|
||||
.o_tvalid(o_tvalid),
|
||||
.o_tready(o_tready),
|
||||
//
|
||||
.supress_threshold(16'h0),
|
||||
.supress_enable(1'b0)
|
||||
);
|
||||
|
||||
|
||||
axi4_bram_1kx64 axi4_bram_1kx64_i1
|
||||
(
|
||||
.s_aclk(clk), // input s_aclk
|
||||
.s_aresetn(aresetn), // input s_aresetn
|
||||
.s_axi_awid(axi_awid), // input [0 : 0] s_axi_awid
|
||||
.s_axi_awaddr(axi_awaddr), // input [31 : 0] s_axi_awaddr
|
||||
.s_axi_awlen(axi_awlen), // input [7 : 0] s_axi_awlen
|
||||
.s_axi_awsize(axi_awsize), // input [2 : 0] s_axi_awsize
|
||||
.s_axi_awburst(axi_awburst), // input [1 : 0] s_axi_awburst
|
||||
.s_axi_awvalid(axi_awvalid), // input s_axi_awvalid
|
||||
.s_axi_awready(axi_awready), // output s_axi_awready
|
||||
.s_axi_wdata(axi_wdata), // input [63 : 0] s_axi_wdata
|
||||
.s_axi_wstrb(axi_wstrb), // input [7 : 0] s_axi_wstrb
|
||||
.s_axi_wlast(axi_wlast), // input s_axi_wlast
|
||||
.s_axi_wvalid(axi_wvalid), // input s_axi_wvalid
|
||||
.s_axi_wready(axi_wready), // output s_axi_wready
|
||||
.s_axi_bid(axi_bid), // output [0 : 0] s_axi_bid
|
||||
.s_axi_bresp(axi_bresp), // output [1 : 0] s_axi_bresp
|
||||
.s_axi_bvalid(axi_bvalid), // output s_axi_bvalid
|
||||
.s_axi_bready(axi_bready), // input s_axi_bready
|
||||
.s_axi_arid(axi_arid), // input [0 : 0] s_axi_arid
|
||||
.s_axi_araddr(axi_araddr), // input [31 : 0] s_axi_araddr
|
||||
.s_axi_arlen(axi_arlen), // input [7 : 0] s_axi_arlen
|
||||
.s_axi_arsize(axi_arsize), // input [2 : 0] s_axi_arsize
|
||||
.s_axi_arburst(axi_arburst), // input [1 : 0] s_axi_arburst
|
||||
.s_axi_arvalid(axi_arvalid), // input s_axi_arvalid
|
||||
.s_axi_arready(axi_arready), // output s_axi_arready
|
||||
.s_axi_rid(axi_rid), // output [0 : 0] s_axi_rid
|
||||
.s_axi_rdata(axi_rdata), // output [63 : 0] s_axi_rdata
|
||||
.s_axi_rresp(axi_rresp), // output [1 : 0] s_axi_rresp
|
||||
.s_axi_rlast(axi_rlast), // output s_axi_rlast
|
||||
.s_axi_rvalid(axi_rvalid), // output s_axi_rvalid
|
||||
.s_axi_rready(axi_rready) // input s_axi_rready
|
||||
);
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
task send_ramp;
|
||||
input [31:0] burst_count;
|
||||
input [31:0] len;
|
||||
input [31:0] sid;
|
||||
|
||||
reg [31:0] data;
|
||||
reg [11:0] seqno;
|
||||
|
||||
begin
|
||||
seqno = 0;
|
||||
data = 0;
|
||||
send_packet(len, data, 0, seqno, (burst_count==1), 0, sid);
|
||||
seqno = seqno + 1;
|
||||
data <= data + len;
|
||||
|
||||
if(burst_count > 2)
|
||||
repeat (burst_count - 2)
|
||||
begin
|
||||
send_packet(len, data, 64'h0, seqno, 0, 0, sid);
|
||||
seqno = seqno + 1;
|
||||
data <= data + len;
|
||||
end
|
||||
if(burst_count > 1)
|
||||
send_packet(len, data, 64'h0, seqno, 1, 0, sid);
|
||||
end
|
||||
endtask // send_ramp
|
||||
|
||||
|
||||
task send_dc;
|
||||
input [31:0] burst_count;
|
||||
input [31:0] len;
|
||||
input [31:0] sid;
|
||||
|
||||
reg [31:0] data;
|
||||
reg [11:0] seqno;
|
||||
|
||||
begin
|
||||
seqno = 0;
|
||||
data = 1 << 14;
|
||||
send_packet(len, data, 0, seqno, (burst_count==1), 0, sid);
|
||||
seqno = seqno + 1;
|
||||
|
||||
|
||||
if(burst_count > 2)
|
||||
repeat (burst_count - 2)
|
||||
begin
|
||||
send_packet(len, data, 64'h0, seqno, 0, 0, sid);
|
||||
seqno = seqno + 1;
|
||||
|
||||
end
|
||||
if(burst_count > 1)
|
||||
send_packet(len, data, 64'h0, seqno, 1, 0, sid);
|
||||
end
|
||||
endtask // send_ramp
|
||||
|
||||
|
||||
task send_burst;
|
||||
input [31:0] burst_count;
|
||||
input [31:0] len;
|
||||
input [31:0] start_data;
|
||||
input [63:0] send_time;
|
||||
input [11:0] start_seqnum;
|
||||
input send_at;
|
||||
input [31:0] sid;
|
||||
|
||||
reg [11:0] seqno;
|
||||
|
||||
begin
|
||||
seqno = start_seqnum;
|
||||
send_packet(len, {seqno,start_data[15:0]}, send_time, seqno, (burst_count==1), send_at, sid);
|
||||
seqno = seqno + 1;
|
||||
|
||||
if(burst_count > 2)
|
||||
repeat (burst_count - 2)
|
||||
begin
|
||||
send_packet(len, {seqno,start_data[15:0]}, 64'h0, seqno, 0, 0, sid);
|
||||
seqno = seqno + 1;
|
||||
end
|
||||
if(burst_count > 1)
|
||||
send_packet(len, {seqno,start_data[15:0]}, 64'h0, seqno, 1, 0, sid);
|
||||
end
|
||||
endtask // send_burst
|
||||
|
||||
task send_packet;
|
||||
input [31:0] len;
|
||||
input [31:0] start_data;
|
||||
input [63:0] send_time;
|
||||
input [11:0] pkt_seqnum;
|
||||
input eob;
|
||||
input send_at;
|
||||
input [31:0] sid;
|
||||
|
||||
reg [31:0] samp0, samp1;
|
||||
|
||||
|
||||
begin
|
||||
// Send a packet
|
||||
samp0 <= start_data;
|
||||
samp1 <= start_data + 1;
|
||||
@(posedge clk);
|
||||
|
||||
i_tlast_r <= 0;
|
||||
i_tdata_r <= { 1'b0, 1'b0 /*trl*/, send_at, eob, pkt_seqnum, len[15:0]+16'd2+send_at+send_at, sid };
|
||||
i_tvalid_r <= 1;
|
||||
@(posedge clk)
|
||||
if(send_at)
|
||||
begin
|
||||
i_tdata_r <= send_time;
|
||||
@(posedge clk);
|
||||
end
|
||||
|
||||
repeat (len[31:1]+len[0]-1)
|
||||
begin
|
||||
i_tdata_r <= {samp0,samp1};
|
||||
samp0 <= samp0 + 2;
|
||||
samp1 <= samp1 + 2;
|
||||
@(posedge clk);
|
||||
end
|
||||
|
||||
i_tdata_r <= {samp0,samp1};
|
||||
i_tlast_r <= 1'b1;
|
||||
@(posedge clk);
|
||||
i_tvalid_r <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_packet
|
||||
|
||||
task send_raw_packet;
|
||||
input [31:0] len;
|
||||
|
||||
reg [63:0] data;
|
||||
|
||||
begin
|
||||
data = 0;
|
||||
@(posedge clk);
|
||||
repeat (len-1) begin
|
||||
i_tlast_r <= 0;
|
||||
i_tdata_r <= data;
|
||||
i_tvalid_r <= 1;
|
||||
@(posedge clk);
|
||||
while (~i_tready) @(posedge clk);
|
||||
data = data + 1;
|
||||
end
|
||||
i_tlast_r <= 1;
|
||||
i_tdata_r <= data;
|
||||
i_tvalid_r <= 1;
|
||||
@(posedge clk);
|
||||
while (~i_tready) @(posedge clk);
|
||||
i_tvalid_r <= 0;
|
||||
@(posedge clk);
|
||||
end
|
||||
endtask // send_raw_packet
|
||||
|
||||
task receive_raw_packet;
|
||||
input [31:0] len;
|
||||
output fail;
|
||||
reg [63:0] data;
|
||||
|
||||
begin
|
||||
data = 0;
|
||||
fail = 0;
|
||||
|
||||
@(posedge clk);
|
||||
repeat (len-1) begin
|
||||
o_tready_r <= 1;
|
||||
@(posedge clk);
|
||||
while (~o_tvalid) @(posedge clk);
|
||||
//$display("Data = %d, o_tdata = %d, o_tlast = %d",data,o_tdata,o_tlast);
|
||||
|
||||
fail = fail || (data !== o_tdata);
|
||||
fail = fail || ~(o_tlast === 0);
|
||||
data = data + 1;
|
||||
|
||||
end
|
||||
o_tready_r <= 1;
|
||||
@(posedge clk);
|
||||
while (~o_tvalid) @(posedge clk);
|
||||
//$display("Data = %d, o_tdata = %d, o_tlast = %d",data,o_tdata,o_tlast);
|
||||
fail = fail || (data !== o_tdata);
|
||||
fail = fail || ~(o_tlast === 1);
|
||||
o_tready_r <= 0;
|
||||
@(posedge clk);
|
||||
if (fail) $display("receive_raw_packet size %d failed",len);
|
||||
|
||||
end
|
||||
endtask // receive_raw_packet
|
||||
|
||||
|
||||
|
||||
assign aresetn = ~reset;
|
||||
|
||||
//
|
||||
// Bring in a simulation script here
|
||||
//
|
||||
`include "simulation_script.v"
|
||||
|
||||
endmodule // axi_dram_fifo_tb
|
||||
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
|
||||
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
|
||||
// a multi-word sequence to encode it (and the escape character when it appears as data input).
|
||||
//
|
||||
// 0x1234567887654321 with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE without last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
|
||||
module axi_embed_tlast
|
||||
#(parameter WIDTH=64)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tlast,
|
||||
input i_tvalid,
|
||||
output i_tready,
|
||||
//
|
||||
output reg [WIDTH-1:0] o_tdata,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
|
||||
);
|
||||
|
||||
localparam PASS = 0;
|
||||
localparam ZERO = 1;
|
||||
localparam ONE = 2;
|
||||
localparam ESCAPE = 3;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam LAST = 1;
|
||||
localparam ESC = 2;
|
||||
localparam FINISH = 3;
|
||||
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
reg [1:0] select;
|
||||
|
||||
reg [31:0] checksum;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
checksum <= 0;
|
||||
end else if (i_tready && i_tvalid && i_tlast) begin
|
||||
checksum <= 0;
|
||||
end else if (i_tready && i_tvalid) begin
|
||||
checksum <= checksum + i_tdata[31:0] + i_tdata[63:32];
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
end else begin if (o_tready)
|
||||
state <= next_state;
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
case(state)
|
||||
IDLE: begin
|
||||
if (i_tlast && i_tvalid)
|
||||
begin
|
||||
next_state = LAST;
|
||||
select = ESCAPE;
|
||||
end
|
||||
else if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid)
|
||||
begin
|
||||
next_state = ESC;
|
||||
select = ESCAPE;
|
||||
end
|
||||
else
|
||||
begin
|
||||
next_state = IDLE;
|
||||
select = PASS;
|
||||
end
|
||||
end // case: IDLE
|
||||
//
|
||||
//
|
||||
LAST: begin
|
||||
select = ONE;
|
||||
next_state = FINISH;
|
||||
end
|
||||
//
|
||||
//
|
||||
ESC: begin
|
||||
select = ZERO;
|
||||
next_state = FINISH;
|
||||
end
|
||||
//
|
||||
//
|
||||
FINISH: begin
|
||||
select = PASS;
|
||||
if (i_tvalid)
|
||||
next_state = IDLE;
|
||||
else
|
||||
next_state = FINISH;
|
||||
end
|
||||
endcase // case(state)
|
||||
end // always @ (*)
|
||||
|
||||
//
|
||||
// Muxes
|
||||
//
|
||||
always @*
|
||||
begin
|
||||
case(select)
|
||||
PASS: o_tdata = i_tdata;
|
||||
ZERO: o_tdata = 0;
|
||||
ONE: o_tdata = {checksum[31:0],32'h1};
|
||||
ESCAPE: o_tdata = 64'hDEADBEEFFEEDCAFE;
|
||||
endcase // case(select)
|
||||
end
|
||||
|
||||
assign o_tvalid = (select == PASS) ? i_tvalid : 1'b1;
|
||||
assign i_tready = (select == PASS) ? o_tready : 1'b0;
|
||||
|
||||
endmodule // axi_embed_tlast
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
//
|
||||
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
|
||||
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
|
||||
// a multi-word sequence to encode it (and the escape character when it appears as data input).
|
||||
//
|
||||
// 0x1234567887654321 with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE with last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
// 0xDEADBEEFFEEDCAFE without last becomes
|
||||
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
|
||||
//
|
||||
|
||||
module axi_extract_tlast
|
||||
#(parameter WIDTH=64)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output reg o_tlast,
|
||||
output reg o_tvalid,
|
||||
input o_tready,
|
||||
//
|
||||
output reg checksum_error_reg
|
||||
|
||||
);
|
||||
|
||||
reg [1:0] state, next_state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam EXTRACT1 = 1;
|
||||
localparam EXTRACT2 = 2;
|
||||
localparam EXTRACT3 = 3;
|
||||
|
||||
assign o_tdata = i_tdata;
|
||||
|
||||
reg [31:0] checksum, old_checksum;
|
||||
reg checksum_error;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
checksum <= 0;
|
||||
old_checksum <= 0;
|
||||
end else if (o_tready && i_tvalid && o_tlast) begin
|
||||
checksum <= 0;
|
||||
old_checksum <= 0;
|
||||
end else if (i_tready && i_tvalid && (state == IDLE)) begin
|
||||
checksum <= checksum + i_tdata[31:0] + i_tdata[63:32];
|
||||
old_checksum <= checksum;
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
checksum_error_reg <= checksum_error;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= IDLE;
|
||||
end else begin
|
||||
state <= next_state;
|
||||
end
|
||||
|
||||
always @(*) begin
|
||||
checksum_error = 0;
|
||||
case(state)
|
||||
//
|
||||
// Search for Escape sequence "0xDEADBEEFFEEDCAFE"
|
||||
// If ESC found don't pass data downstream but transition to next state.
|
||||
// else pass data downstream.
|
||||
//
|
||||
IDLE: begin
|
||||
o_tlast = 1'b0;
|
||||
if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid)
|
||||
begin
|
||||
next_state = EXTRACT1;
|
||||
o_tvalid = 1'b0;
|
||||
i_tready = 1'b1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
next_state = IDLE;
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
end // else: !if((i_tdata == 'hDEADBEEFFEEDCAFE) && i_tvalid)
|
||||
end // case: IDLE
|
||||
//
|
||||
// Look at next data. If it's a 0x1 then o_tlast should be asserted with next data word.
|
||||
// if it's 0x0 then it signals emulation of the Escape code in the original data stream
|
||||
// and we should just pass the next data word through unchanged with no o_tlast indication.
|
||||
//
|
||||
EXTRACT1: begin
|
||||
o_tvalid = 1'b0;
|
||||
i_tready = 1'b1;
|
||||
o_tlast = 1'b0;
|
||||
if (i_tvalid) begin
|
||||
if (i_tdata[31:0] == 'h1)
|
||||
begin
|
||||
if (old_checksum != i_tdata[63:32])
|
||||
checksum_error = 1'b1;
|
||||
next_state = EXTRACT2;
|
||||
end
|
||||
else // We assume emulation and don't look for illegal codes.
|
||||
begin
|
||||
next_state = EXTRACT3;
|
||||
end // else: !if(i_tdata == 'h1)
|
||||
end else begin // if (i_tvalid)
|
||||
next_state = EXTRACT1;
|
||||
end // else: !if(i_tvalid)
|
||||
end // case: EXTRACT1
|
||||
//
|
||||
// Assert o_tlast with data word.
|
||||
//
|
||||
EXTRACT2: begin
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
o_tlast = 1'b1;
|
||||
if (i_tvalid & o_tready)
|
||||
next_state = IDLE;
|
||||
else
|
||||
next_state = EXTRACT2;
|
||||
end
|
||||
//
|
||||
// Emulation, don't assert o_tlast with dataword.
|
||||
//
|
||||
EXTRACT3: begin
|
||||
o_tvalid = i_tvalid;
|
||||
i_tready = o_tready;
|
||||
o_tlast = 1'b0;
|
||||
if (i_tvalid & o_tready)
|
||||
next_state = IDLE;
|
||||
else
|
||||
next_state = EXTRACT2;
|
||||
end
|
||||
endcase // case(state)
|
||||
end
|
||||
|
||||
|
||||
|
||||
endmodule // axi_extract_tlast
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// Ultra fast critical path FIFO.
|
||||
// Only 2 entrys but no combinatorial feed through paths
|
||||
//
|
||||
|
||||
|
||||
module axi_fast_extract_tlast
|
||||
#(parameter WIDTH=64)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tlast,
|
||||
output reg o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [WIDTH:0] data_reg1, data_reg2;
|
||||
|
||||
reg [1:0] fifo_state;
|
||||
|
||||
localparam EMPTY = 0;
|
||||
localparam HALF = 1;
|
||||
localparam FULL = 2;
|
||||
|
||||
reg [1:0] extract_state;
|
||||
|
||||
localparam IDLE = 0;
|
||||
localparam EXTRACT1 = 1;
|
||||
localparam EXTRACT2 = 2;
|
||||
localparam EXTRACT3 = 3;
|
||||
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
fifo_state <= EMPTY;
|
||||
end else begin
|
||||
case (fifo_state)
|
||||
// Nothing in either register.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream has nothing to take from us.
|
||||
EMPTY: begin
|
||||
if ((extract_state == IDLE) && (i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
|
||||
// Embeded escpae code received.
|
||||
extract_state <= EXTRACT1;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
end else if ((extract_state == EXTRACT1) && i_tvalid) begin
|
||||
// Now work out if its a genuine embeded tlast or emulation.
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
if (i_tdata[31:0] == 'h1) begin
|
||||
extract_state <= EXTRACT2;
|
||||
end else begin
|
||||
extract_state <= EXTRACT3;
|
||||
end
|
||||
end else if ((extract_state == EXTRACT2) && i_tvalid) begin
|
||||
// Extract tlast.
|
||||
data_reg1 <= {1'b1,i_tdata};
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= HALF;
|
||||
extract_state <= IDLE;
|
||||
end else if (i_tvalid) begin
|
||||
// Get here both for normal data and for EXTRACT3 emulation data.
|
||||
data_reg1 <= {1'b0,i_tdata};
|
||||
fifo_state <= HALF;
|
||||
extract_state <= IDLE;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else begin
|
||||
// Nothing to do.
|
||||
fifo_state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
// First Register Full.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream can always read from us.
|
||||
HALF: begin
|
||||
if ((extract_state == IDLE) && (i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid) begin
|
||||
// Embeded escpae code received.
|
||||
extract_state <= EXTRACT1;
|
||||
if (o_tready) begin
|
||||
// If meanwhile we get read then go empty...
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
end else begin
|
||||
// ...else stay half full.
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end else if ((extract_state == EXTRACT1) && i_tvalid) begin
|
||||
// Now work out if its a genuine embeded tlast or emulation.
|
||||
if (i_tdata[31:0] == 'h1) begin
|
||||
extract_state <= EXTRACT2;
|
||||
end else begin
|
||||
extract_state <= EXTRACT3;
|
||||
end
|
||||
if (o_tready) begin
|
||||
// If meanwhile we get read then go empty...
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
fifo_state <= EMPTY;
|
||||
end else begin
|
||||
// ...else stay half full.
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end else if ((extract_state == EXTRACT2) && i_tvalid) begin
|
||||
// Extract tlast.
|
||||
data_reg1 <= {1'b1,i_tdata};
|
||||
extract_state <= IDLE;
|
||||
if (o_tready) begin
|
||||
// We get read and writen same cycle...
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= HALF;
|
||||
end else begin
|
||||
// ...or we get written and go full.
|
||||
data_reg2 <= data_reg1;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= FULL;
|
||||
end
|
||||
end else if (i_tvalid) begin
|
||||
// Get here both for normal data and for EXTRACT3 emulation data.
|
||||
data_reg1 <= {1'b0,i_tdata};
|
||||
extract_state <= IDLE;
|
||||
if (o_tready) begin
|
||||
// We get read and writen same cycle...
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else begin
|
||||
// ...or we get written and go full.
|
||||
data_reg2 <= data_reg1;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
fifo_state <= FULL;
|
||||
end
|
||||
end else if (o_tready) begin // if (i_tvalid)
|
||||
// Only getting read this cycle so go empty
|
||||
fifo_state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end else begin
|
||||
// Absolutley nothing happens, everything stays the same.
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end // case: HALF
|
||||
// Both Registers Full.
|
||||
// Upstream can not push to us in this fifo_state.
|
||||
// Downstream can always read from us.
|
||||
FULL: begin
|
||||
if (o_tready) begin
|
||||
fifo_state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
else begin
|
||||
fifo_state <= FULL;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end
|
||||
endcase // case(fifo_state)
|
||||
end // else: !if(reset | clear)
|
||||
|
||||
assign {o_tlast,o_tdata} = (fifo_state == FULL) ? data_reg2 : data_reg1;
|
||||
|
||||
|
||||
endmodule // axi_fast_fifo
|
||||
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Ultra fast critical path FIFO.
|
||||
// Only 2 entrys but no combinatorial feed through paths
|
||||
//
|
||||
|
||||
|
||||
module axi_fast_fifo
|
||||
#(parameter WIDTH=64)
|
||||
(
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
//
|
||||
input [WIDTH-1:0] i_tdata,
|
||||
input i_tvalid,
|
||||
output reg i_tready,
|
||||
//
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output reg o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
reg [WIDTH-1:0] data_reg1, data_reg2;
|
||||
|
||||
reg [1:0] state;
|
||||
|
||||
localparam EMPTY = 0;
|
||||
localparam HALF = 1;
|
||||
localparam FULL = 2;
|
||||
|
||||
always @(posedge clk)
|
||||
if (reset | clear) begin
|
||||
state <= EMPTY;
|
||||
data_reg1 <= 0;
|
||||
data_reg2 <= 0;
|
||||
o_tvalid <= 1'b0;
|
||||
i_tready <= 1'b0;
|
||||
|
||||
end else begin
|
||||
case (state)
|
||||
// Nothing in either register.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream has nothing to take from us.
|
||||
EMPTY: begin
|
||||
if (i_tvalid) begin
|
||||
data_reg1 <= i_tdata;
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else begin
|
||||
state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end
|
||||
end
|
||||
// First Register Full.
|
||||
// Upstream can always push data to us.
|
||||
// Downstream can always read from us.
|
||||
HALF: begin
|
||||
if (i_tvalid && o_tready) begin
|
||||
data_reg1 <= i_tdata;
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end else if (i_tvalid) begin
|
||||
data_reg1 <= i_tdata;
|
||||
data_reg2 <= data_reg1;
|
||||
state <= FULL;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
end else if (o_tready) begin
|
||||
state <= EMPTY;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b0;
|
||||
end else begin
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end // case: HALF
|
||||
// Both Registers Full.
|
||||
// Upstream can not push to us in this state.
|
||||
// Downstream can always read from us.
|
||||
FULL: begin
|
||||
if (o_tready) begin
|
||||
state <= HALF;
|
||||
i_tready <= 1'b1;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
else begin
|
||||
state <= FULL;
|
||||
i_tready <= 1'b0;
|
||||
o_tvalid <= 1'b1;
|
||||
end
|
||||
end
|
||||
endcase // case(state)
|
||||
end // else: !if(reset | clear)
|
||||
|
||||
assign o_tdata = (state == FULL) ? data_reg2 : data_reg1;
|
||||
|
||||
|
||||
endmodule // axi_fast_fifo
|
||||
@@ -0,0 +1,42 @@
|
||||
module axi_lite_slave
|
||||
(
|
||||
input aclk, // Global AXI clock
|
||||
input aresetn, // Global AXI reset, active low.
|
||||
//
|
||||
// AXI Write address channel
|
||||
//
|
||||
input [31 : 0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
|
||||
input [2 : 0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
input m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
|
||||
output m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Write data channel.
|
||||
//
|
||||
input [31 : 0] m_axi_wdata, // Write data
|
||||
input [3 : 0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
|
||||
input m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
|
||||
output m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
|
||||
//
|
||||
// AXI Write response channel signals
|
||||
//
|
||||
output [1 : 0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
|
||||
output m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
|
||||
input m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
|
||||
//
|
||||
// AXI Read address channel
|
||||
//
|
||||
input [31 : 0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
|
||||
input [2 : 0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
|
||||
input m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
|
||||
output m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
|
||||
//
|
||||
// AXI Read data channel
|
||||
//
|
||||
output [31 : 0] m_axi_rdata, // Read data.
|
||||
output [1 : 0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
|
||||
output m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
|
||||
input m_axi_rready, // Read ready. This signal indicates that the master can accept the read data and response
|
||||
//
|
||||
//
|
||||
//
|
||||
)
|
||||
Reference in New Issue
Block a user