DMA项目总结

本文详细介绍了STM32DMA的基本配置流程,包括设置传输方式(内存到内存、内存到外设、外设到外设),工作模式,以及DMA控制器、仲裁器、AHB总线接口的工作原理。文章还讨论了DMA的传输模式、流控制、组成部分和具体实现细节,如同步FIFO和仲裁器的策略优化。
摘要由CSDN通过智能技术生成

DMA的基本配置流程

  1. 配置通道的请求源

  1. 配置传输的方式(例如:存储器到存储器,存储器到外设,外设到外设)

  1. 配置源和目的地址以及传输数量

  1. 配置工作模式

  1. 每组通道之间独立运行

DMA的工作流程

  1. 从外设数据寄存器或者内存中取出数据

  1. 将取出的数据进行存储

  1. 传输数量寄存器的自减,直到为0然后完成

【STM32】 DMA原理,步骤超细详解,一文看懂DMA_stm32 dma_Z小旋的博客-CSDN博客

传输方式1 mem to mem

传输方式2 mem to 外设

传输方式3 外设 to mem

传输方式4 外设 to 外设

DMA的传输模式

DMA流控制

DMA主要组成部分

本项目中DMA的设计规格

不支持mem to mem 传输

  1. DMA内部的可配置的寄存器

一共12组,对应4个通道,每个通道3个寄存器

       在使用DMA传输数据时,DMA控制器需要了解每次传输的大小以便正确配置传输参数。对于数据来自外部UART等外设的情况,可以通过以下方式让DMA知道每次传输的大小:

  1. 固定传输大小:如果每次传输的数据大小是固定的,即可在配置DMA通道时将传输大小预先设置好。例如,如果每次从外部UART接收10个字节的数据,那么可以将DMA通道的传输大小设置为10字节。

  2. 硬件触发信号:某些外设可以提供硬件触发信号,用于指示DMA传输的开始和结束。这样,DMA控制器可以根据触发信号的上升沿或下降沿来确定传输的开始和结束,并据此计算传输的大小。

  3. 软件查询:在有些情况下,外设可能无法直接提供传输大小信息。此时,可以通过软件查询的方式获取每次传输的大小。例如,在接收数据时,可以在每次接收完成后查询UART接收缓冲区中的有效数据长度,并将该长度作为传输大小告知DMA控制器。

  1. DMA的总线接口

  2. DMA的仲裁器

  3. DMA的channel控制器

  1. DMA总体架构

  • dmac_ahb_ctrl模块

dma_ahb_ctrl

  • dma_intf模块(内部寄存器配置模块)是ahb_slave端口用于被cpu配置工作模式

这里为什么write 操作寄存了一拍而read操作没寄存?

对于写入操作寄存器而言,需要进行寄存的原因是为了确保写入操作的稳定性和正确性。具体原因如下:

  1. 同步数据:写入操作通常需要将数据从外设或CPU传输到寄存器中。由于AHB总线上的信号传输有一定的延迟,将写入的数据直接传递给寄存器可能会导致数据不同步的问题。为了解决这个问题,通常会采用“写入操作寄存一拍”的方式,即先将写入数据暂时保存在一个寄存器中,待下一个时钟周期到来时再将数据稳定地写入目标寄存器。

  2. 稳定时序:通过寄存写入操作可以确保写入的数据在一个稳定的时钟周期内被传递到目标寄存器。这是因为寄存器的更新和时钟信号有着确定的时序关系,可以避免在写入过程中产生过渡态或不稳定状态。

相比之下,读取操作不需要寄存的主要原因是读取操作本身并不会对内部寄存器的稳定性产生影响。

总之,针对AHB的Slave接口配置寄存器的写入操作需要进行寄存,主要是为了确保数据同步和操作的稳定性。而读取操作不需要寄存,因为

读取的数据可以直接从寄存器中读取,不需要进行额外的处理和稳定化

  • dma_arb仲裁器模块

固定优先级仲裁的写法:
        两种:1.全组合逻辑采用req与其补码按位与产生gnt信号;
module fixed_pri_arb#(
			parameter REQ_WIDTH = 16)
		(
			input [ REQ_WIDTH-1:0] req;
			output reg [ REQ_WIDTH-1:0] gnt
			);
			

	assign gnt = req& (~(req-1));
		
	
endmodule

2.利用mux和时序逻辑

 

 

 Round Robin 仲裁器


