2021年4月10日土曜日

PYNQ V2.6でDMA転送をする話(手打ちでDMAコントローラを制御する)



前回,前々回ではPYNQのライブラリでDMA転送を行うための回路の作成とプログラミングを行いました.今回は,pynq.lib.dmaの関数ではなく,AXI-Liteのレジスタを直接操作して同じようなことができないかを探る話です.  



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の製品ガイドを見てください.

参考
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.

2021年4月9日金曜日

PYNQ V2.6でDMA転送をする話(ビットストリーム生成)



絶賛就活中,中の人です.気晴らしがてら進めていた研究の方でPYNQのDMAを使うことがあったのですが,その際に得た知見をまとめようと思います.
ビットストリームはこちらに上げてあります.  



1.環境



ボード:PYNQ-Z2(TUL製,xc7z020clg400-1搭載)
Vivado : Vivado 2020.1
PYNQ : V2.6
OS Ubuntu 20.04LTS

特に変哲のない感じですが,PYNQ V2.6はVivado 2020.1でビルドされているので,それに合わせてバージョンを決めた感じです.

2.どんな回路を作るか



CPUにぶら下がっているDDR3メモリとPL部をつなぎ,メモリ->FIFO(PL部)->メモリというような簡単な回路で動作確認を行います.

3.PS部とPL部の橋渡し.


PYNQ-Z2で使用されているZYNQ 7000シリーズでは,FPGA(PL)とCPU(PS)に接続されたDDR3メモリをAXIバスを介して接続することができます.この機能はZYNQ内に存在するポートを使うことで使用できます.
このポートいくつか存在しており,32b GP AXI Master Ports(M_AXI_GPx),32b GP AXI Slave Ports(S_AXI_GPx),AXI High Performance 32b/64b Slave Ports(S_AXI_HPx),64b AXI ACP Slave Portのように通信の種類に応じて分けられています.


4.AXIバスの通信規格

AXIバスの通信規格にはいくつかの種類があります(AXI4だとAXI4(-Full),AXI-Lite,AXI-Stream).
今回使用するDMAのIPの制約で,FIFOとDMAコントローラ間はAXI-Stream,PS(S_AXI_HP)とDMAコントローラ間はAXI-Fullを使用します.またいくつかのIPでAXI-Liteによる制御が必要なので,そちらも使用します.

参考

5.ブロックダイアグラムを作る

ここからブロックダイアグラムを作っていきます.

5-1.PSの設定



PSとして,Zynq7 Processing System(5.5)を使います.
DDRメモリやFIXIOへの接続ポートを先に生成しておきます。


ここではメモリとPLを接続するため,ポート(M AXI GP0とS AXI HP0)の設定を行います.Vivado 2020.1ではM AXI GP0の方は最初から有効化されているようで,有効化されていないS_AXI_HP0は手動で有効化します.この際に,S_AXI_HP0のデータ幅は64bitにしておきます.

5-2.DMAコントローラの設定



今回使うIPはXilinx提供のAXI Direct Memory Access(7.1)です.
PYNQのv2.6からScatter Gather Engineをサポートするようになったのですが,今回はシンプルモードで試すのでチェックを外しておきましょう.
また,バッファ長を設定するレジスタ(Width of Buffer Length Register)は最大値の26まで引き上げておきましょう.
ここではAddress Widthを32bitとしておきます.
DMAのIPを二つ配置し,それぞれRead-Only, Write-Onlyにすることも可能ですが,今回はR/Wの双方を使えるようにしておきます.
今回の回路ではint16のデータを流すので,Read Channel側のMemory Map Data Widthを32bit,Stream Data Widthを16bitに設定しておきます.

5-3.FIFOの設定



FIFOとして,Xilinx提供のAXI4-Stream Data FIFO(2.0)を使用します.特段設定をかえる必要はないと思います.

5-4.IPの配置と配線



PSのブロックをDiagramに追加し,DDRとFIXED_IOの自動配線を終えたのち,DMAコントローラとFIFOを追加します.
そして,DMAコントローラとFIFOのAXI Streamのバスをつなぎます.
DMAコントローラとPSのS_AXI_HP0との接続にはAXI SmartConnectをかませました(Vivado2020.1の自動配線だと,別のIPが出てきました).
その後、自動接続を使い、他の配線を行いました。

6. 合成とファイルの生成


先程のブロック図をValidate Designにかけると、アドレスが振られていない警告が出てくるので、アドレスエディタでアサインしておきましょう。もう一度チェックを走らせると、CriticalWarningは消えているはずです。

Generate Output Products,Generate HDL Wrapperをし,ビットストリームを生成しました.
PYNQ上で実行する際には,.bit及び.hwhが必要になります(V2.6では.tclは不要みたいです).
以下の二つのフォルダから.bit及び.hwhを探します.

