PYNQで始めるFPGA開発 - 入門編 使用

FPGAってなに?開発コストが高いんじゃないの?Pythonで開発できるってほんと?初心者でも簡単にC++とPythonだけでFPGA開発する方法を調べました!まだプログラミングで消耗している非フルスタックなエンジニアの方は、非ノイマン型エンジニアになってもっと消耗しましょうハードも作れるフルスタックなエンジニアを目指しましょう!!
什么是FPGA?开发成本不是很高吗?你真的可以用Python开发吗?我们已经研究了如何简单地使用C++和Python开发FPGA,即使是初学者!如果你是一个非全栈工程师,你仍然在编程中精疲力竭,成为一个非诺伊曼工程师,让你变得更精疲力竭,成为一个全栈工程师,你也可以做硬件!

はじめに 导言(导言)

FPGAはField-Programmable Gate Arrayの略で、製造後に回路構成を変更することができます。CPUやGPUなどの汎用集積回路と違い、FPGAは用途に応じて構成を変更することができるため、ハマる用途ではCPUやGPUよりも速い処理が可能です。用途に特化した回路で処理するためオーバーヘッドが少なく、高い電力効率も実現できます。
FPGA是现场可编程门阵列的缩写,可以在制造后改变电路配置。与CPU和GPU等通用集成电路不同,FPGA可以根据应用程序更改配置,因此对于上瘾应用程序,处理速度比CPU和GPU快。由于它是由特定于应用的电路处理的,因此它可以实现更高的功率效率。

CPUやGPUを使うには設計した計算アルゴリズムをC言語などのソフトウェアプログラムとして記述します。FPGAを使うには論理回路を設計し、Verilogなどのハードウェア記述言語(HDL)で回路を記述します。回路設計を行う必要があるためCPUやGPUのソフトウェア記述と比べて開発コストが高いとされています。
要使用CPU或GPU,请将设计的计算算法编写为软件程序,如C语言。要使用FPGA,您需要设计逻辑电路,并使用硬件描述语言(HDL)(如Verilog)来描述电路。由于需要进行电路设计,因此与CPU和GPU的软件描述相比,开发成本较高。

近年、高位合成(HLS)と呼ばれる回路設計手法が注目されています。従来のHDL設計ではクロックなど具体的な回路動作を設計する必要がありました。HLS設計ではC言語などを用いて計算アルゴリズムを記述することで回路を設計することができ、具体的な回路動作を設計する必要がないため短期開発を実現します。また、抽象度の高いアルゴリズムで記述できるためテストを簡単に行えます。某ゲーム会社のハードを実装したの記事1によると、HLS設計はHDL設計より30倍短い期間で開発できるそうです。
近年来,被称为高阶合成(HLS)的电路设计方法引起了人们的关注。在传统的HDL设计中,有必要设计具体的电路行为,如时钟。在HLS设计中,可以通过使用C语言等语言编写计算算法来设计电路,由于不需要设计具体的电路操作,因此可以实现短期开发。此外,由于可以用高抽象度的算法编写,测试也很容易。根据一家游戏公司的硬件实现的文章1,HLS设计的开发时间比HDL设计短30倍。

本稿では、FPGAを用いた数値計算の高速化について説明します。Xilinx社Vivado HLS2によるFPGA回路設計と、PYNQ3による制御プログラム設計について説明し、HLS設計でパフォーマンスを出すための最適化手法について解説します。本稿はソフトウェア開発の経験がある方、FPGA開発をこれから始める方、FPGA設計に興味がある方を想定しています。FPGAとは何かもっと知りたい方はXilinxが日本語で出している公式のFPGA講座4をおすすめします。
在本文中,我们将讨论如何使用FPGA来提高数值计算的速度。本文介绍了Xilinx VivadoHLS2的FPGA电路设计和PYNQ3的控制程序设计,并解释了HLS设计中性能的优化方法。本文是为那些有软件开发经验的人,即将开始FPGA开发的人,或者对FPGA设计感兴趣的人设计。如果你想了解更多关于FPGA的信息,我推荐Xilinx以日语提供的官方FPGA课程4

Vivado HLS

Vivado HLS2とはXilinx社が提供する高位合成ソフトを含む開発環境です。C++で記述されたプログラムをVerilogなどのHDLによる回路記述に変換するコンパイラだと思って良いでしょう。同時に付属するVivadoはFPGA回路を設計するための回路合成ソフトです。Vivado HLSによって合成されたHDLなどの回路記述はVivadoによってFPGA回路に合成されます。こちらはコンパイラと違い、論理回路の配置や配線を行う回路合成ソフトです。これらのソフトは一部無料で提供されていますが、本稿で紹介する設計法は全て無料の機能で実現できます。
VivadoHLS2是Xilinx公司提供的一个高级合成软件开发环境。你可以把它看作是一个编译器,它将用C++编写的程序转换为HDL编写的电路描述,如Verilog。Vivado是一个电路合成软件,用于设计FPGA电路。由Vivado HLS合成的HDL等电路描述由Vivado合成为FPGA电路。与编译器不同的是,它是一个电路合成软件,用于配置逻辑电路和布线。虽然这些软件是免费提供的,但本文中介绍的所有设计方法都可以通过免费功能实现。

環境

