眼鏡止水

FPGA、ネットワーク機器、料理、そしてメガネ女子。

【openXC7】オープンソースのツールチェーンでKintex-7のFPGAボードを使ってみる

概要

先日、AliExpressでKintex-7 Base CというFPGAボードを購入しました。

このボードに搭載されているFPGAはKintex-7 325Tであり、Vivadoで使用するには有料ライセンスが必要となります。 しかし、オープンソースのツールチェーンであるopenXC7がこのFPGAに対応しているため、それを使って安く済ませようと思います。 本記事では、openXC7のインストールからKintexボードでのLチカまでを行います。

本編

openXC7のインストーラのリポジトリを開くと、「こっちはsnapベースなのでUbuntu向けで、他のディストリビューションはnixを使ってね」という旨が書かれています。 snap版とnix版の主な違いとして、yosysにGHDLのプラグインが入っている点があるようです。 ただ、これはVHDLを使う人のための機能のようで、Verilogメインの筆者にとってはひとまず不要そうです。 また、筆者はUbuntu (WSL2) を使うため、こちらのインストーラを使います。

$ wget -qO - https://raw.githubusercontent.com/openXC7/toolchain-installer/main/toolchain-installer.sh | bash

しばらく待つと、yosysとopenxc7 (nextpnr-xilinx, bbasm, fasm2frames, xc7frames2bit, bit2fasm) がインストールされました。

続いて、openxc7の使い方を理解するために、デモ用のプロジェクトを動かしてみます。 リポジトリをクローンするのですが、HTTPSだとうまくいかなかったため、SSHで行いました。

$ # git clone https://github.com/openXC7/demo-projects.git
$ git clone git@github.com:openXC7/demo-projects.git
$ cd demo-projects

とりあえずArty用のプロジェクトをmakeしたところ、pypy3が入っていないと怒られました。

