AXI lite 总线源码时碰见未见过的运算符,所以搜索记录下,此运算符在for语句用得多。
https://zhuanlan.zhihu.com/p/77528158
https://zhuanlan.zhihu.com/p/145936888
以上链接为AXI lite的代码分析
“+:”、"-:"语法
运算符,运用在多位的变量中,如下:
slv_reg0[(byte_index8) +: 8] <= S_AXI_WDATA[(byte_index8) +: 8];
"+:"
变量[起始地址 +: 数据位宽]
等价于
变量[(起始地址+数据位宽-1):起始地址]
data[0 +: 8] //等价于--> data[7:0]
data[15 +: 2] //等价于--> data[16:15]
"-:"
变量[结束地址 -: 数据位宽]
等价于
变量[结束地址:(结束地址-数据位宽+1)]
data[7 -: 8] //等价于--> data[7:0]
data[15 -: 2] //等价于--> data[15:14]
//
由FDMA代码里的AWSIZE引出是基本概念
参考:
https://zhuanlan.zhihu.com/p/96804919
AXI FULL、AXI Lite、AXI Stream
AXI FULL传输数据是全双工的。也就是说。读写是同时进行的。
一条AXI总线上有5个通道。两个用于读。三个用于写。
AXI是支持多个数据的。这种传输模式叫突发传输。至于为什么叫突发而不叫连续,自己品。
一个突发传输是靠几个信号来描述的。这些信号同在AXI总线里。
Burst length: ARLEN[7:0]和AWLEN[7:0]表示的就是这个。。。表示的是连续传输的周期数。一个burst内部是不可以被打断的。。所以data valid一旦高起来就要把一个burst传完再拉低,slave的ready信号可以中断,但是最终还是要高起来传完一个burst. 对于master来说,一旦开始传数,一个burst之间的valid应该是不会低的。
Burst size:
指的是 每一拍含有的 Byte数,而不是bit数,所以总线32位,每次只能传输32bit burst size = 32,也就是AWSIZE[2:0] = b101,那么每一拍含有 32 x8 bits
指的是一个burst里面有多少Byte. ARSIZE[2:0]和AWSIZE[2:0] 里就是这个。**为什么只有3bit呢?因为只有8种情况。1,2,4,8,16,32,64,128。(对应arsize/awsize的0,1,2,3,4,5,6,7)**这个里面其实有个非常小的问题。。一般来说。总线位宽和burst size一致的。。比如总线64bit, burst size是8Byte. 但是你要说我头铁非要给个不一样的值。。那也没问题。你总线是8BTYE, 你给了个2BYTE的Burst size, 那你就要指定,每次传输这个2BYTE要放到8BYTE的哪几个BTYE上去。所以没事儿别折腾自己。
那你说我非要折腾自己呢?也行。这种传输叫narrow transfers。往哪个地方上写,可以用WSTRB[N]这歌信号控制。数据会写到WDATA[(8n)+7: (8n)]
比如上面这个例子,32bit的地址总线。burst size给了个8bit. 那就靠WSTRB这个信号确定往哪儿写。比如上面。传输了5次。可以控制数据往不同地方写。
Burst type: 这个有三种。FIXED表示你一直往初始地址猛怼。INCR表示你从初始地址开始累加。WRAP表示加到某个值后返回初始地址。
另外要说的一点是。AXI是支持非对齐传输的。
例如上面这种情况。如果地址从7开始。传输5次。前三个BYTE是无效的,不会被传输。用WSTRB信号就可保证这一点。keep信号表示的是BTYE是否有效。
AXI-Lite:
AXI-LITE是Burst-length严格定于1, burst size严格定于总线位宽的AXI.
最明显的阉割就是不支持burst length, 只能一个数据一个数据读写,读写的位宽和总线位宽一致的。其他阉割可以自己看文档。
AXI-STREAM:
顾名思义,是stream。流的意思。视频流,数据流什么的。AXI-STREAM和AXI之间的关系不像是相互阉割的关系。而是各有所长。当然,他们用的握手协议还是一样的。
AXI-STREAM相比于AXI最显著的特点是,总线上没有数目。只用TLAST表示传输结束。这样导致AXI-STREAM的信号非常简单。
TLAST 由于总线上没有传输数目。所以最后一个数据时TLAST会高起,表明传输完成了。
TKEEP 这是一个多比特的信号。比如总线8个Byte. 这个信号8bit. 每一个bit对应的是总线上对应的BYTE是不是有效的。
TSTRB 这也是一个多比特信号。也是说明总线上的数据是不是有效的。
那这个地方你可能会问,TKEEP和TSTSTRB到底有啥区别?
区别是这样的。TKEEP要是为0。代表了这个数据完全没用,可以被扔掉了。
在TKEEP为1的前提下,表示这个信号不能扔掉。那TSTRB就起作用了。TSTRB为1,表示对应的数据有效,是个好数据。TSTRB为0时,表示这个是个占位数据,没啥意义,但是不能丢掉。为什么需要占位数据呢?是因为有时候需要AXISTREAM传输的数据队形不能乱。就像下图对应的情况。
其他细节处查手册
总结
AXI-FULL作用是给定地址与传输数量,进行burst传输。
AXI-LITE作用是给定地址,单个数据的读写。
AXI-STREAM作用是不给地址,不给数量,像水管一样靠last这个阀门传输数据。
//
FDMA代码分析AXI总线
`timescale 1 ns / 1 ps
/*
Company : Liyang Milian Electronic Technology Co., Ltd.
Brand: 米联客(msxbo)
Technical forum:uisrc.com
taobao: osrc.taobao.com
Create Date: 2019/12/17
Module Name: uiFDMA
Description:
Copyright: Copyright (c) msxbo
Revision: 1.0
Signal description:
1) _i input
2) _o output
3) _n activ low
4) _dg debug signal
5) _r delay or register
6) _s state mechine
*/
module uiFDMA#
(
parameter integer C_M_AXI_BURST_LEN = 64 ,
parameter integer C_M_AXI_ID_WIDTH = 1 ,
parameter integer C_M_AXI_ID = 0 ,
parameter integer C_M_AXI_ADDR_WIDTH = 32 ,
parameter integer C_M_AXI_DATA_WIDTH = 32
)
(
//user logic
input wire pkg_wr_areq ,
output wire pkg_wr_last ,
input wire [C_M_AXI_DATA_WIDTH-1 :0] pkg_wr_data ,
output wire pkg_wr_en ,
input wire [C_M_AXI_ADDR_WIDTH-1 :0] pkg_wr_addr ,
input wire [C_M_AXI_ADDR_WIDTH-1 :0] pkg_wr_size ,
input wire pkg_rd_areq ,
output wire pkg_rd_last ,
output wire [C_M_AXI_DATA_WIDTH-1 :0] pkg_rd_data ,
output wire pkg_rd_en ,
input wire [C_M_AXI_ADDR_WIDTH-1 :0] pkg_rd_addr ,
input wire [C_M_AXI_ADDR_WIDTH-1 :0] pkg_rd_size ,
//input wire INIT_AXI_TXN ,
input wire M_AXI_ACLK ,
input wire M_AXI_ARESETN ,
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID ,//写地址ID,用于写地址信号组的标记
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 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 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 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 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 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 BURST_SIZE = C_M_AXI_BURST_LEN * C_M_AXI_DATA_WIDTH/8;
// AXI4LITE signals
//AXI4 internal temp signals
//write
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_awaddr ;
reg axi_awvalid ;
wire [C_M_AXI_DATA_WIDTH-1 : 0] axi_wdata ;
reg axi_wlast ;
reg axi_wvalid ;
//read
reg [C_M_AXI_ADDR_WIDTH-1 : 0] axi_araddr ;
reg axi_arvalid ;
reg axi_rready ;
// wire [C_TRANSACTIONS_NUM+2 : 0] burst_size_bytes;
assign M_AXI_AWID = C_M_AXI_ID;
assign M_AXI_AWADDR = 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 = 1'b0;
assign M_AXI_AWCACHE = 4'b0010;
assign M_AXI_AWPROT = 3'h0;
assign M_AXI_AWQOS = 4'h0;
assign M_AXI_AWVALID = axi_awvalid;
assign M_AXI_WDATA = axi_wdata;
assign M_AXI_WSTRB = {(C_M_AXI_DATA_WIDTH/8){1'b1}};
assign M_AXI_WLAST = axi_wlast;
assign M_AXI_WVALID = axi_wvalid;
assign M_AXI_BREADY = axi_bready;
assign M_AXI_ARID = C_M_AXI_ID;
assign M_AXI_ARADDR = 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_ARVALID = axi_arvalid;
assign M_AXI_RREADY = axi_rready;
reg [7 :0 ] w_word_cnt ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] WR_BASE_ADDR ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] RD_BASE_ADDR ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] wr_data_cnt ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] rd_data_cnt ;
reg w_cycle_flag ;
reg r_cycle_flag ;
reg read_data_flag;
wire w_next = (axi_wvalid && M_AXI_WREADY);
wire r_next = (M_AXI_RVALID && axi_rready);
assign pkg_wr_en = w_next;
assign pkg_wr_last = (w_next && wr_data_cnt==pkg_wr_size-1);//user logic for addr update
assign axi_wdata = pkg_wr_data;
assign pkg_rd_en = r_next;
assign pkg_rd_last = (r_next && rd_data_cnt==pkg_rd_size-1);//user logic for addr update
assign pkg_rd_data = M_AXI_RDATA;
//----------------------------------------------------------------------------
//AXI4 FULL Write
//AXI4 data is ready for axi master write to slave
reg w_fdma_locked;
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
w_fdma_locked <= 1'b0;
else if(w_fdma_locked==1'b0 && pkg_wr_areq== 1'b1) begin
w_fdma_locked <= 1'b1;
end
else if(pkg_wr_last == 1'b1)
w_fdma_locked <= 1'b0;
//AXI4 write burst lenth busrt addr --------------------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
axi_awaddr <= 'd0;
else if(w_fdma_locked==1'b0 && pkg_wr_areq)
axi_awaddr <= pkg_wr_addr;
else if(axi_awvalid == 1'b1 && M_AXI_AWREADY == 1'b1)
axi_awaddr <= axi_awaddr + BURST_SIZE ;
//AXI4 write cycle flag---------------------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
w_cycle_flag <= 1'b0;
else if(w_cycle_flag == 1'b0 && w_fdma_locked)
w_cycle_flag <= 1'b1;
else if(w_cycle_flag == 1'b1 && axi_wlast == 1'b1)
w_cycle_flag <= 1'b0;
//AXI4 write addr valid---------------------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
axi_awvalid<= 1'b0;
else if(w_cycle_flag == 1'b1 && M_AXI_AWREADY==1'b1)
axi_awvalid <= 1'b0;
else if(w_cycle_flag == 1'b0 && w_fdma_locked == 1'b1)
axi_awvalid <= 1'b1;
//AXI4 write data---------------------------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
axi_wvalid <= 1'b0;
else if(w_cycle_flag == 1'b0 && w_fdma_locked == 1'b1)
axi_wvalid <= 1'b1;
else if(w_cycle_flag == 1'b1 && axi_wlast == 1'b1)
axi_wvalid <= 1'b0;//
//AXI4 write data user counter for burst lenth----------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
w_word_cnt <= 'd0;
else if(w_word_cnt==C_M_AXI_BURST_LEN)
w_word_cnt <= 'd0;
else if(w_next)
w_word_cnt <= w_word_cnt + 1'b1;
else
w_word_cnt <= w_word_cnt ;
//AXI4 write data user counter for frame space lenth----------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
wr_data_cnt <= 'd0;
else if(w_next && wr_data_cnt== pkg_wr_size-1)
wr_data_cnt <= 'd0;
else if(w_next)
wr_data_cnt <= wr_data_cnt + 1'b1;
else
wr_data_cnt <= wr_data_cnt;
//AXI4 write data last data-----------------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
axi_wlast <= 1'b0;
else if(w_word_cnt == M_AXI_AWLEN-1)
axi_wlast <= 1'b1;
else
axi_wlast <= 1'b0;
assign axi_bready = 1'b1;
//----------------------------------------------------------------------------
//AXI4 FULL Read-----------------------------------------
reg r_fdma_locked;
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
r_fdma_locked <= 1'b0;
else if(r_fdma_locked==1'b0 && pkg_rd_areq==1'b1) begin
r_fdma_locked <= 1'b1;
end
else if(pkg_rd_last == 1'b1)
r_fdma_locked <= 1'b0;
//AXI4 read addr read addr burst-------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
axi_araddr <='d0;
else if(r_fdma_locked==1'b0 && pkg_rd_areq==1'b1)//pkg_rd_ardy pkg read addr is ready
axi_araddr <= pkg_rd_addr;
else if(axi_arvalid == 1'b1 && M_AXI_ARREADY == 1'b1)
axi_araddr <= axi_araddr + BURST_SIZE;
//AXI4 r_cycle_flag-------------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 0)
r_cycle_flag <= 1'b0;
else if(r_fdma_locked && M_AXI_ARREADY && axi_arvalid )
r_cycle_flag <= 1'b0;
else if(r_fdma_locked && read_data_flag == 1'b0 )//pkg_rd_dreq pkg request to read
r_cycle_flag <= 1'b1;
//AXI4 read addr valid-----------------------------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
axi_arvalid <= 1'b0;
else if(r_cycle_flag && axi_arvalid && M_AXI_ARREADY )
axi_arvalid <= 1'b0;
else if(r_cycle_flag && axi_arvalid == 1'b0)
axi_arvalid <= 1'b1;
//AXI4 read_data_flag ready for read data----------------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
read_data_flag <= 1'b0;
else if(r_cycle_flag && axi_arvalid && M_AXI_ARREADY)
begin
read_data_flag <= 1'b1;
axi_rready <= 1'b1;
end
else if(read_data_flag && r_next && M_AXI_RLAST)
begin
read_data_flag <= 1'b0;
axi_rready <= 1'b0;
end
//AXI4 data user counter for frame space lenth-----------
always @(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
rd_data_cnt <= 'd0;
else if(r_next && rd_data_cnt== pkg_rd_size-1)
rd_data_cnt <= 'd0;
else if(r_next)
rd_data_cnt <= rd_data_cnt + 1'b1;
else
rd_data_cnt <= rd_data_cnt;
endmodule