【【SOC简单设计流程之SD卡读写DMA环路实验】】

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的简单设计流程

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值