$ cd blinky-digilent-arty
$ make
...
pypy3 /snap/openxc7/current/opt/nextpnr-xilinx/python/bbaexport.py --device xc7a35tcsg324-1 --bba xc7a35tcsg324.bba
make: pypy3: No such file or directory
make: *** [../openXC7.mk:39: ../chipdb//xc7a35tcsg324.bin] Error 127

pypy3をインストールし、再度makeしたところ、bitstreamの生成ができました。 blinky.bitがそのbitstreamと思われます。

$ sudo apt install -y pypy3
$ make
$ ll
total 17020
drwxr-xr-x  2 yamada yamada    4096 Feb 15 22:22 ./
drwxr-xr-x 24 yamada yamada    4096 Feb 15 22:16 ../
-rw-r--r--  1 yamada yamada     125 Feb 15 22:16 Makefile
-rw-r--r--  1 yamada yamada 2192116 Feb 15 22:22 blinky.bit
-rw-r--r--  1 yamada yamada   27068 Feb 15 22:22 blinky.fasm
-rw-r--r--  1 yamada yamada 5580828 Feb 15 22:22 blinky.frames
-rw-r--r--  1 yamada yamada 9598042 Feb 15 22:18 blinky.json
-rw-r--r--  1 yamada yamada     243 Feb 15 22:16 blinky.v
-rw-r--r--  1 yamada yamada     176 Feb 15 22:16 blinky.xdc

ここで、デモのMakefileを参考にして作成した、openXC7の処理の流れを示します。

openXC7の処理の流れ

始めに、yosysで入力Verilogファイルの論理合成を行い、ネットリストをJSON形式で出力します。

${PROJECT}.json: ${TOP_VERILOG} ${ADDITIONAL_SOURCES}
    yosys -p "synth_xilinx -flatten -abc9 ${SYNTH_OPTS} -arch xc7 -top ${TOP_MODULE}; write_json ${PROJECT}.json" $< ${ADDITIONAL_SOURCES}

続いて、nextpnr-xilinxを用いて、あらかじめ用意しておいたパーツのデータベースファイルと制約ファイルとともに配置と配線を行います。

${PROJECT}.fasm: ${PROJECT}.json ${CHIPDB}/${DBPART}.bin ${XDC}
    nextpnr-xilinx --chipdb ${CHIPDB}/${DBPART}.bin --xdc ${XDC} --json ${PROJECT}.json --fasm $@ ${PNR_ARGS} ${PNR_DEBUG}

このデータベースファイルは以下のルールによって生成されます。

${CHIPDB}/${DBPART}.bin:
    ${PYPY3} ${NEXTPNR_XILINX_PYTHON_DIR}/bbaexport.py --device ${PART} --bba ${DBPART}.bba
    bbasm -l ${DBPART}.bba ${CHIPDB}/${DBPART}.bin
    rm -f ${DBPART}.bba

その後、おそらくファイルフォーマットの変換などを行って、bitstreamを生成するようです。

${PROJECT}.frames: ${PROJECT}.fasm
    fasm2frames --part ${PART} --db-root ${PRJXRAY_DB_DIR}/${FAMILY} $< > $@

${PROJECT}.bit: ${PROJECT}.frames
    xc7frames2bit --part_file ${PRJXRAY_DB_DIR}/${FAMILY}/${PART}/part.yaml --part_name ${PART} --frm_file $< --output_file $@

これをもとに、購入したFPGAボードでLチカを行ってみます。 以下に、Lチカ (8bitアップカウンタ) のVerilogコードを示します。

module blink (
        input wire clk, // 50 MHz
        input wire rst_n,
        output wire [7:0] led
);
        localparam CNT_MAX = 50000000 / 4;
        reg [31:0] r_cnt;
        reg [7:0] r_led;

        always @(posedge clk) begin
                if (!rst_n) begin
                        r_cnt <= 0;
                        r_led <= 0;
                end else begin
                        r_cnt <= (r_cnt == CNT_MAX-1) ? 0 : r_cnt + 1;
                        r_led <= (r_cnt == CNT_MAX-1) ? r_led + 1 : r_led;
                end
        end
        assign led = r_led;
endmodule

このコードは、ボードから供給される50MHzのクロックによってカウンタを駆動します。 8bitのLEDに表示される値は、1秒に4回インクリメントされます。 このファイルをblink.vという名前で保存しました。 また、制約ファイルとしてblink.xdcというファイルを作成しました (内容は販売者から提供されたものを利用)。

以下のコマンドで論理合成を行い、ネットリストをblink.jsonに保存します。

$ yosys -p "synth_xilinx -flatten -abc9 -arch xc7 -top blink; write_json blink.json" blink.v

yosysで論理合成する様子

続いて、データベースファイルの作成を行います。 ただし、openXC7は、購入したFPGAボードの搭載するXC7K325TFFG676のスピードグレードが-1のものしか対応しておらず、このままではエラーとなります。

$ pypy3 /snap/openxc7/current/opt/nextpnr-xilinx/python/bbaexport.py --device xc7k325tffg676-2 --bba xc7k325tffg676.bba
Traceback (most recent call last):
  File "/snap/openxc7/current/opt/nextpnr-xilinx/python/bbaexport.py", line 353, in <module>
    main()
  File "/snap/openxc7/current/opt/nextpnr-xilinx/python/bbaexport.py", line 34, in main
    d = import_device(args.device, xraydb_root, metadata_root)
  File "/snap/openxc7/current/opt/nextpnr-xilinx/python/xilinx_device.py", line 498, in import_device
    with open(prjxray_root + "/" + name + "/package_pins.csv") as ppf:
FileNotFoundError: [Errno 2] No such file or directory: '/snap/openxc7/x1/opt/nextpnr-xilinx/python/../external/prjxray-db/kintex7/xc7k325tffg676-2/package_pins.csv'

そこで、スピードグレードを-1としてデータベースファイルを生成します。 データベースファイルはxc7k325tffg676.binという名前で保存されます。

$ pypy3 /snap/openxc7/current/opt/nextpnr-xilinx/python/bbaexport.py --device xc7k325tffg676-1 --bba xc7k325tffg676.bba
$ bbasm -l xc7k325tffg676.bba xc7k325tffg676.bin

配置、配線、そしてbitstreamの生成を行います。 ここでもやはりスピードグレードに-1を指定します。 これで、blink.bitという名前でbitstreamが出来上がります。

$ nextpnr-xilinx --chipdb xc7k325tffg676.bin --xdc blink.xdc --json blink.json --fasm blink.fasm
$ fasm2frames --part xc7k325tffg676-1 --db-root /snap/openxc7/current/opt/nextpnr-xilinx/external/prjxray-db/kintex7/ blink.fasm > blink.frames
$ xc7frames2bit --part_file /snap/openxc7/current/opt/nextpnr-xilinx/external/prjxray-db/kintex7/xc7k325tffg676-1/part.yaml --part_name xc7k325tffg676-1 --frm_file blink.frames --output_file blink.bit
$ ll
total 1829212
drwxr-xr-x  2 yamada yamada       4096 Feb 16 00:18 ./
drwxr-x--- 34 yamada yamada       4096 Feb 15 23:53 ../
-rw-r--r--  1 yamada yamada   11443716 Feb 16 00:18 blink.bit
-rw-r--r--  1 yamada yamada      58022 Feb 16 00:09 blink.fasm
-rw-r--r--  1 yamada yamada   31229748 Feb 16 00:15 blink.frames
-rw-r--r--  1 yamada yamada    9628798 Feb 15 23:21 blink.json
-rw-r--r--  1 yamada yamada        403 Feb 15 23:18 blink.v
-rw-r--r--  1 yamada yamada       1467 Feb 15 23:18 blink.xdc
-rw-r--r--  1 yamada yamada 1360660175 Feb 16 00:03 xc7k325tffg676.bba
-rw-r--r--  1 yamada yamada  460057016 Feb 16 00:06 xc7k325tffg676.bin

作成したbitstreamでFPGAをコンフィギュレーションします。 MakefileにはopenFPGAloaderを使った手法が記されていますが、筆者の手元にはVivado Lab Editionがあるため、それを用いてコンフィギュレーションします。 その結果、無事にLチカ (アップカウンタ) が動作しました。

というわけで、openXC7を用いて無事にKintex-7のFPGAボードを動かすことができました。 今後、このボードを用いて自作プロセッサの開発などに取り組もうと思います。

ライセンス

openXC7のデモリポジトリのライセンスを示します。

BSD 3-Clause License

Copyright (c) 2022, Hans Baier
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.