動作するOSはWindowsとLinuxです。特に理由がない場合はUbuntuの使用を推奨します。本稿執筆時のVivado HLSの最新バージョンは2020.1です。本稿では2020.1を元に説明していきます。インストール手順は公式ドキュメント5に詳しいですが、以下にUbuntu 18.04でのインストール手順について簡単に説明します。Ubuntu Serverへのインストールでは追加の手順が必要となります。
运行的操作系统是Windows和Linux。如果没有特别的原因,建议使用Ubuntu。在撰写本文时,Vivado HLS的最新版本是2020.1。在这篇文章中,我将以2020.1为基础解释这一点。官方文档5中详细介绍了安装说明,但下面是Ubuntu 18.04中安装说明的简要说明。在Ubuntu服务器上安装需要额外的步骤。

  1. PCを用意します。4コア以上の64bitCPU、16GBメモリ、空き200GB以上のストレージを推奨します。
    我准备好了PC。建议使用四核或更高的64位CPU、16 GB内存和200 GB或更多可用存储空间。
  2. Ubuntu 18.04をインストールします。筆者はMacBook ProにVMWare Fusionを用いた仮想環境を用います。
    安装Ubuntu 18.04。笔者在MacBook Pro中使用了使用VMWare Fusion的虚拟环境。
  3. Xilinxのアカウントを用意し、ダウンロードページからVivado HLSのインストーラをダウンロードします。
    准备好Xilinx帐户,并从下载页面下载Vivado HLS安装程序。
  4. Ubuntuのターミナルから $ sudo sh ./Xilinx-(略)64.bin を実行します。
    在Ubuntu终端中使用$ sudo sh ./运行Xilinx-(缩写)64.bin
    ※オプションは全てデフォルトのまま続行します。 所有选项都将继续默认设置。
    ※インストールには数時間かかることがあります。 * 安装可能需要几个小时。
    ※OSのタイムゾーン設定が間違っている場合、インストールに失敗することがあります。
    * 如果操作系统的时区设置不正确,安装可能会失败。
  5. インストールが完了したら、ターミナルからVivado HLSとVivadoの起動を確認します。
    安装完成后,检查Vivado HLS和Vivado从终端启动。
    ※Vivado HLSの起動は $ /tools/Xilinx/Vivado/2020.1/bin/vivado_hls を実行します。
    要启动Vivado HLS,请运行$ /tools/Xilinx/Vivado/2020.1/bin/vivado_hls
    ※Vivadoの起動は $ /tools/Xilinx/Vivado/2020.1/bin/vivado を実行します。
    要启动Vivado,请运行$ /tools/Xilinx/Vivado/2020.1/bin/Vivado

PYNQ

PYNQ3とはXilinx社のFPGAを簡単に扱うPythonライブラリと、Ubuntuベースの組み込み用イメージで構成されるオープンソースのプロジェクトです。本稿において、単にPYNQという場合はPythonライブラリのことを指します。PYNQを使うことで設計したFPGA回路の制御やCPU-FPGA間のデータ転送をPythonだけで記述することが可能となります。組み込み用のFPGA SoCからサーバー用FPGAカードにまで幅広い用途で使うことができます。PYNQのダウンロードで配布されているイメージファイルをSDカードに書き込むだけで使うことができます。
PYNQ3是一个开源项目,由Xilinx的FPGA易于处理的Python库和基于Ubuntu的嵌入式映像组成。在本文中,PYNQ指的是Python库。使用PYNQ设计的FPGA电路的控制和CPU-FPGA之间的数据传输都可以只用Python来描述。它可以用于从嵌入式FPGA SoC到服务器FPGA卡的广泛应用。您可以简单地将PYNQ下载中分发的图像文件刻录到SD卡上。

環境

PYNQは組み込み用のFPGA SoCからサーバー用FPGAカード、AWS上のクラウドサービスでも使うことが可能です。本稿執筆時のPYNQの最新バージョンは2.5.1です。本稿では組み込み用のFPGA SoCボードであるPYNQ-Z16 FPGAボードを元に説明します。PYNQセットアップ方法は公式ドキュメント7に詳しいですが、以下に簡単に説明します。
PYNQ可以用于嵌入式FPGA SoC、服务器FPGA卡和AWS上的云服务。在撰写本文时,PYNQ的最新版本是2.5.1。在本文中,我们将以PYNQ-Z16 FPGA板为基础,这是一种嵌入式FPGA SoC板。如何设置PYNQ在官方文档7中有详细的描述,下面是一个简短的解释。

  1. PYNQ-Z16(Z2やUltra96[^ultra96]でも可)を手元に用意します。必ず実機を用意してください。
    PYNQ-Z16(也可以是Z2或Ultra 96 [^ultra 96])可用。一定要准备好真正的东西。
    ※その他に有線LANケーブルとLANスイッチの空きポートが必要です。
    * 其他需要有线LAN电缆和可用的LAN交换机端口。
    ※PYNQ-Z2やUltra96では一部の説明が異なりますが、大まかな開発法は同じです。
    ※PYNQ-Z2和Ultra 96的部分描述有所不同,但大致的开发方法是相同的。
  2. PYNQのダウンロードから手元のFPGAボードに合うSDカードイメージをダウンロードします。
    从PYNQ下载中下载SD卡图像,以匹配您的FPGA板。
    ※Ultra96用のSDカードイメージはこちらで配布されています。
    ※ Ultra 96的SD卡映像可在分发。
  3. ダウンロードしたイメージをSDカードに書き込みます。
    将下载的图像写入SD卡。
    Etcherというイメージ書き込みソフトがおすすめです。
    我推荐一个名为Etcher的图像写入软件。
  4. SDカードを挿入し、電源ケーブルと有線LANを接続し、FPGAボードの電源を入れます。
    插入SD卡,连接电源线和有线局域网,然后打开FPGA板。
  5. PCのブラウザから http://pynq:9090/ に接続して、Jupyter Notebookにアクセスできることを確認します。
    从您的PC浏览器连接到http://pynq:9090/,并确保您可以访问Jupyter Notebook。
    ※LAN環境によってはhostnameを解決できないことがあります。
    * 在某些局域网环境中,主机名可能无法解析。
    ※その場合は、DHCPからFPGAボードに割り当てられたIPアドレスを頑張って探します。
    在这种情况下,请努力查找从DHCP分配给FPGA板的IP地址。
    ※http://:9090/ とすることでJupyter Notebookにアクセスできます。
    您可以通过http://:9090/访问Jupyter Notebook。

