# Tutorial: DMA 實現 with PYNQ on ZedBoard
此專案將展示如何在zedboard平台上實現DMA,並透過Jupyter控制PS來使用DMA,實現PS與PL之間互動。
## DMA簡介

傳統外部存取記憶體,需要透過CPU中繼後才能進去RAM。實際上中繼這步驟, CPU收到存儲Interrupt後,會將資料複製進cache,再寫入新位置。而這些存取要求對CPU來說將會是個負擔,理由為
1.外部設備可能操作時脈異於CPU。
2.CPU本身不用介入傳送,無須取指,取值,除存等動作。
3.Interrupt令CPU需要停下當前動作。
Direct Memory Access(DMA)為硬體對硬體,令外部設備得以直接存取RAM而不用透過CPU之技術。為此我們會需要DMA控制器(它本身就是一個硬體)
## DMA 控制器
PYNQ 提供並支援DMA IP以及相關函式庫。我們可以直接使用vivado中的DMA IP並使用PYNQ於軟體端應用。值得一提的是PYNQ內建的DMA api只支援簡易模式。
下圖為DMA block diagram。

此DMA具有一組AXI lite界面作為控制,以及一對介於PS及Overlay IP間的讀寫通道。可以理解成DMA controller本身具有AXI master端口,去對PS中DRAM讀寫,將stream提供給PL端的IP。
值得一提的是DMA預設所有連接DMA的串流IP在寫入完成後,提供AXI TLAST訊號。如果沒有正確提供此訊號,DMA將不會完成資料轉接。
## 系統摘要
詳細參閱 http://www.pynq.io/
總之PYNQ是一套可以部屬Jupyter的linux平台。

ZYNQ是一塊集合arm跟fpga的晶片,可以理解成裡面有一顆手機那種處理器以及一塊可以自己編寫的邏輯閘。

我們使用arm processor 執行Jupyter,讓我們可以使用網頁瀏覽器進入Jupyter直接控制arm processor。
以下實做DMA自傳自收,使用Jupyter控制arm processor,連接PL中的DMA控制器做存儲。
## DMA自傳自收 with Jupyter
### Step1 Hardware
+ 打開Block Diagram,加入zynq7 processor, dma, axi4-fifo, gpio等,其他模組使用自動生成。

此專案不使用fifo,如下圖直接將dma中MM2S<->S2MM直連也行。

記得以下重點設定
+ dma要將Scatter Gather Engine disable
+ processing 將HP0 enable
此系統
:::info
如果遇到
>[BD 41-703] Slave segment </processing_system7_0/S_AXI_HP0/HP0_DDR_LOWOCM> is mapped into master segment </processing_system7_0/Data/SEG_processing_system7_0_HP0_DDR_LOWOCM>, but there is no path between them. Please delete the master segment or check your design to ensure a valid path can be created.
檢查Address Editor中processing_system7_0欄目是否有processing_system7_0,如有則將其刪除。
:::
完成後驗證並生成bit file.
### Step2 將硬體描述檔及驅動複製進Jupyter
複製以下檔案進Jyputer目錄下,建議新開一個資料夾並一同放入,注意兩者除附檔名,檔名要相同。
+ ./*.runs/impl_1/***.bit
+ ./*.srcs/source_1/bd/design_1/hw/handoff/***.hwh
:::info
可以使用```http://192.168.2.99:9090/lab``` 在瀏覽器存儲Jupyter目錄
IP位置可能因使用環境不同而改變,建議進入router查看DCHP分配
:::
### Step3 在Jupyter使用硬體
在Step2資料夾之位置新開一個python3 file, 輸入以下code
```shell=python
from pynq import Overlay
import time
import numpy as np
from pynq.lib import AxiGPIO
ol = Overlay("./design_1.bit")
led_ip = ol.ip_dict['axi_gpio_0']
leds = AxiGPIO(led_ip).channel1
mask = 0xffffffff
leds.write(0xee, mask)
```
以上為載入image file(bit file)並控制LED之流程,其中overlay即為該image file.
這邊可以使用ol?來確認有正確載入image,並確認bit file內部IP正確。

接著輸入以下code
```shell=python
import pynq.lib.dma
from pynq import Xlnk
import numpy as np
xlnk = Xlnk()
dma = ol.axi_dma_0
input_buffer = xlnk.cma_array(shape=(5,), dtype=np.uint32)
output_buffer = xlnk.cma_array(shape=(5,), dtype=np.uint32)
for i in range(5):
input_buffer[i] = i + 10
print(input_buffer)
dma.sendchannel.transfer(input_buffer) #memory to stream
dma.recvchannel.transfer(output_buffer)
dma.sendchannel.wait()
dma.recvchannel.wait()
print(output_buffer)
```
按下執行後需要稍等結果,點擊哪一個In模組並執行為僅執行該cell。未執行模組為In[],執行中為In[*],正確執行後In[]中方框將出現數字。
因為此設計是blocking(sequential),wait()就是在等interrrupt。
### Result

Jupyter 印出準備輸入DMA資料,並印出從DMA讀回的資料。
## Reference
+ NCU-CE, SDR LAB
+ http://www.pynq.io/
+ https://pynq.readthedocs.io/en/latest/pynq_libraries/dma.html
+ Vivado Design Suite Tutorial - Embedded Processor Hardware Design(UG940)
###### tags: `Zedboard`,`DMA`, `Tutorial`,`PYNQ`,`zynq7000`
>jessest94106@g.ncu.edu.tw
Department of Space Science & Engineering
Center for Astronautical Physics & Engineering
National Central University, Taiwan
[name=PieappleJ ]