1.简介
AXI4协议是ZYNQ中非常重要的一个协议,包括AXI4_FULL、AXI4_LITE、AXI4_Stream三大协议。是XILINX官方IP的重要接口以及PS、PL通讯的主要手段。
AXI4协议分为读写两类通道,这两类通道又分为地址通道和数据通道,另外写通道多一个写响应通道。所以AXI4的LITE和FULL都有五个通道。
通道信号的命名规则为xx_xx_xx。
第一个xx有S和M两种,M为Master表示AXI主机。S为Slave表示AXI从机。
第二个xx有axi和axis两种,axi表示该接口为axi_full或者lite。axis表示axi_stream。
第三个xx表示通道以及通道内的型号,前两个字母表示通道的简写,其中有r(read),w(write),a(addr),data这几个含义的简写。
axi_lite和axi_full最大的区别就是一个不支持突发一个支持突发。
axi和axis的区别就是一个带地址映射一个不带地址映射。
2.AXI4_LITE
AXI4_LITE一次只能传输一个数据,不支持突发
axi_slave内部的寻址方式:
当SLAVE接收到地址后,如何判断这是SLAVE内部的那个寄存器呢?当我们运用官方IP例化一个四个32位寄存器的SLAVE接口时,查看内部代码。
在寄存器的赋值语句块中方可以看到:
case中是主机发送的地址位。可以看到当位宽为32位四个寄存器时例化。SLAVE接口只认axi_addr第三位和第四位的地址。其余地址不认。OPT_MEM_ADDR_BITS=1代表有效的地址位只有两位,可以识别四个寄存器。ADDR_LSB表示从第三位开始有效,因为一个字节占就是axi_addr的1bit,32位有4字节。
这里贴出例化的axi_slave和改造后的axi_master,axi_test以及测试仿真模块。
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
以及
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 1;
`timescale 1 ns / 1 ps
module axi_master #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// The master requires a target slave base address.
// The master will initiate read and write transactions on the slave with base address specified here as a parameter.
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h00000000,
// Width of M_AXI address bus.
// The master generates the read and write addresses of width specified as C_M_AXI_ADDR_WIDTH.
parameter integer C_M_AXI_ADDR_WIDTH = 32,
// Width of M_AXI data bus.
// The master issues write data and accept read data where the width of the data bus is C_M_AXI_DATA_WIDTH
parameter integer C_M_AXI_DATA_WIDTH = 32
// and read transactions the master will perform as a part of this example memory test.
)
(
// Users to add ports here
input wire [C_M_AXI_ADDR_WIDTH-1: 0] wr_addr ,
input wire wr_start ,
input wire [C_M_AXI_ADDR_WIDTH-1 :0] wr_data ,
output reg wr_done ,
input wire [C_M_AXI_ADDR_WIDTH-1 :0] rd_addr ,
input wire rd_start ,
output reg [C_M_AXI_ADDR_WIDTH-1 :0] rd_data ,
output reg rd_done ,
// User ports ends
// Do not modify the ports beyond this line
// Initiate AXI transactions
// Asserts when ERROR is detected
// Asserts when AXI transactions is complete
// AXI clock signal
input wire M_AXI_ACLK,
// AXI active low reset signal
input wire M_AXI_ARESETN,
// Master Interface Write Address Channel ports. Write address (issued by master)
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR,
// Write channel 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.
output wire [2 : 0] M_AXI_AWPROT,
// Write address valid.
// This signal indicates that the master signaling valid write address and control information.
output wire M_AXI_AWVALID,
// Write address ready.
// This signal indicates that the slave is ready to accept an address and associated control signals.
input wire M_AXI_AWREADY,
// Master Interface Write Data Channel ports. Write data (issued by master)
output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_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.
output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB,
// Write valid. This signal indicates that valid write data and strobes are available.
output wire M_AXI_WVALID,
// Write ready. This signal indicates that the slave can accept the write data.
input wire M_AXI_WREADY,
// Master Interface Write Response Channel ports.
// This signal indicates the status of the write transaction.
input wire [1 : 0] M_AXI_BRESP,
// Write response valid.
// This signal indicates that the channel is signaling a valid write response
input wire M_AXI_BVALID,
// Response ready. This signal indicates that the master can accept a write response.
output wire M_AXI_BREADY,
// Master Interface Read Address Channel ports. Read address (issued by master)
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR,
// 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.
output wire [2 : 0] M_AXI_ARPROT,
// Read address valid.
// This signal indicates that the channel is signaling valid read address and control information.
output wire M_AXI_ARVALID,
// Read address ready.
// This signal indicates that the slave is ready to accept an address and associated control signals.
input wire M_AXI_ARREADY,
// Master Interface Read Data Channel ports. Read data (issued by slave)
input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA,
// Read response. This signal indicates the status of the read transfer.
input wire [1 : 0] M_AXI_RRESP,
// Read valid. This signal indicates that the channel is signaling the required read data.
input wire M_AXI_RVALID,
// Read ready. This signal indicates that the master can accept the read data and response information.
output wire M_AXI_RREADY
);
// function called clogb2 that returns an integer which has the
// value of the ceiling of the log base 2
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
// TRANS_NUM_BITS is the width of the index counter for
// number of write or read transaction.
// Example State machine to initialize counter, initialize write transactions,
// initialize read transactions and comparison of read data with the
// written data words.
reg [1:0] mst_exec_state;
// AXI4LITE signals
//write address valid
reg axi_awvalid;
//write data valid
reg axi_wvalid;
//read address valid
reg axi_arvalid;
//read data acceptance
reg axi_rready;
//write response acceptance
reg axi_bready;
//write address
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
//write data
reg [C_M_AXI_DATA_WIDTH-1 : 0] axi_wdata;
//read addresss
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
//Asserts when there is a write response error
wire write_resp_error;
//Asserts when there is a read response error
wire read_resp_error;
//A pulse to initiate a write transaction
reg start_single_write;
//A pulse to initiate a read transaction
reg start_single_read;
//Asserts when a single beat write transaction is issued and remains asserted till the completion of write trasaction.
reg write_issued;
//Asserts when a single beat read transaction is issued and remains asserted till the completion of read trasaction.
reg read_issued;
//flag that marks the completion of write trasactions. The number of write transaction is user selected by the parameter C_M_TRANSACTIONS_NUM.
reg writes_done;
//flag that marks the completion of read trasactions. The number of read transaction is user selected by the parameter C_M_TRANSACTIONS_NUM
reg reads_done;
//The error register is asserted when any of the write response error, read response error or the data mismatch flags are asserted.
reg error_reg;
//Expected read data used to compare with the read data.
//Flag marks the completion of comparison of the read data with the expected read data
// I/O Connections assignments
//Adding the offset address to the base addr of the slave
assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr;
//AXI 4 write data
assign M_AXI_WDATA = axi_wdata;
assign M_AXI_AWPROT = 3'b000;
assign M_AXI_AWVALID = axi_awvalid;
//Write Data(W)
assign M_AXI_WVALID = axi_wvalid;
//Set all byte strobes in this example
assign M_AXI_WSTRB = 4'b1111;
//Write Response (B)
assign M_AXI_BREADY = axi_bready;
//Read Address (AR)
assign M_AXI_ARADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_araddr;
assign M_AXI_ARVALID = axi_arvalid;
assign M_AXI_ARPROT = 3'b001;
//Read and Read Response (R)
assign M_AXI_RREADY = axi_rready;
//--------------------
//Write Address Channel
//--------------------
always @(posedge M_AXI_ACLK)
begin
//Only VALID signals must be deasserted during reset per AXI spec
//Consider inverting then registering active-low reset for higher fmax
if (M_AXI_ARESETN == 0)
begin
axi_awvalid <= 1'b0;
end
//Signal a new address/data command is available by user logic
else
begin
if (wr_start)
begin
axi_awvalid <= 1'b1;
end
//Address accepted by interconnect/slave (issue of M_AXI_AWREADY by slave)
else if (M_AXI_AWREADY && axi_awvalid)
begin
axi_awvalid <= 1'b0;
end
end
end
//--------------------
//Write Data Channel
//--------------------
//The write data channel is for transfering the actual data.
//The data generation is speific to the example design, and
//so only the WVALID/WREADY handshake is shown here
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_wvalid <= 1'b0;
end
//Signal a new address/data command is available by user logic
else if (wr_start)
begin
axi_wvalid <= 1'b1;
end
//Data accepted by interconnect/slave (issue of M_AXI_WREADY by slave)
else if (M_AXI_WREADY && axi_wvalid)
begin
axi_wvalid <= 1'b0;
end
end
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 )
begin
wr_done <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_WREADY && axi_wvalid)
begin
wr_done <= 1;
end
else
begin
wr_done <= 0;
end
end
//----------------------------
//Write Response (B) Channel
//----------------------------
//The write response channel provides feedback that the write has committed
//to memory. BREADY will occur after both the data and the write address
//has arrived and been accepted by the slave, and can guarantee that no
//other accesses launched afterwards will be able to be reordered before it.
//The BRESP bit [1] is used indicate any errors from the interconnect or
//slave for the entire write burst. This example will capture the error.
//While not necessary per spec, it is advisable to reset READY signals in
//case of differing reset latencies between master/slave.
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_bready <= 1'b0;
end
// accept/acknowledge bresp with axi_bready by the master
// when M_AXI_BVALID is asserted by slave
else if (M_AXI_BVALID && ~axi_bready)
begin
axi_bready <= 1'b1;
end
// deassert after one clock cycle
else if (axi_bready)
begin
axi_bready <= 1'b0;
end
// retain the previous value
else
axi_bready <= axi_bready;
end
//Flag write errors
assign write_resp_error = (axi_bready & M_AXI_BVALID & M_AXI_BRESP[1]);
//----------------------------
//Read Address Channel
//----------------------------
// A new axi_arvalid is asserted when there is a valid read address
// available by the master. start_single_read triggers a new read
// transaction
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_arvalid <= 1'b0;
end
//Signal a new read address command is available by user logic
else if (rd_start)
begin
axi_arvalid <= 1'b1;
end
//RAddress accepted by interconnect/slave (issue of M_AXI_ARREADY by slave)
else if (M_AXI_ARREADY && axi_arvalid)
begin
axi_arvalid <= 1'b0;
end
// retain the previous value
end
//--------------------------------
//Read Data (and Response) Channel
//--------------------------------
//The Read Data channel returns the results of the read request
//The master will accept the read data by asserting axi_rready
//when there is a valid read data available.
//While not necessary per spec, it is advisable to reset READY signals in
//case of differing reset latencies between master/slave.
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_rready <= 1'b0;
end
// accept/acknowledge rdata/rresp with axi_rready by the master
// when M_AXI_RVALID is asserted by slave
else if (M_AXI_RVALID && ~axi_rready)
begin
axi_rready <= 1'b1;
end
// deassert after one clock cycle
else if (axi_rready)
begin
axi_rready <= 1'b0;
end
// retain the previous value
end
//Flag write errors
assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]);
//--------------------------------
//User Logic
//--------------------------------
//Address/Data Stimulus
//Address/data pairs for this example. The read and write values should
//match.
//Modify these as desired for different address patterns.
//Write Addresses
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_awaddr <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (wr_start)
begin
axi_awaddr <= wr_addr;
end
end
// Write data generation
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_wdata <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (wr_start)
begin
axi_wdata <= wr_data;
end
end
//Read Addresses
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_araddr <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (rd_start)
begin
axi_araddr <= rd_addr;
end
end
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 )
begin
rd_data <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_RVALID && axi_rready)
begin
rd_data <= M_AXI_RDATA;
end
end
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 )
begin
rd_done <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_RVALID && axi_rready)
begin
rd_done <= 1;
end
else
begin
rd_done <= 0;
end
end
// Add user logic here
// User logic ends
endmodule
`timescale 1 ns / 1 ps
module axi_slave #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// 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 = 4
)
(
// Users to add ports here
// 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 (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel 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,
// Write address valid. This signal indicates that the master 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 (issued by master, acceped by Slave)
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 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,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// 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 (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// 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,
// Read 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 data (issued by slave)
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 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
);
// AXI4LITE signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
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_rvalid;
// Example-specific design signals
// 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 64 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32) + 1;
localparam integer OPT_MEM_ADDR_BITS = 1;
//----------------------------------------------
//-- Signals for user logic register space example
//------------------------------------------------
//-- Number of Slave Registers 4
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg0;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg1;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg2;
reg [C_S_AXI_DATA_WIDTH-1:0] slv_reg3;
wire slv_reg_rden;
wire slv_reg_wren;
reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
integer byte_index;
reg aw_en;
// 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_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RVALID = axi_rvalid;
// 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;
aw_en <= 1'b1;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// slave is ready to accept write address when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_awready <= 1'b1;
aw_en <= 1'b0;
end
else if (S_AXI_BREADY && axi_bvalid)
begin
aw_en <= 1'b1;
axi_awready <= 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;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
begin
// Write Address latching
axi_awaddr <= S_AXI_AWADDR;
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 && S_AXI_AWVALID && aw_en )
begin
// slave is ready to accept write data when
// there is a valid write address and write data
// on the write address and data bus. This design
// expects no outstanding transactions.
axi_wready <= 1'b1;
end
else
begin
axi_wready <= 1'b0;
end
end
end
// Implement memory mapped register select and write logic generation
// The write data is accepted and written to memory mapped registers when
// axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
// select byte enables of slave registers while writing.
// These registers are cleared when reset (active low) is applied.
// Slave register write enable is asserted when valid address and data are available
// and the slave is ready to accept the write address and write data.
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
end
else begin
if (slv_reg_wren)
begin
case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 0
slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h1:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 1
slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h2:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 2
slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
2'h3:
for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )
if ( S_AXI_WSTRB[byte_index] == 1 ) begin
// Respective byte enables are asserted as per write strobes
// Slave register 3
slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];
end
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
slv_reg2 <= slv_reg2;
slv_reg3 <= slv_reg3;
end
endcase
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;
end
else
begin
if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
begin
// indicates a valid write response is available
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0; // 'OKAY' response
end // work error responses in future
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_araddr <= 32'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID)
begin
// indicates that the slave has acceped the valid read address
axi_arready <= 1'b1;
// Read address latching
axi_araddr <= S_AXI_ARADDR;
end
else
begin
axi_arready <= 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_arready && S_AXI_ARVALID && ~axi_rvalid)
begin
// Valid read data is available at the read data bus
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0; // 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
// Read data is accepted by the master
axi_rvalid <= 1'b0;
end
end
end
// Implement memory mapped register select and read logic generation
// Slave register read enable is asserted when valid address is available
// and the slave is ready to accept the read address.
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= slv_reg1;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
// Output register or memory read data
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rdata <= 0;
end
else
begin
// When there is a valid read address (S_AXI_ARVALID) with
// acceptance of read address by the slave (axi_arready),
// output the read dada
if (slv_reg_rden)
begin
axi_rdata <= reg_data_out; // register read data
end
end
end
// Add user logic here
// User logic ends
endmodule
`timescale 1ns / 1ps
module axi_test(
input wire clk ,
input wire rst_n ,
input wire wr_done ,
output wire [31:0] wr_addr ,
output wire wr_start ,
output wire [31:0] wr_data ,
input wire rd_done ,
input wire [31:0] rd_data ,
output wire [31:0] rd_addr ,
output wire rd_start
);
parameter CNT_MAX = 100;
reg [10:0] cnt ;
always @(posedge clk or negedge rst_n) begin
if (rst_n == 0) begin
cnt <= 0;
end
else if (cnt == CNT_MAX) begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
assign wr_start = cnt == CNT_MAX;
assign rd_start = cnt == CNT_MAX;
assign wr_addr = 2<<2;
assign rd_addr = 2<<2;
assign wr_data = 25236;
endmodule
`timescale 1ns / 1ps
module tb_top();
reg clk_100M ;
reg rst_n ;
always #5 clk_100M = !clk_100M;
initial begin
clk_100M = 0;
rst_n = 0;
#200
rst_n = 1;
#10000
$stop;
end
design_1_wrapper u_design_1_wrapper(
.clk_100M (clk_100M ),
.rst_n (rst_n )
);
endmodule
连接如图
仿真后的波形如下图,写数据的过程如下图
读数据的过程如下图:
3.AXI_FULL
这里先把改造后的源码贴出来,改造后提供突发的写起始地址、写开始信号、以及写数据和写数据有效信号,读信号同理。
`timescale 1 ns / 1 ps
module AXI_Master_Full #
(
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000 ,
parameter integer C_M_AXI_BURST_LEN = 16 ,
parameter integer C_M_AXI_ID_WIDTH = 1 ,
parameter integer C_M_AXI_ADDR_WIDTH = 32 ,
parameter integer C_M_AXI_DATA_WIDTH = 32 ,
parameter integer C_M_AXI_AWUSER_WIDTH = 0 ,
parameter integer C_M_AXI_ARUSER_WIDTH = 0 ,
parameter integer C_M_AXI_WUSER_WIDTH = 0 ,
parameter integer C_M_AXI_RUSER_WIDTH = 0 ,
parameter integer C_M_AXI_BUSER_WIDTH = 0
)
(
// Users to add ports here
input wire [C_M_AXI_DATA_WIDTH-1 : 0] wr_data ,
input wire wr_start ,
output wire wr_data_vaild ,
input wire [C_M_AXI_ADDR_WIDTH-1 : 0] wr_addr ,
output wire wr_done ,
output reg wr_mux ,
output wire [C_M_AXI_DATA_WIDTH-1 : 0] rd_data ,
input wire rd_start ,
output wire rd_data_vaild ,
input wire [C_M_AXI_ADDR_WIDTH-1 : 0] rd_addr ,
output wire rd_done ,
output reg rd_mux ,
// User ports ends
// Do not modify the ports beyond this line
input wire M_AXI_ACLK ,
input wire M_AXI_ARESETN ,
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID ,
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR ,
output wire [7 : 0] M_AXI_AWLEN ,
output wire [2 : 0] M_AXI_AWSIZE ,
output wire [1 : 0] M_AXI_AWBURST ,
output wire M_AXI_AWLOCK ,
output wire [3 : 0] M_AXI_AWCACHE ,
output wire [2 : 0] M_AXI_AWPROT ,
output wire [3 : 0] M_AXI_AWQOS ,
output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER ,
output wire M_AXI_AWVALID ,
input wire M_AXI_AWREADY ,
output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA ,
output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB ,
output wire M_AXI_WLAST ,
output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER ,
output wire M_AXI_WVALID ,
input wire M_AXI_WREADY ,
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID ,
input wire [1 : 0] M_AXI_BRESP ,
input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER ,
input wire M_AXI_BVALID ,
output wire M_AXI_BREADY ,
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID ,
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR ,
output wire [7 : 0] M_AXI_ARLEN ,
output wire [2 : 0] M_AXI_ARSIZE ,
output wire [1 : 0] M_AXI_ARBURST ,
output wire M_AXI_ARLOCK ,
output wire [3 : 0] M_AXI_ARCACHE ,
output wire [2 : 0] M_AXI_ARPROT ,
output wire [3 : 0] M_AXI_ARQOS ,
output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER ,
output wire M_AXI_ARVALID ,
input wire M_AXI_ARREADY ,
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID ,
input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA ,
input wire [1 : 0] M_AXI_RRESP ,
input wire M_AXI_RLAST ,
input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER ,
input wire M_AXI_RVALID ,
output wire M_AXI_RREADY
);
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
localparam integer C_TRANSACTIONS_NUM = clogb2(C_M_AXI_BURST_LEN-1);
localparam integer C_MASTER_LENGTH = 12;
localparam integer C_NO_BURSTS_REQ = C_MASTER_LENGTH-clogb2((C_M_AXI_BURST_LEN*C_M_AXI_DATA_WIDTH/8)-1);
wire [C_TRANSACTIONS_NUM+2 : 0] burst_size_bytes;
wire start_single_burst_write;
wire start_single_burst_read;
assign burst_size_bytes = C_M_AXI_BURST_LEN * C_M_AXI_DATA_WIDTH/8;
//Write Address Channel
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_awaddr ;
reg axi_awvalid ;
assign M_AXI_AWID = 'd0 ;
assign M_AXI_AWADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_awaddr ;
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN - 1 ;
assign M_AXI_AWSIZE = clogb2((C_M_AXI_DATA_WIDTH/8)-1) ;
assign M_AXI_AWBURST = 2'b01 ;
assign M_AXI_AWLOCK = 'd0 ;
assign M_AXI_AWCACHE = 4'b0010 ;
assign M_AXI_AWPROT = 'd0 ;
assign M_AXI_AWQOS = 'd0 ;
assign M_AXI_AWUSER = 'd0 ;
assign M_AXI_AWVALID = axi_awvalid ;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0)
axi_awvalid <= 1'b0;
else if (~axi_awvalid && start_single_burst_write)
axi_awvalid <= 1'b1;
else if (M_AXI_AWREADY && axi_awvalid)
axi_awvalid <= 1'b0;
else
axi_awvalid <= axi_awvalid;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0)
axi_awaddr <= 'b0;
else if (start_single_burst_write)
axi_awaddr <= wr_addr;
else
axi_awaddr <= axi_awaddr;
//Write Data Channel
reg axi_wlast ;
reg axi_wvalid ;
reg [C_TRANSACTIONS_NUM : 0] write_index ;
wire [C_M_AXI_DATA_WIDTH-1 : 0] axi_wdata ;
wire wnext ;
assign M_AXI_WSTRB = {(C_M_AXI_DATA_WIDTH/8){1'b1}} ;
assign M_AXI_WUSER = 'd0 ;
assign wnext = M_AXI_WREADY & axi_wvalid ;
assign M_AXI_WDATA = axi_wdata ;
assign M_AXI_WLAST = axi_wlast ;
assign M_AXI_WVALID = axi_wvalid ;
assign axi_wdata = wr_data;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0 )
axi_wvalid <= 1'b0;
else if (~axi_wvalid && start_single_burst_write)
axi_wvalid <= 1'b1;
else if (wnext && axi_wlast)
axi_wvalid <= 1'b0;
else
axi_wvalid <= axi_wvalid;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0)
axi_wlast <= 1'b0;
else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))
axi_wlast <= 1'b1;
else if (wnext)
axi_wlast <= 1'b0;
else if (axi_wlast && C_M_AXI_BURST_LEN == 1)
axi_wlast <= 1'b0;
else
axi_wlast <= axi_wlast;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0 || start_single_burst_write == 1'b1)
write_index <= 0;
else if (wnext && (write_index != C_M_AXI_BURST_LEN-1))
write_index <= write_index + 1;
else
write_index <= write_index;
//Write Response (B) Channel
reg axi_bready ;
assign M_AXI_BREADY = axi_bready;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0)
axi_bready <= 1'b0;
else if (M_AXI_BVALID && ~axi_bready)
axi_bready <= 1'b1;
else if (axi_bready)
axi_bready <= 1'b0;
else
axi_bready <= axi_bready;
//Read Address Channel
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_araddr ;
reg axi_arvalid ;
assign M_AXI_ARID = 'b0 ;
assign M_AXI_ARADDR = C_M_TARGET_SLAVE_BASE_ADDR + axi_araddr ;
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN - 1 ;
assign M_AXI_ARSIZE = clogb2((C_M_AXI_DATA_WIDTH/8)-1) ;
assign M_AXI_ARBURST = 2'b01 ;
assign M_AXI_ARLOCK = 1'b0 ;
assign M_AXI_ARCACHE = 4'b0010 ;
assign M_AXI_ARPROT = 3'h0 ;
assign M_AXI_ARQOS = 4'h0 ;
assign M_AXI_ARUSER = 'b1 ;
assign M_AXI_ARVALID = axi_arvalid ;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0)
axi_arvalid <= 1'b0;
else if (~axi_arvalid && start_single_burst_read)
axi_arvalid <= 1'b1;
else if (M_AXI_ARREADY && axi_arvalid)
axi_arvalid <= 1'b0;
else
axi_arvalid <= axi_arvalid;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0)
axi_araddr <= 'b0;
else if (start_single_burst_read)
axi_araddr <= rd_addr;
else
axi_araddr <= axi_araddr;
//Read Data (and Response) Channel
reg axi_rready ;
reg [C_TRANSACTIONS_NUM : 0] read_index ;
wire rnext ;
assign M_AXI_RREADY = axi_rready ;
assign rnext = M_AXI_RVALID && axi_rready ;
assign M_AXI_RUSER = 'd0 ;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0 || start_single_burst_read)
read_index <= 0;
else if (rnext && (read_index != C_M_AXI_BURST_LEN-1))
read_index <= read_index + 1;
else
read_index <= read_index;
always @(posedge M_AXI_ACLK)
if (M_AXI_ARESETN == 0 )
axi_rready <= 1'b0;
else if (M_AXI_RVALID)
if (M_AXI_RLAST && axi_rready)
axi_rready <= 1'b0;
else
axi_rready <= 1'b1;
// Add user logic here
reg wr_start_r ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] wr_addr_r ;
reg [C_M_AXI_DATA_WIDTH-1 : 0] wr_data_r ;
reg axi_rready_r;
reg rd_start_r ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] rd_addr_r ;
always @(posedge M_AXI_ACLK)begin
if(M_AXI_ARESETN == 0)begin
wr_start_r <= 1'b0 ;
rd_start_r <= 1'b0 ;
wr_addr_r <= 0 ;
rd_addr_r <= 0 ;
axi_rready_r<= 1'b0 ;
end
else begin
wr_start_r <= wr_start ;
rd_start_r <= rd_start ;
wr_addr_r <= wr_addr ;
rd_addr_r <= rd_addr ;
axi_rready_r<= axi_rready ;
end
end
assign start_single_burst_write = {wr_start_r,wr_start} == 2'b01;
assign wr_data_vaild = wnext ;
assign wr_done = axi_bready;
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 0)
wr_mux <= 1'b0 ;
else if(wr_done)
wr_mux <= 1'b0 ;
else if(wr_start)
wr_mux <= 1'b1 ;
assign start_single_burst_read = {rd_start_r,rd_start} == 2'b01;
assign rd_data_vaild = rnext ;
assign rd_data = M_AXI_RDATA;
assign rd_done = {axi_rready_r,axi_rready} == 2'b10;
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 0)
rd_mux <= 1'b0 ;
else if(rd_done)
rd_mux <= 1'b0 ;
else if(rd_start)
rd_mux <= 1'b1 ;
// User logic ends
endmodule