PYNQを用いたFPGA設計のアウトライン 基于PYNQ的FPGA设计大纲

1. 高速化するターゲットの決定 1.确定要加速的目标

これが最も重要なことです。どの計算を高速化するか決定します。FPGAでの設計を開始する前に、対象の計算がFPGAによってどのくらい高速化できるのか見積もりを立てることも重要です。本稿では簡単な行列積の高速化を例に設計法を説明します。
这是最重要的事情。决定要加快哪些计算速度。在开始使用FPGA进行设计之前,对FPGA可以加快目标计算速度的估计也很重要。本文以一种简单矩阵积的加速为例,说明了设计方法。

2. Vivado HLSによるC++設計 2. Vivado HLS的C++设计

C++を用いて高速化したい計算の動作を記述します。この記述のことを計算カーネルと呼びます。Vivado HLSでは設計される回路パフォーマンスを見積もることができます。所定のパフォーマンスを満たすかこの段階で確認し、パフォーマンスを満たさない場合は計算カーネルの再設計が必要です。
描述您希望使用C++加速的计算行为。这个描述被称为计算内核。Vivado HLS允许您估计要设计的电路性能。在此阶段,您将检查是否满足预定的性能,如果性能不满足,则需要重新设计计算内核。

3. Vivadoでの合成 3. Vivado中的合成

Vivado HLSで設計した計算カーネルを、Vivadoを用いてFPGA回路へと合成します。この工程では周辺回路やクロック信号の接続を設計し、動作周波数などのパラメータを設定します。大規模な回路の設計ではVivadoを用いたFPGA回路の合成には数十時間から数日かかることがあります。簡単な行列積の高速化でも、合成時間には数十分かかります。しかし、多くの場合で、この工程が一回で完了することは稀です。
用Vivado HLS设计的计算内核被合成为FPGA电路。在此过程中,设计外围电路和时钟信号的连接,并设置工作频率和其他参数。在大型电路设计中,使用Vivado合成FPGA电路可能需要几十个小时甚至几天的时间。即使是简单的矩阵积加速,合成时间也需要几十分钟。然而,在许多情况下,这一过程很少在一次中完成。

4. PYNQでのホスト記述 4. PYNQ中的主机描述

PYNQを用いてFPGA回路を制御するホストプログラムを記述します。FPGA回路化を行なった計算カーネルを呼び出して利用するための周辺プログラムが必要です。行列積の高速化の例では、画像ファイルなど行列データの読み書き、CPUとFPGA間のデータ転送、FPGA回路の制御、計算結果の検証などが必要となります。PYNQではホストプログラムを全てPythonで記述するため、開発コストが削減され、既存のPythonライブラリとの連携が容易になります。
编写了一个利用PYNQ控制FPGA电路的主机程序。需要一个外围程序来调用和使用FPGA电路化的计算内核。在矩阵积加速的例子中,需要读取和写入图像文件和其他矩阵数据,在CPU和FPGA之间传输数据,控制FPGA电路,验证计算结果。由于PYNQ的所有主机程序都是用Python编写的,因此降低了开发成本,并简化了与现有Python库的集成。

Vivado HLSによる計算カーネルの設計 基于Vivado HLS的计算内核设计

ターゲット計算を決める 确定目标计算。

ここから、FPGA回路を設計を開始します。本稿では実装が簡単な数値計算である行列積の高速化を行います。また数値型や数値計算精度を決める必要があります。FPGAの特徴として計算精度を柔軟に変更できる点が挙げられ、端数の精度や異なる精度の演算を定義することができます。今回の実装では符号付き16bit整数(short型)とします。Pythonの数値計算ライブラリNumpyで記述すると以下となります。高速化ターゲットは最後の一行 y = np.dot(x1, x2) に該当します。
从这里,我们开始设计FPGA电路。本文提供了矩阵积的加速,这是一种易于实现的数值计算。此外,还需要确定数值类型和数值计算精度。FPGA的一个特点是可以灵活地改变计算精度,并允许您定义分数精度和不同精度的计算。在这个实现中,它是一个有符号的16位整数(短类型)。在Python的数值计算库Numpy中,它如下所示:加速目标位于最后一行y = np.dot(x1, x2)

