目录
- 一、AXI接口特点
- 二、AXI接口的握手机制
- 2.1 握手原理
- 2.2 握手机制的三种情形
- 三、AXI接口的通道
- 3.1 AXI4-Stream
- 3.1.1 通道信号
- 3.1.2 数据字节类型
- 3.1.3 流格式
- 3.2 AXI4-Lite和AXI4-Full
- 3.1.1 读地址通道
- 3.1.2 读数据通道
- 3.1.3 写地址通道
- 3.1.4 写数据通道
- 3.1.5 写响应通道
- 3.1.6 通道信号的编码
- 四、时序
- 五、源码分析
- AXI4-Stream
- 主机模块
- 从机模块
- AXI4-Lite
- 主机模块
- 从机模块
- AXI-Full
- 主机模块
- 从机模块
- 六、功能仿真
- AXI4-Stream
- AXI4-Lite
- AXI-Full
参考:IHI0022D_amba_axi_protocol_spec、IHI0051A_amba4_axi4_stream_v1_0_protocol_spec、ug1037-vivado-axi-reference-guide
一、AXI接口特点
AXI-Full:高性能存储映射接口,用于需要指定地址的高速数据传输,支持最大256长度的突发传输;
AXI-Lite:轻量级存储映射接口,用于访问一些低速外设中的寄存器(如读写控制和状态寄存器等),不支持突发传输;
AXI-Stream:流接口,用于高速数据流通信,不需要地址,允许无限制长度的突发传输;
- 存储映射:如果一个协议是存储映射的,那么主机所发出的会话(无论读或写)就会指定一个地址。这个地址对应于系统存储空间中的一个地址,表明是针对该存储空间的读写操作。
- 流传输:向某一方向,以固定的速度,连续不断地输出。
突发传输:在一次事务中进行连续的数据传输而无需在每次传输之间重新设置地址和控制信号,AXI-Full的突发传输过程只需要首地址。
二、AXI接口的握手机制
2.1 握手原理
valid:由源端产生,表示当前地址或数据线上的信息是有效的;
ready:由目的端产生,表示已经准备好接收地址、数据以及控制信息;
握手:只有检测到valid和ready同时为高时,才能进行数据传输。这种机制使得主、从机都可以控制信息的传输。
valid和ready需要完全独立,不能相互依赖,避免二者相互等待而造成锁死
2.2 握手机制的三种情形
①
1.源端在T1之后将VALID拉高,表明INFORMATION信号线上传输的是有效信息;
2.目的端在T2之后将READY拉高,表明已准备好接收数据;
3.此时源端必须保持数据稳定,直到T3时刻进行数据传输;
- 一旦VALID拉高,源端必须保持其有效状态,直到成功握手;
- 源端不允许等待READY拉高后才拉高VALID(简单来说就是VALID信号是由主机自主产生的);
②
- 允许目的端等待VALID拉高后再拉高READY;
- 如果拉高了READY,可以在拉高VALID之前取消拉高READY;
③
三、AXI接口的通道
3.1 AXI4-Stream
3.1.1 通道信号
信号 | 源 | 含义 |
---|---|---|
TVALID | M | 置1表示主机发出的数据有效 |
TREADY | S | 置1表示从机准备好接收数据 |
TDATA[(8x-1):0] | M | 流数据,宽度是1个整数字节 |
TLAST | M | 置1表示此时传输的是数据流的最后一个数据,用于指示传输的结束 |
TSTRB[(x-1):0] | M | 指示TDATA的相关字节是数据字节还是位置字节 |
TKEEP[(x-1):0] | M | 指示TDATA的相关字节是否可以作为数据流的一部分处理(是否为空字节) |
TID[(i-1):0] | M | 区分同一接口上传输的多个数据流(源),用于多机通信 |
TDEST[(d-1):0] | M | 指示数据流粗略路由信息(目的),用于多机通信 |
TUSER[(u-1):0] | M | 用户自定义信号,用于多机通信 |
- 具有相同TID和TDEST值的传输来自同一流。
3.1.2 数据字节类型
- 数据字节:包含在源和目的之间传输的有效信息的一个字节数据;
- 占位字节:指示流中数据字节相对位置的字节,是一个占位符,不包含传输的任何相关数据值;
- 空字节:不包含任何数据信息或相对位置信息的字节;
- TKEEP置1表示传输的是数据字节或位置字节,必须传输到目的端;TKEEP置0表示传输的是可以从流中删除的空字节。
- TKEEP置1后,用TSTRB指示相关字节具体是数据字节还是位置字节。TSTRB置1表示是数据字节,包含有效信息;TSTRB置0表示是位置字节,不包含有效信息。
3.1.3 流格式
- 字节流:传输一些数据字节和空字节,每次握手后可以传输任意数量的数据字节,空字节没有意义,可以从流中插入或删除,如下面两图传输的信息完全相同。
- 连续对齐流:传输一些数据字节,每个数据包没有空字节或位置字节。
- 连续不对齐流:数据包的开始和结束位置处有任意数量的位置字节,开始和结束中间全是数据字节。
- 稀疏流:数据包包含数据字节和位置字节的任意组合,通常大多数字节是数据字节。
3.2 AXI4-Lite和AXI4-Full
- 突发传输不能被提前终止,写突发中可以通过禁用所有写选通来阻止进一步的写,读突发中主机可以丢弃读取的数据,但都必须完成突发中所有的传输。
3.1.1 读地址通道
信号 | 源 | 含义 | AXI-lite是否支持该信号 |
---|---|---|---|
ARADDR[x:0] | M | 读突发传输中第一次传输的地址(AXI-lite中是每次传输的地址) | 是 |
ARPROT[2:0] | M | 保护类型,指示传输的安全级别以及传输是数据访问还是指令访问 | 是 |
ARVALID | M | 置1表明读地址和控制信息有效 | 是 |
ARREADY | S | 置1表示从机可以接收一个读地址和相关控制信息 | 是 |
ARID[x:0] | M | 读地址组信号的标识,指定某些特殊传输任务的顺序 | 否 |
ARLEN[7:0] | M | 突发长度,表示一次突发中与该地址相关的传输数量。实际的突发长度是ARLEN+1,支持 2 n ( n = 0 , 1 , 2...8 ) 2^n(n=0,1,2...8) 2n(n=0,1,2...8) | 否 |
ARSIZE[2:0] | M | 突发大小,表示一次突发中每次传输的数据大小。SIZE值对应的数据大小为 2 S I Z E 2^{SIZE} 2SIZE字节,该字节数不能超过数据位宽对应的字节数。 | 否 |
ARBURST[1:0] | M | 突发类型,和突发大小共同决定了如何计算突发内每次传输的地址 | 否 |
ARLOCK | M | 锁类型,置0表示正常传输,置1表示独有传输 | 否 |
ARCACHE[3:0] | M | 内存类型 | 否 |
ARQOS[3:0] | M | 服务质量,一般直接置0 | 否 |
突发类型AxBURST:
- FIXED:突发中每次传输的地址相同,该类型用于重复访问同一位置;
- INCR:突发中每次传输的地址都是前一次传输地址的增量,增量为突发大小,该类型较常用;
- WRAP(回环突发):突发传输的最低地址与传输数据的总大小对齐,该最低地址被定义为回环边界。每次传输后,地址以与INCR相同方式增加,但是如果地址增加到(回环边界+传输数据总大小),就会退回到回环边界。该类型突发长度必须是2,4,8,16。
3.1.2 读数据通道
信号 | 源 | 含义 | AXI-lite中是否支持该信号 |
---|---|---|---|
RDATA[x:0] | S | 读数据,AXI-Lite的数据宽度是32位或64位,AXI-Full的数据宽度可以是 2 n ( n = 3 , 4...10 ) 2^n(n=3,4...10) 2n(n=3,4...10)位 | 是 |
RRESP[1:0] | S | 读响应,指示读传输的状态 ,AXI-Lite不支持EXOKAY状态 | 是 |
RVALID | S | 置1表示要读的数据有效 | 是 |
RREADY | M | 置1表示主机可以接收读数据 | 是 |
RID[x:0] | S | 读数据传输标识,RID值必须与正在响应的读传输的ARID值匹配 | 否 |
RLAST | S | 置1表示一次读突发中的最后一次传输 | 否 |
响应类型xRESP:
- OKAY:正常访问成功;
- EXOKAY:独有访问成功;
- SLVERR:从机错误,访问已到达从机,但从机希望向主机返回错误时使用 ;
- DECERR:解码错误,指示该传输地址上没有从机;
3.1.3 写地址通道
信号 | 源 | 含义 | AXI-lite中是否支持该信号 |
---|---|---|---|
AWADDR[x:0] | M | 写突发传输中第一次传输的地址(AXI-lite中是每次传输的地址) | 是 |
AWPROT[2:0] | M | 保护类型,指示传输的安全级别以及传输是数据访问还是指令访问 | 是 |
AWVALID | M | 置1表示写地址和控制信息有效 | 是 |
AWREADY | S | 置1表示从机可接收一个地址和相关控制信号 | 是 |
AWID[x:0] | M | 写地址组信号的标识,指定某些特殊传输任务的顺序 | 否 |
AWLEN[7:0] | M | 突发长度,表示一次突发中与该地址相关的传输数量。实际的突发长度是AWLEN+1,支持 2 n ( n = 0 , 1 , 2...8 ) 2^n(n=0,1,2...8) 2n(n=0,1,2...8) | 否 |
AWSIZE[2:0] | M | 突发大小,表示一次突发中每次传输的数据大小。SIZE值对应的数据大小为 2 S I Z E 2^{SIZE} 2SIZE字节,该字节数不能超过数据位宽对应的字节数。 | 否 |
AWBURST[1:0] | M | 突发类型,和突发大小共同决定了如何计算突发内每次传输的地址 | 否 |
AWLOCK | M | 锁类型,置0表示正常传输,置1表示独有传输; | 否 |
AWCACHE[3:0] | M | 内存类型 | 否 |
AWQOS[3:0] | M | 服务质量,一般直接置0 | 否 |
AXI-Lite所有传输的宽度必须为32位或64位,并且不可修改、不可缓冲,不支持独有传输。
3.1.4 写数据通道
信号 | 源 | 含义 | AXI-lite中是否支持该信号 |
---|---|---|---|
WID[x:0] | M | 写数据传输标识,WID值必须与AWID值匹配 | 是 |
WDATA[x:0] | M | 写数据,AXI-Lite的数据宽度是32位或64位,AXI-Full的数据宽度可以是 2 n ( n = 3 , 4...10 ) 2^n(n=3,4...10) 2n(n=3,4...10)位 | 是 |
WSTRB[x:0] | M | 写选通,指示内存中要更新的字节的位置,写数据总线每8位有一个选通位,即WSTRB[x]对应于WDATA[(8x+7):8x] | 是 |
WVALID | M | 置1表示写数据有效且WSTRB可用 | 是 |
WREADY | S | 置1表示从机可以接收写数据 | 是 |
WLAST | M | 置1表示一次写突发中的最后一次传输 | 否 |
- WSTRB:主机需确保写选通只对包含有效数据的字节通道为高,当WVALID为低时,写选通可以取任何值;
3.1.5 写响应通道
信号 | 源 | 含义 | AXI-lite中是否支持该信号 |
---|---|---|---|
BRESP[1:0] | S | 写响应,指示写传输的状态,AXI-Lite不支持EXOKAY状态 | 是 |
BVALID | S | 置1表示写响应有效 | 是 |
BREADY | M | 置1表示主机可以接收写响应信息。 | 是 |
BID[x:0] | S | 写响应标识,BID值必须与正在响应的写传输的AWID值匹配 | 否 |
3.1.6 通道信号的编码
- AxSIZE:
- AxBURST:
- AxPROT:
- AxCACHE:
- xRESP:
四、时序
突发读:
重叠突发读:
突发写:
五、源码分析
AXI4-Stream
流接口的源码是在AXI-Stream时序的基础上以读写FIFO的场景来描述传输过程。
主机模块
- 初始化计数状态:主机在开始传输前会等待一定的时钟周期,等待过后进入发送数据状态;
INIT_COUNTER:
// The slave starts accepting tdata when
// there tvalid is asserted to mark the
// presence of valid streaming data
if ( count == C_M_START_COUNT - 1 )
begin
mst_exec_state <= SEND_STREAM;
end
else
begin
count <= count + 1;
mst_exec_state <= INIT_COUNTER;
end
- 发送数据状态:将数据有效信号置1,表示将要发送有效的数据。主从机握手后,将发送使能信号置1,开始连续发送自增数据。发送NUMBER_OF_OUTPUT_WORDS个数据后,将tlast置1;
assign axis_tlast = (read_pointer == NUMBER_OF_OUTPUT_WORDS-1);
assign tx_en = M_AXIS_TREADY && axis_tvalid;
assign axis_tvalid = ((mst_exec_state == SEND_STREAM) && (read_pointer < NUMBER_OF_OUTPUT_WORDS));
// Streaming output data is read from FIFO
always @( posedge M_AXIS_ACLK )
begin
if(!M_AXIS_ARESETN)
begin
stream_data_out <= 1;
end
else if (tx_en)// && M_AXIS_TSTRB[byte_index]
begin
stream_data_out <= read_pointer + 32'b1;
end
end
- 数据开始发送滞后于tvalid拉高1周期,tlast拉高提前于发送最后一个数据1周期。last信号要与最后一个数据对齐,将tvalid和tlast信号延迟一拍;
always @(posedge M_AXIS_ACLK)
begin
if (!M_AXIS_ARESETN)
begin
axis_tvalid_delay <= 1'b0;
axis_tlast_delay <= 1'b0;
end
else
begin
axis_tvalid_delay <= axis_tvalid;
axis_tlast_delay <= axis_tlast;
end
end
- clogb2函数是用移位的方式来计算数据位宽,输入参数是原数据减1;
function integer clogb2 (input integer bit_depth);
begin
for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
bit_depth = bit_depth >> 1;
end
endfunction
从机模块
- 检测到数据有效后,进入到写FIFO状态;
if (S_AXIS_TVALID)
begin
mst_exec_state <= WRITE_FIFO;
end
else
begin
mst_exec_state <= IDLE;
end
- 写FIFO状态:将tready信号置1,表示准备好接收数据,主从机握手后,将FIFO写使能置1,然后写指针自增;写入NUMBER_OF_INPUT_WORDS个数据后,将done置1。
assign axis_tready = ((mst_exec_state == WRITE_FIFO) && (write_pointer <= NUMBER_OF_INPUT_WORDS-1));
always@(posedge S_AXIS_ACLK)
begin
if(!S_AXIS_ARESETN)
begin
write_pointer <= 0;
writes_done <= 1'b0;
end
else
if (write_pointer <= NUMBER_OF_INPUT_WORDS-1)
begin
if (fifo_wren)
begin
// write pointer is incremented after every write to the FIFO
// when FIFO write signal is enabled.
write_pointer <= write_pointer + 1;
writes_done <= 1'b0;
end
if ((write_pointer == NUMBER_OF_INPUT_WORDS-1)|| S_AXIS_TLAST)
begin
// reads_done is asserted when NUMBER_OF_INPUT_WORDS numbers of streaming data
// has been written to the FIFO which is also marked by S_AXIS_TLAST(kept for optional usage).
writes_done <= 1'b1;
end
end
end
- AXIS数据总线宽度为4字节,使用generate-for语句生成4个FIFO块,每个块写入AXIS发送数据的1个字节;
assign fifo_wren = S_AXIS_TVALID && axis_tready;
// FIFO Implementation
generate
for(byte_index=0; byte_index<= (C_S_AXIS_TDATA_WIDTH/8-1); byte_index=byte_index+1)
begin:FIFO_GEN
reg [(C_S_AXIS_TDATA_WIDTH/4)-1:0] stream_data_fifo [0 : NUMBER_OF_INPUT_WORDS-1];
// Streaming input data is stored in FIFO
always @( posedge S_AXIS_ACLK )
begin
if (fifo_wren)// && S_AXIS_TSTRB[byte_index])
begin
stream_data_fifo[write_pointer] <= S_AXIS_TDATA[(byte_index*8+7) -: 8];
end
end
end
endgenerate
S_AXIS_TDATA[7 -: 8]表示S_AXIS_TDATA[7:0]。
AXI4-Lite
主机模块
- 在读写状态,链路上无有效信号时,产生start_single脉冲,开始一次读写传输;issued信号指示当前正在进行一次传输,只有issued为0即没有正在进行的传输时,才会产生start_single脉冲;
if (~axi_awvalid && ~axi_wvalid && ~M_AXI_BVALID && ~last_write && ~start_single_write && ~write_issued)
begin
start_single_write <= 1'b1;
write_issued <= 1'b1;
end
else if (axi_bready)
begin
write_issued <= 1'b0;
end
else
begin
start_single_write <= 1'b0; //Negate to generate a pulse
end
- 每次传输开始后读写索引变量加1,最后一次传输握手后产生last信号,与最后一次传输的数据对齐。最后一次传输响应后,产生读/写完成信号,表示传输过程结束,随后进入下个过程;
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
last_write <= 1'b0;
//The last write should be associated with a write address ready response
else if ((write_index == C_M_TRANSACTIONS_NUM) && M_AXI_AWREADY)
last_write <= 1'b1;
else
last_write <= last_write;
end
//Check for last write completion.
//This logic is to qualify the last write count with the final write
//response. This demonstrates how to confirm that a write has been
//committed.
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
writes_done <= 1'b0;
//The writes_done should be associated with a bready response
else if (last_write && M_AXI_BVALID && axi_bready)
writes_done <= 1'b1;
else
writes_done <= writes_done;
end
- 检测到start_single脉冲后,将主机发出的valid或ready拉高,握手后就拉低,由于lite接口每次传输1个数据,握手信号持续1周期即可; 主机ready信号是等待从机valid信号拉高后才置1;
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_arvalid <= 1'b0;
end
//Signal a new read address command is available by user logic
else if (start_single_read)
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
- 每个valid都和数据对应,从机检测到valid就会接收数据,握手后生成下一次的写数据和读写地址偏移量,最后传输的地址实际是基地址+地址偏移量;内存地址以字节为单位,数据位宽为4字节,所以每次传输的地址偏移量加4;
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_awaddr <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else if (M_AXI_AWREADY && axi_awvalid)
begin
axi_awaddr <= axi_awaddr + 32'h00000004;
end
end
- 读写响应xRESP[1]置1说明读写传输错误,产生错误标志resp_error;读写数据不一致时,产生错误标志read_mismatch;
assign read_resp_error = (axi_rready & M_AXI_RVALID & M_AXI_RRESP[1]);
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
read_mismatch <= 1'b0;
//The read data when available (on axi_rready) is compared with the expected data
else if ((M_AXI_RVALID && axi_rready) && (M_AXI_RDATA != expected_rdata))
read_mismatch <= 1'b1;
else
read_mismatch <= read_mismatch;
end
从机模块
- 复位后写地址使能为1,这表示从机可以接收写地址。检测到写地址和写数据通道有效信号后,axi_awready置1,同时写地址使能置0,此时正在进行一次有效传输,不再接收写地址。读写地址通道握手后,发出写响应有效信号,写响应通道握手后,写地址使能置1,axi_awready置0,此时已完成一次传输,又可以重新接收写地址;
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
- 写地址通道和写数据通道握手后会向AXI-lite从机写数据,模块会将数据接收到存储映射寄存器slv_reg;ar/awaddr的高位是选择位,用于选择将数据接收到哪个slv_reg。
ADDR_LSB表示选择位的最低位,对于32位数据总线,最低位是2,对于64位数据总线,最低位是3。
OPT_MEM_ADDR_BITS表示有几个选择位,当添加多个存储映射寄存器时,写地址宽度和选择位都会增加;
在向AXI-lite模块某地址中写时会指定地址偏移量,偏移量之间相差1个数据字节宽度,偏移量的值和选择位的值大小相等。当指定偏移量为0时,写地址的选择位就是0,数据将被接收到slv_reg0;在接收数据时是按字节接收,只有写选通为1的字节才能被接收到slv_reg。
slv_reg0[(byte_index * 8) + : 8] <= S_AXI_WDATA[(byte_index * 8) + : 8] 等价于slv_reg[7:0]<=S_AXI_WDATA[7:0],slv_reg[15:8]<=S_AXI_WDATA[15:8]…;
assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;
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
- 按选择位将slv_reg赋值给reg_data_out,读地址通道握手后再赋值给读数据;
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
若要读取PS写到AXI-lite的数据,只需读取存储映射寄存器slv_reg;
若要将数据写到PS端,只需将reg_data_out <=
右端替换为要写的数据。
AXI-Full
主机模块
- 整体过程由状态机控制,当没有有效的读/写地址并且当前没有正在进行的突发传输时,产生开始一次读/写突发传输脉冲。
if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active)
begin
start_single_burst_write <= 1'b1;
end
else
begin
start_single_burst_write <= 1'b0; //Negate to generate a pulse
end
- 检测到读/写脉冲后,拉高读/写地址有效信号,握手后就拉低,每次突发传输只需要传输一个地址,握手信号只需持续1个周期。
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
begin
axi_awvalid <= 1'b0;
end
// If previously not valid , start next transaction
else if (~axi_awvalid && start_single_burst_write)
begin
axi_awvalid <= 1'b1;
end
/* Once asserted, VALIDs cannot be deasserted, so axi_awvalid
must wait until transaction is accepted */
else if (M_AXI_AWREADY && axi_awvalid)
begin
axi_awvalid <= 1'b0;
end
else
axi_awvalid <= axi_awvalid;
end
- 读/写地址通道握手后,产生下一次突发的地址。读/写数据通道握手后,产生下一次传输的数据。
assign burst_size_bytes = C_M_AXI_BURST_LEN * C_M_AXI_DATA_WIDTH/8;//一次突发传输64字节
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
begin
axi_awaddr <= 'b0;
end
else if (M_AXI_AWREADY && axi_awvalid)
begin
axi_awaddr <= axi_awaddr + burst_size_bytes;
end
else
axi_awaddr <= axi_awaddr;
end
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
axi_wdata <= 'b1;
//else if (wnext && axi_wlast)
// axi_wdata <= 'b0;
else if (wnext)
axi_wdata <= axi_wdata + 1;
else
axi_wdata <= axi_wdata;
end
- 每进行突发中的1次读/写数据传输,index计1次数,直到完成C_M_AXI_BURST_LEN次数据传输。
assign wnext = M_AXI_WREADY & axi_wvalid; //握手信号
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 || start_single_burst_write == 1'b1)
begin
write_index <= 0;
end
else if (wnext && (write_index != C_M_AXI_BURST_LEN-1))
begin
write_index <= write_index + 1;
end
else
write_index <= write_index;
end
- 如果只有1次突发传输,直接拉高last信号,持续1个时钟周期;如果突发传输超过2次,直到完成(C_M_AXI_BURST_LEN-1)次突发传输后拉高tlast信号,持续1个时钟周期。这里计数到C_M_AXI_BURST_LEN-2是因为last信号要和最后一个数据对齐。
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
begin
axi_wlast <= 1'b0;
end
// axi_wlast is asserted when the write index
// count reaches the penultimate count to synchronize
// with the last write data when write_index is b1111
// else if (&(write_index[C_TRANSACTIONS_NUM-1:1])&& ~write_index[0] && wnext)
else if (((write_index == C_M_AXI_BURST_LEN-2 && C_M_AXI_BURST_LEN >= 2) && wnext) || (C_M_AXI_BURST_LEN == 1 ))
begin
axi_wlast <= 1'b1;
end
// Deassrt axi_wlast when the last write data has been
// accepted by the slave with a valid response
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;
end
- 每进行1次突发传输,burst_counter计1次数,当burst_counter最高位是1时,就计到了最大值64,停止计数。
源码中给出的是在4K地址范围进行突发传输,每次突发传输64字节,故最大计到4K/64=64,位宽为12-6+1=7。
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);//突发计数器位宽-1
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1 )
begin
write_burst_counter <= 'b0;
end
else if (M_AXI_AWREADY && axi_awvalid)
begin
if (write_burst_counter[C_NO_BURSTS_REQ] == 1'b0)
begin
write_burst_counter <= write_burst_counter + 1'b1;
//write_burst_counter[C_NO_BURSTS_REQ] <= 1'b1;
end
end
else
write_burst_counter <= write_burst_counter;
end
- burst_active信号指示当前正在进行1次突发读/写传输,检测到写响应握手信号后burst_active置0,每开启1次传输置1。
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
burst_write_active <= 1'b0;
//The burst_write_active is asserted when a write burst transaction is initiated
else if (start_single_burst_write)
burst_write_active <= 1'b1;
else if (M_AXI_BVALID && axi_bready)
burst_write_active <= 0;
end
- done信号用于指示所有突发传输都已经完成,当burst_counter计到最大且收到最后一次数据响应时done置1.
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
writes_done <= 1'b0;
//The writes_done should be associated with a bready response
//else if (M_AXI_BVALID && axi_bready && (write_burst_counter == {(C_NO_BURSTS_REQ-1){1}}) && axi_wlast)
else if (M_AXI_BVALID && (write_burst_counter[C_NO_BURSTS_REQ]) && axi_bready)
writes_done <= 1'b1;
else
writes_done <= writes_done;
end
从机模块
- axi_awv_awr_flag是当前正在进行1次写传输的状态标志,检测到写地址有效后axi_awv_awr_flag置1,检测到写数据的last后置0.读写地址通道握手后,W/RREADY信号才拉高,
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
- 检测到读/写地址有效后将读/写地址锁存,axi_awlen_cntr用于计数1次突发的每次传输,根据突发类型产生相应的读/写地址。
突发传输中起始地址要和AxSIZE对应的传输数据大小对齐,即起始地址的[ADDR_LSB-1:0]应该为0,而ADDR_LSB由AxSIZE决定。如源码中AxSIZE值是2,对应的突发大小是4(3’b100)个字节,4的[1:0]位是0,ADDR_LSB就是2,即起始地址的[1:0]位是0。
aw_wrap_en :这里是利用进位判断到达回环边界,如起始axi_awaddr =6’b011001,到达回环边界时axi_awaddr=6‘b011001+7’b1000000=7’b1011001,7’b1011001&7’b1000000=7’b1000000,也就是说当地址增加了64字节后最高位变成了1.
assign aw_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_awlen));//回环突发数据总大小64字节
assign aw_wrap_en = ((axi_awaddr & aw_wrap_size) == aw_wrap_size)? 1'b1: 1'b0;//回环使能,表示到达回环边界
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
- 这里和AXI-lite从机接口将数据接收到寄存器中类似,只不过由于传输多个数据,变为根据WSTRB将一次突发传输的全部数据按字节分别存储到4个RAM中。写通道握手后,RAM写使能,然后根据写选通将数据字节存储到byte_ram中。进入到读状态后,RAM读使能,将byte_ram的数据读出。
byte_ram:用数组定义的存储数据字节的RAM
OPT_MEM_ADDR_BITS:RAM地址的位宽-1,和burst_length有关
USER_NUM_MEM :用户需要多少组这样的RAM
mem_address :按读写状态标志获取RAM地址,范围是0~burst_length
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 : 15];
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 (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
六、功能仿真
AXI4-Stream
直接使用官方的主从、机IP核进行连接,并在testbench中给出时钟和复位信号。
AXI4-Lite
AXI-lite主从机对地址的处理不同,不能直接相连,需要添加互联模块。
AXI-Full
写通道:
读通道: