【AXI总线专题】AXI-FULL-Master
概述
参考文档:
《3-2-03米联客2022版AXI4总线专题-20211123.pdf》
《IHI0022E_amba_axi_and_ace_protocol_spec.pdf》
1.axi-full概述
和axi-lite差不多,基本信号都差不多,就是多了几个信号。主要是因为axi-full支持突发传输,axi4支持最大256次的突发传输。
2.信号定义
3.测试
测试工程通过手动编写一个axi-full-master的模块,和slave进行通信,测试并查看仿真波形。其中的slave模式使用官方直接生产的模块。
如下图所示,一共是两个模块,一个master一个slave。我们直接把这两个模块添加到block design中,省去了例化一个个填信号的步骤。然后直接在tb文件中直接例化block design就可以了。
tb文件示例代码:
c
module sim_tb_top();
reg clk,rst;
initial begin
rst = 0;
#100;
@(posedge clk)rst = 1;
end
always begin
clk = 0;
#10;
clk = 1;
#10;
end
/*直接例化的block design,就一个时钟和一个复位信号,其他都是内部信号*/
system_wrapper system_wrapper_u0
(
.M_AXI_ACLK_0 (clk) ,
.M_AXI_ARESETN_0 (rst)
);
endmodule
4.仿真波形
如下图所示,图中列出了写地址,写数据,写响应的时序波形。
在写地址过程中,因为是突发传输,这个过程只需要写入突发传输的起始地址,以及突发长度就行,因此这里的valid和ready信号持续的信号周期很短。
在写数据过程中,数据一直处于突发传输过程,此时的valid和ready信号需要一直保持为高电平。
last信号是和最后一个数据对齐,也就是说,在传输最后一个数据的过程中,就应该将last信号拉高,表明此时正在传输最后一个数据。
写响应阶段,是在所有数据都写入完成后,才产生valid和ready信号。
下图是读数据阶段。
程序设计的流程是先写完成后再读数据。因此在上面写响应完成后,开始一段读操作。读地址阶段的时序和写地址的时序一样。重点看下读数据阶段。
master将M_AXI_RREADY拉高后,就可以接收slave的数据了。接着slave拉高M_AXI_RVALID信号,可以看到,slave并不是一直拉高valid信号的,而是呈现脉冲式的拉高,所以接收到的数据中间都会有无效数据,就是这里的0。当然这里的0会被忽略,因为有效数据是在valid和ready同时有效的时候才正确被接收。
同样,last信号也是在接收最后一个数据的时候同时被拉高。
下图是整个程序流程的总览,即实现的写数据读数据的不断循环。整个过程读写都可以正常进行。
5.附录
clogb2函数
功能:位宽计算函数
例如,要将8个数据存到fifo中,那么fifo的深度是8,就是从0~7,这里的最大值是7,对应的就是3'b111;地址位宽就是3,这个函数就用来计算这个3。
可以应用的场景,比如在逻辑中需要点亮LED灯,每隔500ms翻转一次,常规操作如下:
c
// Add user logic here
//ref_clk=50M cnt_number=25,000,000
reg [24:0] cnt_500ms;
always@(posedge clk or negedge rst)
if(!rst)
cnt_500ms <= 'b0;
else if(cnt_500ms == 'd24_999_999)
cnt_500ms <= 1'b0;
else
cnt_500ms <= cnt_500ms + 1'b1;
always @(posedge clk or negedge rst) begin
if(!rst)
led <= 1'b1;
else if(cnt_500ms == 'd24_999_999)
led <= ~led;
end
如上所示,这里需要定义一个cnt_500ms 的变量,用来计数,通过计算,我们得知,这个变量需要计数25000000 次才是500ms,那么这个变量在定义的时候就要确定它的位宽,也就是上面计算的[24:0],就是25bit。通常都是要借助程序员计算器来计算这个位宽值。
现在有了这个函数就可以直接使用这个函数来计算了
示例如下:
c
reg [clogb2(24999999):0] cnt_500ms;
参考链接:
Verilog定义计算位宽的函数clogb2
axi4中的一些参数解释
参考链接:[axi][学习笔记]s_axi_awlen/s_axi_awsize 和s_axi_wdata关系 这个里面解释了比较容易混淆的awlen和awsize等的关系。
M_AXI_AWLEN
:axi-full是突发传输的模式,一次突发传输可以传输多个数据,个数为1~256;最多传输256个数据,这个值就是用来设置一次突发传输的数据个数。
M_AXI_AWSIZE
:突发传输过程中,每个传输的数据的大小(以字节为单位)。这是一个3位的信号。支持的数据的大小为1、2、4、8、16、32、64、128;那么对应关系就是
000:1
001:2
010:4
011:8
100:16
101:32
110:64
111:128
这个值和数据位宽是有关系的,数据位宽就是下面显示的M_AXI_DATA_WIDTH ;假设数据位宽是128,那么传输一个128bit的数据就是传输16个字节,也就是突发传输过程中的数据的大小是16字节,对应到M_AXI_AWSIZE 的值就是100;
使用举例:
parameter integer C_M_AXI_DATA_WIDTH = 32;
assign M_AXI_AWSIZE = clogb2((C_M_AXI_DATA_WIDTH/8)-1);//这里就是使用位宽计算公式,计算出来是010;对应上面就是4字节,也就是32位的位宽
M_AXI_WDATA
:这个是需要传输的数据,这个数据在定义的时候是需要定义位宽的,比如
c
output wire [M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA ,
wishbone总线
参考链接:CPU设计实战-Wishbone总线接口
为什么有个逻辑工程中会使用到axi_to_wishbone?我们分析一下下面这个模块,就是将axi总线转换成wishbone总线,目的就是为了简化接口的操作,因为axi总线的接口比较多。
c
module axi_to_wishbone(
input aclk,
input aresetn,
//wishbone signals
output reg [31:0] wb_addr_o,
input [31:0] wb_dat_i,
output reg [31:0] wb_dat_o,
output reg wb_we_o,//读写使能信号,低表示读操作,高表示写操作
output reg wb_stb_o,//总线选用信号,有效时代表发起一次总线操作
output wb_cyc_o,//总线周期信号,代表总线正在被占用,在总线使用时须持续有效
input wb_ack_i,//操作结束信号,操作结束给一个有效位
//axi signals
input [31:0] m_axi_araddr,
input [1:0] m_axi_arburst,
input [3:0] m_axi_arcache,
input [7:0] m_axi_arlen,
input [0:0] m_axi_arlock,
input [2:0] m_axi_arprot,
input [3:0] m_axi_arqos,
output reg m_axi_arready,
input [3:0] m_axi_arregion,
input [2:0] m_axi_arsize,
input m_axi_arvalid,
input [31:0] m_axi_awaddr,
input [1:0] m_axi_awburst,
input [3:0] m_axi_awcache,
input [7:0] m_axi_awlen,
input [0:0] m_axi_awlock,
input [2:0] m_axi_awprot,
input [3:0] m_axi_awqos,
output reg m_axi_awready,
input [3:0] m_axi_awregion,
input [2:0] m_axi_awsize,
input m_axi_awvalid,
input m_axi_bready,
output [1:0] m_axi_bresp,
output reg m_axi_bvalid,
output reg [31:0] m_axi_rdata,
output reg m_axi_rlast,
input m_axi_rready,
output [1:0] m_axi_rresp,
output reg m_axi_rvalid,
input [31:0] m_axi_wdata,
input m_axi_wlast,
output reg m_axi_wready,
input [3:0] m_axi_wstrb,
input m_axi_wvalid
);
c
axi_to_wishbone host_to_wb(
//clock and reset
.aclk (sys_clk_i),
.aresetn (sys_rst_n_i),
//wishbone signals
.wb_addr_o (host_wb_addr),
.wb_dat_i (host_wb_dat_o ),
.wb_dat_o (host_wb_dat_i ),
.wb_we_o (host_wb_we),
.wb_stb_o (host_wb_stb),
.wb_cyc_o (host_wb_cyc),
.wb_ack_i (host_wb_ack),
//AXI signals
.m_axi_araddr (reg_m_axi_araddr ),
.m_axi_arburst (reg_m_axi_arburst ),
.m_axi_arcache (reg_m_axi_arcache ),
.m_axi_arlen (reg_m_axi_arlen ),
.m_axi_arlock (reg_m_axi_arlock ),
.m_axi_arprot (reg_m_axi_arprot ),
.m_axi_arqos (reg_m_axi_arqos ),
.m_axi_arready (reg_m_axi_arready ),
.m_axi_arregion (reg_m_axi_arregion),
.m_axi_arsize (reg_m_axi_arsize ),
.m_axi_arvalid (reg_m_axi_arvalid ),
.m_axi_awaddr (reg_m_axi_awaddr ),
.m_axi_awburst (reg_m_axi_awburst ),
.m_axi_awcache (reg_m_axi_awcache ),
.m_axi_awlen (reg_m_axi_awlen ),
.m_axi_awlock (reg_m_axi_awlock ),
.m_axi_awprot (reg_m_axi_awprot ),
.m_axi_awqos (reg_m_axi_awqos ),
.m_axi_awready (reg_m_axi_awready ),
.m_axi_awregion (reg_m_axi_awregion),
.m_axi_awsize (reg_m_axi_awsize ),
.m_axi_awvalid (reg_m_axi_awvalid ),
.m_axi_bready (reg_m_axi_bready ),
.m_axi_bresp (reg_m_axi_bresp ),
.m_axi_bvalid (reg_m_axi_bvalid ),
.m_axi_rdata (reg_m_axi_rdata ),
.m_axi_rlast (reg_m_axi_rlast ),
.m_axi_rready (reg_m_axi_rready ),
.m_axi_rresp (reg_m_axi_rresp ),
.m_axi_rvalid (reg_m_axi_rvalid ),//block design中的glbreg_m_axi这个slave接口的地址已经分配好了,在sdk中使用Xil_Out();函数就可以和这个slave通信了。
.m_axi_wdata (reg_m_axi_wdata ),//这个从外面(block_design中)写进来的数据,即glbreg_m_axi_wdata,这个axi是从机;写进来后给axi_to_wishbone,转换到成host_wb_dat_i,从axi_to_wishbone模块输出
.m_axi_wlast (reg_m_axi_wlast ),//再给wb_bus_switch;在wb_bus_switch中,根据不同的通道地址,将这个数据再转给ch1_wb_dat_o~ch5_wb_dat_o输出出去。ch1_wb_dat_o对应的是glbreg_wb_dat_i;
.m_axi_wready (reg_m_axi_wready ),//glbreg_wb_dat_i这个数据再进入到global_register这个模块中,在这个模块中,将这个数据赋值给不同的寄存器,用来配置逻辑的工作模式。
.m_axi_wstrb (reg_m_axi_wstrb ),
.m_axi_wvalid (reg_m_axi_wvalid )//同上,wb_bus_switch中的ch1_wb_dat_i~ch5_wb_dat_i先传给host_wb_dat_o;然后这个数据再进入到axi_to_wishbone的wb_dat_i
//在axi_to_wishbone中传给m_axi_rdata,就是axi从机的输出数据接口,给axi主机读。
);