【AXI总线专题】-AXI-LITE总线解读
参考手册
《3-2-03米联客2022版AXI4总线专题-20211123.pdf》
《IHI0022E_amba_axi_and_ace_protocol_spec.pdf》
内容已包含在下载文件中
1.axi-lite概述
AXI-LITE总线只支持单次突发,也就是说每次传输只能传一个数据。另外,传输的数据位宽只支持32bit或者64bit
2.信号定义
Write address channel
AWPORT
一般默认为0,定义为:
output wire [2 : 0] M_AXI_AWPROT,
Write data channel
这里的WSTRB
类似与掩码。如果总线位宽为32bit,则WSTRB[3:0]=4’b1111,表示32bit位宽的数据每一位都是有效的。
Write response channel
BRESP反馈信号的值,返回值为0表示OKAY;注意这里的valid的方向,是从机到主机,和上面的写地址通道以及写数据通道是反方向的。
Read address channel
Read data channel
3.测试
使用代码手动实现一个axi-lite-master的功能。用户可以通过该代码,对axi-lite-slave进行读写操作。
需要注意的是,该代码中使用了fifo对数据进行缓存。目的是axi-lite只支持单次突发,也就是一次只能传输一个数据。但是用户侧如果在执行写操作的时候,往往会一次性写入很多数据,这里就需要通过fifo执行缓存,数据先写入fifo,然后再慢慢通过axi-lite发送出去。
axi-lite-slave使用官方生成的代码。
仿真tb代码参考如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2025/05/07 15:25:31
// Design Name:
// Module Name: sim_top_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module sim_top_tb();
reg clk,rst;
initial begin
rst = 0;
#100;
@(posedge clk)rst = 1;
end
always begin
clk = 0;
#10;
clk = 1;
#10;
end
parameter P_ADDR_WIDTH = 32;
parameter P_DATA_WIDTH = 32;
wire M_AXI_ACLK ;
wire M_AXI_ARESETN ;
wire [P_ADDR_WIDTH - 1 : 0] M_AXI_AWADDR ;
wire [2 :0] M_AXI_AWPROT ;
wire M_AXI_AWVALID ;
wire M_AXI_AWREADY ;
wire [P_DATA_WIDTH - 1 : 0] M_AXI_WDATA ;
wire [P_DATA_WIDTH/8 - 1 : 0] M_AXI_WSTRB ;
wire M_AXI_WVALID ;
wire M_AXI_WREADY ;
wire [1 :0] M_AXI_BRESP ;
wire M_AXI_BVALID ;
wire M_AXI_BREADY ;
wire [P_ADDR_WIDTH - 1 : 0] M_AXI_ARADDR ;
wire [2 :0] M_AXI_ARPROT ;
wire M_AXI_ARVALID ;
wire M_AXI_ARREADY ;
wire [P_DATA_WIDTH - 1 : 0] M_AXI_RDATA ;
wire [1 :0] M_AXI_RRESP ;
wire M_AXI_RVALID ;
wire M_AXI_RREADY ;
reg [P_ADDR_WIDTH - 1:0] ri_write_addr ;
reg [P_DATA_WIDTH - 1:0] ri_write_data ;
reg ri_write_valid ;
reg [P_ADDR_WIDTH - 1:0] ri_read_addr ;
reg ri_read_valid ;
wire [P_DATA_WIDTH - 1:0] w_read_data ;
wire w_read_data_valid ;
assign M_AXI_ACLK = clk;
assign M_AXI_ARESETN = rst;
//assign M_AXI_WSTRB = 1;
AXI_LITE_Master#(
.P_ADDR_WIDTH (P_ADDR_WIDTH ) ,
.P_DATA_WIDTH (P_DATA_WIDTH ) //axi-lite总线支持32bit和64bit两种位宽
)
AXI_LITE_Master_u0
(
/*----axi lite----*/
.M_AXI_ACLK (M_AXI_ACLK ),
.M_AXI_ARESETN (M_AXI_ARESETN ),
.M_AXI_AWADDR (M_AXI_AWADDR ),
.M_AXI_AWPROT (M_AXI_AWPROT ),
.M_AXI_AWVALID (M_AXI_AWVALID ),
.M_AXI_AWREADY (M_AXI_AWREADY ),
.M_AXI_WDATA (M_AXI_WDATA ),
.M_AXI_WSTRB (M_AXI_WSTRB ),//M_AXI_WSTRB
.M_AXI_WVALID (M_AXI_WVALID ),
.M_AXI_WREADY (M_AXI_WREADY ),
.M_AXI_BRESP (M_AXI_BRESP ),
.M_AXI_BVALID (M_AXI_BVALID ),
.M_AXI_BREADY (M_AXI_BREADY ),
.M_AXI_ARADDR (M_AXI_ARADDR ),
.M_AXI_ARPROT (M_AXI_ARPROT ),
.M_AXI_ARVALID (M_AXI_ARVALID ),
.M_AXI_ARREADY (M_AXI_ARREADY ),
.M_AXI_RDATA (M_AXI_RDATA ),
.M_AXI_RRESP (M_AXI_RRESP ),
.M_AXI_RVALID (M_AXI_RVALID ),
.M_AXI_RREADY (M_AXI_RREADY ),
/*----user prot----*/
/*为什么分axi接口和用()户接口。这里的是将用户自定义接口转成axi接口,这样就可以和axi接口的其他IP直接相连*/
.i_write_addr (ri_write_addr ),
.i_write_data (ri_write_data ),
.i_write_valid (ri_write_valid ),
.i_read_addr (ri_read_addr ),
.i_read_valid (ri_read_valid ),
.o_read_data (w_read_data ),
.o_read_data_valid (w_read_data_valid)
);
axi_lite_slave_v1_0_S00_AXI #
(
.C_S_AXI_DATA_WIDTH (P_DATA_WIDTH),
.C_S_AXI_ADDR_WIDTH (P_ADDR_WIDTH)
)
axi_lite_slave_v1_0_S00_AXI_u0
(
.S_AXI_ACLK (M_AXI_ACLK ),
.S_AXI_ARESETN (M_AXI_ARESETN ),
.S_AXI_AWADDR (M_AXI_AWADDR ),
.S_AXI_AWPROT (M_AXI_AWPROT ),
.S_AXI_AWVALID (M_AXI_AWVALID ),
.S_AXI_AWREADY (M_AXI_AWREADY ),
.S_AXI_WDATA (M_AXI_WDATA ),
.S_AXI_WSTRB (M_AXI_WSTRB ),
.S_AXI_WVALID (M_AXI_WVALID ),
.S_AXI_WREADY (M_AXI_WREADY ),
.S_AXI_BRESP (M_AXI_BRESP ),
.S_AXI_BVALID (M_AXI_BVALID ),
.S_AXI_BREADY (M_AXI_BREADY ),
.S_AXI_ARADDR (M_AXI_ARADDR ),
.S_AXI_ARPROT (M_AXI_ARPROT ),
.S_AXI_ARVALID (M_AXI_ARVALID ),
.S_AXI_ARREADY (M_AXI_ARREADY ),
.S_AXI_RDATA (M_AXI_RDATA ),
.S_AXI_RRESP (M_AXI_RRESP ),
.S_AXI_RVALID (M_AXI_RVALID ),
.S_AXI_RREADY (M_AXI_RREADY )
);
task write_data(input [31:0] addr,input [31:0]data);
begin:write_task
ri_write_addr <= 'd0;
ri_write_data <= 'd0;
ri_write_valid <= 'd0;
@(posedge clk);
ri_write_addr <= addr;
ri_write_data <= data;
ri_write_valid <= 'd1;
@(posedge clk);
ri_write_addr <= 'd0;
ri_write_data <= 'd0;
ri_write_valid <= 'd0;
@(posedge clk);
end
endtask
task read_data(input [31:0] addr);
begin:read_task
ri_read_addr <= 'd0;
ri_read_valid <= 'd0;
@(posedge clk);
ri_read_addr <= addr;
ri_read_valid <= 'd1;
@(posedge clk);
ri_read_addr <= 'd0;
ri_read_valid <= 'd0;
@(posedge clk);
end
endtask
initial
begin
ri_write_addr = 0;
ri_write_data = 0;
ri_write_valid = 0;
ri_read_addr = 0;
ri_read_valid = 0;
wait(rst);//wait后面的括号中填的是退出等待的条件
repeat(10);
write_data(10,100);//这边的代码是顺序执行的,先写再读
read_data(10);
fork
write_data(1,99);//使用fork需要注意的是,这两句代码是并行执行,不是顺序执行的
read_data(1);//也就是同时执行写和读的操作
join
end
endmodule
4.仿真波形
下图所示,是在tb中执行用户端口信号的操作
首先是往地址为10的空间写入数据100;然后再执行读地址100处的数据,这里就有先写后读的顺序
其次是往地址为1的空间写入数据99;同时执行读操作。这里的写和读就是同时进行的
用户侧的逻辑代码,到axi这里实现的效果如下。
在上面我们执行往地址1处写入数据99的操作,同时执行读操作。到axi这里就进行了仲裁,把原来同时进行的读写,这里进行了变换,可以看出,先往地址1上写入数据99;然后再执行了读的操作。