[電子工作]PYNQ:PythonでFPGAを動かそう

ーーーー目次ーーーー

1.PYNQとは?

2.PYNQの使い方

 

RISC-V互換 32bit/64bitプロセッサの設計に挑戦

ーーーーーーーーーー

1.PYNQとは?

  PYNQとは、Xilinx社のARMを搭載したFPGAのプラットフォームを容易に扱えるようにする

  オープンソースのプロジェクトです。具体的には、Zynqを搭載したボード上でLinuxとPython

  が動作し、ハードとソフトの開発が可能となります。公式HPはこちら

  PYNQ対応のボードは様々な会社から提供されていますし、非対応のボードに移植している方

  もいます。こちらに紹介されているボードを入手すれば、確実に動作させることが可能です。

  私が入手したのは、上記の写真のPYNQ-Z2です。仕様は次の通り。

 

   搭載FPGA(Zynq):Xilinx社 XC7Z020-1CLG400C

    CPU:ARM Cortex-A9 デュアルコア650MHz

    ロジックセル:85K

    ブロックRAM:4.9Mbit

    DSPスライス:220

   DDR3:512MB、16bit、1050Mbps

   QSPI Flash Memory:16MB

   MicroSDカードスロット

   HDMI:入力1ポート、出力1ポート

   USB:USB2.0ホスト1ポート、スレーブ1ポート(JTAG、UART)

   Gigabitイーサネット

   オーディオ:入力、出力(24bit DAC)

   Arduinoコネクタ:3.3V I/O

   Raspberry Piコネクタ

 

  CPUの性能やメインメモリが心もとないですが、FPGAでアクセラレーションできますので、大して

  問題ではありません。特徴は次の通り。

   ・microSDカードに標準のPYNQ環境を書き込めば、LinuxとJupyter Notebookが起動。

   ・最大130万ゲート相当のFPGAとして使える。

   ・Pythonから、FPGAのbitファイルの書き込み、レジスタの読み書きが行える。

   ・Pythonから、HDMI入力から映像をキャプチャしたり、HDMI出力で映像を表示できる。

   ・音声の入出力もできるはず。(私は試していません。)

   ・A.I.(Deep learning)処理を、FPGAでアクセラレーション可能。

   ・回路合成は、Vivadoのweb pack(無償版)でOK。(30日以降も使用可能)

  つまり、このボードとPCさえあれば、bitファイルの書き込みも、回路合成も、出来てしまいます。

  追加の費用が不要です!

  そして、最も適した使い方は、「HDMI入力の映像や音声を入力し、FPGA(Programmable Logic)で

  A.I.処理し、HDMIで映像出力したり、音声を出力する。」でしょう。

 

  なお、私がこのボードを購入した理由は次のとおりです。

  自作RISC-V中国製FPGAで開発していましたが、使用率が97%を超え、これ以上の拡張ができなく

  なったため、50万ゲート以上のFPGAを探していました。どれも2.5万円以上して高いなぁ、と思っていた

  ところ、Xilinx社のXC7Z020を搭載したボードも同じぐらいの価格で売られています。そこでXC7Z020を

  搭載したボードを検索し、一番安かったのが、上記のTUL社のPYNQ-Z2です。残念ながら国内の一般

  向け販売店で扱っているところがなかったのでU.S.A.の販売店から購入しましたが、価格は何と、

  1.5万円強!(送料無料) 思わず買ってしまいました。

 

2.PYNQの使い方

  PYNQの初期設定や、標準でついているサンプルの使い方は、ネットで様々な記事がありますので、

  ここでは、自分で作成した回路を動かす手順を記載します。

  まず、PYNQボード以外に必要なものは次の通り。

    ・8GB以上のMicroSDカード:こちらのSD cardイメージを書き込んで下さい。

    ・PC:WindowsやLinuxが動けば良いです。回路合成する時は、高速、大容量メモリ(多分、16GB以上)

       のものを準備下さい。

    ・Vivado(私は、Linux版の2020.2を使用):web pack(無償版)を標準設定でPCにインストール。

    ・有線LAN環境:上の写真のようにクロスのLANケーブルでPCと直結でもOK。

       但し、IPアドレスが、ディフォルトの192.168.2.99になる場合と、ルータに接続していた時のアドレスに

       なる場合があり、両方で通信を確認する必要あり。

    ・microUSBケーブル:電源ケーブル、兼シリアルモニタ(8bit,115.2kbps)

       シリアルモニタは、ディフォルトで、ユーザー名xilinx、パスワードxilinxでログインできます。

       >>ifconfig

       などでIPアドレスが確認できますし、Linuxのコマンドが使えます。

    ・HDMIで映像の入出力を行う場合は、HDMI出力をもつ機材(PCでも良い)と、HDMIモニタが必要。

 

  開発の手順は次の通り。

  (1)TUL社のHPから、PYNQ-Z2のBoardFile、XDCをダウンロードする。

  (2)ここの記事等を参考に、Vivadoを使って、baseとなるoverlayを作成する。

     GPIOを端子に接続する必要はありませんが、自作の回路を動かす場合でもPYNQの環境が動く

     必要がありますので、baseでエラーが出ないように作っておく必要があります。手順は次の通り。

    1. Quick StartでCreate Projectを選び、適当な名前を付ける。

    2. Project TypeでRTL Project、Default PartーBoardでPYNQ-Z2を選択。

    3. IP INTEGRATORーCreate Block Designを起動。

    4. ZYNQ7 Processint Systemを選択し、Run Block Automationを実行。他のブロックを接続しない

      場合は、手でM_AXI_GP0とACLKFCLK_CLK0を接続。

    5. Sourcesのdesign_1を右クリックして、Create HDL Wrapperを実行。

    6. 一旦、GenerateBitstreamを実行し、エラーが出ないことを確認する。

  (3)(2)で出来たフォルダーに、自作回路のRTLやxdcファイルを置きましょう。

  (4)自作回路のxdcを作成します。全端子、コメントアウト(先頭に「#」が付いている)されているので、

     使いたい端子の設定の「#」を削除します。

     自作RISC-V(RISCV32IMAFC)の場合、次の行を書き換えました。

     Arduino Digital I/O On Outer Analog Headerとしてa[5:0]も使えますが、デジタルI/Oと同時に

     XADCが接続されており、1Vに落とすための分割抵抗が付いています。

     そのため、プルダウン抵抗が付いたような接続になっているため、使う時は注意して下さい。

## Clock signal 125 MHz

set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { sysclk }]; #IO_L13P_T2_MRCC_35 Sch=sysclk

