Kanade Electronicskanaelecom
🔩

Tang Nano 9K の使い方

IDE を使わず Tang Nano 9K を CLI だけで開発する

今さらですが Tang Nano 9K を触ります。

Interface のおかげで全人類 FPGA に入門しているので、この記事では IDE を使わずに全て CLI だけで開発します。

Tang Nano 9K は Gowin GW1NR-LV9QN88PC6/I5 を載せた小さな FPGA ボードです。 8640 LUT4、27 MHz クロック、32 Mbit SPI Flash、HDMI、RGB LCD、SPI LCD、USB-JTAG / USB-UART が載っています。 小さいのにちゃんと遊べる。えらい。

全体の流れ

IDE を使わない場合、だいたい次の流れになります。

段階ツール入出力
合成Yosys*.vtop.json
配置配線nextpnr-himbaecheltop.json + *.cstpnr.json
パックapycula (gowin_pack)pnr.jsontop.fs
書き込みopenFPGALoadertop.fs → FPGA SRAM / Flash

Gowin FPGA のオープンソース環境は、昔の記事だと nextpnr-gowin と書かれていることがあります。 最近は nextpnr-himbaechel を使うのが本筋っぽいです。 古い記事をコピペすると、デバイス名や family 指定でハマります。

Tang Nano 9K で使う値は次です。

項目
FPGA partGW1NR-LV9QN88PC6/I5
Gowin familyGW1N-9C
openFPGALoader boardtangnano9k
パッキング時の device optionGW1N-9C

いちばん楽な方法

いちばん楽なのは OSS CAD Suite を使う方法です。 Yosys、nextpnr、openFPGALoader などがまとまっています。

tar xf oss-cad-suite-linux-x64-*.tgz
source oss-cad-suite/environment

yosys -V
nextpnr-himbaechel --version
gowin_pack --help
openFPGALoader --version

Docker すら動かすのが面倒な現代っ子なので、まずはこれでよいです。 ただし、ツールの中身を追いたい、最新の Gowin 対応を踏みたい、CI に載せたい、という場合は自分でビルドします。

Yosys

なんでも合成してくれる Yosys 先生。

公式 README では、サブモジュール込みで clone することが推奨されています。

git clone --recurse-submodules https://github.com/YosysHQ/yosys.git
cd yosys

sudo apt update
sudo apt install gawk git make python3 lld bison clang flex \
  libffi-dev libfl-dev libreadline-dev pkg-config tcl-dev zlib1g-dev \
  graphviz xdot

make config-clang
make
sudo make install

Verilog から Gowin 用の JSON ネットリストを作るには synth_gowin を使います。

yosys -p "read_verilog src/top.v; synth_gowin -top top -json build/top.json"

SystemVerilog を使いたい場合は、対応状況に注意します。 Yosys 標準の Verilog フロントエンドだけで行くなら、まずは Verilog-2005 ぐらいに寄せた方が平和です。

apycula

apycula は Gowin FPGA のビットストリーム形式やチップデータベースを扱うプロジェクトです。 gowin_packgowin_bba が入ります。

python3 -m pip install --user apycula
export PATH="$HOME/.local/bin:$PATH"

gowin_pack.fs ファイルを作ります。

gowin_pack -d GW1N-9C -o build/top.fs build/pnr.json

-d GW1N-9C が Tang Nano 9K の family 指定です。 ここを古い Tang Nano 用の GW1N-1 にすると、当然ながら違うチップ向けのパックになります。 コピペは敵。

nextpnr

これまたおなじみ、なんでも配置配線 (Place and Route) してくれる nextpnr 先生。

Gowin では himbaechel アーキテクチャを有効にしてビルドします。

git clone https://github.com/YosysHQ/nextpnr.git
cd nextpnr

cmake -S . -B build \
  -DARCH=himbaechel \
  -DHIMBAECHEL_UARCH=gowin
cmake --build build -j"$(nproc)"
sudo cmake --install build

もし gowin_bba が見つからないと言われたら、apycula を入れたパスが通っているか確認します。

which gowin_bba
export PATH="$HOME/.local/bin:$PATH"

Tang Nano 9K の配置配線はこうです。

nextpnr-himbaechel \
  --json build/top.json \
  --write build/pnr.json \
  --device GW1NR-LV9QN88PC6/I5 \
  --vopt family=GW1N-9C \
  --vopt cst=tangnano9k.cst

.cst はピン配置ファイルです。 トップモジュールのポートを、FPGA の物理ピンに対応させます。

LED を 1 個だけ使うなら、雰囲気はこんな感じです。 実際のピン名は Sipeed のピンマップやサンプルを確認してください。

IO_LOC "led" 10;
IO_PORT "led" IO_TYPE=LVCMOS33;

未割り当てのトップレベルポートがあると nextpnr が怒ります。 怒ってくれるのは助かる。 未接続のピンが勝手に生える方が怖いです。

openFPGALoader

なんでも書き込みしてくれる openFPGALoader 先生。

git clone https://github.com/trabucayre/openFPGALoader.git
cd openFPGALoader
mkdir build
cd build
cmake ..
cmake --build . -j"$(nproc)"
sudo make install

Linux では udev ルールを入れないと一般ユーザから USB-JTAG を触れないことがあります。

sudo cp 99-openfpgaloader.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger
sudo usermod -a "$USER" -G plugdev

このあと一度ログアウト・ログインします。 それが面倒なら、とりあえず sudo openFPGALoader ... で動作確認してもよいです。

接続確認はこれです。

openFPGALoader --detect

SRAM に書くなら -m を付けます。 電源を切ると消えますが、開発中はこれで十分です。

openFPGALoader -m -b tangnano9k build/top.fs