import numpy as np
x1 = np.random.randint(-1000, 1000, size=(16, 16), dtype=np.int16)
x2 = np.random.randint(-1000, 1000, size=(16, 16), dtype=np.int16)

y = np.dot(x1, x2)

新規HLSプロジェクトの作成 创建新的HLS项目

  1. Vivado HLSを起動します。 启动Vivado HLS。
    Ubuntuでは、ターミナルで $ /tools/Xilinx/Vivado/2020.1/bin/vivado_hls を実行します。
    在Ubuntu中,在终端中运行$ /tools/Xilinx/Vivado/2020.1/bin/vivado_hls
    Windowsではアプリ一覧からVivado HLSを選択して起動します。
    在Windows中,从应用程序列表中选择Vivado HLS并启动它。
  2. 次の画面が表示されます。Create New Projectをクリックします。
    您将看到下一个屏幕。单击“创建新项目”。
  3. プロジェクト名を入力します。今回は matrix-product とし、Nextをクリックします。
    输入项目名称。这一次,我们将使用matrix-product,然后单击Next。

    PYNQ开始FPGA开发-入门_#pragma

  4. 合成対象とするC++の関数名であるTop Functionを決めます。今回は product とし、Nextをクリックします。
    选择要合成的C++函数的顶部函数。这一次,我们将选择产品,然后单击下一步。
    Top Functionに指定した文字列は、PYNQライブラリからカーネルを呼び出す際に必要となります。
    从PYNQ库调用内核时,需要为Top Function指定的字符串。

    PYNQ开始FPGA开发-入门_Python_02

  5. テストベンチを追加します。今回は何も追加しません。空欄のままNextをクリックします。
    添加一个测试台。这一次我什么也不加。将其保留为空白,然后单击Next。

    PYNQ开始FPGA开发-入门_#pragma_03

  6. ターゲットFPGAの情報を入力します。Part Selectionの右の[...]をクリックするとデバイス選択画面が表示されます。
    输入目标FPGA的信息。在零件选择的右边[.]单击显示设备选择屏幕。
    PYNQ-Z1 / Z2の場合は以下のように xc7z020clg484-1 を選択します。
    对于PYNQ-Z1 / Z2,请选择xc 7z 020 clg 484 -1,如下所示:
    Ultra96の場合は xczu3eg-sbva484-1-e を選択します。
    对于Ultra 96,请选择xczu 3eg-sbva 484 - 1-e
  7. OKをクリックすると選択したデバイスが表示されています。Finishをクリックするとプロジェクトが作成されます。
    单击OK查看选定的设备。单击Finish创建项目。
    その他にClockの設定がありますが初期値のまま続行します。これらの設定は後から変更することが可能です。
    还有其他的时钟设置,但保持初始值。您可以稍后更改这些设置。

    PYNQ开始FPGA开发-入门_Python_04

C++によるの計算カーネル作成 使用C++创建计算内核

  1. カーネルを記述するC++ファイルを作成します。左側のメニューにあるSourceを右クリックし、New Fileを選択します。
    创建一个描述内核的C++文件。右键单击左侧菜单中的Source,然后选择New File。
  2. 作成するファイル名を kernel.cpp とします。ファイルの保存場所に指定はありません。OKをクリックします。
    假设要创建的文件名为kernel.cpp。没有指定文件的位置。然后单击OK。

    PYNQ开始FPGA开发-入门_#pragma_05

  3. プロジェクトに新規ファイルが追加されました。計算カーネルを記述します。以下のサンプルをコピーしてください。
    已将新文件添加到项目中。描述一个计算内核。请把下面的样品复印一份。
    プラグマ #pragma HLS XXX... によってVivado HLSのコンパイラに追加の指示を与えてます。
    注记#pragma HLSXXX... 它为Vivado HLS编译器提供了额外的说明。
    このサンプルでは、回路の制御方式 (port=returnの行) と、入出力引数のデータ転送方式を指示しています。
    本示例说明了电路的控制方法(端口=返回行)和输入/输出参数的数据传输方法。
    PYNQでFPGA回路を制御するためにはこれらのプラグマが必要となるため、忘れないようにしましょう。
    为了使用PYNQ控制FPGA电路,这些插头是必要的,所以不要忘记。

kernel-1.cpp

void product(short x1[16][16], short x2[16][16], short y[16][16]) {
#pragma HLS interface s_axilite port=return
#pragma HLS interface s_axilite port=x1
#pragma HLS interface s_axilite port=x2
#pragma HLS interface s_axilite port=y
    for (int i=0; i<16; i++) {
        for (int j=0; j<16; j++) {
            short t = 0;
            for (int k=0; k<16; k++) {
                t += x1[i][k] * x2[k][j];
            }
            y[i][j] = t;
        }
    }
}

