DMA环通测试
=============

本章介绍一个重要的功能模块，DMA（Direct Memory Access，直接内存存取），是指外部设备不通过CPU直接与系统内存交换数据的接口技术。要将外设数据读入内存或将内存传送到外设，一般都要通过CPU控制完成，如采用查询或中断方式。如前面讲到的BRAM实验。

虽然中断方式可以提高CPU的利用率，但是也会有效率问题，对于批量传送数据的情况，采用DMA方式，可解决效率与速度问题，CPU只需要提供地址和长度给DMA，DMA即可接管总线，访问内存，等DMA完成工作后，告知CPU，交出总线控制权。

本章中采用Vitis给的DMA例子，稍做修改，在DMA工作结束后，发出结束中断，告知CPU，使CPU读取内存数据。

实验说明
--------

1. 先来认识下AXI DMA模块，此模块用到了三种总线，AXI4-Lite用于对寄存器进行配置，AXI4 Memory Map用于与内存交互，在此模块中又分立出了AXI4 Memory Map Read和AXI4 Memory Map Write两个接口，又分别叫做M_AXI_MM2S和M_AXI_S2MM，一个是读一个是写，这里要搞清楚，不能混淆。AXI4 Stream接口用于对外设的读写，其中AXI4 Stream Master（MM2S）用于对外设写，AXI4-Stream Slave(S2MM)用于对外设读。另外还支持Scatter/Gather功能，但本实验不再讲述，留待用户研究。（MM2S表示Memory Map to Stream，S2MM表示Stream to Memory Map）。

..

   AXI Memory Map数据宽度支持32，64，128，256，512，1024bits
   AXI Stream数据宽度支持8，16，32，64，128，256，512，1024bits

.. image:: images/09_media/image1.png
      
2. 本实验中采用直接寄存器模式，如下图为寄存器说明，主要分为两部分，一是MM2S，包括Control Register，Status Register，Source Address，Transfer Length四部分，二是S2MM，同样包括Control Register，Status Register，Destination Address，Buffer Length四部分，注意这里的Source Address和Destination Address指的是内存地址。

.. image:: images/09_media/image2.png
      
.. image:: images/09_media/image3.png
      
3. 以下为MM2S_DMACR控制寄存器说明，比较重要的是Bit0，Run/Stop，表示开启或停止DMA。

其他内容不再讲述。

.. image:: images/09_media/image4.png
      
.. image:: images/09_media/image5.png
      
在这里有几个中断可以设置，IOC_IrqEn，使能完成中断，Dly_IrqEn使能延迟中断，Err_IrqEn使能错误中断。

.. image:: images/09_media/image6.png
      
4. MM2S_DMASR为状态寄存器说明，bit12,13,14为中断状态，写1可清除中断。

.. image:: images/09_media/image7.png
      
.. image:: images/09_media/image8.png
      
5. MM2S_DA，MM2S_LENGTH代表地址和长度设置，S2MM端的寄存器与MM2S类似，不再讲述，本实验功能是MM2S从DDR3中读取数据，写到AXI Stream Data FIFO，再从FIFO读出写到DDR3，实现环通测试，需要打开S2MM_DMACR的IOC_Irq，即写内存结束中断，功能框图如下所示：

.. image:: images/09_media/image9.png

硬件环境搭建
------------

1. 实验中，需要用到HP接口，高速访问DDR3：

.. image:: images/09_media/image10.png
      
设置如下：

.. image:: images/09_media/image11.png
      
2. 打开PL-PS中断接口，连接DMA中断

.. image:: images/09_media/image12.png
      
3. 设置时钟100MHz

.. image:: images/09_media/image13.png
      
4. 其他部分设置，如UART，DDR3设置参考前面的实验，这里不再重复讲述。点击”+”，添加DMA模块。

.. image:: images/09_media/image14.png
      
5. DMA设置如下，Width of Buffer Length Register指的是LENGTH寄存器的宽度，如23bits，也就是最大传输2^23=8,388,608bytes，这里按默认设置14，打开读和写通道，不打开Allow Unaligned Transfers，如果不打开，Memory Map Data Width设置为32，那么地址就必须是0x0，0x4，0x8，0xC等，按4字节对齐。Max Busrt Size可以设置为2, 4, 8, 16, 32, 64, 128, 256。

