赞
踩
从学习一些高档的单片机就能看到DMA的作用。DMA可以让外设不经过CPU的干预,直接把数据搬运到内存。这样做不仅仅是体现在不需要CPU干涉,而且能够极大的提高外设数据的吞吐量。举个简单的例子,我们需要用AD转换器去采集50Hz的交流信号,假设采样率是1024次/T。那么需要的AD转换器的采样率是50*1024=51200。在非突发,并行采集的条件下,AD转换器每秒需要中断CPU 51200次。对于百兆的CPU来讲,这个速度还是可以接受的,但是如果要在保证数据不丢失,又要对数据做一些FIR,FFT等操作呢?显然,每次数据采集完成中断CPU的方法已经不能满足这种场景。这时候就可以利用DMA接收完成中断来触发数据处理。再引入一种乒乓操作:
A:
B:
如上图所示,我们需要在内存中开辟两块缓冲区,在写入A缓冲区的时候去读取B缓冲区,在写入B缓冲区的时候,去读取A缓冲区。对于缓冲,总会想到一个例子,当我们去食堂盛饭,总是需要一个餐具的,食堂工作人员需要先把饭盛到你的盘子中,而不是直接把饭让你去吃掉。想想下如果没有餐具的缓冲,食堂的拥挤程度会翻多少倍,对于数据通常也是这个样子。
言归正传,有个这两个缓冲区, == 我们把DMA的接收完成中断length’设置成51200,那么这个时候,DMA控制器以20ms的周期去中断CPU,加上每次中断切换缓冲区,最终我们为数据处理创造了将近20ms的时间。== 由此可见,在这个方面,DMA给单片机、DSP带来的好处是革命性的。
上一篇写了在zynq上实现的DMA数据的发送,对于发送来讲,DMA接收显得更加重要,接收属于异步事件,如果没有一种机制让CPU知道这个事件的来临,那么CPU需要一直去检测这个信号。这种资源的浪费是灾难性的,说道这里突然想起以前学习MCU时候的一个疑问,“在两个外设同时中断CPU情况下,优先级高的中断被处理,优先级低的中断有可能被flush掉吗?”现在来看这个问题可以分为两种情况了,地址优先级低的外设中断速度不是那么高,优先级高的中断服务程序执行时间不是那么长。也就是 低优先级外设两次中断间隔时间>高优先级中断的中断服务程序执行时间(这里不考虑中断响应时间)。这种情况下,低优先级中断标志是不会被flush掉的。
看图,外设的中断是从它自己的控制器上给到CPU的。所以第一种情况是没问题的。第二种情况是 低优先级外设两次中断间隔时间<高优先级中断的中断服务程序执行时间(同样这里不考虑中断响应时间)。这个时候外设只有一条中断信号线,自然就不能再次通知CPU了。在想一下,如果相同是同一个中断,本次中断还没有执行完,下一次中断又来了会发生什么情况?那如果本次中断没有执行完,又来了两次中断呢?期间的屏蔽中断,清除中断标志又该怎么设置,留给读者。
上次只完成到中断发送,虽然道理都懂了,但是实验没有成功,不能安心的写出来。
红线连起来的是DMA控制器到数据fifo的通路。其他模块通过AXI-stream把数据写到fifo,DMA控制器再把数据搬到DDR。
//FSM always @(posedge s2mm_clk) begin if(!arst_n) begin s2mm_tdata <=64'b0; //clean all reg s2mm_tkeep <=8'h0; s2mm_tlast <=1'b0; s2mm_tvalid <=1'b0; c_state <=P_IDLE; end else if(start) begin case(c_state) P_IDLE:begin if(s2mm_tready) begin c_state<= P_DATA1; s2mm_tlast <=1'b0; end else c_state<=P_IDLE; end P_DATA1:begin if(s2mm_tready) begin c_state<= P_DATA2; s2mm_tdata<=64'd7654321; s2mm_tkeep<=8'hff; s2mm_tvalid<=1'b1; end else c_state<=P_DATA1; end P_DATA2:begin if(s2mm_tready) begin c_state<= P_DATA3; s2mm_tdata<=64'd1020201; s2mm_tkeep<=8'hff; s2mm_tvalid<=1'b1; end else c_state<=P_DATA2; end P_DATA3:begin if(s2mm_tready) begin c_state<= P_DATA4; s2mm_tdata<=64'd5467201; s2mm_tkeep<=8'hff; s2mm_tvalid<=1'b1; end else c_state<=P_DATA3; end P_DATA4:begin if(s2mm_tready) begin c_state<= P_DATA5; s2mm_tdata<=64'd12301; s2mm_tkeep<=8'hff; s2mm_tlast <=1'b1; end else c_state<=P_DATA4; end P_DATA5:begin if(s2mm_tready) begin s2mm_tdata<=64'd0; s2mm_tkeep<=8'h00; s2mm_tlast <=1'b0; s2mm_tvalid<=1'b0; if(repeat_user) c_state<=P_IDLE; else c_state<=P_DATA5; end else c_state<=P_DATA5; end default:c_state <= P_IDLE; endcase end else c_state<=P_IDLE; end
这是一段简单的AXI-stream从机的写数据状态机。这里功能简单,直接用”一段式”状态机来描述。其实多段式的并不会写☺。verilog是一种比机器码还要机器的语言,最起码以前经常用C的我是这么认为,每一句话都是在一个时钟跳变沿上写的。机器码最起码已经指令化了,但是verilog就好像在用D触发器和与非门在设计逻辑。
下面是PL部分的代码:
void dma_s2mm_test() { XAxiDma_Config *config; XAxiDma InstancePtr; int err = 0; config = XAxiDma_LookupConfig(XPAR_AXI_DMA_1_DEVICE_ID); err = XAxiDma_CfgInitialize(&InstancePtr, config); if(err) printf("dma controler init fail\n"); else printf("dma controler init success\n"); XAxiDma_Reset(&InstancePtr); while(XAxiDma_ResetIsDone(&InstancePtr)==0); //waiting reset finish err = XAxiDma_SimpleTransfer(&InstancePtr, (u32)mm2s_buf1,(u32)65536,1); //mm2s /*1这里要打断点!!!!!!!*/ XAxiDma_IntrEnable(&InstancePtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); /*2这里要打断点!!!!!!!*/ Xil_DCacheInvalidateRange((u32)mm2s_buf1,65536); }
程序逻辑都非常简单,但是调试确实一定要小心。想起一个故事,安培科拉顿与电磁感应现象的发现擦肩而过,当时电流周围能够产生磁场已经被大家公认,但是磁生电并没有被发现。安培为了减小线圈和磁铁对电流表的影响,他把发电装置(线圈和磁铁)放在一个屋子,检测装置(电流表)放在另一个屋子。他让线圈和磁铁产生 == 相对运动 == 以后,马上跑到另外一个屋子去看电流表,这时候电流表已经停止了摆动。6年后,也就是1931年,法拉第发现了电磁感应。之后又麦克斯韦又推导出了麦克斯韦方程组,把人类推向了电气时代。
总是跑题☺,不过这次因为实验的时机搞错了,也用了好久才发现。下看下正常的实验流程:
加载bitstream->设置ILA波形捕获条件,就用tready信号->烧写ARM固件->设置vio,使PL发送数据到PS->程序执行到1断点处!(很重要)这个时候DMA控制器被初始化完成(这个数据信号成功的被捕获,但是PS没存数据不对)->执行 Xil_DCacheInvalidateRange((u32)mm2s_buf1,65536);,内存数据被刷新。
在实验过程中,没有正确的搞清楚这个流程,一直在排查其他问题,花费了大量的时间。
这是DMA中断接收的block以及一些重要信号的捕获点。
FIFO depth:FIFO深度,关于FIFO深度的计算,FIFO的作用;
Memory type :一种是用block memory,另外一种是由ltu生成的;
independent clocks: 独立时钟,实现跨时钟域的数据交互;
CDC sync stages:相当于”打拍“
Packet mode :一次地址,多个数据;
Enable Scatt Gatter Engine :此模式下回向不同的缓冲区写数据,这里不用;
中断产生的时机:
当AXI主机发送完成最后一个位的数据以后,携带一个tlast信号,将其置位,这时候,在DMA控制器的中断引脚产生中断信号;
可以看到,当FIFO中的数据被读空的时候,剩下最后一位的时候,这时候产生了中断。主要还是tlast信号,接下来,我们把tlast信号不置位看看会有什么现象。
改成了0;这样就能看出是不是这个last信号造成了接收完成中断;
再看一些AXI-stream上的信号:
每个信号都在预期之内,但是调试的时候就是没有在PS测看到数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。