Slowly start learning LiteX and Migen.
以前に、SpinalHDL というハードウェア記述言語や、オープンソース(あまり好きな言葉ではないが)の論理合成ツール Yosys、Place and Route ツール nextpnr を使って、設計した論理回路や RISC-V SoC を Lattice 社の FPGA 上で動かしてました。
SpinalHDL による SoC 設計は面白いのですが、SpinalHDL プロジェクトの進み方は基本的に Charles Papon さんの意向次第なので、私の期待している方向にプロジェクトが進まないことがあります。(以前の様子ですと、Papon さんは SMP による Linux 動作に取り組んでいらしたようです。)また、やはり Scala ベースの Chisel や SpinalHDL を利用するには、どうしてもプログラミング言語的な抵抗も強く残ります。
そこでしばらく、以前から気になっていた LiteX + Migen の勉強をしてみようと思い立ちました。こちらの良い(取り付きやすい)点は、以下のようなところです。
- 比較的コミュニティが大きく、各種のプロジェクトに取り組んでいる方がたくさんいる。
- 多くの方の尽力もあり、IP(Ethernet, PCI-Express など)が比較的整備されているぽい。
- LiteScope(FPGA の組込みロジックアナライザ)など、ツールも整っている。
- 新しいプラットフォーム(FPGA や評価ボード)を追加するためのフレームワークが考慮されている。
- SMP の Linux を動かすことも、それほど難しくないらしい。
- Zephyr OS(RTOS)の対応状況も良いらしい。
- そしてなにより、ハードウェア記述に利用されているツールが Migen(FHDL?)という、(私の使い慣れている)Python ベースのもので、言語的抵抗が少ない。
といったところでしょうか。
Migen 自体は、SpinalHDL に比べて AXI や APB といったバスライブラリ等が充実していないようですが、LiteX のほうにライブラリが整っているので、標準的なバスを利用した設計もできそうです。
余談ですが、LiteX で重要な RISC-V ソフトコアとして利用されているのが VexRiscv です。VexRiscv は SpinalHDL で書かれています。つまり、SpinalHDL で生成した Verilog ファイルを、Migen から読み込み、ハイブリッドな設計となっているのが LiteX SoC と言えます。
ソフトウェア設計でも、何か一つのプログラミング言語で全ての応用が可能な訳ではなく、適材適所に C、Java、Python、JavaScript など、さまざまな言語が利用されています。HDL でも、いろいろな言語が併用されていくことになるのでしょう。
ちなみに、Migen の GitHub を覗くと、ここ 2年間ほど開発が停まっているようです。さらに、nMigen という新しい構想もあったようですが、そちらの開発も活発ではないようです。しかしながら、私の私見では、Migen は既に LiteX プロジェクトに取り込まれていて、LiteX の下位層としてメンテナンスされている印象ですので、LiteX を利用する限りでは、Migen の先行きを特に不安視しなくても良いように感じています。
まずはインストール
LiteX のサイト(Home · enjoy-digital/litex Wiki)を覗いても、いったいどこから手を付けて良いのか悩みます。一から順にこの通りにやればマスターできます、というような情報は、まだまとまっていない印象です。ドキュメントの整備が遅れているとも言えますが、まだまだ開発途上のプロジェクトということもあり、ある程度は、ソースコードを読んだり、自分でネット検索したりして情報を集めなくてはいけないということでしょう。
とりあえずは、インストールしないと始まらないので、そこから手を付けましょう。以下のサイトを見るとインストール方法が書かれています。
今回私は、macOS 上の VMware 仮想 OS である Ubuntu 18.04 上にインストールすることにしました。LiteX だけならまだしも、その他に Yosys、nextpnr、もしかしたら Xilinx Vivado などが必要になると考えられ、それら全体を仮想 OS 一つに閉じ込めてしまったほうが良さそうだからです。あ、Vivado をインストールする場合には、仮想 OS のハードディスクは少なくとも数十 GB 必要になるはずです。
さて。実は、私は上記のインストール説明(Quick start guide)に従いましたが、事前に必要となるツールやライブラリがたくさんあり、やや苦労しました。残念ながら全ては覚えていませんが、以下に、思いつくものだけメモしておきます。以下にない説明については、ネット検索したほうが確実でしょう。
Python
インストール説明によると、Python 3.6 以降が利用できるとあります。私は最初、システム上にインストールされている Python 3.6.9 を利用したのですが、コマンドラインから実行する python3 と /usr/bin/env から起動される python3 の間でライブラリパッケージ(pip3 でインストールしたもの)のおかしな挙動が起きてしまい、結局 pyenv を利用することにしました。pyenv から Python 3.9.7 をインストールし、それを使いました。
余談ですが、macOS でも Linux でも、システム上にインストールされている Python をそのまま使うと、ライブラリパッケージ関連でさまざまな不具合に遭遇することがあります。HomeBrew などでインストールされる Python も同様です。これらの Python は、システム上あるいは HomeBrew の都合でインストールされる Python と考えたほうが気が楽です。
私は個人的に、pyenv をお勧めします。必要な環境やプロジェクトに応じて Python を完全に分離できるので、非常に重宝です。pyenv-virtualenv というものもあり、さらに有用です。
./litex_setup.py にが途中でエラーになったとき
必要なパッケージやツールをインストールしたあと、./litex_setup.py install で再開できます。update だとうまくいかないことがあるようです。
ninja
pip3 install ninja でインストールするよう指示がありますが、私はこれだけではうまく行きませんでした。sudo apt install ninja-build が必要でした。
ターゲットのビルド
ドキュメントには、”Go to litex-boards/litex_boards/targets and execute the target you want to build.” とサラっと書いてあります。このディレクトリを見ると、例えば digilent_arty.py とか tinyfpga_bx.py といった Python 実行ファイルがあります。これらのファイルを実行すると、カレントディレクトリに build というディレクトリを生成し、その中に各種ファイル(Verilog ファイルやコンストレイントファイル、ファームウェアなど)を生成してくれます。
私は TInyFPGA-BX を持っており、手動のビルドプロセスがだいたい分かっているので、tinyfpga_bx.py 内のコードを覗いてみました。後述の Migen を少し勉強すると、やっていることはなんとなく分かるのですが、argparse.ArgumentParser() を使って何をしているのか、最初分かりませんでした。なんで Builder() とやらを呼び出すのに ArgumentParser が必要なの!?
実は、例えば tinyfpga_bx.py を実行するときに -h オプションを付けると理解できます。この Python 実行ファイルを実行するとき、ビルドで参照される様々なオプションをコマンドラインから変更できるようになっていた訳ですね。
ちなみに Builder() というのは、使い方のドキュメントが見つからないのですが、単純に Migen(FHDL)を Verilog に変換するだけではなく、ビットストリームの生成(論理合成、プレーシング、ルーティング)から、最低限のファームウェア(ブートローダ。LiteX では BIOS と呼んでいるようです)の生成、ファイルのアップロードまでを自動的にこなしてくれるライブラリルーチンのようです。また、tinyfpga_bx.py の場合には利用されていませんが、soc.platform.create_programmer() という機能を呼び出すことで、自動的にビットストリームの書き込むこともできるようです。
なお、tinyfpga_bx.py の先頭付近で import している litex_boards/platforms/tinyfpga_bx.py というファイルを覗くと、非常に参考になります。これを見ると、ボード上の SPI Flash メモリに対応していること、UART のピンがアサインされていることなども分かります。
LiteX や Migen をどこから学ぶか
さて、LiteX をなんとか使えそうなことが分かったので(順序が逆かも知れませんが)LiteX と Migen の使い方を調べましょう。残念ながら、いまのところ「これを読んで、順番に試していけば修得できる!」というチュートリアルはなさそうです。私もいろいろ探したのですが。。。
順序としては、Migen を先に覚えたほうが良さそうです。と言っても、初めから全て理解することは難しそうなので、まずは雰囲気から理解することにしましょう。以下、私のお勧めする順番で紹介します。以下、それぞれの表題自体がハイパーリンクになっていますので、クリックしてみてください。(あ、いずれも英語です。あしからず。)
fpga_101
これは、litex-hub にあるチュートリアル集です。
この中で、Lesson-001: Discover FPGAs というスライドは、LiteX がどんなプロジェクトで利用されているか、FPGA とは何か、という説明です。FPGA の利用経験のある方は、ざっと目を通すだけで十分でしょうし、そうでない方は、ざっと目を通したあと、他の FPGA 入門資料で勉強するのが良いでしょう。その際は、Verilog 構文の詳細まで覚える必要はなく、組み合わせ回路は(なんとなく)こうやって記述するんだ、順序回路ってのはこういうものか、という程度の理解で十分でしょう。
次に、Lesson-002: Create your first FPGA design というスライドを読むと、Verilog と Migen(FHDL)は何が違うのか、Migen とはいったいどういうものなのか、構文の雰囲気、などが分かります。Chisel や SpinalHDL を御存知の方だと、理解が早いかと思います。(このスライド、読んでいくとデジタル時計の設計方法まで説明してくれそうで期待するのですが、途中で尻切れトンボになっています。何故なのでしょう? そこからは有償のセミナーなのでしょうか? 🙂 )
簡単に言うと、Migen とは、Python の力を借りてハードウェア設計を組み立てていくためのツールです。Python で書いたネイティブのロジックがそのまま Verilog に変換される訳ではないことに注意してください。上記スライドにもありますが、以下を正しく区別することが重要です。
- ハードウェアを生成するためのコード(code to generate the hardware)
- 目的のハードウェアを記述するためのコード(code to describe your hardware)
例えば、あるカウンタ回路を作りたいとします。カウンタ回路に必要なレジスタ、加算器、比較器などを記述するのは後者(目的のハードウェアを記述するためのコード)になります。また、例えばある大きな回路で複数のカウンタ(4ビットカウンタを 2つ、8ビットカウンタを 3つ、など)が必要だとします。これを、例えば Python の構文でディクショナリやループ、イテレータを駆使してカウンタ回路を繰り返し生成するコードがあるとしたら、これは前者(ハードウェアを生成するためのコード)です。最初はピンと来ないと思いますが、しばらく使っていると、あるとき突然、この違いが明確に理解できるようになると思いますので、最初はあまり深く考えなくて良いでしょう。
LiteX Wiki
これは LiteX の本家ドキュメントサイトですが、まだまだ TODO や WIP(Worki in Progress)が多いです。
その中でチュートリアルとしては Tutorials Resources がありますが、私が一番参考になったのは LiteX for Hardware Engineers でした。多少 Verilog の経験があれば、この説明が非常に参考になるでしょう。(Fomu Workshop も面白そうなのですが、Fomu に強く依存したチュートリアルのようなので、ざっとしか見ていません。)
migen/examples
Migen の配布に含まれているサンプル回路(コード)です。ディレクトリ basic に含まれるコードは、実行すると Verilog コードが出力されます。ディレクトリ sim に含まれるコードは、実行するとシミュレーション(テストベンチ)が実行されます。
正直、もう少し系統的なサンプルコード、具体的な回路(UART や PWM など)、またそれらの説明があれば良いのになあ、と思います。
Migen Manual
公式ドキュメントとしては、非常に内容不足だと思います。特に、サンプルコードが徹底的に不足しています。ざっと眺めて、溜息をつきましょう。 🙂
LiteX SoC cores ソースコード
Migen 公式ドキュメントやサンプルコードには諦めをつけ、この辺りから LiteX のライブラリ(IP)のコードを眺め出すと良さそうです。実は、私もまだあまり理解できていないので、この辺がある程度理解できたら、また説明したいと思います。
その他いろいろ(備忘録)
- TinyFPGA-BX 用に SoC をビルドする際は、./tinyfpga_bx.py --cpu-variant lite などのようにしないと、プレーシング時に ICESTORM_RAM が足りなくなります。まだちゃんと調べていませんが、VexRiscv のキャッシュメモリサイズの違いが効いているのかな、と予想しています。(pythondata-cpu-vexriscv/pythondata_cpu_vexriscv/verilog/VexRiscv*.yaml を見ての感触)
なお、この辺が参考になります。 - LiteX では、OpenOCD は使わないのでしょうか? その代わり、wishbone-tool というツールを使うと、Wishbone バス経由で GDB サーバーを起動できるようです。
ただし、ここを見ると OpenOCD についての言及がありますね。今度、調べてみたいと思います。