Flash に書くなら -f です。 電源を入れ直しても残ります。

openFPGALoader -f -b tangnano9k build/top.fs

公式ドキュメントでは Gowin の .fs は SRAM と Flash で同じファイルを使い、オプションで書き込み先を変える、という扱いです。 Tang Nano 9K の board name は tangnano9k です。

WSL

WSL2 で USB を使うには、USB デバイスを接続する の手順に従います。

PowerShell を admin で開き、

usbipd wsl list
usbipd wsl attach --busid <BUS-ID>

WSL2 側で lsusb して BL702 っぽいデバイスが見えれば認識されています。

lsusb
openFPGALoader --detect

ここでエラーが出たので、WSL-support に従ってコマンドをたたいたら、いけました。 USB 周りは環境差が大きいです。 動かないときは、Windows 側でデバイスを掴んでいないか、WSL に attach できているか、udev ルールが効いているかを順に見ます。

Windows

Windows で手早く書き込むだけなら MSYS2 を使えます。

  1. MSYS2 をインストール
  2. UCRT x64 環境を起動
  3. openFPGALoader をインストール
pacman -S mingw-w64-ucrt-x86_64-openFPGALoader

あとは同じように実行します。

openFPGALoader -b tangnano9k build/top.fs

合成から全部 Windows ネイティブでやるより、WSL2 + USB passthrough か、OSS CAD Suite を展開して使う方が楽だと思います。

Makefile

最小構成の Makefile はこんな感じです。

TOP := top
SRC := src/top.v
CST := tangnano9k.cst
BUILD := build

DEVICE := GW1NR-LV9QN88PC6/I5
FAMILY := GW1N-9C
BOARD := tangnano9k

.PHONY: all clean load flash

all: $(BUILD)/$(TOP).fs

$(BUILD):
	mkdir -p $(BUILD)

$(BUILD)/$(TOP).json: $(SRC) | $(BUILD)
	yosys -p "read_verilog $(SRC); synth_gowin -top $(TOP) -json $@"

$(BUILD)/pnr.json: $(BUILD)/$(TOP).json $(CST)
	nextpnr-himbaechel \
		--json $< \
		--write $@ \
		--device $(DEVICE) \
		--vopt family=$(FAMILY) \
		--vopt cst=$(CST)

$(BUILD)/$(TOP).fs: $(BUILD)/pnr.json
	gowin_pack -d $(FAMILY) -o $@ $<

load: $(BUILD)/$(TOP).fs
	openFPGALoader -m -b $(BOARD) $<

flash: $(BUILD)/$(TOP).fs
	openFPGALoader -f -b $(BOARD) $<

clean:
	rm -rf $(BUILD)

開発中は make load、完成したら make flash です。 SRAM 書き込みは速いので、L チカを壊しては直す遊びに向いています。

ファイル構成はこうしておくと見通しがいいです。

.
├── Makefile
├── tangnano9k.cst
└── src
    └── top.v

src/top.v の最小例です。 27 MHz クロックを分周して LED を点滅させます。

module top(
    input wire clk,
    output wire led
);
    reg [24:0] counter = 0;

    always @(posedge clk) begin
        counter <= counter + 1'b1;
    end

    assign led = counter[24];
endmodule

この例では clkled.cst に割り当てる必要があります。 ピン番号は基板の版や使う LED によって変わりうるので、Sipeed のピンマップやサンプルプロジェクトを見てください。 ここで適当を書くと、物理世界が爆発します。

Docker 化

というわけで、積み上げた開発環境を爆破し更地にして、Docker 化します。

コマンドたちがさえずる綺麗な環境を守りたい。。。

ただし、USB-JTAG で実機に書き込むところまで Docker に閉じ込めると少し面倒です。 おすすめは、合成・配置配線・パックまでは Docker、書き込みだけホストの openFPGALoader です。

FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    ca-certificates \
    clang \
    cmake \
    curl \
    flex \
    bison \
    gawk \
    git \
    lld \
    make \
    ninja-build \
    pkg-config \
    python3 \
    python3-pip \
    python3-venv \
    tcl-dev \
    libffi-dev \
    libfl-dev \
    libreadline-dev \
    zlib1g-dev \
    graphviz \
    xdot \
    && rm -rf /var/lib/apt/lists/*

RUN git clone --recurse-submodules https://github.com/YosysHQ/yosys.git /opt/yosys \
    && cd /opt/yosys \
    && make config-clang \
    && make -j"$(nproc)" \
    && make install

RUN python3 -m venv /opt/venv \
    && /opt/venv/bin/pip install --upgrade pip \
    && /opt/venv/bin/pip install apycula

ENV PATH="/opt/venv/bin:${PATH}"

RUN git clone https://github.com/YosysHQ/nextpnr.git /opt/nextpnr \
    && cd /opt/nextpnr \
    && cmake -S . -B build -GNinja \
      -DARCH=himbaechel \
      -DHIMBAECHEL_UARCH=gowin \
    && cmake --build build -j"$(nproc)" \
    && cmake --install build

WORKDIR /work

ビルドします。

docker build -t tangnano9k-toolchain .

プロジェクトをマウントして make します。

docker run --rm -it \
  -v "$PWD":/work \
  tangnano9k-toolchain \
  make

できた build/top.fs をホスト側で書き込みます。

openFPGALoader -m -b tangnano9k build/top.fs

Docker から直接書き込みたい場合は、USB デバイスを渡します。

docker run --rm -it \
  --device /dev/bus/usb \
  -v "$PWD":/work \
  tangnano9k-toolchain \
  openFPGALoader -m -b tangnano9k build/top.fs

Linux では権限の都合で --privileged が必要になることもあります。 でも個人的には、書き込みはホストでやる方がトラブルが少ないです。

参考