create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { sysclk }];

##LEDs

set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { led[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]

set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { led[1] }]; #IO_L6P_T0_34 Sch=led[1]

set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { led[2] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=led[2]

set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { led[3] }]; #IO_L23P_T3_35 Sch=led[3]

##Buttons

set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { btn[0] }]; #IO_L4P_T0_35 Sch=btn[0]

set_property -dict { PACKAGE_PIN D20 IOSTANDARD LVCMOS33 } [get_ports { btn[1] }]; #IO_L4N_T0_35 Sch=btn[1]

set_property -dict { PACKAGE_PIN L20 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L9N_T1_DQS_AD3N_35 Sch=btn[2]

set_property -dict { PACKAGE_PIN L19 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L9P_T1_DQS_AD3P_35 Sch=btn[3]

##Arduino Digital I/O

set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { ar[0] }]; #IO_L5P_T0_34 Sch=ar[0]

set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { ar[1] }]; #IO_L2N_T0_34 Sch=ar[1]

set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { ar[2] }]; #IO_L3P_T0_DQS_PUDC_B_34 Sch=ar[2]

set_property -dict { PACKAGE_PIN V13 IOSTANDARD LVCMOS33 } [get_ports { ar[3] }]; #IO_L3N_T0_DQS_34 Sch=ar[3]

set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { ar[4] }]; #IO_L10P_T1_34 Sch=ar[4]

set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { ar[5] }]; #IO_L5N_T0_34 Sch=ar[5]

set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { ar[6] }]; #IO_L19P_T3_34 Sch=ar[6]

set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { ar[7] }]; #IO_L9N_T1_DQS_34 Sch=ar[7]

set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { ar[8] }]; #IO_L21P_T3_DQS_34 Sch=ar[8]

set_property -dict { PACKAGE_PIN V18 IOSTANDARD LVCMOS33 } [get_ports { ar[9] }]; #IO_L21N_T3_DQS_34 Sch=ar[9]

set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { ar[10] }]; #IO_L9P_T1_DQS_34 Sch=ar[10]

set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { ar[11] }]; #IO_L19N_T3_VREF_34 Sch=ar[11]

set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { ar[12] }]; #IO_L23N_T3_34 Sch=ar[12]

set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { ar[13] }]; #IO_L23P_T3_34 Sch=ar[13]

  (5)Vivadoで自作の回路RTLをAdd sourcesする。そして、「design_1_wrapper.v」を開くか、エディタで、

     「フォルダ名.gen/sources_1/bd/design_1/hdl/design_1_wrapper.v」を起動し、元の記述を残したまま、

     自分の回路を追加する。

     自作RISC-V(RISCV32IMAFC)の場合、次のような記述を追加しました。

module design_1_wrapper

・・・

RISCV32IMAFC u_riscv (

.sysclk (sysclk),//125MHz

.btn (btn),

.led (led),

.ar (ar)); 

endmodule

  (6)VivadoでSynthesis、Implementation、Generate Bitstreamし、bitファイルを生成する。

  (7)回路合成できたbitファイルとhwhファイルをPYNQにアップロードします。ディフォルトの設定では、bitファイルは

    「プロジェク名.runs/impl_1/」の下、hwhファイルは「プロジェクト名.gen/sources_1/bd/design_1/hw_handoff/」

    の下にあります。

    Linuxの場合、PCから次のようなコマンドでPYNQの pynq/overlays/ノートブック名 の下にコピーします。

      >>scp design_1_wrapper.bit xilinx@192.xxx.xxx.xxx:pynq/overlays/test/test.bit

      >>scp design_1.hwh xilinx@192.xxx.xxx.xxx:pynq/overlays/test/test.hwh

  (8)PYNQのJupyter NotebookでNewを押して、(7)と同じ名前の新しいノートブックを作成します。そして、

from pynq import Overlay

base = Overlay("./test.bit")

    のようなコマンドを入力しrunすれば、そのbitファイルがFPGA部分に読み込まれ、実行されます。

 

おまけ.PYNQによる、RISC-V互換 32bit/64bitプロセッサの設計に挑戦はこちら

 

Arduinoの紹介はこちら。 

 

「TangPrimer:中国製FPGAでプロセッサの設計に挑戦」はこちら。