プロジェクト名.runs/impl_1/デザイン名_wrapper.bit
プロジェクト名.src/source_1/bd/デザイン名/hw_handoff/デザイン名.hwh

これらのファイルをPYNQ上のフォルダに投げ込めば,Vivado上での操作は完了です.
(プロジェクト名.src/source_1/bd/デザイン名/hw_handoff/デザイン名.tcl は無くても動くみたいです。)

7.まとめ


ビットストリームの生成まで完成しました.次はPYNQ上での操作に移っていきます



©2021 shts All Right Reserved.

PYNQ V2.6でDMA転送をする話(JupyterNotebook上での実行)



前回に引き続きDMA転送の話です.今回は前回作成したビットストリームを実行していきます.今回のnotebookはこちらに上げています



1.PYNQで動かす


PYNQ上のJupyter Notebookにアクセスする話は割愛します.
とりあえず適当なからフォルダを作り,その中に先ほどの.bitと.hwhを入れ,.ipynbをそのフォルダ内で新規で作成しておきます..bitと.hwhはファイル名をそろえておきます(今後はdesign_1.bit, design_1.hwhとする).

1-1.オーバーレイのロード


from pynq import Overlay
from pynq import PL

OL = Overlay("design_1.bit")
print(OL.ip_dict.keys())
dma = OL.axi_dma_0

ビットストリームの読み込みはこれで完了です..bitのファイル名を使用して.hwhを読み込むようなので,名前はそろえておきましょう. OL.ip_dictには使用したIPのうち,PS側から操作できるものが辞書として登録されています. この辞書内からdmaのIPを探し出し,dmaとしておきましょう.

1-2.xlnkとAllocate


import numpy as np
from pynq import allocate
data_src = allocate((100,), dtype=np.int16)
for i in range(100):
    data_src[i] = i + 1

data_dst = allocate((512,), dtype=np.int16)

print("size of data_src :", data_src.nbytes, "Byte")
print("size of data_dst :", data_dst.nbytes, "Byte")

V2.6のPYNQでは,旧来より使われてきたXlnkに代わり,allocateを使うようになっています.今回はAXI-Streamのデータ幅を16bitにしたので,dtypeも16bitのモノに設定しました.入出力のバッファ領域をこれで確保しました.

1-3.実行


print(data_dst)

dma.sendchannel.transfer(data_src)
dma.sendchannel.wait()
print("send done")

dma.recvchannel.transfer(data_dst)
dma.recvchannel.wait()
print("receive done")

print(data_dst)

dmaのメンバ関数にsendchannel,recvchannelがあり,それぞれにtransferとwaitがあります.この辺は以前のPYNQと変わらずに使えます.

2.まとめ


とりあえずこのような形でPYNQ+DMAの動作を確認出来ました.
そのうちVivado HLSで作成したIPを埋め込み,動作させたいと思います.

参考


©2021 shts All Right Reserved.

2021年4月3日土曜日

スイッチサイエンスのジャンク品買ってみた


絶賛就活終わらない中の人です.先日Twitterを眺めていたところ,このような記事が流れてきました.



たまたまその時間は待機できそうだったので争奪戦に参加しました.結果1セット手に入れることができたので,少し見ていこうかと思います.


1.内容物



中身はこんな感じでした.内容物としては,

M5StickC x 1
M5Gray x 1
M5Faces x 1

となっていました(https://www.switch-science.com/catalog/7081/の例2のパターン).

2.少し動かしてみた


M5Gray.UIFlowのファームが元から書き込まれていた.

最初にUSBケーブルをつなぎ,電源だけを供給させて動作するか試してみました.
StickCとGrayについては,元からUIFlowのファームが書き込まれており,電源を入れただけで動作しました.

FacesにArduinoのサンプルコードを入れたところ.
加速度センサはMPU6886らしい.

Facesに関しては電源を入れても何も表示されませんでした.PCに接続したところCOMポートが割り当てられたので,試しにArduinoでサンプルコードを入れたところ無事動作することがわかりました.3種類のキーボードも動作することを確かめました.


唯一,Facesのベース内部のフレキが根元から断線していたため,グレードルが使えませんでした.今のところは特に使う場面は無いので,当面は放置になりそうです.

3.まとめ


ジャンクということもあり全く動かないことを覚悟していたのですが,あっさり動いてしまいました.返品されたもののうち軽い修正で使えそうなものを選んだとなっていたのですが,出荷前にある程度の修理や動作確認も行ったように見えました.
保証は当然なく,壊れても自己責任なので初心者には絶対にお勧めできませんが,個人的には満足な買い物でした.


©2021 shts All Right Reserved.