.. image:: images/09_media/image15.png
      
6. AXI Stream Data FIFO设置如下，设置深度为1024，TDATA Width为4字节，打开TKEEP，TLAST

.. image:: images/09_media/image16.png
      
7. 添加Concat，连接MM2S和S2MM中断输出到IRQ_F2P

.. image:: images/09_media/image17.png
      
8. 最终连线如下图所示

.. image:: images/09_media/image18.png
      
9. 在综合之后可以通过Set Up Debug添加AXI DMA模块的M_AXI_MM2S, M_AXI_S2MM，中断以及FIFO的M_AIXS，S_AXIS信号到ILA逻辑分析仪中，观察数据流向。

.. image:: images/09_media/image19.png
      
Vitis程序开发
-------------

1. 本实验程序是根据simple_poll例子做的修改，在platform.spr的BSP里可以通过导入例子来学习模块的应用。

.. image:: images/09_media/image20.png
      
2. 设置MAX_PKT_LEN，也就是长度，单位为字节，TEST_START_VALUE为起始的数据值，NUMBER_OF_TRANSFERS为测试次数。

.. image:: images/09_media/image21.png
      
3. 定义发送和接收数组

.. image:: images/09_media/image22.png
      
4. 在XAxiDma_Setup函数中，打开S2MM的IOC中断，关闭MM2S的所有中断。在S2MM接收完数据后会发出中断。

.. image:: images/09_media/image23.png
      
5. 在XAxiDma_Setup函数，初始化TxBufferPtr之后，需要将Cache里的数据刷新到内存中，这里非常重要，由于DMA需要访问DDR3，而CPU与DDR3之间是通过Cache交互的，数据暂存在Cache里，可能没有真正刷新到DDR3，如果外部设备也就是DMA想要读取DDR3的值，必须将Cache里的数据刷新到DDR3中，这样DMA才能读到正确的值。调用Xil_DCacheFlushRang函数，需要给出内存地址和长度。

.. image:: images/09_media/image24.png
      
6. 打开MM2S通路和S2MM通路。

.. image:: images/09_media/image25.png
      
7. 中断设置方法与前面例程一样

.. image:: images/09_media/image26.png
      
8. 在中断服务程序中，首先清除中断，由于DDR3中的数据已经更新，但Cache中的数据并没有更新，CPU如果想从DDR3中读取数据，需要调用Xil_DCacheInvalidateRang函数，将Cache数据作废，这样CPU就能从DDR3中读取正确的数据。同样也要给出内存地址和长度。

.. image:: images/09_media/image27.png
      
9. 之后CPU从DDR3中读取数据进行对比，检验数据的正确性。

.. image:: images/09_media/image28.png
      
程序验证
--------

1. 选择Debug Configurations，采用Debug模式，点击Debug

.. image:: images/09_media/image29.png
      
2. 打开ILA，设置触发条件m_axi_mm2s_rvalid上升沿

.. image:: images/09_media/image30.png
      
3. 回到Vitis的Debug界面，不用设置断点，点击Resume

.. image:: images/09_media/image31.png
      
4. 此时可以看到ILA已经触发，可以观察采集到的数据，由于设置了测试次数为2，可以看到S2MM中断了两次。具体的数据可以放大波形观察。

.. image:: images/09_media/image32.png
      
5. 在串口调试工具中可以看到打印信息，中断了两次，并且测试成功

.. image:: images/09_media/image33.png
      
6. 也可以在Vitis调试中，观察memory信息，设置断点如下图，在中断服务函数中设置断点

.. image:: images/09_media/image34.png
      
7. 重新Run
   Configurations，再点击Resume按键运行至断点处，在Memory窗口添加TxBufferPtr和RxBufferPtr，即可观察对比数据

.. image:: images/09_media/image35.png
      
本章小结
--------

本章知识点较多，运用了DMA进行内存的访问，并使用DMA中断，结合ILA逻辑分析仪观察数据，CPU读写内存时Cache的处理，大家可以多做些练习，灵活运用DMA。
