本文转载自:Linest-5的CSDN博客
注:本文由作者授权转发,如需转载请联系作者本人
AXI4 协议介绍
AXI 的英文全称是 Advanced eXtensible Interface, 即高级可扩展接口,它是 ARM 公司所提出的 AMBA(Advanced Microcontroller Bus Architecture)协议的一部分。
AXI 协议是一种高性能、高带宽、低延迟的片内总线,具有如下特点:
1. 总线的地址/控制和数据通道是分离的;
2. 支持不对齐的数据传输;
3. 支持突发传输, 突发传输过程中只需要首地址;
4. 具有分离的读/写数据通道;
5. 支持显著传输访问和乱序访问;
6. 更加容易进行时序收敛。
AXI4 协议支持以下三种类型的接口:
1. AXI4: 高性能存储映射接口。
2. AXI4-Lite:简化版的 AXI4 接口, 用于较少数据量的存储映射通信。
3. AXI4-Stream: 用于高速数据流传输,非存储映射接口。
AXI4 接口由五个独立的通道构成:
1. 读地址
2. 读数据
3. 写地址
4. 写数据
5. 写响应
下图是使用读地址和读数据通道实现读传输过程的示意图:
从上图中可以看到, 在一个读传输过程中, 主机首先在读地址通道给出读地址和控制信号, 然后从机由读数据通道返回读出的数据。 另外我们需要注意的是,这是一次突发读操作,主机只给出一个地址,从该地址连续突发读出四个数据。
写传输过程如下图所示, 它用到了写地址、写数据和写响应三个通道。 主机在写地址通道给出写地址和控制信号,然后在写数据通道连续突发写四个数据。从机在接收数据之后,在写响应通道给出响应信号。
AXI 总线中的每个通道都包含了一组信息信号,还有一个 VALID 和一个 READY 信号。 VALID 信号由源端(source) 产生,表示当前地址或者数据线上的信息是有效的;而 READY 信号由目的端(destination)产生,则表示已经准备好接收地址、数据以及控制信息。 VALID 和 READY 信号提供了 AXI 总线中的握手机制,如下图所示:
在上图中, ACLK 为时钟信号, 在 AXI 协议中,所有的输入信号都在是 ACLK 的上升沿采样,所有的输出信号必须在 ACLK 的上升沿之后才能改变。在 T1 之后, 源端将 VALID 拉高,表明 INFORMATION信号线上传输的是有效的地址、数据或者控制信息。目的端在 T2 之后将 READY 拉高, 表明它已经准备好接收数据,此时源端必须保持 INFORMATION 数据稳定不变, 直到 T3 时刻进行数据传输。需要注意的是,源端不允许等目的端的 READY 信号拉高之后, 才将 VALID 信号置为有效状态。 而且,一旦 VALID 拉高,源端必须保持其处于有效状态, 直至成功握手(在时钟上升沿检测到 VALID 和 READY同时为有效状态)。
实验任务
通过自定义带有 AXI4 接口的 IP 核,利用 AXI4 接口对 DDR3 进行读写测试。
实验框图
从下图的实验框图可以知道实验的大致内容:
硬件设计(Vivado 部分)
自定义 IP
这次的实验依然是基于之前的 Hello World 工程,将 Hello World 另存为新的工程并重新命名。
将此工程封装成一个带有 AXI4 接口的 IP,在菜单栏中点击 “Tools”,然后在下拉列表中选择“Create and Package New IP”
按照提示选择 next,在下图此界面选择带有 AXI4 接口的 IP 封装方式。
在下图界面中,将 IP 的命名并设定其保存路径,注意路径不宜设置过长。
在下图的配置界面中,配置以下参数:
在最后一个配置界面中选择比编辑 IP 并点击完成。
这时候就会出现一个新的工程界面,也就是编辑 IP 的工程,在这个工程中可以对 IP 进行一些配置和编辑。
不用对工程内容做任何改动,直接关闭即可。
需要注意的是,在 IP 所存放的文件中我们只需要保留红色方框中的文件夹即可,其余文件及文件夹是用于对 IP进行编辑的工程文件,我们可以直接删除。
重新回到原来的工程中,点击 IP Catalog 在右边就可以看到刚刚生成的 IP
MicroBlaze 配置
打开 block design 进行设置,双击打开 MicroBlaze 进行配置
在首界面中勾选存储于指令优化选项.
在之后的配置界面进行如下的地址空间配置,这样 MB 的配置就完成了
配置 PLL IP
将 PLL 添加一个时钟输出,并配置复位为低有效,clk_out1 是给 microblaze_0、 uart、 spi 等模块提供时钟; clk_out2 给 mig_7series_0 模块提供时钟。
配置 MIG IP
接着添加 MIG(Memory Interface Generalor) IP 核,双击对其进行配置
在以下界面须配置:
在 Memory Options 目录, Input Clock Period 选择“200M” 。
在 FPGA Options 目录中进行如下的配置
对照原理图对引脚进行分配
对 MIG IP 配置完成后再进行 BD 的连线,完成如下:
将自定义 IP 添加至 BD 中,M_AXI 是 AXI-Full 类型的主机接口,实验将通过这个接口对 DDR3 进行读写操作。DDR3_TEST IP 核在检测到 m_axi_init_axi_txn 端口的上升沿后会启动读写过程, 并将读出的数据与写入的数据作比较,比较完成后 m_axi_txn_done 输出高电平。另外,在比较完成后, m_axi_error 信号会指示整个过程是否出错。如果在读写过程中出错,或者在比较的过程中发现读出的数据与写入的数据不一致,那么m_axi_error 将会拉高。
并对其进行配置:
添加 Utility Vector Logic IP 核和消抖模块。 将 Utility Vector Logic IP 核配置成非门, 位宽为 1, 作为反向器使用。这是因为需要使用按键来作为 DDR_TEST IP 核的启动信号,达芬奇开发板上的按键在按下的时候为低电平, 因此我们通过添加一个反向器。
添加 Utility Vector Logic IP 核并对其进行如下配置:
并对其进行连线
添加源文件 IP
再 BD 中添加 RTL 文件,右键选择 Add Sources
其中消抖模块的代码:
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2021 All rights reserved
// -----------------------------------------------------------------------------
// Author : Linest-5
// File : key_debounce.v
// Create : 2021-09-24 15:31:21
// Revise : 2021-09-24 17:08:11
// Editor : sublime text4, tab size (4)
// -----------------------------------------------------------------------------
module key_debounce(
input wire clk,
input wire rst_n,
input wire key,
output wire po_key
);
parameter END_CNT = 249999;
reg [17:0] cnt;
reg cnt_flag;
reg po_key_flag;
//定义cnt
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt <= 'd0;
end
else if (cnt == END_CNT) begin
cnt <= 1'b0;
end
else if (key == 1'b0)begin
cnt <= cnt + 1'b1;
end
else begin
cnt <= 'd0;
end
end
//定义cnt_flag
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
cnt_flag <= 1'b0;
end
else if (key == 1'b1) begin
cnt_flag <= 1'b0;
end
else if (cnt == END_CNT) begin
cnt_flag <= 1'b1;
end
end
//定义po_key_flag
always @(posedge clk or negedge rst_n) begin
if (rst_n ==1'b0) begin
po_key_flag <= 1'b0;
end
else if (cnt == END_CNT && cnt_flag == 1'b0) begin
po_key_flag <= 1'b1;
end
else begin
po_key_flag <= 1'b0;
end
end
assign po_key = po_key_flag;
endmodule
再 BD 中添加刚刚加入的 RTL 模块
并对此模块进行连线
最后对引脚进行分配,直接生成比特流。
软件设计(SDK 部分)
再 SDK 中的操作就比较简单了,和之前的实验类似,添加一个新的工程,并添加新的源文件
#include
#include "xil_cache.h"
#include "xil_printf.h"
#include "xil_io.h"
int main(){
int i;
char c;
Xil_DCacheDisable();
print("AXI4 DDR TEST!\n\r");
while(1){
scanf("%c",&c);
if(c=='c'){
printf("start\n\r");
for(i=0;i<4096;i=i+4){
printf("%d is %d\n",i,(int)Xil_In32(0x88000000+i)
);
}
}
}
return 0;
}
最后烧写程序,在串口上输入 c 即可从 DDR 中读取数据,随后LED0也会亮表示读写完成。