// 可改变优先级的固定优先级仲裁器
module fixed_pri_arb#(
			parameter NUM_REQ = 16)
		(
			input [ NUM_REQ-1:0] req;
			input [NUM_REQ-1:0] base;
			output reg [ NUM_REQ-1:0] gnt
			);
			

	wire [2*NUM_REQ-1:0] double_req 
	wire [2*NUM_REQ-1:0] double_gnt;
	assign double_req = {req,req};
	
	assign double_gnt = double_req&~(double_req-base);
	
	assign gnt = double_gnt[NUM_REQ-1:0] | double_gnt[2*NUM_REQ-1:NUM_REQ];
		
endmodule

// 利用gnt信号左移一位作为上面代码中的base信号
module fixed_pri_arb#(
			parameter NUM_REQ = 16)
		(
			input clk,
			input rstn,
			input [NUM_REQ-1] reg,
			output reg [ NUM_REQ-1:0] gnt
			);
			
	wire [NUM_REQ-1:0] hist_q,hist_d; 
	always@(posedge clk or negedge rstn)begin
		if(!rstn)
			hist_q <= {{NUM_REQ-1{1'b0}},1'b1};
		else
			if(|req)
				hist_q<= {gnt[NUM_REQ-2:0],gnt[NUM_REQ-1]};
	endmodule
	
arbit_ base #(
				.NUM_REQ(NUM_REQ)
				) arbiter(
					.req(req),
					.gnt(gnt),
					.base(hist_q)
				);
endmodule

优化timing和area之后的:

       前面的思路是换优先级,而request不变,另一个思路是优先级不变,但是我们从request入手:当某一路request已经grant之后,我们人为地把进入fixed priority arbiter的这一路req给屏蔽掉,这样相当于只允许之前没有grant的那些路去参与仲裁,grant一路之后就屏蔽一路,等到剩余的request都依次处理完了再把屏蔽放开,重新来过。这就是利用屏蔽mask的办法来实现round robin的思路

module round_robin_arbiter #(

 parameter N = 16

)(

input         clk,

input         rst,

input [N-1:0] req,

output[N-1:0] grant

);


logic [N-1:0] req_masked;

logic [N-1:0] mask_higher_pri_reqs;

logic [N-1:0] grant_masked;

logic [N-1:0] unmask_higher_pri_reqs;

logic [N-1:0] grant_unmasked;

logic no_req_masked;

logic [N-1:0] pointer_reg;


// Simple priority arbitration for masked portion

assign req_masked = req & pointer_reg;

assign mask_higher_pri_reqs[N-1:1] = mask_higher_pri_reqs[N-2: 0] | req_masked[N-2:0];

assign mask_higher_pri_reqs[0] = 1'b0;

assign grant_masked[N-1:0] = req_masked[N-1:0] & ~mask_higher_pri_reqs[N-1:0];


// Simple priority arbitration for unmasked portion

assign unmask_higher_pri_reqs[N-1:1] = unmask_higher_pri_reqs[N-2:0] | req[N-2:0];

assign unmask_higher_pri_reqs[0] = 1'b0;

assign grant_unmasked[N-1:0] = req[N-1:0] & ~unmask_higher_pri_reqs[N-1:0];


// Use grant_masked if there is any there, otherwise use grant_unmasked. 

assign no_req_masked = ~(|req_masked);

assign grant = ({N{no_req_masked}} & grant_unmasked) | grant_masked;


// Pointer update

