TL;DR
- Xilinx ISEがインストールされたdockerイメージを作成しました。
- Mac上から自動でビルドする環境を整備し、テンプレートとして公開しました。
はじめに
FPGAを販売するXilinx社では、ISE(最近のデバイス用にはVivado)と呼ばれる自社製の開発環境を用意しています。FPGAのコンフィグレーションに必要なBit stream file(*.bit
)はこのISEを使って生成できますが、逆に言えばXilinx社謹製のISEを使わなければ生成できません。そして、ISEは対応OSがWindowsとLinuxのみなんですよね...見事にMacがハブれれてしまいました。メインで使っている端末がMacということもあり、MacからFPGAの開発ができると嬉しいな...と思いしばらくいじっていたので、備忘録を兼ねて記事にします。
以前の開発環境
一年ほど前にFPGAボードの開発をしていた際には、次のような環境で開発していました。
- ターゲットデバイス
- 開発&シミュレーション
- Mac端末
- 言語: VHDL
- エディタ: Visual Studio Code
- テスト用コンパイラ: ghdl
- 実装
- Windows端末
- エディタ&コンパイラ: Xilinx ISE
状況としてはMacの方でVHDLコードを書き、ghdlでコンパイルして挙動を確認します。だいたいいい感じまでできたらソースコードをDropboxなりGithubなりにアップロードして、Windows端末にダウンロードし、Xilinx ISEで作成したプロジェクトにソースコードを加えて、実装用のBit stream fileを生成していました。
なぜこのような面倒な手順を踏んでいたかというと、Windows端末が研究室の端末で、自宅に持ち帰りが難しかったこと、加えてXilinx ISE上でのソースコードの編集がお世辞にもやりやすいとは言えなかった(補完やSyntax Highlightが貧弱だった)ため、ソースの編集はVS Code上でやりたかったという事情があります。Mac上でVMを使うことも考えたのですが、Mac上のストレージの容量が少なく、VMを立てたくもない状況でした。
そんなこんなで、このしち面倒臭い環境でしばらく開発をしており、そのときの目的が達成された後には、もう金輪際FPGAに関わることもないだろうと思っていたわけです。ただ、幸か不幸かまたFPGAに触る機会ができたので、この際Macで実装までできるようにしてやろうと思いまして、環境構築に取り組みました。
Mac上でのFPGA開発環境の構築
あらすじは次の通りです。
- Docker for MacでXilinx ISEインストール済みのdockerイメージを作成
- Pythonのinvokeパッケージを使ってコンパイル、テスト環境の構築
以下の手順では次のソフトウェアがインストールされていることを前提にしています。
- Docker for Mac
- Python 3.x
- XQuartz
- invoke (Python package)
- ghdl
docker、XQuartz、Python3、ghdlはHomebrewから、invokeはAnaconda経由で入手しました。invokeはpipでも入手できるようです。
1. Docker for MacでXilinx ISEインストール済みのdockerイメージを作成
~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~
以下の手順でISE Webpack editionをインストールしたものはDocker Hub上でseekworser/ise_webpack
で公開しているため、Webpack版であれば
docker pull seekworser/ise_webpack
で事足ります。容量が30 GB以上あるので注意してください。
~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~/~
dockerを使ってXilinx Vivadoをインストールしている方がいらっしゃったので、その記事を参考にしました。
Xilinx の開発ツールを Docker コンテナに閉じ込める
この方はVivado HLx 2018.2: All OS installer Single-File Downloadをダウンロードしてきて、インストールまで含めてDockerfileで自動化しているのですが、ISEではSingle file Downloadが存在しないため、泣く泣くインストールは手動で行います。
そこで、まずfull installer for Linuxをダウンロードしてきて、次のようなDockerfileを作成しました。
FROM ubuntu
RUN \
sed -i -e "s%http://[^ ]\+%http://ftp.jaist.ac.jp/pub/Linux/ubuntu/%g" /etc/apt/sources.list && \
apt update && \
apt upgrade -y && \
apt -y --no-install-recommends install \
ca-certificates curl sudo xorg dbus dbus-x11 ubuntu-gnome-default-settings gtk2-engines \
ttf-ubuntu-font-family fonts-ubuntu-font-family-console fonts-droid-fallback lxappearance && \
apt-get autoclean && \
apt-get autoremove && \
rm -rf /var/lib/apt/lists/* && \
echo "%sudo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
COPY Xilinx_ISE_DS_Lin_14.7_1015_1.tar /
RUN mkdir /ise-installer &&\
tar --strip-components 1 -C /ise-installer \
-xvf Xilinx_ISE_DS_Lin_14.7_1015_1.tar &&\
rm /Xilinx_ISE_DS_Lin_14.7_1015_1.tar
前半は先のリンクの方から丸パクリで、X11環境のインストールその他です。後半でインストール用のフォルダを/ise_installer
下に展開しておきます。(ファイル名はバージョンに合わせて変えてください)
ここからGUIインストーラを立ち上げ、ISEをインストールします。Docker for MacでX11 GUIを立ち上げる際には次を参考にしました。
Mac+dockerでx11アプリケーションを起動する
先ほどのDockerfile、ダウンロードしたインストーラを全て同じフォルダに入れ、さらにhost側でXQuartzを立ち上げた状態で以下のコマンドを実行します。
docker build --no-cache --rm -t ise-installer .
xhost + 127.0.0.1
docker run -e DISPLAY=docker.for.mac.localhost:0 --name ise-installed ise-installer /ise-installer/xsetup
その後XQuartzでインストーラが起動するため、指示にしたがってインストールを行います。インストール後に、
(host)$ docker commit ise-installed ise-installed
(host)$ docker run -i --name ise ise-installed /bin/bash
(container)$ rm -rf /ise-installer
(container)$ exit
(host)$ docker commit ise ise
(host)$ docker container rm ise-installer ise-installed ise
(host)$ docker image rm ise-installer ise-installed
を順に実行し、ISEのインストールされたイメージise
を作成します。
2. Pythonのinvokeパッケージを使ってコンパイル、テスト環境の構築
先ほど作成したimageと、Pythonのinvokeパッケージを使用して、コンフィギュレーション用のBit stream fileを作成する環境を作成します。seekworser/ise_project_templateでPapilio pro用に作成したものは公開しています。
ディレクトリ構成は次のようになっています。
.
├── Readme.md
├── log/
├── out/
├── test/
├── src/
│ ├── project.yaml
│ ├── sample.vhd
│ ├── sample_ucf.ucf
│ └── test_sample.vhd
├── sample.bit
└── tasks.py
logファイルの出力、中間ファイルの出力、ソースコード保管、test用のファイルの出力用のディレクトリとinvoke用のPythonスクリプトが置いてあります。
sample.vhd
がメインのVHDLファイルです。
entity sample is
-- write ports
end sample;
architecture behavior of sample is
-- write behavior
end behavior;
また、sample_ucf.ucf
はFPGAの制約ファイルです。
プロジェクトが変わるごとにMakefile
もといtasks.py
をいじるのは嫌だったので、project.yaml
にコンパイル用のパラメータを格納しておくようにします。ついでに使うFPGAの情報もpart:
以下に書いておきます。
src_files:
- file_name: sample.vhd
language: vhdl
library: work
entity_names:
- sample
test_files:
- file_name: test_sample.vhd
entity_names:
- test_sample
- test_sample2
ucf_file: sample_ucf.ucf
top_module: sample
part:
device: xc6slx9
package: tqg144
speed: -2
tasks.py
ではこの情報を読み取って、ビルドやテストなどを行うようコードを書いておきます。例えば次のようなイメージです。
import yaml
import invoke
def get_project_parameter(*keys):
param = yaml.load(open(PROJECT_YAML_FILE_NAME, "r+"))
for item in keys:
param = param[item]
return param
@invoke.task
def bitgen(c):
c.run(
"docker run --rm -i -v $(PWD):/project seekworser/ise_webpack:latest sh <<_EOT_\n"
"cd /project\n"
"{ise_base:s}bitgen -w {outdir:s}{top_module:s}.ncd {top_module:s}.bit {outdir:s}{top_module:s}.pcf\n"
"mv *.drc {logdir:s}{top_module:s}_drc.log\n"
"mv *.bgn {logdir:s}{top_module:s}_bitgen.log\n"
"rm -rf *.xwbt _xmsgs xilinx_device_details.xml *.xrpt\n"
"_EOT_".format(
ise_base=ISE_BASE,
top_module=get_project_parameter(TOP_MODULE_KEY),
outdir=OUTDIR,
logdir=LOGDIR,
)
)
return
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
# some other tasks
# ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
ISE_BASE = "/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/"
OUTDIR = "./out/"
SRCDIR = "./src/"
LOGDIR = "./log/"
PROJECT_YAML_FILE_NAME = SRCDIR + "project.yaml"
TOP_MODULE_KEY = "top_module"
最終的にどうなったか
以上の結果として
invoke builde
のコマンド一発でMacからBit stream fileが生成できるようになりました。ISEの使いにくいエディタを使う必要もなくなったため、開発がよりスムーズに進むようになりました。