HLS合成とレポート HLS综合和报告

  1. HLS合成を実行します。上部アイコンの右三角ボタン [▶︎] C Synthesisをクリックします。
    运行HLS合成。单击顶部图标上的右三角形按钮[▶] C合成。
  2. 以下のような合成レポートが表示されたら合成は成功です。合成結果に関する情報を見ることができます。
    如果出现以下合成报告,则合成成功:您可以查看有关合成结果的信息。
    Performance Estimatesでは計算カーネルのパフォーマンスの見積もりを見ることができます。
    在性能估计中,您可以看到计算内核的性能估计。
    Latencyの項目にサイクル数と実行時間の見積もりが表示されています。
    延迟字段显示估计的周期数和运行时间。
    今回の計算カーネルの実行には8737クロックサイクルが必要で、87.370マイクロ秒の時間がかかることがわかります。
    我们可以看到,这个计算内核的运行需要8737个时钟周期,需要87.370微秒的时间。
    レポートを閉じてしまった場合、上部アイコンのOpen Report (C Synthesisの3つ右) から合成レポートが開けます。
    如果您关闭了报表,则将从顶部图标的Open Report (C Synthesis中的右三个)打开合成报表。

    PYNQ开始FPGA开发-入门_Python_06

  3. IPコアを作成します。上部アイコンのExport RTL (C Synthesisの2つ右) をクリックします。
    创建一个IP核心。单击顶部图标中的“导出RTL”(C Synthesis右侧的两个)。
    IPコアとはFPGA回路を合成するための回路情報と入出力データの通信方式が含まれるパッケージです。
    IP核心是一个包含电路信息和输入/输出数据通信方法的封装,用于合成FPGA电路。
    HLS合成したカーネルをVivadoから呼び出すためにIPコアの形式を経由します。 HLS通过IP核心的形式从Vivado调用合成的内核。 
  4. Export RTLの画面が表示されたら、デフォルトの設定のままOKをクリックしてIPコア(IPカタログ)を作成します。
    出现Export RTL屏幕时,单击OK保留默认设置以创建IP核心(IP目录)。
    Vivado HLSでの作業は以上になります。  与Vivado HLS的合作将是更多。

    PYNQ开始FPGA开发-入门_#pragma_07

Vivadoを用いた回路合成 使用Vivado的电路合成

次に、FPGA回路を合成するためのVivadoプロジェクトを作成します。
接下来,我们创建了Vivado项目来合成FPGA电路。
HLSによる開発では、Vivadoを用いてHLS設計したIPとベンダーに用意されている設計済みIPの配置と配線を行います。
在使用HLS进行开发的过程中,将使用Vivado对HLS设计的IP和供应商提供的预先设计的IP进行放置和布线。

新規Vivadoプロジェクトの作成 创建新的Vivado项目

  1. Vivadoを起動します。 启动Vivado。
  2. プロジェクトを作成します。  创建一个项目。
  3. 次の画面が表示されます。Nextをクリックします。
    您将看到下一个屏幕。单击Next。
  4. 次のように記入してNextをクリック。  填写以下内容,然后单击下一步。

    PYNQ开始FPGA开发-入门_Ubuntu_08

  5. そのままでNextをクリック。  继续,点击下一步。
  6. そのままでNextをクリック。  继续,点击下一步。

    PYNQ开始FPGA开发-入门_Ubuntu_09

  7. そのままでNextをクリック。  继续,点击下一步。
  8. パーツにxc7z020clg484-1を選択して、Nextをクリック。
    为部件选择xc 7z 020 clg 484 -1,然后单击Next。

    PYNQ开始FPGA开发-入门_Python_10

  9. Finishをクリックするとプロジェクトが作成されます。
    单击Finish创建项目。

IP設計

  1. ブロックデザインを作成します。 创建一个block design。
    IP INTEGRATORのCreate Block Designをクリックします。
    在IP集成器中单击“创建块设计”。
  2. OKをクリック。  按一下OK。
  3. Vivado HLSで作成したIPコアを追加します。
    添加在Vivado HLS中创建的IP核心。
    上部の歯車アイコンからSettingを開きます。  从顶部的齿轮图标打开设置。

    PYNQ开始FPGA开发-入门_Python_11

  4. IPのRepositoryタブを開いて、ブラスボタンをクリックします。
    打开IP的存储库选项卡,然后单击黄铜按钮。
  5. HLS設計で作成したプロジェクトを選択します。  选择在HLS设计中创建的项目。

    PYNQ开始FPGA开发-入门_Python_12

  6. IPsの右の数字が1となっていれば、HLS設計したIPが認識されています。
    如果IP右边的数字为1,则HLS设计的IP已被识别。

    PYNQ开始FPGA开发-入门_Python_13

  7. OKをクリックして設定を閉じます。  单击OK关闭设置。
  8. ハードコアされたARM CPUを認識するためのIPコアを追加します。
    添加一个IP核心来识别硬核ARM CPU。
    Diagramの中のプラスボタンから、図のようにZYNQを選択します。
    从“Diagram”中的加号按钮中选择ZYNQ,如图所示。

    PYNQ开始FPGA开发-入门_#pragma_14

  9. 同様にHLSで設計したIPを追加します。 添加一个类似的IP在HLS中设计。
    HLSの関数名と同じ、Productとなっています。
    它与HLS中的函数名相同,是Product。

    PYNQ开始FPGA开发-入门_Python_15

  10. 二種類のAutomationを実行します。  我们有两种类型的自动化。
  11. バリデーションを実行して、エラーがないことを確かめます。
    运行验证以确保没有错误。 

    PYNQ开始FPGA开发-入门_Ubuntu_16

  12. 最後に、ラッパーRTLを作成します。  最后,我们将创建一个rapper RTL。 

