1.概要
DMAコントローラはAXI-Liteを用いて制御しており,PYNQのDMAライブラリ内でもAXI-Liteのレジスタを直接操作してDMA転送を行っています.今回はDMAライブラリが何をしてるかを理解するため,この操作を温かみのある手打ちで実装します.
ビットストリームは前回使用したものを転用するため,DMAのモードはシンプルモードのままでいきます.
参考
Python productivity for Zynq (Pynq),"pynq.lib.dma — Python productivity for Zynq (Pynq)"
2.方針
PS-PL間のDMA転送は,PSからPLへのデータの流れ(Memory-Mapped to Stream:MM2S)とPLからPSへのデータの流れ(Stream to Memory-Mapped:S2MM)の二つが存在し,これらを制御する必要があります.
MM2S,S2MMの制御手順はほぼ同じではあるものの独立して制御するため,同じ機能のレジスタがMM2S用,S2MM用の2つ準備されています.
今回はデータ転送直後にデータ読み出しを行うので,S2MMもMM2Sも同じようなタイミングで操作します.
3.方法
実際にAXI4_Liteを用いて,DMAコントローラを操作していきます.アクセスするレジスタとアドレスの関係は以下の通りです(カッコ内はソースコード内での変数名).
MM2S
DMA制御レジスタ(MM2S_DMACR):0x00
DMA状態レジスタ(MM2S_DMASR):0x04
ソースアドレスレジスタ(MM2S_SA):0x18
転送長さレジスタ(MM2S_LENGTH):0x28
S2MM
DMA制御レジスタ(MM2S_DMACR):0x30
DMA状態レジスタ(MM2S_DMASR):0x34
ソースアドレスレジスタ(MM2S_SA):0x48
転送長さレジスタ(MM2S_LENGTH):0x58
スキャッターギャザーモード(SGモード)でのアドレスはAXI DMA IPの製品ガイドを見てください.
参考
Xilinx.Inc, "LogiCORE IP AXI DMA v7.1 製品ガイド (PG021)"
Lauri's blog,"AXI Direct Memory Access"
3-1.リセット
1. S2MMのDMA制御レジスタ(0x30)に0x04を書き込む(リセットビットを1にする)
2. MM2SのDMA制御レジスタ(0x00)に0x04を書き込む(リセットビットを1にする)
(S2MMとMM2Sの状態を確認)
3. S2MMのDMA制御レジスタに0x00を書き込む(全停止)
4. MM2SのDMA制御レジスタに0x00を書き込む(全停止)
(S2MMとMM2Sの状態を確認)
最初にS2MM/MM2Sのリセットと停止を行います.通信に失敗してDMAのIPが動作したままだと転送が行えなくなります.
S2MMとMM2Sの状態はxxxx_DMASRを読み出して確認します.
3-2.転送
5. S2MMで書き込むメモリ領域の先頭アドレスをデスティネーションアドレスレジスタ(0x48)に書き込む
6. MM2Sで読み込むメモリ領域の先頭アドレスをソースアドレスレジスタ(0x18)に書き込む
(S2MMとMM2Sの状態を確認)
7. S2MMのDMA制御レジスタ(0x30)に0xF001を書き込む(DMAチャンネルの実行&IOC_IrqENとErr_IrqEnの設定)
8. MM2SのDMA制御レジスタ(0x00)に0xF001を書き込む(DMAチャンネルの実行&IOC_IrqENとErr_IrqEnの設定)
(S2MMとMM2Sの状態を確認)
9. S2MMの転送長を転送長さレジスタ(0x58)に書き込む(バイト単位)
10. MM2Sの転送長を転送長さレジスタ(0x28)に書き込む(バイト単位)
11. MM2Sの同期を待つ
12. S2MMの同期を待つ(ここでフリーズするなら、全メモリの範囲がアドレスエディタで割り当てられているかを確認すること)
(S2MMとMM2Sの状態を確認)
転送長を書き込んだ段階で実際にDMAの転送が行われます.
S2MMやMM2Sの同期は、DMAのステータスがErr_IrqかIdleではない間、whileで待つようにしました。
4.まとめ
手打ちでDMAコントローラを制御してみました.マイコンのレジスタの設定とほぼ同じような感じで操作できるようになっており,個人的には比較的わかりやすかったです.
PYNQ V2.6からはSGモード用の関数も準備されており,直に叩くメリットはほぼないのですが,関数内で何をしているかが分かっていただければ幸いです.
©2021 shts All Right Reserved.