SOC简单设计流程之SD卡读写DMA环路实验
实验目的 把 SD卡中的数据从读进入 DDR中存储 再通过DMA 写进 FIFO 完成一个小型的循环 再读回到 PL端的 DDR3中 再 写到 SD卡上
本次实验参考了正点原子第十三章 SD卡读写TXT文本实验 和 第十八章 AXI DMA环路测试
这是基本的逻辑框图 (忽略掉 我添加的 小IP 还没使用 )
步骤一 创建vivado工程
创建一个zynq的IP核
大部分可以参考 正点原子的 SD卡设置 zynq和 DMA中的IP核设置
配置如下
在这里插入图片描述
点击左侧的 MIO Configuration 页面,在右侧展开 I/O Peripherals > UART0,可以看到更具体的引脚配置信息。其中 MIO14 作为 RX 引脚、MIO15 作为 TX 引脚
需要注意的是,TF 卡的引脚连接到 ZYNQ 的 Bank 1 端口(BANK501),Bank 1 的 IO 电压为 1.8V,因此在 MIO 的配置界面将 Bank1 的电压改为“LVCMOS 1.8V”。
勾选了 CD 信号,CD 信号用于指示当前开发板有没有连接 TF 卡,这里勾选中 CD 信号,并将引脚分配至 MIO 10。
点击左侧的 DDR Configuration 页面,在右侧 DDR Controller Configuration 下的“Memory Part”一栏选择 DDR 的器件,XC7Z020 的核心板选择 MT41J256M16RE-125,XC7Z010 的核心板选择 MT41J128M16 HA-125。需要注意的是,我们在这里选择的型号并不是领航者核心板上的 DDR3 型号,而是参数接近的型号,或者说兼容的型号。其他的配置选项保持默认即可。
点击左侧的 Clock Configuration 页面,展开 PL-Fabric Clocks,可以看到默认勾选 FCLK_CLK0,且时钟频率为 50MHz,这里我们将其修改为 100MHz
点击左侧的 PS-PL Configuration 页面,然后在右侧展开 General 下的 HP Slave AXI Interface,可以看到有 4 个 HP 接口,这里我们只用其中的 S AXI HP0 interface,DATA WIDTH 保持默认即可
因为 DMA 在传输完成后通过发送中断通知 CPU,所以我们需要开启 PL 到 PS 的中断。点击左侧的 Interrupts 页面,勾选右侧的 Fabric interrupts 并展开,勾选 PL-PS Interrupt Ports 下的IRQ_F2P[15:0]
最后形成的zynq结构是
添加 DMA IP。添加 DMA IP,如同添加 ZYNQ7 Processing System IP,只不过搜索词由 zynq 变为 dma
DMA需要取消勾选SG模式
添加 axis_data_fifo IP
添加 Concat IP。Concate IP 实现了单个分散的信号,整合成总线信号。这里 2 个独立的中断信号,可以合并在一起接入到 ZYNQ IP 的中断信号上。
模块连接。在 Diagram 窗口中,点击“Run connection Automation”,进行自动连接,在弹出的界面中勾选“AllAutomation”,然后点击 OK
点击“Run Block Automation”,在弹出的界面中勾选“All Automation”,然后点击 OK
1 自动连接完成后,发现 Concat IP 未连接,我们手动进行连接
另外添加的 axis_data_fifo 也未连接,我们同样手动连接。首先将 DMA 的 M_AXIS_MM2S 端口与axis_data_fifo 的 S_AXIS 进行连接
然后将 axis_data_fifo 上的 M_AXIS 端口连接到 DMA 的 S_AXIS_S2MM 端口
在这里插入图片描述
现在我们连接 axis_data_fifo 的时钟和复位。单击 axis_data_fifo 的 s_axis_aresetn 端口并将其连接到 DMA的 axi_resetn 端口,单击 axis_data_fifo 的 s_axis_aclk 端口并将其连接到 DMA 的 m_axi_mm2s_aclk 端口
这是最后的实现结构图
到这里我们的 Block Design 就设计完成了,按“F6”键进行“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
step3:生成顶层 HDL 模块3-1 在 Sources 窗口中,选中 Design Sources 下的 sysetm.bd, 这就是我们刚刚完成的 Block Design 设计。右键点击 sysetm.bd,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
step4:生成 Bitstream 文件并导出到 SDK4-1 在左侧 Flow Navigator 导航栏中找到 PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”。在连续弹出的对话框中依次点击“YES”、“OK”。然后 Vivado 工具开始依次对设计进行综合、实现、并生成 Bitstream 文件。
4-2 导出硬件。在生成 Bitstream 之后,在菜单栏中选择 File > Export > Export hardware 导出硬件,并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择 File > Launch SDK,启动 SDK 软件。
记得BSP中 勾选
最后烧录记得勾选 每次烧录都刷新 FPGA端的烧录
这个在点击烧录的 run as 里面的 最后一个设置
下面给出 本次传递的代码
#include "xparameters.h" //包含vivado所导出的信息包含vivado的基地址
#include "xil_printf.h" //调用打印函数的时候,需要引用这个头文件
#include "ff.h" //使用FATFS库函数时需要
#include "xstatus.h" //需要的一些关键信息
#include"stdio.h" // 专业写法
#include "xdevcfg.h"
#include "xil_io.h"
/下面讲述关于 DMA
#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "sleep.h"
/************************** Variable Definitions *****************************/
#define FILE_NAME "ZDYZ.txt" //定义文件名
const char src_str[30] = "123456789"; //定义文本内容
static FATFS fatfs; //文件系统
#define FILE_READ_NAME "read.txt"
static XAxiDma axidma; //XAxiDma实例
static XScuGic intc; //中断控制器的实例
volatile int tx_done; //发送完成标志
volatile int rx_done; //接收完成标志
volatile int error; //传输出错标志
/************************** Constant Definitions *****************************/
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID
#define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR //0x00100000
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) //0x01100000
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x10500000) //0x11600000
#define RESET_TIMEOUT_COUNTER 10000 //复位时间
//#define TEST_START_VALUE 0x0 //测试起始值
//#define MAX_PKT_LEN 0x100 //发送包长度 对应十进制是256 DMA传递的单次包长最大是256
//指的是stream流的包长度 不是突发长度
//函数声明
int platform_init_fs();
int sd_mount() ; // 挂载SD卡
int sd_write_data(char *file_name,u32 src_addr,u32 byte_len); //SD卡写数据
int sd_read_data(char *file_name,u32 src_addr,u32 byte_len) ; //SD卡读数据
static void tx_intr_handler(void *callback);
static void rx_intr_handler(void *callback);
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
u16 tx_intr_id, u16 rx_intr_id);
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
u16 rx_intr_id);
// main函数
int main()
{
int status; // 当前状态
int len; // 长度
char dest_str[9] = " "; //设定放入DDR的数组名字
int i = 0 ; // 传递函数使用的顺序i t
int t = 0 ;
int value = 0 ;
char pause_data[9] =" " ; //
u8 *tx_buffer_ptr;
u8 *rx_buffer_ptr;
XAxiDma_Config *config;
tx_buffer_ptr = (u8 *) TX_BUFFER_BASE;
rx_buffer_ptr = (u8 *) RX_BUFFER_BASE;
status = sd_mount(); //挂载SD卡----调用函数
///
if(status != XST_SUCCESS){
xil_printf("Failed to open SD card!\n");
return 0;
}
else
xil_printf("Success to open SD card!\n");
//为了展示效果 我决定写一下sd卡
len = strlen(src_str); //计算字符串长度
//SD卡写数据
sd_write_data(FILE_NAME,(u32)src_str,len);
//
printf("Successful write SD in Pc ! \r\n");
sd_read_data(FILE_NAME,(u32)dest_str,len); //读数据进入dest_str
printf("Successful Read SD ! \r\n");
//到目前我们已经把数据放到了dest_str
// 写数据
for(i=0; i<len; i++)
{
Xil_Out32(TX_BUFFER_BASE+i,dest_str[i]);
}
i = 0 ;
//读数据
printf("Successful Write SD in DDR! \r\n");
printf("tx_buffer_ptr send Read data i s %s\n",tx_buffer_ptr);
config = XAxiDma_LookupConfig(DMA_DEV_ID); //查找DMA的配置信息
if (!config) {
xil_printf("No config found for %d\r\n", DMA_DEV_ID);
return XST_FAILURE;
}
//初始化DMA引擎
status = XAxiDma_CfgInitialize(&axidma, config);
if (status != XST_SUCCESS) {
xil_printf("Initialization failed %d\r\n", status);
return XST_FAILURE;
}
if (XAxiDma_HasSg(&axidma)) {
xil_printf("Device configured as SG mode \r\n");
return XST_FAILURE;
}
status = setup_intr_system(&intc, &axidma, TX_INTR_ID, RX_INTR_ID);
if (status != XST_SUCCESS) {
xil_printf("Failed intr setup\r\n");
return XST_FAILURE;
}
//初始化标志信号
tx_done = 0;
rx_done = 0;
error = 0;
//usleep(10);
//数据地址都确定了
Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, len); //刷新Data Cache
printf("DDR send Read data5 i s %s\n",tx_buffer_ptr);
usleep(1) ;
status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) tx_buffer_ptr,
len, XAXIDMA_DMA_TO_DEVICE);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_DCacheFlushRange((UINTPTR) tx_buffer_ptr, len); //刷新Data Cache
status = XAxiDma_SimpleTransfer(&axidma, (UINTPTR) rx_buffer_ptr,
len, XAXIDMA_DEVICE_TO_DMA);
首先是器件例化的指针 ,起始或目的地址 ,传输长度 , DMA传输方向
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, len); //刷新Data Cache
while (!tx_done && !rx_done && !error);
printf("DDR get Read data is %s\n",rx_buffer_ptr);
//传输出错
if (error) {
xil_printf("Failed test transmit %s done, "
"receive %s done\r\n", tx_done ? "" : " not",
rx_done ? "" : " not");
goto Done;
}
//传输完成,检查数据是否正确
xil_printf("Successfully ran AXI DMA Loop\r\n");
disable_intr_system(&intc, TX_INTR_ID, RX_INTR_ID);
Done: xil_printf("--- Exiting main() --- \r\n");
for(i=0; i<len; i++)
{
value = Xil_In32(RX_BUFFER_BASE+i);
pause_data[t] = value;
t++;
}
sd_write_data(FILE_READ_NAME,(u32) pause_data,len);
printf("SD Write Successfully ! \r\n");
return XST_SUCCESS;
}
//初始化文件系统
int platform_init_fs()
{
FRESULT status;
TCHAR *Path = "0:/";
BYTE work[FF_MAX_SS];
//注册一个工作区(挂载分区文件系统)
//在使用任何其它文件函数之前,必须使用f_mount函数为每个使用卷注册一个工作区
status = f_mount(&fatfs, Path, 1); //挂载SD卡
if (status != FR_OK) {
xil_printf("Volume is not FAT formated; formating FAT\r\n");
//格式化SD卡
status = f_mkfs(Path, FM_FAT32, 0, work, sizeof work);
if (status != FR_OK) {
xil_printf("Unable to format FATfs\r\n");
return -1;
}
//格式化之后,重新挂载SD卡
status = f_mount(&fatfs, Path, 1);
if (status != FR_OK) {
xil_printf("Unable to mount FATfs\r\n");
return -1;
}
}
return 0;
}
//挂载SD(TF)卡
int sd_mount()
{
FRESULT status;
//初始化文件系统(挂载SD卡,如果挂载不成功,则格式化SD卡)
status = platform_init_fs();
if(status){
xil_printf("ERROR: f_mount returned %d!\n",status);
return XST_FAILURE;
}
return XST_SUCCESS;
}
//SD卡写数据
int sd_write_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT bw; //f_write函数返回已写入的字节数
//打开一个文件,如果不存在,则创建一个文件
f_open(&fil,file_name,FA_CREATE_ALWAYS | FA_WRITE);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil, 0);
//向文件中写入数据
f_write(&fil,(void*) src_addr,byte_len,&bw);
//关闭文件
f_close(&fil);
return 0;
}
//SD卡读数据
int sd_read_data(char *file_name,u32 src_addr,u32 byte_len)
{
FIL fil; //文件对象
UINT br; //f_read函数返回已读出的字节数
//打开一个只读的文件
f_open(&fil,file_name,FA_READ);
//移动打开的文件对象的文件读/写指针 0:指向文件开头
f_lseek(&fil,0);
//从SD卡中读出数据
f_read(&fil,(void*)src_addr,byte_len,&br);
//关闭文件
f_close(&fil);
return 0;
}
//DMA TX中断处理函数
static void tx_intr_handler(void *callback)
{
int timeout;
u32 irq_status;
XAxiDma *axidma_inst = (XAxiDma *) callback;
//读取待处理的中断
irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DMA_TO_DEVICE);
//确认待处理的中断
XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DMA_TO_DEVICE);
//Tx出错
if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
error = 1;
XAxiDma_Reset(axidma_inst);
timeout = RESET_TIMEOUT_COUNTER;
while (timeout) {
if (XAxiDma_ResetIsDone(axidma_inst))
break;
timeout -= 1;
}
return;
}
//Tx完成
if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
tx_done = 1;
}
//DMA RX中断处理函数
static void rx_intr_handler(void *callback)
{
u32 irq_status;
int timeout;
XAxiDma *axidma_inst = (XAxiDma *) callback;
irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA);
XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA);
//Rx出错
if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) {
error = 1;
XAxiDma_Reset(axidma_inst);
timeout = RESET_TIMEOUT_COUNTER;
while (timeout) {
if (XAxiDma_ResetIsDone(axidma_inst))
break;
timeout -= 1;
}
return;
}
//Rx完成
if ((irq_status & XAXIDMA_IRQ_IOC_MASK))
rx_done = 1;
}
//建立DMA中断系统
// @param int_ins_ptr是指向XScuGic实例的指针
// @param AxiDmaPtr是指向DMA引擎实例的指针
// @param tx_intr_id是TX通道中断ID
// @param rx_intr_id是RX通道中断ID
// @return:成功返回XST_SUCCESS,否则返回XST_FAILURE
static int setup_intr_system(XScuGic * int_ins_ptr, XAxiDma * axidma_ptr,
u16 tx_intr_id, u16 rx_intr_id)
{
int status;
XScuGic_Config *intc_config;
//初始化中断控制器驱动
intc_config = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == intc_config) {
return XST_FAILURE;
}
status = XScuGic_CfgInitialize(int_ins_ptr, intc_config,
intc_config->CpuBaseAddress);
if (status != XST_SUCCESS) {
return XST_FAILURE;
}
//设置优先级和触发类型
XScuGic_SetPriorityTriggerType(int_ins_ptr, tx_intr_id, 0xA0, 0x3);
XScuGic_SetPriorityTriggerType(int_ins_ptr, rx_intr_id, 0xA0, 0x3);
//为中断设置中断处理函数
status = XScuGic_Connect(int_ins_ptr, tx_intr_id,
(Xil_InterruptHandler) tx_intr_handler, axidma_ptr);
if (status != XST_SUCCESS) {
return status;
}
status = XScuGic_Connect(int_ins_ptr, rx_intr_id,
(Xil_InterruptHandler) rx_intr_handler, axidma_ptr);
if (status != XST_SUCCESS) {
return status;
}
XScuGic_Enable(int_ins_ptr, tx_intr_id);
XScuGic_Enable(int_ins_ptr, rx_intr_id);
//启用来自硬件的中断
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
(void *) int_ins_ptr);
Xil_ExceptionEnable();
//使能DMA中断
XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
XAxiDma_IntrEnable(&axidma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
return XST_SUCCESS;
}
//此函数禁用DMA引擎的中断
static void disable_intr_system(XScuGic * int_ins_ptr, u16 tx_intr_id,
u16 rx_intr_id)
{
XScuGic_Disconnect(int_ins_ptr, tx_intr_id);
XScuGic_Disconnect(int_ins_ptr, rx_intr_id);
}
感谢 上述为 SOC的简单设计流程