• DMA在FPGA的应用之我见


    首先,来做一个简单的实验,利用DMA来实现on-chip-memory和SRAM之间的传输,同时也在做一个关于SRAM不同地址之间的传输。

    一、硬件设计

      1、首先设计自己的SOPC结构,包括CPU、jtag_uart、sram、sysid、onchip-memory,时钟就用50M即可。都不需要任何的设置。如下图所示:

      2、对于QuartusII上顶层文件就不需要有什么可讲的,主要代码如下。

    复制代码
    1 Reset_Delay delay1 (.iRST(KEY[0]),.iCLK(CLOCK_50),.oRESET(CPU_RESET));
    2
    3 dma_system u0 (
    4 // 1) global signals:
    5   .clk_0(CLOCK_50),
    6 .reset_n(CPU_RESET),
    7
    8 // the_sram_0
    9   .SRAM_ADDR_from_the_sram_0(SRAM_ADDR),
    10 .SRAM_CE_N_from_the_sram_0(SRAM_CE_N),
    11 .SRAM_DQ_to_and_from_the_sram_0(SRAM_DQ),
    12 .SRAM_LB_N_from_the_sram_0(SRAM_LB_N),
    13 .SRAM_OE_N_from_the_sram_0(SRAM_OE_N),
    14 .SRAM_UB_N_from_the_sram_0(SRAM_UB_N),
    15 .SRAM_WE_N_from_the_sram_0(SRAM_WE_N)
    16 );
    复制代码

    二、软件设计

      由于主要是设计到DMA的应用,所以主要就是做一下软件方面的设计。

    核心代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #include <stdio.h>
    #include <string.h>
    #include "system.h"
    #include "sys/alt_dma.h"
    #include "unistd.h"
    //#include "altera_avalon_uart_regs.h"
    #include "io.h"
    #include <sys/alt_cache.h>
     
    static volatile int rx_done = 0;
    //回调函数
    static void done (void* handle,void* data)
    {
        rx_done++;
    }
     
    #define TRANSFER_LENGTH 128
    int main(void)
    {
      char *source = (void*)(SRAM_0_BASE+0x10000);
      char *dest = (void*)(SRAM_0_BASE+0x11000);
         memset(source,0x54,TRANSFER_LENGTH);
        alt_dma_txchan tx;
        //创建DMA发送信道
        tx = alt_dma_txchan_open("/dev/dma_0");
        //当信道创建成功
        if(tx == NULL)
        {
            printf("Failed to open transit channel. ");
        }
        else
        {
            printf("Create the transit channel successfully. ");
        }
        //创建DMA接收通道
        alt_dma_rxchan  rx;
        rx = alt_dma_rxchan_open("/dev/dma_0");
        //当信道创建成功
        if(rx == NULL)
        {
            printf("Failed to open receive channel. ");
        }
        else
        {
            printf("Create the receive channel successfully. ");
        }
     
        int rc;
        if((rc=alt_dma_txchan_send(tx,
                              source,
                              TRANSFER_LENGTH,
                              NULL,
                              NULL))<0)
           {
            printf("Error: failed to post transmit request ");
            exit(1);
           }
       //提交DMA接收请求 指定接收数据的位置(sram)以及传输数据量
        if((rc=alt_dma_rxchan_prepare(rx,
                                dest,
                                TRANSFER_LENGTH,
                                done,
                                NULL))< 0)
           {
           printf ("Error: failed to post receive request ");
           exit(1);
           }
    // 等待发送结束
    while (!rx_done)
        printf("Not Not ");
        printf("Transmit successful ");
    //usleep(5000000);
    int loop,errorcount=0;
    for(loop=1;loop<TRANSFER_LENGTH;loop++)
    {
      //对比缓冲区数据
        if(source[loop]!=dest[loop])
        {
            printf("Verify failed at location: 0x%X ",loop);
            errorcount++;
        }
    }
    if(errorcount==0)
    {
        printf("Transfer successfully ! ");
    }
    else
    {
        printf("Transfer failed ! ");
    }
     
    //while(1);
     
    }
    1
      
    1
     

      上面的代码主要是依照说明书上的代码来讲的,首先是分配两个地址空间,一个源地址空间,一个目标地址空间。由于在SOPC里面,DMA连接的时候,我把读写端都连在了SRAM和on-chip-memory上,这样的话,就可以在他们之间传,也可以在任何一个空间的不同地址传。上面演示的是SRAM上不同的地址空间传输,这个实验如果成功的话,那么另外的实验也能成功。

      传输成功之后,我们做了一个小小的测试程序,就是测试两个地址上的数据是否一样,如果一样的话,就输出Transfer successfully!,否则就输出Transfer failed!最后的结果是显示正确的。

      这个实验表面上看起来似乎没什么,可我在做这个DMA测试的时候经历了很多。总结一下:

      1、第一个问题就是出现printf()函数打印不出来,这个问题可让我伤脑筋了,刚开始我以为是硬件坏了,所以我写了一个hello world程序测试了一下,发现有的时候确实就输出不成功!这里面的原因我想了很久。最后我得出了原因。原来是我的操作不当引起的,在quartus升级到9.1之后,里面改动了很多,如果你的硬件信息出现了改变,需要重新生成一下bsp工程库,另外也需要重新编译一下你的工程,在下载你的工程到硬件。这里面的步骤绝对不能有错误。这里面我就犯了这样的错误,我首先没有重新编译一下这个工程,只是run了一下,而我run的时候是用快捷键的,这里面的run其实没有重新编译你的工程,所以一定要重新编译,重新run一次。

      2、第二个问题就是接收通道打开不成功,还有就是传输显示没有传完,但是回调函数调用了,这样就告诉你传输成功啦!这些问题,都是我无法想象的。原因其实也是很简单,硬件这东西,他本身没有任何的差错能力,就是说,你必须完全按照正确的步骤来进行,才能得到你想要的结果,运行的步骤绝对不能求急,原因同上。

      现在再来看看DMA的一些基本原理。下面是DMA传输的基本应用。而我们不需要了解DMA内部的寄存器等等是什么样子的,它提供给你的那些API函数都是有统一的接口方便你使用。

    Nios II中的DMA传输有以下三种形式:

    1、 存储器到存储器

    这种情况下需要同时打开发送通道和接收通道,而且源地址和目标地址都是自增的。

    //打开发送通道

    tx = alt_dma_txchan_open("/dev/dma_0");

    //tx_buf是源地址、传输数据块长度是length

    dma_res = alt_dma_txchan_send(tx, tx_buf, length, NULL, NULL);

    //打开接收通道

    rx = alt_dma_rxchan_open("/dev/dma_0");

    //rx_buf是目标地址、传输数据块长度是length、dma_done()是DMA完成后被调用的回调函数

    dma_res = alt_dma_rxchan_prepare(rx, rx_buf, length, dma_done, NULL); 

    2、 存储器到外设

    这种情况下只要打开发送通道,而且源地址是自增的,目标地址是固定的。

    tx = alt_dma_txchan_open("/dev/dma_0"); // 打开发送通道

    alt_dma_txchan_ioctl(tx, ALT_DMA_TX_ONLY_ON, (void *)dst_addr); // dst_addr是目标地址

    dma_res = alt_dma_txchan_send(tx, tx_buf, length, dma_done, NULL); // tx_buf是源地址

    3、 外设到存储器

    这种情况下只要打开接收通道,而且源地址是固定的,目标地址是自增的。

    rx = alt_dma_rxchan_open("/dev/dma_0"); // 打开接收通道

    alt_dma_rxchan_ioctl(rx, ALT_DMA_RX_ONLY_ON, (void *)source_addr); // source_addr是源地址

    dma_res = alt_dma_rxchan_prepare(rx, rx_buf, length, dma_done, NULL); // rx_buf是目标地址

    其中通过alt_dma_txchan_ioctl,alt_dma_rxchan_ioctl还可以设置每次发送和接收的字节数。

  • 相关阅读:
    c#MD5珍藏
    html跳转倒计时
    微信支付.NET版开发总结(JS API),好多坑,适当精简
    zip文件jQuery工作地点选择城市代码
    百度地图Api进阶教程-点击生成和拖动标注4.html
    MySQL注入
    SQL语法:inner join on, left join on, right join on详细使用方法
    Mysql INNER,LEFT ,RIGHT join的使用
    让Sql语句区分大小写
    关于try和finaly 里面return的问题
  • 原文地址:https://www.cnblogs.com/lueguo/p/3412206.html
Copyright © 2020-2023  润新知