回路合成と成果物 电路合成和产品

  1. 回路合成を行います。合成は数十分かかります。 我们将进行电路合成。合成可能需要几十分钟。
    左部メニューのGenerate Bitstreamをクリックし、ポップアップのOKをクリックします。
    单击左侧菜单中的Generate Bitstream,然后单击弹出窗口中的OK。
  2. 合成結果の成果物として.bitと.hwhファイルが生成されます。
    将生成.bit和.hwh文件作为合成的交付项。
    FinderからVivadoプロジェクトのディレクトリを開いてファイル名で検索すると簡単に見つかります。
    通过从Finder中打开Vivado项目目录并按文件名搜索,您可以很容易地找到它。
    分かりやすいディレクトリにコピーしておきます。 将其复制到易于理解的目录中。
    Vivado環境での作業は以上です。  在Vivado环境中工作就到此为止了。

    PYNQ开始FPGA开发-入门_#pragma_17

     

    PYNQ开始FPGA开发-入门_Ubuntu_18

     

PYNQでホストプログラムを書く 用PYNQ编写宿主程序

次に、FPGA回路を制御するホストプログラムを記述します。
然后编写了控制FPGA电路的主机程序。
実際にFPGAで数値計算を実行します。 实际上,数值计算是在FPGA上进行的。

Jupyter Notebookの作成とホストプログラムの記述
创建Jupyter Notebook并编写宿主程序

  1. PCのブラウザからJupyter Notebookにログインします。デフォルトパスワードはxilinxです。
    从PC的浏览器登录到Jupyter Notebook。默认密码为xilinx。

    PYNQ开始FPGA开发-入门_#pragma_19

  2. 作業用ディレクトリを作成し、作成したディレクトリに移動します。
    创建工作目录并导航到您创建的目录。
  3. design_1_wrapper.bitとdesign_1.hwhをアップロードします。
    上载design_1_wrapper.bit和design_1.hwh。
    design_1_wrapper.bitをdesign_1.bitにリネームしておきます。
    我们将design_1_wrapper.bit重命名为design_1.bit。
  4. 新しいNotebookを作成します。  创建一个新的笔记本。

    PYNQ开始FPGA开发-入门_Ubuntu_20

  5. 以下のサンプルプログラム1を記述して実行する。 描述并执行以下示例程序1。
    テキストエリアにコピペしてください。 请把它复制到文本区域。
    実行にはRunをクリックするか、Shift+Enterを入力します。
    要运行,请单击Run或键入Shift+Enter。
    JupyterNotebookでは%timeを使うことで簡単に処理速度を計測できます。
    在JupyterNotebook中,使用%time可以很容易地测量速度。
     

pynq-host-1.py

import pynq
import numpy as np

# 合成した回路をPYNQを通じてFPGAに書き込む
ol = pynq.Overlay('./design_1.bit')
mmio = ol.product_0.mmio

# Memory Mapped I/Oを通じてNumpyインターフェイスでAXI LITEレジスタにアクセスできる
# Numpyのアクセス幅が32bit整数であるため、アドレスを32bit/8bit(1byte)=4で割る
def ndarray_from_mmio(name, size, dtype):
    reginfo = ol.ip_dict['product_0']['registers'][name]
    addr_start = reginfo['address_offset'] // 4
    addr_end = addr_start + reginfo['size'] // 4
    mmio_array = mmio.array[addr_start:addr_end]
    mmio_array.dtype = np.int16
    return mmio_array.reshape(size)

mmio_x1 = ndarray_from_mmio('Memory_x1', size=(16, 16), dtype=np.int16)
mmio_x2 = ndarray_from_mmio('Memory_x2', size=(16, 16), dtype=np.int16)
mmio_y1 = ndarray_from_mmio('Memory_y', size=(16, 16), dtype=np.int16)

DONE = 0x02

def mydot(x1: np.ndarray, x2: np.ndarray) -> np.ndarray:
    # 入力データを書き込む
    mmio_x1[:] = x1
    mmio_x2[:] = x2
    # 回路の動作開始の指示
    mmio.write(0, 1)
    # 回路が終了するのを待つ
    while not mmio.read(0) & DONE: pass
    # 結果を返す
    return mmio_y1.copy()

x1 = np.random.randint(-1000, 1000, size=(16, 16), dtype=np.int16)
x2 = np.random.randint(-1000, 1000, size=(16, 16), dtype=np.int16)

y_true = np.dot(x1, x2)
y_test = mydot(x1, x2)

print('Validation:', np.all(y_true == y_test))
print()
print('=> Running 1000 times on CPU')
%time for i in range(1000): _ = np.dot(x1, x2)
print()
print('=> Running 1000 times on FPGA')
%time for i in range(1000): _ = mydot(x1, x2)

考察

