目录
1.1 DMA的基本原理
1.1.1 DMA 的概念
DMA(direct memory access,直接内存访问),是一种避开CPU,可以直接对外设和内存进行访问的技术,可以实现外设与内存、内存之间、外设之间的数据的访问和搬移。
1.1.2 DMA 的特性
①作为外设:DMA是CPU的slave,类似于一般的性的外设,DMA配置好之后,不需要CPU的干预就能直接服务外设,让CPU可以去处理别的事物,提高系统的性能。
②作为控制器: DMA在对内存的操作上是独立于CPU的,是外设和内存的master,DMA具有一般CPU没有的高效操作,能提高系统的吞吐率。
1.1.3 DMA的作用举例
①两块内存的数据拷贝,将SDRAM的数据读入到SRAM,或者SRAM的数据写入到FLASH;
②外设数据的定时自动采集,每完成一次ADC的数据采集,就将ADC采集结果存储到指定的SRAM空间;
③外设数据的定时自动发送,将SRAM存储的数据依次写入到USART进行发送;
1.1.4 DMA的基本配置流程
①配置通道请求源;
②配置传输方式(存储器到存储器或者其他);
③配置源、目的地址、传输数量;
④配置工作模式;
⑤每组通道之间的工作方式都是独立的。
1.1.5 DMA的工作流程
①从外设数据寄存器或者内存中取出数据;
②将取出的数据进行存储;
③传输数量寄存器的自减,直到为0,传输完成;
1.2 DMA的系统构成
这是一个基于cortexM3的soc的系统架构。
DMA是和CPU平级;
DMA可以通过ahb2apb桥直接连接外设;
DMA的系统构成
①理论上DMA和CPU一样,访问趋于可以做到全地址覆盖;
②实际的soc系统中,可能同时存在多个DMA,根据应用需求,DMA访问的地址空间会不同;
③同一个DMA会有多个master接口,master功能不同,所映射的地址范围不同;
④有的master接口还会绕开总线矩阵的仲裁,而直接连接到外设的ahb2apb桥,加快对外设的访问速度;
1.3 DMA的传输方式
1.3.1 存储器到存储器
特定存储区内容拷贝到另一个存储区空间,比如:两块地址空间的数据拷贝,或者不同类型存储器之间的数据搬移,SDRAM数据搬移到SRAM;
配置完成后,数据流会马上由源地址存储到FIFO中,达到FIFO的门限之后,再向目的地址传递;
1.3.2 存储器到外设
特定存储区内容转移至外设的数据寄存器中,比如:内容中存储的表格数据,连续发送到外设,将SIN表格值输出到DAC;
配置完成后,数据流会马上由源地址存储到FIFO中,达到FIFO的门限之后,如果由外设的请求,数据会一次一次的像外设寄存器传递;
1.3.3 外设到存储器
外设数据寄存器内容转移到特定存储区,比如:连续的usart数据接收;
一旦由外设的DMA请求,DMA就从外设取数据放到FIFO,达到FIFO门限,则从FIFO连续往memory发送数据;
1.3.4 外设到外设
外设数据寄存器之间的转移,比如:ADC采样的值通过USART发送出去;
一旦由外设的DMA请求,DMA就从源地址外设取数据,发送到目的地址外设的数据寄存器;
1.4 DMA的工作模式
1.4.1 正常传输模式
通道配置好后,DMA只按照配置传输一次,当传输完成后,DMA通道被自动关闭,需要重新配置DMA通道才能继续新的传输;
1.4.2 循环传输模式
当传输完成一次之后,会按照上一次的配置,重新接着传送,除非关闭DMA通道,射处理连续的数据流;
1.4.3 双缓冲模式
设置有两片存储区,当第一片存储器传输完成后,自动切换到第二片继续传输,并不停切换;
双缓冲一般应用于:外设到存储器或者存储器到外设的传输。在信号的编解码数据、视频、音频的连续输出方面非常高效;
1.5 DMA的流控制
1.5.1 DMA的流控制
控制DMA批量数据(数据流)的传输。
1.5.2 DMA 的流控制方式
①软件流控制
通过软件配置,来结束传输;
②外设流控制
在传输之前无法确定传输数目,DMA不知道数据的传输数量时,从而无法自己结束传输。需要外设产生特定的传输结束信号,比如SDIO。一般的外设不具备流控制能力。
③DMA 流控制
固定数量的传输,DMA知道传输的数量,那么DMA可以控制传输的完成。比如memory到memory的传输,只能用DMA做流控制。
2 DMA设计——DMA设计规格详解
2.1 DMA的设计规格介绍
①1个32位ahb slave interface;
②1个32位ahb master interface;
③4个独立可配置的通道,固定优先级仲裁;
④支持外设与存储器之间的传输(外设到存储器、存储器到外设);
⑤可编程的数据传输数目:最大为1024;
*ahb interface只支持基本的单次传输;
2.2 DMA的寄存器功能
12个寄存器,分为四组,一个通道一组寄存器;
名称 | 偏移地址 | 位宽 |
---|---|---|
ch_0_ctrl | 0X00 | [0] :ch_0_en, 通道0使能;[4]:che_0_target,0为memory到外设,1为外设到memory;[17~8]:ch_0_size,传输大小,单位为4B |
ch_0_sour | 0X04 | 31~0:通道0源地址 |
ch_0_dest | 0X08 | 31~0:通道0目的地址 |
ch_1_ctrl | 0X0C | [0] :ch_0_en, 通道0使能;[4]:che_0_target,0为memory到外设,1为外设到memory;[17~8]:ch_0_size,传输大小,单位为4B |
ch_1_sour | 0X10 | 31~0:通道0源地址 |
ch_1_dest | 0X14 | 31~0:通道0目的地址 |
ch_2_ctrl | 0X18 | [0] :ch_0_en, 通道0使能;[4]:che_0_target,0为memory到外设,1为外设到memory;[17~8]:ch_0_size,传输大小,单位为4B |
ch_2_sour | 0X1C | 31~0:通道0源地址 |
ch_2_dest | 0X20 | 31~0:通道0目的地址 |
ch_3_ctrl | 0X24 | [0] :ch_0_en, 通道0使能;[4]:che_0_target,0为memory到外设,1为外设到memory;[17~8]:ch_0_size,传输大小,单位为4B |
ch_3_sour | 0X28 | 31~0:通道0源地址 |
ch_3_dest | 0X2C | 31~0:通道0目的地址 |
2.3 DMA的总体结构
总共分为五个模块,其中channel模块包含四路同步FIFO。
总结:dmac顶层模块的端口
①ahb_slave :作为CPU外设,供CPU配置DMA;
②ahb_master :读写存储器和外设;
③req[3:0] :外设输入的dma_req 信号;
④ack[3:0] :输出给外设的dma_ack信号;
2.4 DMA的总线接口
ahb_master接口(amba 2.0)
名称 | 输入/输出 | 位宽(bit) | 描述 |
---|---|---|---|
HSEL | Output | 1 | |
HADDR | Output | 32 | |
HSIZE | Output | 3 | |
HTRANS | Output | 2 | |
HWRITE | Output | 1 | 1:写;0:读; |
HWDATA | Output | 32 | |
HREADY | Output | 1 | |
HRDATA | Input | 32 | |
HREADYOUT | Input | 1 | |
HRESP | Input | 2 |
内部的读写时序类似于一个SRAM。
名称 | 输入/输出 | 位宽(bit) | 描述 |
---|---|---|---|
clk | Input | 1 | 时钟 |
rst | Input | 1 | 复位 |
wr | Input | 1 | 写信号 |
rd | Input | 1 | 读信号 |
addr | Input | 32 | 内部地址 |
wdata | Input | 32 | 写数据 |
rdata | Output | 32 | 读数据 |
rd_en | Output | 1 | 读使能 |
总结
将内部产生的读、写信号,地址,数据,转换成AHB的时序接口
①总线接口时一个AHB的master接口;
②channel_ctrl 发出读、写操作,由ahb_ctrl转换为AHB的时序;
③wr为高,表示需要向AHB的addr写数据;
④rd为高,表示需要向AHB的addr读数据;
简要示意图如下:
2.5 DMA的寄存器模块
AHB_SLAVE接口
名称 | 输入/输出 | 位宽(bit) | 描述 |
---|---|---|---|
HCLK | |||
HRESETn | |||
HSEL_SLV | Input | ||
HADDR_SLV | Input | ||
HSIZE_SLV | Input | ||
HTRANS_SLV | Input | ||
HWRITE_SLV | Input | ||
HWDATA_SLV | Input | ||
HREADY_SLV | Input | ||
HRDATA_SLV | Output | ||
HREADYOUT_SLV | Output | ||
HRESP_SLV | Output |
在CPU内部进行dma进行配置之后,对寄存器需要进行输出;
名称 | 输入/输出 | 位宽(bit) | 描述 |
---|---|---|---|
ch_0_en/1/2/3 | Output | 1 | 通道使能 |
ch_0_target/1/2/3 | Output | 1 | 传输方向【M2P,P2M】 |
ch__0_size/1/2/3 | Output | 10 | 传输大小 |
ch_0_sour | Output | 32 | 源地址 |
ch_0_dest | Output | 32 | 目的地址 |
总结
将内部产生的读写信号,地址,数据,转换成AHB的时序接口
①寄存器模块时接在AHB的slave模块,挂接在AHB总线上
②接收来自AHB的配置,并解析出4个通道的配置信息,宫其他模块使用
2.6 DMA的仲裁器
名称 | 输入/输出 | 位宽(bit) | 描述 |
---|---|---|---|
clk | Input | 1 | 时钟 |
rst | Input | 1 | 复位 |
req_0 | Input | 1 | 外设0的请求信号 |
req_1 | Input | 1 | 外设1的请求信号 |
req_2 | Input | 1 | 外设2的请求信号 |
req_3 | Input | 1 | 外设3的请求信号 |
ack_0 | Output | 1 | 外设0的应答信号 |
ack_1 | Output | 1 | 外设1的应答信号 |
ack_2 | Output | 1 | 外设2的应答信号 |
ack_3 | Output | 1 | 外设3的应答信号 |
ch_0/1/2/3en | Input | 1 | 来自配置寄存器,1表示通道打开 |
target_0/1/2/3 | Input | 1 | 来自配置寄存器,0:数据从memory到外设;1:数据从外设到memory; |
en_0 | Output | 1 | 1:通道0正在工作,其他通道等待; |
en_1 | Output | 1 | 1:通道1正在工作,其他通道等待; |
en_2 | Output | 1 | 1:通道2正在工作,其他通道等待; |
en_3 | Output | 1 | 1:通道3正在工作,其他通道等待; |
req_done | Input | 1 | 表示当前req被处理完 |
ch_0/1/2/3_t0_done | Input | 1 | 表示t0方向传输结束 |
fifo_0_empty | Input | 1 | FIFO0空标志 |
fifo_1_empty | Input | 1 | FIFO1空标志 |
fifo_2_empty | Input | 1 | FIFO2空标志 |
fifo_3_empty | Input | 1 | FIFO3空标志 |
fifo_0_full | Input | 1 | FIFO0满标志 |
fifo_1_full | Input | 1 | FIFO1满标志 |
fifo_2_full | Input | 1 | FIFO2满标志 |
fifo_3_full | Input | 1 | FIFO3满标志 |
总结
仲裁器根据输入的req信号,仲裁出当前优先级最高的通道并进行操作
①采用固定优先级算法对4个通道进行仲裁(req0>req1>req2>req3);
②多个通道req同时来到时,会先处理优先级高的,再处理优先级低的;
③通道req处理完成后会回ack握手信号;
2.7 DMA的channel控制器
通道控制器包含3个模块
①dmac_fifo,同步fifo,用于数据的存储;
②dmac_channel ,例化4个通道的fifo;
③dmac_channel_ctrl,对通道进行传输控制;
总结
通道控制器需要根据仲裁结果,对通道进行管理
①接收arbiter仲裁后的使能信号
②发起对FIFO的操作
③发起对ahb_ctrl模块的读写;
3 DMA的RTL部分
3.1 dmac 顶层模块
声明了输入输出端口;
例化了内部的所有模块;
整个顶层除了例化模块,没有多余的逻辑;
编写顶层代码,除了例化模块外,尽量减少其他的逻辑;
代码实现
`timescale 1ns / 1ns
/*--------------------------------------------------------------------------------
-- Module : dmac_top
-- Description :
------------------------------------------------------------------------------*/
module dmac_top(
/*-------------------------- Interface definitation ------------------------*/
input HCLK,
input HRESETn,
//AHB SLAVE PORTS
input HSEL_SLV,
input HREADYIN_SLV,
input [1:0] HTRANS_SLV,
input [2:0] HSIZE_SLV,
input HWRITE_SLV,
input [31:0] HADDR_SLV,
input [31:0] HWDATA_SLV,
output HREADYOUT_SLV,
output HRESP_SLV,
output [31:0] HRDATA_SLV,
//AHB MASTER PORTS
output HSEL,
output HREADY,
output [1:0] HTRANS,
output [2:0] HSIZE,
output HWRITE,
output [31:0] HADDR,
output [31:0] HWDATA,
input HREADY_IN,
input HRESP,
input [31:0] HRDATA,
//handshake signal
input req_0,
input req_1,
input req_2,
input req_3,
output ack_0,
output ack_1,
output ack_2,
output ack_3
/*-----------------------------------------------------------*/
);
/*------------- Parameter and Internal Signal ---------------*/
/*------------- Internal Interface definitation -------------*/
//
wire ch_0_en;
wire ch_1_en;
wire ch_2_en;
wire ch_3_en;
//
wire ch_0_target;
wire ch_1_target;
wire ch_2_target;
wire ch_3_target;
//
wire [9:0] ch_0_size;
wire [9:0] ch_1_size;
wire [9:0] ch_2_size;
wire [9:0] ch_3_size;
//
wire [31:0] ch_0_sour;
wire [31:0] ch_1_sour;
wire [31:0] ch_2_sour;
wire [31:0] ch_3_sour;
//
wire [31:0] ch_0_dest;
wire [31:0] ch_1_dest;
wire [31:0] ch_2_dest;
wire [31:0] ch_3_dest;
//
wire en_0;
wire en_1;
wire en_2;
wire en_3;
//
wire ch_0_t0_done;
wire ch_1_t0_done;
wire ch_2_t0_done;
wire ch_3_t0_done;
//
wire wr;
wire rd;
wire [31:0] addr;
wire [31:0] wdata;
wire [31:0] rdata;
wire rd_en;
wire [31:0] wr_fifo_data_0;
wire [31:0] wr_fifo_data_1;
wire [31:0] wr_fifo_data_2;
wire [31:0] wr_fifo_data_3;
wire [31:0] rd_fifo_data_0;
wire [31:0] rd_fifo_data_1;
wire [31:0] rd_fifo_data_2;
wire [31:0] rd_fifo_data_3;
wire wr_fifo_0;
wire wr_fifo_1;
wire wr_fifo_2;
wire wr_fifo_3;
wire rd_fifo_0;
wire rd_fifo_1;
wire rd_fifo_2;
wire rd_fifo_3;
wire fifo_0_full;
wire fifo_1_full;
wire fifo_2_full;
wire fifo_3_full;
wire fifo_0_empty;
wire fifo_1_empty;
wire fifo_2_empty;
wire fifo_3_empty;
wire req_done;
/*------------------------Main Code--------------------------*/
dmac_intf Inst_dmac_intf(.HCLK(HCLK),