always @ (posedge clk) begin

  if (rst) begin

    pointer_reg <= {N{1'b1}};

  end else begin

    if (|req_masked) begin // Which arbiter was used?

      pointer_reg <= mask_higher_pri_reqs;

    end else begin

      if (|req) begin // Only update if there's a req 

        pointer_reg <= unmask_higher_pri_reqs;

      end else begin

        pointer_reg <= pointer_reg ;

      end

    end

  end

end

endmodule

第一部分:

上面IDLE状态包含了 ST状态

dma_arb模块第二部分:

dma_arb模块第三部分

首先arb模块优先级:最高为req0即来自外部的perip请求然后是req1,req2,req3;

  • dma的channel模块(内部例化四个同步fifo)

  • dma的fifo模块

同步fifo读写指针产生

空满信号产生时序图:

  • dma的channel控制器模块(重点)

主要分为四个部分

dma_chan_ctrl模块的关键信号说明

DMA 测试方案

DMA验证思路

sky DMA项目

支持burst传输,一次burst传输16个32bit(4个byte)数

ARM DMA Spec

关键问题:

  1. ahb总线知识

AMBA总线—AHB总线协议详解_ahb总线的信号_SD.ZHAI的博客-CSDN博客

AHB-slave设备有两个hready端口,一个hready_in,一个hready_out,hready_out用来告诉ahb-master我是否准备好接收或者发送数据,hready_in是ahb-slave端口用来在pipline操作时判断上一拍其他ahb-slave设备有没有完成操作

  1. 总体控制流程

如上图,首先该DMA拥有四条独立的通道,且通道之间采用优化的循环优先级仲裁,主要组成模块有:

  • ahb_ctrl模块(该模块主要功能为转换dma内部的读写操作为ahb总线的master端口的读写操作)

  • dma_intf模块(该模块主要功能是作为cpu端口的一个ahb---slave端口主要是用于接收来自cpu端口的内部寄存器配置信息,主要有ch_x_ctrl(这里面又包含了bit[0]==通道使能,bit[4] == 指示传输方向,bit[17:8]指示传输size),ch_x_dest_addr,ch_x_sour_addr)

  • arbiter模块,该模块采用严格的优化循环优先级仲裁,初始状态为req0>req1>req2>req3,然后若无外部的perip请求,则判断内部从mem到perip方向的取mem数据有没有完成,最后判断从perip到mem方向的perip从dma内部的fifo中取数据的判断。对于仲裁的实现,参考sky的课程,并且该模块有一个握手信号即:ack

  • channel_ctrl模块,该模块主要控制dma内部四个通道的fifo的读写,

  1. 数据怎么读取的

  1. 仲裁器怎么工作的

首先采取的是严格优化的循环优先级仲裁,其次最高优先级为req0然后req1、req2、req3,然后就是mem to perip时读mem的未完成的通道传输,再然后就是perip to mem 的写mem只要fifo非空且,通道使能就读出dma内部fifo中的数据写到mem中

然后就是通过MUX选通器构成的如下图:

  1. 仲裁器用到了哪些状态机

其中对于状态的跳转采用了状态机,状态机一共有九种状态,分别为IDLE、ST0、ST1、ST2、ST3、W0、W1、W2、W3,其中IDLE状态就是最初始的优先级顺序,然后当响应了req0之后req0的请求就变为了最低。

  1. 如果需要仲裁器突发情况怎么做,仲裁器怎么工作的 怎么进一步优化 比如遇倒burst 和 暂停等

轮询仲裁器verilog仲裁器设计_闲庭信步sss的博客-CSDN博客

  1. FIFO怎么写的 同步和异步 重点异步

异步fifo参考代码full判断需要格雷码的最高两位相反

dc综合之后一般在0.1mm2左右

  1. 然后还有握手怎么做到的

参考AXI协议通道握手,外设与DMA运行时钟频率不同的情况下还会产生CDC处理的情况,即外部请求送到DMA内部需要进行跨时钟域同步,以及异步fifo等操作

  1. 时钟频率,数据读取速度怎么计算

  2. 为什么AXI的写通道要比读通道多一个响应通道呢?

  • 由于写通道的地址与数据都是由主机传输给从机,所以主机无法判断发送过去的数据是否可以被从机顺利接收,或是发起的写命令是否合法,因此需要一个写响应信号来让从机可以高速主机写地址是否合法,写数据是否被成功写入。所以写响应通道要发送BVALID和BRESP。其中BVALID要与主机发过来的BREADY进行握手代表写数据结束。

  • 开始时需要握手以确定双方是否准备好,结束后仍需要握手来确认是否成功传输数据;

  • AXI的握手信号分为VALID和READY,其中VALID信号是传输段发送的,READY是接收端发送的。只有在VALID和READY同时有效的时候,信息才会被传递。VALID有效代表发送以准备好,READY有效代表接收已准备好,当其中一方有效,另一方无效时,有效的一方信号需要保持,等待另一方信号的有效;

  1. DMA有那些工作模式

DMA有三种模式。

第一种叫突发模式(Burst Mode),或者块传输模式(Block Transfer Mode)。这种模式下CPU必须等DMA传输完整块数据后才能访存。

第二种是Cycle Stealing Mode(不知道中文怎么翻译,循环窃取模式?),每传输一个字节会马上把内存总线控制器交还给CPU并且发出控制请求。如果这个时候CPU需要访问内存则可以马上获得内存总线的控制权对内存进行访问,CPU不需访问内存则马上获得总线控制权传输下一个字节。

第三种是透明模式(Transparent Mode)。设备只在CPU不使用内存控制器的时候传输数据。这是最高效的模式,但为了判断CPU什么时候访问内存什么时候不访问,硬件上的实现复杂很多。

2.DMA如何应对错误情况

2.1.当SOC系统中DMA与CPU访问地址冲突怎么办(同时访问相同地址) 

  • 调度算法:在SOC系统中,可以使用调度算法来控制DMA和CPU的访问。通过合理的调度和优先级设置,CPU和DMA的访问可以按照一定的规则进行交替或优先级划分,以减少冲突。 

  • 数据缓存:使用数据缓存可以减少CPU和DMA对于相同地址数据的竞争。CPU和DMA可以将需要访问的数据缓存在各自的缓存中,避免频繁地直接访问相同的地址。这样可以降低数据冲突的概率,并提高总体的系统性能。 

  • 互斥锁/信号量:在某些情况下,可以使用互斥锁或信号量来实现对共享数据的互斥访问。当CPU或DMA需要访问某个地址时,必须首先获取互斥锁或信号量。如果其他设备已经占用了锁或信号量,则需要等待其释放后才能进行访问,以避免数据冲突。 

  • 设计合理的硬件架构:在SOC设计的早期阶段,可以考虑采用合理的硬件架构,通过有效的总线划分、数据通道隔离等方式来降低DMA和CPU的竞争。合理的硬件设计可以减少冲突的可能性。 

2.2如果系统设计出来了不便于修改且发生了这类错误怎么办? 

  • 数据缓存策略:使用合适的数据缓存策略可以减少冲突。可以采用多级缓存结构,并根据DMA和CPU的工作负载特点进行合理的缓存管理和数据预取。通过增加缓存的容量和优化缓存替换算法,减少DMA和CPU对同一地址数据的竞争。 

  • 数据拆分:如果可能的话,可以将需要同时被DMA和CPU访问的大数据块拆分成多个较小的数据块。这样,CPU和DMA可以分别处理不同的数据块,从而减少竞争和冲突。 

  • 数据重映射:在一些情况下,可以通过数据重映射来避免冲突。例如,可以为DMA和CPU分配不同的地址空间,然后通过硬件或软件手段将它们映射到同一物理地址。这样,DMA和CPU实际访问的地址是不同的,避免了直接的冲突。 

  • 时间分片调度:如果冲突无法完全避免,可以考虑使用时间分片调度机制。即在不同的时间片内,只允许DMA或CPU访问某个地址,而另一方则处于等待状态。通过合理的时间分配,尽量减少DMA和CPU同时对同一地址的竞争。 

3.链表式DMA

  1. 配置链表:首先,CPU需要创建一个链表,每个节点表示一个数据传输操作。每个节点会包含有关该传输操作的信息,如源地址、目的地址、传输大小等。这些节点按照传输的顺序连接在一起,形成链表结构。

  2. 启动链表传输:CPU将链表的头节点的地址或索引设置到DMA控制器中,告知DMA从哪里开始执行链表传输。启动链表传输后,DMA会根据链表的内容执行一系列的数据传输操作。

  3. 执行传输:DMA按照链表中节点的顺序逐个执行数据传输操作。对于每个节点,DMA会根据节点中的源地址、目的地址、传输大小等参数进行数据传输。DMA控制器会负责在外设和存储器之间建立数据通路,并进行数据传输操作,而无需CPU的直接干预。

  4. 节点处理:当DMA完成一个节点的数据传输后,它会根据链表中的指针信息找到下一个节点,并继续执行下一个数据传输操作。这样,DMA可以依次处理链表中的所有节点,完成整个数据传输过程。

  5. 传输完成与中断:当DMA完成整个链表的数据传输后,它可以发送一个中断请求给CPU,以通知传输的完成。CPU接收到DMA发送的中断请求后,可以进行相应的处理,例如更新状是态、做进一步的操作等。

4.其它仲裁算法的一些优化 

5.DMA中的异步处理

       对于req从慢时钟到快时钟的请求,为了防止在快时钟域下的DMA channel多次采到同一个请求,需要将同步之后的req信号写入到内部的flag 寄存器中,对应的通道请求会读取flag寄存器中的值进行真正的仲裁,同时也会把flag寄存器的值通过反相器与外部的req相与将当前的req给mask掉即不会重复进行相应。

        对于从快到慢的请求,我们采用了直接的两级同步,因为采用的握手即DMA响应完之后会返回一个ack信号,此时来自其它的请求才会拉低

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值