Nios II Step By Step 3--Nios II 中的DMA
Nios II Step By Step 3--Nios II 中的DMA
8 X# s S$ F4 w
www.fpga-design.net/ X; K1 Q M( Z; o4 S1 j9 [
有了上一讲HAL的基础,我们来关注一下DMA在NIOS 中的实现和编程。DMA是个老问题了,从8086/8088一直到现在,完成不需要CPU参与的数据搬家,源和目标可以是内存也可以是设备,在NIOS II中通过基于HAL编程完成。
下图是三中基本的DMA传输:
FPGA设计网论坛 专业FPGA设计论坛" J! F- w; u1 _ g7 ]) S! E
在NIOS II的HAL DMA设备模式中,DMA传输被分为两类:transmit 和 receive。NIOS提供两种设备驱动实现transmit channels和receive channels,transmit channels把缓冲区数据发送到目标设备,receive channels读取设备数据存放到缓冲区。 ! q5 K3 u& o# a% W0 j; f6 v
为了适应大家不同的开发环境,下面我们完成一个相对简单的DMA操作,复制SDRAM内存缓冲区到on_chip_memory中,如果我们在库工程属性中设置了SDRAM为主内存,那么程序中分配的数组缓冲区就在SDRAM中,我们用指针赋值让指针指向on_chip_memory。这个操作完全可以在程序中用memcpy来实现,我们趋简就繁,就是为了尝试一下DMAJ。
首先我们在SOPC Builder中增加一个名字为dma_0的DMA设备。两个表单设置都选默认。
+ Z, A+ Y! z0 S+ f
第二步,DMA设备有三个PORT,两个MASTER PORT:read_master、write_master,一个SLAVE PORT:control_port_slave。需要在SOPC BUILDER中设置AVALONE交换总线,设置read_master和sdram连接,write_master和on_chip_memory连接,具体见下图(交叉点为黑色)。 @! A0 k7 @+ j* R
& @8 ^; i& e$ n
在sopc builder中生成系统,并在Quartus II中编译下载,硬件部分就OK了。如果你的DMA操作不是内存到内存的,而是内存到设备,或者设备到内存,那么你需要在上面这一步中加以设置,设备只支持读写,是CPU读写还是DMA读写设备不加以区分。
在程序中,我们要使用DMA必须包含:sys/alt_dma.h。
因为是内存DMA操作,所以我们必须实现transmit channels和receive channels,这在NIOS II中就是打开两个设备。在NIOS II IDE中生成一个以Hello World为模板的memory_dma工程项目修改一下程序如下:
#include <stdio.h>
#include <stdlib.h> 9 D. t: h- {% o; Y" |/ j c
#include <sys/alt_dma.h> FPGA设计网论坛 专业FPGA设计论坛( J% p. s N0 w6 b
#include "system.h" www.fpga-design.net/ x" e- Y: m U! \# M3 `
static volatile int rx_done = 0; www.fpga-design.net9 [! B/ k! J0 k& e, c
( m* L9 }9 l- H; l' g
static void done (void* handle, void* data)
{
rx_done++;
www.fpga-design.net* Q q% n! p$ K, Q2 C
} ! M3 r. N1 ]6 ~/ ^
int main (int argc, char* argv[], char* envp[]) & q# h. l0 G5 J% @, O
{ ( R Q6 U8 N! m& @2 ^, B& q
int rc;
static char buff[256];
alt_dma_txchan txchan;
alt_dma_rxchan rxchan;
3 d$ V3 o- y; X6 i' v' X% ~
void* tx_data = (void*) buff;
www.fpga-design.net8 ?6 @* {( D4 I' c h& X6 T
void* rx_buffer = (void*) 0x01000000;
( g) \: A7 G0 L7 P8 D( x( I+ k
5 ^0 s8 y" \6 o8 N, R6 l+ [
if ((txchan = alt_dma_txchan_open("/dev/dma_0")) == NULL)
{
7 t: |. a$ h- e; ~
printf ("Failed to open transmit channel\n");
0 m7 i4 L: r6 Q) m$ w _" t. q; T
exit (1);
}
www.fpga-design.net' T% `5 S- }4 \! f) b( J3 h8 `
if ((rxchan = alt_dma_rxchan_open("/dev/dma_0")) == NULL)
/ O2 M. c+ k$ d: U4 Z- |, W
{
4 ]" q+ O U6 p8 ^/ ^8 A7 q
printf ("Failed to open receive channel\n");
exit (1);
}
if ((rc = alt_dma_txchan_send (txchan,
: b0 a" H" T+ n! `* C
tx_data,
128,
NULL,
NULL)) < 0)
{
FPGA设计网论坛 专业FPGA设计论坛# R# w n1 c5 ^8 W# [
printf ("Failed to post transmit request, reason = %i\n", rc);
exit (1);
}
if ((rc = alt_dma_rxchan_prepare (rxchan,
# N5 Q* h: X, E6 v* `: B
rx_buffer,
128,
done,
1 L* z: O! s' ]. `( l w
NULL)) < 0)
{
printf ("Failed to post read request, reason = %i\n", rc);
exit (1);
www.fpga-design.net. S0 a* e! b3 D$ C) S3 l
}
6 e9 x G2 D: F# m+ i8 R
4 }% X5 J. }6 W+ }
& \( T0 h- d& T8 g" h6 d+ n, C* W) L
while (!rx_done);
- C% [5 x% @3 p' o' M5 V( O
printf ("Transfer successful!\n");
! i. g" T4 I2 |; ~1 p/ t6 s
return 0;
} 6 F1 o+ u) k# S4 i _) s
我们很多人对DMA理解的很深入,在其他嵌入式领域有丰富的经验,在其他系统上的实现问题很自然会想在NIOS II中是怎么完成的呢,比如DMA完成以后需要中断吗?如何知道DMA传输完成等等,在上面的程序中,实际上是通过回调函数完成的,回调函数在Windows系统的WIN API中以及驱动开发中被大量使用。 FPGA设计网论坛 专业FPGA设计论坛" v5 d, h$ ~8 Y6 X% w
好了,DMA就是如此,还有一些相关的函数需要去尝试一下。尝试非常重要,在资料欠缺的时候,需要创建环境去实验,你的理解是这样的,按这样的理解会有这样的结果,实际做一下到底是怎样的,不符合?是理解错了吗?不断尝试,收益无限
www.fpga-design.net/ X; K1 Q
有了上一讲HAL的基础,我们来关注一下DMA在NIOS 中的实现和编程。DMA是个老问题了,从8086/8088一直到现在,完成不需要CPU参与的数据搬家,源和目标可以是内存也可以是设备,在NIOS II中通过基于HAL编程完成。
![Nios <wbr>II <wbr>Step <wbr>By <wbr>Step <wbr>3--Nios <wbr>II <wbr>中的DMA Nios <wbr>II <wbr>Step <wbr>By <wbr>Step <wbr>3--Nios <wbr>II <wbr>中的DMA](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
![Nios <wbr>II <wbr>Step <wbr>By <wbr>Step <wbr>3--Nios <wbr>II <wbr>中的DMA](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
在NIOS II的HAL DMA设备模式中,DMA传输被分为两类:transmit 和 receive。NIOS提供两种设备驱动实现transmit channels和receive channels,transmit channels把缓冲区数据发送到目标设备,receive channels读取设备数据存放到缓冲区。 ! q5 K3 u& o# a% W0 j; f6 v
![Nios <wbr>II <wbr>Step <wbr>By <wbr>Step <wbr>3--Nios <wbr>II <wbr>中的DMA](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
第二步,DMA设备有三个PORT,两个MASTER PORT:read_master、write_master,一个SLAVE PORT:control_port_slave。需要在SOPC BUILDER中设置AVALONE交换总线,设置read_master和sdram连接,write_master和on_chip_memory连接,具体见下图(交叉点为黑色)。
![Nios <wbr>II <wbr>Step <wbr>By <wbr>Step <wbr>3--Nios <wbr>II <wbr>中的DMA](https://i-blog.csdnimg.cn/blog_migrate/a4c26d1e5885305701be709a3d33442f.gif)
& @8 ^; i& e$ n
在sopc builder中生成系统,并在Quartus II中编译下载,硬件部分就OK了。如果你的DMA操作不是内存到内存的,而是内存到设备,或者设备到内存,那么你需要在上面这一步中加以设置,设备只支持读写,是CPU读写还是DMA读写设备不加以区分。
在程序中,我们要使用DMA必须包含:sys/alt_dma.h。
因为是内存DMA操作,所以我们必须实现transmit channels和receive channels,这在NIOS II中就是打开两个设备。在NIOS II IDE中生成一个以Hello World为模板的memory_dma工程项目修改一下程序如下:
#include <stdio.h>
#include <stdlib.h> 9 D. t: h- {% o; Y" |/ j
#include <sys/alt_dma.h> FPGA设计网论坛 专业FPGA设计论坛( J% p. s
#include "system.h" www.fpga-design.net/ x" e- Y: m
static volatile int rx_done = 0; www.fpga-design.net9 [! B/ k! J0 k& e, c
( m* L9 }9 l- H; l' g
static void done (void* handle, void* data)
{
} ! M3 r. N1 ]6 ~/ ^
int main (int argc, char* argv[], char* envp[]) & q# h. l0 G5 J% @, O
{ ( R
3 d$ V3 o- y; X6 i' v' X% ~
} 6 F1 o+ u) k# S4 i
我们很多人对DMA理解的很深入,在其他嵌入式领域有丰富的经验,在其他系统上的实现问题很自然会想在NIOS II中是怎么完成的呢,比如DMA完成以后需要中断吗?如何知道DMA传输完成等等,在上面的程序中,实际上是通过回调函数完成的,回调函数在Windows系统的WIN API中以及驱动开发中被大量使用。 FPGA设计网论坛 专业FPGA设计论坛" v5 d, h$ ~8 Y6 X% w