この時点ではCPUでの処理よりもFPGAの処理は遅くなってしまってます。
在这一点上,FPGA的处理速度比CPU的处理速度要慢。
考えられる原因として次のようなものがあります。 可能的原因包括:

  1. 転送が遅い。 入出力データの転送として、AXI LiteプロトコルをMemory Mapped IOを用いたCPU制御で実現しています。 より高速な転送方法としてAXI Streamプロトコルが挙げられます。
    转移是缓慢的。 作为输入和输出数据的传输,AXI Lite协议是通过CPU控制与内存映射IO实现的。 AXI流协议是一种更快的传输方式。
    AXI Streamメモリ連続アクセスに対応し、FPGAからDRAMに直接アクセスアクセスできるため、高速なデータ転送を実現できます。
    它支持AXI流存储器连续访问,并允许从FPGA直接访问DRAM,从而实现高速数据传输。
  2. 制御のオーバーヘッドが大きい。 控制的开销很大。
    今回高速化した関数は16×16の行列積であり、そもそもそんなに計算量が多くありません。
    这次加速的函数是一个16×16的矩阵积,这意味着它不需要太多的计算。
    この規模の計算では、FPGAにオフロードするための制御のオーバーヘッドが大部分を占めてしまいます。
    在这种规模的计算中,将其卸载到FPGA的控制开销占据了很大一部分。
  3. そもそも回路が並列化されていない。 起初,电路并不是平行的。
    今回の設計では一切並列化を行なっていません。 100MHzで動作する並列化されていないFPGA回路より、1000MHz程度で動作するCPUの方が明らかに高速に処理できてしまいます。
    在我们的设计中,我们没有做任何平行的事情。 与以100 MHz运行的非并行FPGA电路相比,以1000 MHz左右运行的CPU的处理速度明显更快。

より高パフォーマンスな動作のために 为了实现更高的性能操作

ここまでの説明で、PYNQを用いたFPGA回路のHLS設計の基本について説明しました。
在前面的讨论中,我们讨论了基于PYNQ的FPGA电路的HLS设计的基本原理。
ここからは、今回の実装のさらなる高速化の方法を説明します。
从这里开始,我们将向您展示如何进一步加速此实现。

Vivado HLS編 Vivado HLS编辑

以下に、並列化を施したサンプルコードを載せておきます。
下面,我将提供一个带有并行化的示例代码。
高速化のために対象の計算を少し変更しました。 为了更快的速度,我们稍微改变了对象的计算。
N×16行列と16×16行列の計算を行います。 计算N×16矩阵和16×16矩阵。
計算の変更は、制御のオーバーヘッドの問題を解消するために行います。
更改计算是为了消除控制开销问题。

HLS実装では、プラグマを用いて回路の並列設計などを支持します。
在HLS的实现中,使用一个实用符号来支持电路的并行设计。
#pragma HLS PIPELINE, UNROLL, ARRAY_PARTITIONなどが該当します。
#pragma HLS Pipeline, UnROLL,ARRAY_PARTITION等。
ここでプラグマの詳細な動作についての説明は省略します。
这里省略了对实用程序的详细操作的描述。

kernel-2.cpp

#include <ap_int.h>
#include <hls_stream.h>

struct DataBus {
    ap_int<256> data;
    ap_uint<1> last;
};

void product(short w[16][16], hls::stream<DataBus> &x, hls::stream<DataBus> &y) {
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=w
#pragma HLS INTERFACE axis port=x
#pragma HLS INTERFACE axis port=y
#pragma HLS ARRAY_PARTITION variable=w dim=0
    while (true) {
#pragma HLS pipeline
        short xx[16];
#pragma HLS ARRAY_PARTITION variable=xx
        short yy[16];
#pragma HLS ARRAY_PARTITION variable=yy
        // read input data
        DataBus src = x.read();
        for (int i=0; i<16; i++) xx[i] = short(src.data >> 16*i);
        // matrix product
        for (int j=0; j<16; j++) {
#pragma HLS UNROLL
            short t = 0;
            for (int k=0; k<16; k++) t += xx[k] * w[k][j];
            yy[j] = t;
        }
        // write output
        DataBus dst;
        dst.data = 0;
        for (int i=0; i<16; i++) dst.data |= ap_uint<256>(yy[i]) << 16*i;
        dst.last = src.last;
        y.write(dst);
        if (src.last) break;
    }
}

また、高速化のためにクロック周波数を変更します。 它还改变了时钟频率以获得更快的速度。
と言っても、CPUのように高速化はできず、Z1ボードでは200MHzくらいが妥当です。
但是,不能像CPU那样提高速度,Z1板的速度约为200 MHz。
以下のような手順で対象のクロック周波数を変更できます。
您可以通过以下步骤更改目标时钟频率:
なお、周波数ではなく周期(200MHz => 5ns)を指定します。
另外,指定的不是频率,而是周期(200 MHz => 5 ns)。


最後に、三角ボタンでHLSの合成を行います。 最后,使用三角形按钮合成HLS。
また、IPコアのエクスポートを忘れずに。 另外,别忘了导出IP核心。

Vivado編 Vivado编辑

AXI Streamを用いた通信を実装します。 实现了使用AXI流的通信。
ブロックデザインでDMAを追加します。 在块设计中添加DMA。
配線はドラッグアンドドロップ 拖放布线
クロック周波数を200MHzに変更します。 将时钟频率更改为200 MHz。
詳しい説明は省略しますが、図のように進めるとブロックデザインが完成します。
我将省略详细的解释,但如果你按照图中所示,你就完成了块设计。

