ここ数日、実際の画像を入力にして論理シミュレーションを行っていた。
「そんなことできるの!?」と驚かれたけど、できるんですよ。
論理シミュレーションにあたっては適当な入力を与えて、その出力を調べることで、
回路がちゃんと設計出来ているか確認するわけだ。
シンプルな入力で確認してもいいし、オーバーフローしそうな入力での動作を確認することが出来る。
ともかく、出力が既知な入力を用意して与えればよいわけだから、
すでにソフトウェアシミュレーションで使っている画像を使うというのは理にかなった話だ。
もちろん画像データを与える回路をHDLで記述しても良いのだが、
Verilogではファイルを読み込んでレジスタに展開する機能がある。
これを使えば画像データの入ったRAMはこのように記述できる。
wire [13:0] ROM_ADDR;
reg [7:0] ROM_DOUT;
reg [7:0] ROM [0:128*128-1];
initial $readmemh("rom.hex",ROM);
always @(posedge CLK) begin
ROM_DOUT <= ROM[ROM_ADDR];
end
$readmemhを使うと16進数データ列の書かれたファイルを用意すればいい。
こんな感じで、1行1ワードで書けばいいはず。
3F 5A
あとはそれを使ったメモリのハードウェアを記述すればいいわけ。
では、逆に出力はどうすればいいだろうか。
方法の1つは出力がある度にファイルを書き出す方法。
integer OFH; initial OFH=$fopen("foo.dump","w"); wire DOUT_EN; wire [7:0] DOUT; always @(posedge CLK) begin if(DOUT_EN==1) $fdisplay(OFH,"%02X\n",DOUT) end initial begin wait(END==1); $fclose(OFH); $finish; end
$fopenでファイルを開いて、最後に$fcloseでファイルを閉じる。
$finishの前に$fcloseを入れる必要がある。$finishがないテストベンチだと都合悪いかなぁ。
ここではDOUT_EN==1となったときに、DOUTをファイルに1行書き出すようになっている。
printfのようにフォーマット指定をして書き出すので複数の変数を同時に書き出すこともできる。
今回はこんな感じで出力された順番でそのまま書き出している。
出力されたデータ列をRubyで書いたスクリプトでPGM形式に変換して画像化している。
もしくは出力用のメモリを記述して、それを最後にダンプするという方法もあるかもしれない。
wire RAM_WEN; wire [13:0] RAM_ADDR; wire [7:0] RAM_DIN; reg [7:0] RAM [0:128*128-1]; always @(posedge CLK) begin if(RAM_WEN==1) begin RAM[RAM_ADDR] <= RAM_DIN; end end initial begin wait(END==1); integer OFH=$fopen("out.pgm","w"); $fdisplay(OFH,"P2\n128 128\n256\n"); integer y,x; for(y=0;y<128;y=y+1) begin for(x=0;x<128;x=x+1) begin $fdisplay(OFH,"%d ",RAM[y*128+x]); end $fdisplay(OFH,"\n"); end $fclose(OFH); $finish; end
ランダムアクセスで書き込まれるような場合はこっちの方がいいか。
データを書き出す段階でPGM形式にしてしまうテストベンチだが、こういうのもいいのではないかなという気はした。
テストベンチともなると回路記述とはかなり勝手が違って、普通のプログラムと同じような感じで書くことになる。
他に想定出力を$readmemhで読み込んだりして作って、それと食い違いが発生したらそれを出力するとか。
$displayでコンソールに出力できるから、それで食い違いの内容を出力すると。
大量の入出力に対するテストだとそれが想定出力との照合まで含めると効率がいいかな。
大量の入出力データを作成する方法はファイルを読み込む方法だけではないけど、
実際に即した入力でのシミュレーションとかなると、やっぱりそれが確実な気はするね。
実際の画像を入力にシミュレーションというのは無謀なようで、実は簡単な方法だと。
それでシミュレーションはうまくいったのかという話だが、かなりたくさんのバグが発見された。
カウンターのリセット条件の記述が間違えてたり、最初はひどいミスが大量に見つかった。
そのあたりを片付けたら、ちゃんとまともに出力が出てくるようになったのだが、想定出力と一致しない。
そんなわけで、いろいろ信号を取り出して、正しく動いてるか確かめて……
結果的には符号拡張のミスだったんだけど、なかなかそこに行き着くまでの道のりは長かった。
けど、これFPGAに載せてしまうと、中の信号はなかなか見えないからねぇ。その点ではこんなめんどくさいシミュレーションこそ実は効率が良いと。
もちろん、もっとシンプルなテストベンチでミスを洗い出せればそれが楽なんだけどさ。