要約
自由なVHDLシミュレータを使いましょう.
手堅くGHDLでも良いですが,個人的にはNVCが絶賛開発中でオモロいです.
NVCは,Nick Gassonさん(以下nickgたん)が開発しているVHDLシミュレータで,
- VHDL-1993以上を目標.
- ライセンスはGPLv3を採用. ← 重要
- Cで書かれている.
- バックエンドにLLVMを採用.
- 波形出力にFSTを採用.
- Tclインタラクティブモードをサポート.
- statement/branch/conditionのカバレッジをサポート.
と言う特徴があります.
x86かx86_64なLinuxにgit clone
してテキトーにhogeれば多分それなりに動きます.
以下,cygwinへのportで行った修正作業を書き散らしておきます.
FLOSS開発に興味の無い方は以下の駄文は無視して下さい.
貴重な時間を使って読むべき意味のあるVHDLネタはここで終わりです.
ヤクの毛の生え具合:ぶっ壊れたconfigure
太古の昔にcygwinにportしたのですが,あれからnickgたんがコンスタント且つアクティブにコミットをキメ続けている御蔭でcygwinではconfigure
も通らない程にぶっ壊れて仕舞いました.
$ ./configure --enable-native --enable-coverage
[SNIP]
checking for BZ2_bzclose in -lbz2... yes
llvm-config: error: components given, but unused
[SNIP]
checking for LLVM (engine bitreader bitwriter)... yes
checking for LLVM shared library... yes
[SNIP]
checking for CHECK... yes
which: invalid option -- s
which: invalid option -- t
which: invalid option -- d
which: invalid option -- =
which: invalid option -- g
which: invalid option -- n
which: invalid option -- u
which: invalid option -- 9
which: invalid option -- 9
./configure: line 7487: AX_CHECK_GNU_MAKE: command not found
checking that generated files are newer than configure... done
[SNIP]
ヤクの毛刈り:llvm-config
を直す
llvm-config
でコケているっぽいので,ソースを追います.
AX_LLVM_C([engine bitreader bitwriter])
LLVM_CFLAGS=`$ac_llvm_config_path --cflags`
LLVM_CXXFLAGS=`$ac_llvm_config_path --cxxflags`
LLVM_LDFLAGS="$($ac_llvm_config_path --ldflags)"
LLVM_LIBS="$($ac_llvm_config_path --libs engine bitreader bitwriter)"
LLVM_VERSION="$($ac_llvm_config_path --version)"
LLVM_CONFIG_BINDIR="$($ac_llvm_config_path --bindir)"
LLVM_LIBDIR="$($ac_llvm_config_path --libdir)"
幾つか試すと,llvm-config --libs engine bitreader bitwriter
でコケている事が分かります.
$ llvm-config --libs engine bitreader bitwrtiter
-lLLVM-3.1
llvm-config: error: components given, but unused
[SNIP]
ググると,LLVM本家のBug 13430が釣れました.
報告日は2012/07/21と一年以上絶賛放置中なので,自分で直すしかありません(白目
ゴニョった結果,cygwin ports由来のパッチが犯人である事が分かりました.cygwin ports側の問題なので,llvm.cygport
を直しました.
報告はしたので,公式の方はそのうちテキトーに処理されると思います.
取り敢えず,ローカルの方はllvm.cygport
を弄っている間に出来たパッケージをsetup-{x86,x86_64}.exe
のInstall from Local Directoryでぶっ込んでおきます.
$ cygport llvm.cygport all
[SNIP]
$ mkdir -p /distfiles/hotfix/cygwin/release ; \
cp -a llvm-3.1-4/dist/llvm /distfiles/hotfix/cygwin/release/ ; \
find /distfiles/hotfix/cygwin/release -type d | \
xargs genini | bzip2 -c > /distfiles/hotfix/cygwin/setup.bz2
[then Install from Local Directory by using setup-{x86,x86_64}.exe.]
$ ./configure --enable-native --enable-coverage
[SNIP]
checking for BZ2_bzclose in -lbz2... yes
checking for LLVM (engine bitreader bitwriter)... yes
checking for LLVM shared library... yes
[SNIP]
checking for CHECK... yes
which: invalid option -- s
which: invalid option -- t
which: invalid option -- d
which: invalid option -- =
which: invalid option -- g
which: invalid option -- n
which: invalid option -- u
which: invalid option -- 9
which: invalid option -- 9
./configure: line 7487: AX_CHECK_GNU_MAKE: command not found
checking that generated files are newer than configure... done
[SNIP]
直りました.
ヤクの毛刈り:which: invalid option
を直す
which: invalid option
がキモいので,ソースを追います.
# On cygwin local headers can sometimes be included instead
# of system ones which is worked around using -I-. A better
# solution would be to have automake use -iquote.
case $host_os in
*cygwin* ) CFLAGS="$CFLAGS -I-" ;;
esac
AC_DEFINE_UNQUOTED([SYSTEM_CC], ["$(which $CC)"], [System compiler])
CC
に-std=gnu99
が紛れ込んでいる様なので,どうにかします.
ついでに大昔に追加したヘッダファイルをアレするクソな修正は,既にnvc/src/rt/signal.h
が御亡くなりになっているの様なのでポイすると,こうなります.
${parameter%%word}
で最長サフィックスを削除する黒魔術はdash
互換です! ← 重要
$ ./autogen.sh && ./configure --enable-native --enable-coverage
[SNIP]
checking for CHECK... yes
./configure: line 7481: AX_CHECK_GNU_MAKE: command not found
直りました.
ヤクの毛刈り:AX_CHECK_GNU_MAKE: command not found
を直す
ググると,GNU本家のAutoconf Archiveのページが釣れました.
Autoconf Archiveをgit clone
で${AA}
にぶっこ抜いておいて,ax_check_gnu_make.m4
の追加とその他m4なファイルを更新したら,おもむろに(以下略
$ touch m4/ax_check_gnu_make.m4 && \
for f in m4/*.m4 ; do [-f ${AA}/${f}] && cp ${AA}/${f} ${f} ; done && \
./autogen.sh && ./configure --enable-native --enable-coverage
[SNIP]
checking for CHECK... yes
checking for GNU make... make
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating src/rt/Makefile
config.status: creating test/Makefile
config.status: creating lib/Makefile
config.status: creating lib/std/Makefile
config.status: creating lib/ieee/Makefile
config.status: creating lib/synopsys/Makefile
config.status: creating lxt/Makefile
config.status: creating fst/Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
直りました.
ヤクの毛の生え具合:ぶっ壊れたmake
まぁ,configure
がぶっ壊れていたので,make
が通るワケないですね(白目
$ make
[SNIP]
CC util.o
util.c:546:13: error: ‘is_debugger_running’ defined but not used [-Werror=unused-function]
static bool is_debugger_running(void)
^
[SNIP]
ヤクの毛刈り:NO_STACK_STACE
を直す
src/util.c
を見てみると,スタックトレースを取る為に黒魔術をしているトコロをcygwinにportするのダルくて,大昔にNO_STACK_TRACE
でズルしていたトコロが古くなっていた様なので,直します.
ヤクの毛刈り:ctype.h
関連を直す
$ make
[SNIP]
CC elab.o
elab.c: In function ‘elab_push_scope’:
elab.c:832:10: error: array subscript has type ‘char’ [-Werror=char-subscripts]
*p = tolower(*name);
^
elab.c:853:13: error: array subscript has type ‘char’ [-Werror=char-subscripts]
*p = tolower(*id);
^
[SNIP]
ctype.h
の文字操作は,マクロで実装されている事があります.
cygwinが使っているnewlibを-std=gnu99
で使うと,少なくとも一部はマクロになる様です.
取り敢えず,int
にキャストするのが無難でしょう.
該当箇所は,
$ for f in $(grep -e '_EXFUN' /usr/include/ctype.h | \
sed -e 's/.*\([it][so].*\),.*/\1/' | sort -u) ; \
do \
grep -n -e $f $(find . -name '*.c' -or -name '*.h' -or -name '*.y' -or -name '*.l') ; \
done
./src/sem.c:3148: const bool operator = !isalpha((uint8_t)*istr(name));
./src/sem.c:3166: const bool operator = !isalpha((uint8_t)fname[0]);
./src/common.c:236: while (isdigit(*str) || (*str == '_')) {
./src/parse.c:6283: int n = (isdigit((int)*p) ? (*p - '0')
./src/parse.y:2632: int n = (isdigit((int)*p) ? (*p - '0')
./src/common.c:229: while (isspace(*str))
./src/common.c:252: for (p = copy; (*p != '\0') && !isspace(*p); p++, str++) {
./src/common.c:281: if (!isspace(*str++))
./src/util.c:211: if ((*p == '\n') || (isspace((uint8_t)*p) && col >= right)) {
./src/parse.c:6284: : 10 + (isupper((int)*p) ? (*p - 'A') : (*p - 'a')));
./src/parse.y:2633: : 10 + (isupper((int)*p) ? (*p - 'A') : (*p - 'a')));
./src/common.c:220: *p++ = tolower(*str++);
./src/elab.c:79: *p = tolower((uint8_t)*p);
./src/elab.c:832: *p = tolower(*name);
./src/elab.c:853: *p = tolower(*id);
./src/lib.c:188: *p = tolower((uint8_t)*p);
./src/parse.c:4125: *p = tolower((int)*p);
./src/parse.y:1194: *p = tolower((uint8_t)*p);
./fst/fstapi.c:5926: case 'x': val[0] = toupper(src[++i]);
./fst/fstapi.c:5927: val[1] = toupper(src[++i]);
./src/common.c:72: *p = toupper((uint8_t)*p);
./src/common.c:256: *p = toupper(*p);
./src/lexer.c:2800: *p++ = toupper((int)*str);
./src/lexer.l:352: *p++ = toupper((uint8_t)*str);
./src/lib.c:88: *p = toupper((uint8_t)*p);
./src/nvc.c:61: *p = toupper((uint8_t)*p);
なので,単項演算子の二重評価回避も考慮して直した後,おもむろに(以下略
$ make
[SNIP]
CC nvc.o
CXXLD nvc.exe
Making all in lib
[SNIP]
src/nvc.exe
のビルドまで通りました.
ヤクの毛の生え具合:ぶっ壊れたlib
でも,lib
以下でライブラリがbootstrap出来ません(白目
$ make
[SNIP]
Making all in lib
Making all in std
NVC std/STD.STANDARD
NVC std/STD.TEXTIO
NATIVE std/STD.TEXTIO
/tmp/ccy66pvE.o:fake:(.text+0x2b): undefined reference to `_file_open'
/tmp/ccy66pvE.o:fake:(.text+0x57): undefined reference to `_file_open'
/usr/lib/gcc/i686-pc-cygwin/4.8.2/../../../../i686-pc-cygwin/bin/ld: /tmp/ccy66pvE.o: bad reloc address 0x20 in section `.eh_frame'
collect2: error: ld returned 1 exit status
** Fatal: /usr/bin/gcc failed with status 1
Makefile:498: recipe for target 'std/STD.TEXTIO' failed
[SNIP]
ヤクの毛刈り:共有ライブラリの拡張子
色々モニョると,少なくとも--enable-native
でない場合の面倒を余り見ていない雰囲気が伝わってきます.
取り敢えず,共有ライブラリの拡張子が決め打ちになっている箇所を変更してみました.
しかし,結果が変わりません...
以下にある様に後で悟った事ですが,当該拡張子向けの変更は意味が無かった様です.
適切なパスでdlopen()
する事が前提であれば,少なくともcygwinでは.so
でも.dll
でも動きます.
ヤクの毛刈り:DLL黒魔術
更に調べると,
calll __file_open
void _file_open(int8_t *status, void **_fp, uint8_t *name_bytes,
int32_t name_len, int8_t mode)
{
static void rt_one_time_init(void)
{
[SNIP]
jit_bind_fn("_file_open", _file_open);
$ objdump.exe -t src/nvc.exe | grep -e '_file_open'
[3998](sec 1)(fl 0x00)(ty 20)(scl 2) (nx 0) 0x00078840 __file_open
[3999](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00025b20 ___gcov0._file_open
[4088](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00006d88 ___gcov_._file_open
となっている事が分かります.
つまり,DLLがビミョい臭い.
ググると,stackoverflowのページとsourceware.orgのautobookのページが釣れました.
他の環境にも影響が出そうなので,pull requestを出しつつ,nickgたんに御伺いを奉っておきます.
ヤクの毛の生え具合:GTKWave
NVCがちゃんとport出来た暁には,FSTを読める波形ビューワーが欲しいので,GTKWaveが欲しいですね.
もちろん,cygwinでは公式パッケージになっていません(白目
pull resuestが処理されるまでのヒマつぶしにportします.
ヤクの毛刈り:Judyをパッケージ化
GTKWaveのsvn trunkをぶっこ抜いてきて,configure --help
すると,
$ ./configure --help
[SNIP]
--enable-judy Enables Judy array support
[SNIP]
とあり,Judyを使うっぽいので,ちょちょいとパッケージにして,setup-{x86,x86_64}.exe
のInstall from Local Directoryでぶっ込んでおきます.
$ cygport judy.cygport all
[SNIP]
$ mkdir -p /distfiles/proposed/cygwin/release ; \
cp -a judy-1.0.5-1/dist/judy /distfiles/proposed/cygwin/release/ ; \
find /distfiles/proposed/cygwin/release -type d | \
xargs genini | bzip2 -c > /distfiles/proposed/cygwin/setup.bz2
[then Install from Local Directory by using setup-{x86,x86_64}.exe.]
ヤクの毛刈り:GTKWaveをパッケージ化
configure --help
を参照しつつ,ちょちょいとパッケージにして,setup-{x86,x86_64}.exe
のInstall from Local Directoryでぶっ込んでおきます.
$ cygport gtkwave.cygport all
[SNIP]
$ cp -a gtkwave-3.3.52-1/dist/gtkwave /distfiles/proposed/cygwin/release/ ; \
find /distfiles/proposed/cygwin/release -type d | \
xargs genini | bzip2 -c > /distfiles/proposed/cygwin/setup.bz2
[then Install from Local Directory by using setup-{x86,x86_64}.exe.]
試しに付属のexampleを表示させてみます.
パッと見,動いているっぽいですね.
ヤクの毛の生え具合:ぶっ壊れたlib
,再び
GTKWaveをパッケージ化している間に,nickgたんが光速でマージしてくれました.
ヤクの毛刈り:check
を更新
取り敢えず,--enable-native
は保留して,make check
まで通す事にします.
cygwinのcheck
がぶっ壊れていたので,ちょちょいと更新して,setup-{x86,x86_64}.exe
のInstall from Local Directoryでぶっ込んでおきます.
$ cygport check.cygport all
[SNIP]
$ cp -a check-0.9.11-1/dist/check /distfiles/hotfix/cygwin/release/ ; \
find /distfiles/hotfix/cygwin/release -type d | \
xargs genini | bzip2 -c > /distfiles/hotfix/cygwin/setup.bz2
[then Install from Local Directory by using setup-{x86,x86_64}.exe.]
check
のバージョンが変わったので,一応,configure
から(以下略
$ ./configure --disable-native --enable-coverage && \
make -C test clean && make check
[SNIP]
===================
All 12 tests passed
===================
--disable-native
ですが,make check
まで通りました.
ヤクの毛刈り:DLL黒魔術,再び
やっぱり,--enable-native
したいので,モニョモニョした結果,幾つか悟りが啓けました.
cygwinのDLLは,作成時に未定義参照を許さない様です.
NVCではsrc/rt/jit.c
でDLLをdlopen()
しますが,当該DLLの中にはsrc/rt/rtkern.c
内の関数や別のDLL内の関数を参照する様なネイティブコードを生成する事があります.
この様な場合,インポートライブラリと呼ばれるモノを別途作成して,コイツとリンクしてDLLを作成すれば良い様です.
一般的なやり方は,cygwin本家のページにありましたが,この方法はボツにしました.
理由は幾つかあります.
- 例えば,
_file_open()
は,src/nvc.exe
の中に実体がある.
src/rt/rekern.c
→src/rt/rekern.o
→src/rt/libnvc-rt.a
→src/nvc.exe
- cygwinの為だけに,こんなクソなコマンドを仕込みたくない.
NVCが生成するネイティブコードはVHDLのコードとしての依存関係も継承する為,src/link.c
を大幅に変更しなければならなくなる. - nickgたんは
libtool
がキライらしい.
NVC自身が元々libtool
を使っていない.
つまり, 或るDLLをdlopen()
する実行形式オブジェクト内にあるシンボルを当該DLLから参照する際,当該DLLのリンク時に未定義参照を解決する為のインポートライブラリを,クソなコマンドやlibtool
を使わずに作成したい.
(「そんな都合の良い方法あるんかいな...」と思いながら)探しました...
スタッフみんな(オレ一人),一生懸命探しました...
そしたらね...見つかりました!
PostgreSQLの野良パッチとしてお住まいでした...(島田紳助
結局,当該実行形式オブジェクトのリンク時に-Wl,--export-all-symbols -Wl,--out-implib=...
を付加してインポートライブラリを同時に作成する方法を採用する事にしました.
pull requestすると,nickgたんが再び光速でマージしてくれました.
しかも,わざわざドコからかWindowsマシンを借りてきたらしく,nickgたん自らcygwin portを直し始めると言う不測の事態が発生し,更にゴニョゴニョした結果,--enable-native
で動く様になりました!!1
ヤクの毛の生え具合:今後の課題
FIXME: 画像ファイルのアップロード制限の為,新しいヤクの写真が貼れない.
FIXME: と言うか,下書き保存やプレビューの反応が悪くて, アップロードしたリンクが保存されずに消えた...
ヤクの毛刈り(予定):カバレッジ
調子に乗って,カバレッジを毒味した所,死亡しました.
$ make cov-reset
make[1]: lcov: Command not found
Makefile:781: recipe for target 'cov-reset' failed
[SNIP]
lcovは公式パッケージに無いですね.
しかも,dev-perl/GD依存 = CPAN沼が不可避.
カバレッジはかざり ですが,気が向いたらゴニョるかもしれません.
ヤクの毛刈り(予定):パッケージ化
リリースはかざり ですが,パッケージ化はしたい.