PYNQ开始FPGA开发-入门_#pragma_21



PYNQ开始FPGA开发-入门_Ubuntu_22



PYNQ开始FPGA开发-入门_Ubuntu_23


PYNQ开始FPGA开发-入门_Python_24






PYNQ开始FPGA开发-入门_#pragma_25





ブロックデザインの合成が終わったら、GenerateBitstreamから合成を開始します。
完成块设计的合成后,从GenerateBitstream开始合成。
先の例と同様に、成果物の.bitと.hwhファイルをコピーして、ブラウザを通してFPGAにアップロードしてください。
与前面的示例一样,复制工件的.bit和.hwh文件,并通过浏览器将其上载到FPGA。
なお、ファイル名はそれぞれdesign_2.bitとdesign_2.hwhとしてください。
请注意,文件名分别为design_2.bit和design_2.hwh。

PYNQホスト編 PYNQ主持人

DMA転送に対応したホストプログラムです。 支持DMA传输的主机程序。
コピペして実行してください。 请复制并执行它。
CPUよりFPGAは約3倍高速化されています。 FPGA的速度大约是CPU的三倍。

pynq-host-2.py

import pynq
import numpy as np


# 合成した回路をPYNQを通じてFPGAに書き込む
ol = pynq.Overlay('./design_2.bit')
mmio = ol.product_0.mmio

# pragma HLS array_partitionの影響でアドレス配置が変更された
# プラグマの指定によって適宜変更する必要がある。
# アドレスはVivado HLSの Solution>Impl>misc>Drivers>src>xxx_hw.hで見ることができる
mmio_w = mmio.array[0x010//4:0x810//4]
mmio_w.dtype = np.int16
mmio_w = mmio_w.reshape(16, 16, 4)[:,:,0]

# AXI DMAを使ってメモリ転送を行う
dma_x = ol.axi_dma_0.sendchannel
dma_y = ol.axi_dma_0.recvchannel

# 大量のデータを処理
N = 30000

buff_x = pynq.allocate(shape=(N,16), dtype=np.int16)
buff_y = pynq.allocate(shape=(N,16), dtype=np.int16)

DONE = 0x02

def mydot(x: np.ndarray) -> np.ndarray:
    # 入力データを書き込む
    buff_x[:] = x
    # DMA転送を開始する
    dma_x.transfer(buff_x)
    dma_y.transfer(buff_y)
    # 回路の動作開始の指示
    mmio.write(0, 1)
    # 回路が終了するのを待つ
    while not mmio.read(0) & DONE: pass
    # 結果を返す
    return buff_y.copy()


x = np.random.randint(-1000, 1000, size=(N, 16), dtype=np.int16)
w = np.random.randint(-1000, 1000, size=(16, 16), dtype=np.int16)

# 固定パラメータを書き込む
mmio_w[:] = w

# 動作周波数を設定する
pynq.Clocks.fclk0_mhz = 200

y_true = np.dot(x, w)
y_test = mydot(x)

print('Validation:', np.all(y_true == y_test))
print()
print('=> Running on CPU')
%time _ = np.dot(x, w)
print()
print('=> Running on FPGA')
%time _ = mydot(x)

PYNQライブラリはPythonのオーバーヘッドが存在するため、
PYNQ库存在Python的开销,因此
秒間100回を超えてくるとPythonに起因する遅延が問題となってきます。
如果你每秒超过100次,Python引起的延迟就会成为一个问题。
サンプルでは高速化のためにmmio.arrayに直接アクセスしていますが、
在示例中,为了提高速度,我们直接访问mmio.array。
PYNQライブラリでは実際はmmio.writeメソッドを使った読み書きが推奨されます。
实际上,PYNQ库建议使用mmio.write方法进行读写。
メモリアライメントに気をつけないと、Ubuntuがクラッシュします。
如果你不注意内存对齐,Ubuntu就会崩溃。
また、例えば、dmaの.transfer()はオーバーヘッドの大きい処理です。
例如,dma的.transfer()是一个开销很大的过程。
このオーバーヘッドは、メモリを直叩きすることで解消されます(サンプルでは説明していません)
此开销可通过直接敲击内存来消除(示例中未介绍)
黒魔術ですね。 黑魔法,对吧?

おわりに 结束语(英语:End)

以上で、PYNQを用いたFPGAのHLS設計のチュートリアルを終了します。
至此,我们将完成使用PYNQ的FPGA HLS设计教程。
みなさんが、楽しい、アットホームな、FPGA設計ライフを送ることを期待して。
我们希望每个人都能有一个有趣的,在家的,FPGA设计生活。


あなたが次にすべきこと 你下一步要做的事

  • この行列積プログラムをもっと高速化する 让这个矩阵积程序更快
  • Vivado HLSの設計方法について勉強する 学习Vivado HLS的设计方法
  • PYNQライブラリについて勉強する 学习PYNQ图书馆。
  • 既存のHLS実装を見て学ぶ 查看并学习现有的HLS实现
  • いろんな計算を実装する(FIRフィルタ, テキストマイニングなど)
    实现各种计算(FIR过滤器、文本挖掘等)