AXI4_full QSPI - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
qspi2的封装(axi4_full)
- 将axi4_full的memory设置为2048 bytes,以满足我们最大需要400x3=1200 bytes的需要
`timescale 1 ns / 1 ps
module qspi_axifull_v1_0_S00_AXI #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Width of ID for for write address, write data, read address and read data
parameter integer C_S_AXI_ID_WIDTH = 1,
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = 11,
// Width of optional user defined signal in write address channel
parameter integer C_S_AXI_AWUSER_WIDTH = 0,
// Width of optional user defined signal in read address channel
parameter integer C_S_AXI_ARUSER_WIDTH = 0,
// Width of optional user defined signal in write data channel
parameter integer C_S_AXI_WUSER_WIDTH = 0,
// Width of optional user defined signal in read data channel
parameter integer C_S_AXI_RUSER_WIDTH = 0,
// Width of optional user defined signal in write response channel
parameter integer C_S_AXI_BUSER_WIDTH = 0
)
(
// Users to add ports here
output csx,
output sck,
output [3:0] dout,
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write Address ID
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
// Write address
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
input wire [7 : 0] S_AXI_AWLEN,
// Burst size. This signal indicates the size of each transfer in the burst
input wire [2 : 0] S_AXI_AWSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
input wire [1 : 0] S_AXI_AWBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_AWLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_AWCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Quality of Service, QoS identifier sent for each
// write transaction.
input wire [3 : 0] S_AXI_AWQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
input wire [3 : 0] S_AXI_AWREGION,
// Optional User-defined signal in the write address channel.
input wire [C_S_AXI_AWUSER_WIDTH-1 : 0] S_AXI_AWUSER,
// Write address valid. This signal indicates that
// the channel is signaling valid write address and
// control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_AWREADY,
// Write Data
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte
// lanes hold valid data. There is one write strobe
// bit for each eight bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write last. This signal indicates the last transfer
// in a write burst.
input wire S_AXI_WLAST,
// Optional User-defined signal in the write data channel.
input wire [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Response ID tag. This signal is the ID tag of the
// write response.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Optional User-defined signal in the write response channel.
output wire [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER,
// Write response valid. This signal indicates that the
// channel is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address ID. This signal is the identification
// tag for the read address group of signals.
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
// Read address. This signal indicates the initial
// address of a read burst transaction.
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
input wire [7 : 0] S_AXI_ARLEN,
// Burst size. This signal indicates the size of each transfer in the burst
input wire [2 : 0] S_AXI_ARSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
input wire [1 : 0] S_AXI_ARBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_ARLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_ARCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Quality of Service, QoS identifier sent for each
// read transaction.
input wire [3 : 0] S_AXI_ARQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
input wire [3 : 0] S_AXI_ARREGION,
// Optional User-defined signal in the read address channel.
input wire [C_S_AXI_ARUSER_WIDTH-1 : 0] S_AXI_ARUSER,
// Write address valid. This signal indicates that
// the channel is signaling valid read address and
// control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_ARREADY,
// Read ID tag. This signal is the identification tag
// for the read data group of signals generated by the slave.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
// Read Data
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of
// the read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read last. This signal indicates the last transfer
// in a read burst.
output wire S_AXI_RLAST,
// Optional User-defined signal in the read address channel.
output wire [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER,
// Read valid. This signal indicates that the channel
// is signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// AXI4FULL signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg [C_S_AXI_BUSER_WIDTH-1 : 0] axi_buser;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rlast;
reg [C_S_AXI_RUSER_WIDTH-1 : 0] axi_ruser;
reg axi_rvalid;
// aw_wrap_en determines wrap boundary and enables wrapping
wire aw_wrap_en;
// ar_wrap_en determines wrap boundary and enables wrapping
wire ar_wrap_en;
// aw_wrap_size is the size of the write transfer, the
// write address wraps to a lower address if upper address
// limit is reached
wire [31:0] aw_wrap_size ;
// ar_wrap_size is the size of the read transfer, the
// read address wraps to a lower address if upper address
// limit is reached
wire [31:0] ar_wrap_size ;
// The axi_awv_awr_flag flag marks the presence of write address valid
reg axi_awv_awr_flag;
//The axi_arv_arr_flag flag marks the presence of read address valid
reg axi_arv_arr_flag;
// The axi_awlen_cntr internal write address counter to keep track of beats in a burst transaction
reg [7:0] axi_awlen_cntr;
//The axi_arlen_cntr internal read address counter to keep track of beats in a burst transaction
reg [7:0] axi_arlen_cntr;
reg [1:0] axi_arburst;
reg [1:0] axi_awburst;
reg [7:0] axi_arlen;
reg [7:0] axi_awlen;
//local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
//ADDR_LSB is used for addressing 32/64 bit registers/memories
//ADDR_LSB = 2 for 32 bits (n downto 2)
//ADDR_LSB = 3 for 42 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32)+ 1;
localparam integer OPT_MEM_ADDR_BITS = 8;
localparam integer USER_NUM_MEM = 1;
//----------------------------------------------
//-- Signals for user logic memory space example
//------------------------------------------------
wire [OPT_MEM_ADDR_BITS:0] mem_address;
wire [USER_NUM_MEM-1:0] mem_select;
reg [C_S_AXI_DATA_WIDTH-1:0] mem_data_out[0 : USER_NUM_MEM-1];
genvar i;
genvar j;
genvar mem_byte_index;
// I/O Connections assignments
assign S_AXI_AWREADY = axi_awready;
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BRESP = axi_bresp;
assign S_AXI_BUSER = axi_buser;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RLAST = axi_rlast;
assign S_AXI_RUSER = axi_ruser;
assign S_AXI_RVALID = axi_rvalid;
assign S_AXI_BID = S_AXI_AWID;
assign S_AXI_RID = S_AXI_ARID;
assign aw_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_awlen));
assign ar_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_arlen));
assign aw_wrap_en = ((axi_awaddr & aw_wrap_size) == aw_wrap_size)? 1'b1: 1'b0;
assign ar_wrap_en = ((axi_araddr & ar_wrap_size) == ar_wrap_size)? 1'b1: 1'b0;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
axi_awv_awr_flag <= 1'b0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
// slave is ready to accept an address and
// associated control signals
axi_awready <= 1'b1;
axi_awv_awr_flag <= 1'b1;
// used for generation of bresp() and bvalid
end
else if (S_AXI_WLAST && axi_wready)
// preparing to accept next address after current write burst tx completion
begin
axi_awv_awr_flag <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
axi_awlen_cntr <= 0;
axi_awburst <= 0;
axi_awlen <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)
begin
// address latching
axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_awburst <= S_AXI_AWBURST;
axi_awlen <= S_AXI_AWLEN;
// start address of transfer
axi_awlen_cntr <= 0;
end
else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)
begin
axi_awlen_cntr <= axi_awlen_cntr + 1;
case (axi_awburst)
2'b00: // fixed burst
// The write address for all the beats in the transaction are fixed
begin
axi_awaddr <= axi_awaddr;
//for awsize = 4 bytes (010)
end
2'b01: //incremental burst
// The write address for all the beats in the transaction are increments by awsize
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//awaddr aligned to 4 byte boundary
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The write address wraps when the address reaches wrap boundary
if (aw_wrap_en)
begin
axi_awaddr <= (axi_awaddr - aw_wrap_size);
end
else
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//for awsize = 4 bytes (010)
end
endcase
end
end
end
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
begin
// slave can accept the write data
axi_wready <= 1'b1;
end
//else if (~axi_awv_awr_flag)
else if (S_AXI_WLAST && axi_wready)
begin
axi_wready <= 1'b0;
end
end
end
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
axi_buser <= 0;
end
else
begin
if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
begin
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0;
// 'OKAY' response
end
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_arv_arr_flag <= 1'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
axi_arready <= 1'b1;
axi_arv_arr_flag <= 1'b1;
end
else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
// preparing to accept next address after current read completion
begin
axi_arv_arr_flag <= 1'b0;
end
else
begin
axi_arready <= 1'b0;
end
end
end
// Implement axi_araddr latching
//This process is used to latch the address when both
//S_AXI_ARVALID and S_AXI_RVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_araddr <= 0;
axi_arlen_cntr <= 0;
axi_arburst <= 0;
axi_arlen <= 0;
axi_rlast <= 1'b0;
axi_ruser <= 0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
begin
// address latching
axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_arburst <= S_AXI_ARBURST;
axi_arlen <= S_AXI_ARLEN;
// start address of transfer
axi_arlen_cntr <= 0;
axi_rlast <= 1'b0;
end
else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)
begin
axi_arlen_cntr <= axi_arlen_cntr + 1;
axi_rlast <= 1'b0;
case (axi_arburst)
2'b00: // fixed burst
// The read address for all the beats in the transaction are fixed
begin
axi_araddr <= axi_araddr;
//for arsize = 4 bytes (010)
end
2'b01: //incremental burst
// The read address for all the beats in the transaction are increments by awsize
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The read address wraps when the address reaches wrap boundary
if (ar_wrap_en)
begin
axi_araddr <= (axi_araddr - ar_wrap_size);
end
else
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
//for arsize = 4 bytes (010)
end
endcase
end
else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )
begin
axi_rlast <= 1'b1;
end
else if (S_AXI_RREADY)
begin
axi_rlast <= 1'b0;
end
end
end
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arv_arr_flag && ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0;
// 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
axi_rvalid <= 1'b0;
end
end
end
// ------------------------------------------
// -- Example code to access user logic memory region
// ------------------------------------------
generate
if (USER_NUM_MEM >= 1)
begin
assign mem_select = 1;
assign mem_address = (axi_arv_arr_flag? axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:(axi_awv_awr_flag? axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:0));
end
endgenerate
// implement Block RAM(s)
generate
for(i=0; i<= USER_NUM_MEM-1; i=i+1)
begin:BRAM_GEN
wire mem_rden;
wire mem_wren;
assign mem_wren = axi_wready && S_AXI_WVALID ;
assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid
for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)
begin:BYTE_BRAM_GEN
wire [8-1:0] data_in ;
wire [8-1:0] data_out;
reg [8-1:0] byte_ram [0 : 511];
integer j;
//assigning 8 bit data
assign data_in = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];
assign data_out = byte_ram[mem_address];
always @( posedge S_AXI_ACLK )
begin
if(xfer_final)
BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[3][0]<=1; //reg3.bit0
if(xfer_rdy)
BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[3][1]<=1; //reg3.bit1
if (mem_wren && S_AXI_WSTRB[mem_byte_index])
begin
byte_ram[mem_address] <= data_in;
end
end
always @( posedge S_AXI_ACLK )
begin
if (mem_rden)
begin
mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;
end
end
end
end
endgenerate
//Output register or memory read data
always @( mem_data_out, axi_rvalid)
begin
if (axi_rvalid)
begin
// Read address mux
axi_rdata <= mem_data_out[0];
end
else
begin
axi_rdata <= 32'h00000000;
end
end
// Add user logic here
wire [7:0] cmd;
wire [23:0] addr;
reg [0:9600-1] din;
wire [10:0] vadid_length; /* Max suppport DATA_SIZE=2047*8=16376 */
wire [1:0] data_mode; /*00: cmd, 01: cmd+addr, 10: cmd+addr+1wire data, 11: cmd+addr+4wire data*/
wire cs_mode; /*IDLE state, 0: cs=0, 1:cs=1, to support multi data_bursts while keep cs=0*/
wire [31:0] reg0,reg1,reg2,reg3;
assign reg0={BRAM_GEN[0].BYTE_BRAM_GEN[3].byte_ram[0],
BRAM_GEN[0].BYTE_BRAM_GEN[2].byte_ram[0],
BRAM_GEN[0].BYTE_BRAM_GEN[1].byte_ram[0],
BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[0]};
assign reg1={BRAM_GEN[0].BYTE_BRAM_GEN[3].byte_ram[1],
BRAM_GEN[0].BYTE_BRAM_GEN[2].byte_ram[1],
BRAM_GEN[0].BYTE_BRAM_GEN[1].byte_ram[1],
BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[1]};
assign reg2={BRAM_GEN[0].BYTE_BRAM_GEN[3].byte_ram[2],
BRAM_GEN[0].BYTE_BRAM_GEN[2].byte_ram[2],
BRAM_GEN[0].BYTE_BRAM_GEN[1].byte_ram[2],
BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[2]};
assign reg3={BRAM_GEN[0].BYTE_BRAM_GEN[3].byte_ram[3],
BRAM_GEN[0].BYTE_BRAM_GEN[2].byte_ram[3],
BRAM_GEN[0].BYTE_BRAM_GEN[1].byte_ram[3],
BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[3]};
assign {addr,cmd}=reg0;
assign vadid_length=reg1[26:16];
assign {cs_mode,data_mode}=reg1[2:0];
assign xfer_start=axi_wready && S_AXI_WVALID && (mem_address==2);
integer k;
always @(*)
for(k=0; k<300; k=k+1) begin
din[(32*k+0) +:8]= BRAM_GEN[0].BYTE_BRAM_GEN[3].byte_ram[k+4];
din[(32*k+8) +:8]= BRAM_GEN[0].BYTE_BRAM_GEN[2].byte_ram[k+4];
din[(32*k+16) +:8]= BRAM_GEN[0].BYTE_BRAM_GEN[1].byte_ram[k+4];
din[(32*k+24) +:8]= BRAM_GEN[0].BYTE_BRAM_GEN[0].byte_ram[k+4];
end
qspi2 #(.DATA_SIZE(9600)) qspi2_inst(
// Input Ports - Single Bit
.clk (S_AXI_ACLK),
.rst_n (S_AXI_ARESETN),
.cs_mode (cs_mode),
.xfer_start (xfer_start),
// Input Ports - Busses
.addr (addr),
.cmd (cmd),
.data_mode (data_mode),
.din (din),
.vadid_length (vadid_length),
// Output Ports - Single Bit
.csx (csx),
.sck (sck),
.xfer_final (xfer_final),
.xfer_rdy (xfer_rdy),
// Output Ports - Busses
.dout (dout)
// InOut Ports - Single Bit
// InOut Ports - Busses
);
// User logic ends
endmodule
VIP验证
- 说明,这个testbench是,先写入2564=1024bytes数据到ram,再继续写入444=176bytes数据到ram。
- 然后,用burst_write启动qspi传输。burst_write一次性写入了3个寄存器:reg0, reg1, reg2,把qspi的所有参数都配置好了。传输长度为1200
- 于是,qspi启动传输,一次性将1200bytes传输过去。整个过程正确。
- 接着,我们执行了2次burst读。一次读取256*4=1024bytes,再继续读取(44+4)*4=192 bytes。注意,我们把reg0/1/2/3和data_ram全都读出来了。
- 总之,执行了3次burst写,2次burst读
`timescale 1ns / 1ps
`include "qspi_axifull_v1_0_tb_include.svh"
import axi_vip_pkg::*;
import qspi_axifull_v1_0_bfm_1_master_0_0_pkg::*;
module qspi_axifull_v1_0_tb();
xil_axi_uint error_cnt = 0;
xil_axi_uint comparison_cnt = 0;
axi_transaction wr_transaction;
axi_transaction rd_transaction;
axi_monitor_transaction mst_monitor_transaction;
axi_monitor_transaction master_moniter_transaction_queue[$];
xil_axi_uint master_moniter_transaction_queue_size =0;
axi_monitor_transaction mst_scb_transaction;
axi_monitor_transaction passthrough_monitor_transaction;
axi_monitor_transaction passthrough_master_moniter_transaction_queue[$];
xil_axi_uint passthrough_master_moniter_transaction_queue_size =0;
axi_monitor_transaction passthrough_mst_scb_transaction;
axi_monitor_transaction passthrough_slave_moniter_transaction_queue[$];
xil_axi_uint passthrough_slave_moniter_transaction_queue_size =0;
axi_monitor_transaction passthrough_slv_scb_transaction;
axi_monitor_transaction slv_monitor_transaction;
axi_monitor_transaction slave_moniter_transaction_queue[$];
xil_axi_uint slave_moniter_transaction_queue_size =0;
axi_monitor_transaction slv_scb_transaction;
xil_axi_uint mst_agent_verbosity = 0;
xil_axi_uint slv_agent_verbosity = 0;
xil_axi_uint passthrough_agent_verbosity = 0;
bit clock;
bit reset;
integer result_slave;
bit [31:0] S00_AXI_test_data[3:0];
localparam LC_AXI_BURST_LENGTH = 8;
localparam LC_AXI_DATA_WIDTH = 32;
task automatic COMPARE_DATA;
input [(LC_AXI_BURST_LENGTH * LC_AXI_DATA_WIDTH)-1:0]expected;
input [(LC_AXI_BURST_LENGTH * LC_AXI_DATA_WIDTH)-1:0]actual;
begin
if (expected === 'hx || actual === 'hx) begin
$display("TESTBENCH ERROR! COMPARE_DATA cannot be performed with an expected or actual vector that is all 'x'!");
result_slave = 0; $stop;
end
if (actual != expected) begin
$display("TESTBENCH ERROR! Data expected is not equal to actual.", " expected = 0x%h",expected, " actual = 0x%h",actual);
result_slave = 0;
// $stop;
end
else
begin
$display("TESTBENCH Passed! Data expected is equal to actual.",
" expected = 0x%h",expected, " actual = 0x%h",actual);
end
end
endtask
integer i;
integer j;
xil_axi_uint trans_cnt_before_switch = 48;
xil_axi_uint passthrough_cmd_switch_cnt = 0;
event passthrough_mastermode_start_event;
event passthrough_mastermode_end_event;
event passthrough_slavemode_end_event;
xil_axi_uint mtestID;
xil_axi_ulong mtestADDR;
xil_axi_len_t mtestBurstLength;
xil_axi_size_t mtestDataSize;
xil_axi_burst_t mtestBurstType;
xil_axi_lock_t mtestLOCK;
xil_axi_cache_t mtestCacheType = 0;
xil_axi_prot_t mtestProtectionType = 3'b000;
xil_axi_region_t mtestRegion = 4'b000;
xil_axi_qos_t mtestQOS = 4'b000;
xil_axi_data_beat dbeat;
xil_axi_data_beat [255:0] mtestWUSER;
xil_axi_data_beat mtestAWUSER = 'h0;
xil_axi_data_beat mtestARUSER = 0;
xil_axi_data_beat [255:0] mtestRUSER;
xil_axi_uint mtestBUSER = 0;
xil_axi_resp_t mtestBresp;
xil_axi_resp_t[255:0] mtestRresp;
bit [32767:0] mtestWDataF;//ms
bit [32767:0] mtestRDataF;//ms
axi_transaction pss_wr_transaction;
axi_transaction pss_rd_transaction;
axi_transaction reactive_transaction;
axi_transaction rd_payload_transaction;
axi_transaction wr_rand;
axi_transaction rd_rand;
axi_transaction wr_reactive;
axi_transaction rd_reactive;
axi_transaction wr_reactive2;
axi_transaction rd_reactive2;
axi_ready_gen bready_gen;
axi_ready_gen rready_gen;
axi_ready_gen awready_gen;
axi_ready_gen wready_gen;
axi_ready_gen arready_gen;
axi_ready_gen bready_gen2;
axi_ready_gen rready_gen2;
axi_ready_gen awready_gen2;
axi_ready_gen wready_gen2;
axi_ready_gen arready_gen2;
xil_axi_payload_byte data_mem[xil_axi_ulong];
qspi_axifull_v1_0_bfm_1_master_0_0_mst_t mst_agent_0;
`BD_WRAPPER DUT(
.ARESETN(reset),
.ACLK(clock)
);
initial begin
mst_agent_0 = new("master vip agent",DUT.`BD_INST_NAME.master_0.inst.IF);//ms
mst_agent_0.vif_proxy.set_dummy_drive_type(XIL_AXI_VIF_DRIVE_NONE);
mst_agent_0.set_agent_tag("Master VIP");
mst_agent_0.set_verbosity(mst_agent_verbosity);
mst_agent_0.start_master();
$timeformat (-12, 1, " ps", 1);
end
initial begin
reset <= 1'b0;
#20000ns;
reset <= 1'b1;
repeat (5) @(negedge clock);
end
always #5 clock <= ~clock;
initial begin
S_AXI_TEST ( );
#1ns;
$finish;
end
task automatic S_AXI_TEST;
begin
#1;
for(int i = 0; i < 8; i++) begin
mtestWDataF[i*32+:32] = i+1;
end
$display("Sequential write transfers example similar to AXI BFM WRITE_BURST method starts");
mtestID = 0;
mtestADDR = 0;
mtestBurstLength = 7;
mtestDataSize = xil_axi_size_t'(xil_clog2(32/8));
mtestBurstType = XIL_AXI_BURST_TYPE_INCR;
mtestLOCK = XIL_AXI_ALOCK_NOLOCK;
mtestCacheType = 0;
mtestProtectionType = 0;
mtestRegion = 0;
mtestQOS = 0;
result_slave = 1;
for(int i = 0; i < 256;i++) begin
mtestWUSER = 'h0;
end
mst_agent_0.AXI4_WRITE_BURST( /* first write data*/
mtestID,
4*4/*mtestADDR*/,
255/*mtestBurstLength*/,
mtestDataSize,
mtestBurstType,
mtestLOCK,
mtestCacheType,
mtestProtectionType,
mtestRegion,
mtestQOS,
mtestAWUSER,
{32'hAB00AB00,{254{32'h12345678}},32'h00ED00ED} /*mtestWDataF*/,
mtestWUSER,
mtestBresp
);
mst_agent_0.AXI4_WRITE_BURST( /* first write data*/
mtestID,
4*4+256*4/*mtestADDR*/,
44/*mtestBurstLength*/,
mtestDataSize,
mtestBurstType,
mtestLOCK,
mtestCacheType,
mtestProtectionType,
mtestRegion,
mtestQOS,
mtestAWUSER,
{32'hAB00AB00,{42{32'h12345678}},32'h00ED00ED} /*mtestWDataF*/,
mtestWUSER,
mtestBresp
);
mst_agent_0.AXI4_WRITE_BURST(
mtestID,
0/*mtestADDR*/,
3/*mtestBurstLength*/,
mtestDataSize,
mtestBurstType,
mtestLOCK,
mtestCacheType,
mtestProtectionType,
mtestRegion,
mtestQOS,
mtestAWUSER,
128'h00000000_00000000_04b00007_003600de/*mtestWDataF*/, /*cmd=0xde, addr=0x003600, data_mode=2'b11, cs_mode=1, length=0x4b0=1200*/
mtestWUSER,
mtestBresp
);
#50000;
mst_agent_0.AXI4_READ_BURST (
mtestID,
0/*mtestADDR*/,
255/*mtestBurstLength*/,
mtestDataSize,
mtestBurstType,
mtestLOCK,
mtestCacheType,
mtestProtectionType,
mtestRegion,
mtestQOS,
mtestARUSER,
mtestRDataF,
mtestRresp,
mtestRUSER
);
mst_agent_0.AXI4_READ_BURST (
mtestID,
256*4/*mtestADDR*/,
44+4/*mtestBurstLength*/,
mtestDataSize,
mtestBurstType,
mtestLOCK,
mtestCacheType,
mtestProtectionType,
mtestRegion,
mtestQOS,
mtestARUSER,
mtestRDataF,
mtestRresp,
mtestRUSER
);
$display("Sequential write transfers example similar to AXI BFM WRITE_BURST method completes");
$display("Sequential read transfers example similar to AXI BFM READ_BURST method starts");
// mtestID = 0;
// mtestADDR = 0;
// mtestBurstLength = 7;
// mtestDataSize = xil_axi_size_t'(xil_clog2(32/8));
// mtestBurstType = XIL_AXI_BURST_TYPE_INCR;
// mtestLOCK = XIL_AXI_ALOCK_NOLOCK;
// mtestCacheType = 0;
// mtestProtectionType = 0;
// mtestRegion = 0;
// mtestQOS = 0;
// mst_agent_0.AXI4_READ_BURST (
// mtestID,
// mtestADDR,
// mtestBurstLength,
// mtestDataSize,
// mtestBurstType,
// mtestLOCK,
// mtestCacheType,
// mtestProtectionType,
// mtestRegion,
// mtestQOS,
// mtestARUSER,
// mtestRDataF,
// mtestRresp,
// mtestRUSER
// );
$display("Sequential read transfers example similar to AXI BFM READ_BURST method completes");
COMPARE_DATA(mtestWDataF,mtestRDataF);
$display("Sequential read transfers example similar to AXI VIP READ_BURST method completes");
$display("---------------------------------------------------------");
$display("EXAMPLE TEST S00_AXI: PTGEN_TEST_FINISHED!");
if ( result_slave ) begin
$display("PTGEN_TEST: PASSED!");
end else begin
$display("PTGEN_TEST: FAILED!");
end
$display("---------------------------------------------------------");
end
endtask
endmodule
关键波形
在100MHz时钟下,3次burst写入只开销了3.275us,qspi传输1200bytes开销了48.68us。可见burst写入效率是极其高的,占的时间开销忽略不计。
qspi的时钟是系统时钟的2分频,因此是50MHz。
每次burst写,只需要指定长度和首地址。
qspi的参数配置与启动过程