VCD ファイルを出力するような Lisp のプログラムを書いてみた。
VCD の仕様
VCD(Value Change Dump) は Verilog HDL の仕様の一部で波形をダンプする際のASCII ベースの形式です。ASCII なので中身を覗くことが出来ます。iverilog が生成する VCD ファイルを見ると簡単そうです。自前の回路シミュレータにVCD を出力する機能をつけてみました。
VCD の構成
1364-2005 Figure 18-1 によると VCD File は次の3部構成になっています。
- Header Information
- Node Information
- Value Changes
Node Information は階層化出来るようですが、今回は簡易版なのでどフラットに情報を出力します。Value Changes は終わり(を示すマーク)がありません。これはどうも次々と Value Changes が生成され、それをリアルタイムで表示するということも念頭にあるようです。
Header Information
まずは Header を出力します。format でかなり無理矢理な感じで出力します。date のフォーマットはどうでも良さそうだったので month が数字のままです。
vcd.lisp
(defun vcd-show-header (vcd-obj)
(let ((str (vcd-stream vcd-obj)))
(format str "$date~%")
(multiple-value-bind
(second minute hour day month year day-of-week dst-p tz)
(get-decoded-time)
(format str " ~a ~a ~a ~a:~a:~a (GMT~@d) ~a~%"
(nth day-of-week *day-names*)
month day hour minute second (- tz) year
))
(format str "$end~%")
(format str "$version~%")
(format str " ~a~%" *vcd-version*)
(format str "$end~%")
(format str "$timescale~%")
(let* ((timescale (vcd-timescale vcd-obj))
(timescale-num (car timescale))
(timescale-unit (string-downcase (cadr timescale))))
(format str " ~a~a~%" timescale-num timescale-unit))
(format str "$end~%")))
Node Information
次に Node Information を出力します。本来は Node というだけあって、階層構造を表現できるのですが、簡易版なのでトップだけを出力します。ここでは vars に Variable の定義が入っているものとして、そのすべての情報を出力しています。そして、最後に $enddefinitions を出力して定義の終わりを示します。
vcd.lisp
(defun vcd-show-module-tb (vcd-obj)
(let ((str (vcd-stream vcd-obj))
(vars (vcd-vars vcd-obj)))
(format str "$scope module tb $end~%")
(format str "$upscope $end~%")
(dolist (var-obj vars)
(format str "$var ~a ~a ~a ~a $end~%"
(vcd-var-type var-obj)
(vcd-var-size var-obj)
(vcd-var-id-code var-obj)
(vcd-var-reference-name var-obj)))
(format str "$enddefinitions $end~%")))
Value Changes
あとは延々と Value Changes をかけばいいだけです。vcd-probe という関数を定義して現在時刻と値とIDを書くようにしました。この関数は値が変わるたびに呼ばれます(回路シミュレータがそのように設計されている)。
vcd.lisp
(defun vcd-probe (vcd-obj id-code xsig)
(let ((_id-code id-code)
(_vcd-obj vcd-obj))
(add-action
xsig
(lambda (sig)
(format (vcd-stream vcd-obj) "#~a~%~a~a~%" (get-current-time the-agenda)
(get-xsignal sig) _id-code )))))
使ってみる
実際に使ってみた。と言いたいところですが、package 化する前のテストプログラムだからちょっと古い。雰囲気だけ。clk も add-action で作ってるけど、今は make-clock という関数を用意しています。
vcd.lisp
(setf vcd0 (make-vcd))
(setf clk (make-xsignal :value 0))
(setf i0 (make-xsignal :value 0))
(setf o0 (make-xsignal :value 1))
(setf i1 (make-xsignal :value 1))
(setf o1 (make-xsignal :value 0))
(clk-ff-gate i0 clk o0)
(clk-ff-gate i1 clk o1)
(add-action clk
(lambda (sig)
(let ((now-value (xsignal-value sig)))
(after-delay
5
(lambda ()
(set-xsignal sig (- 1 now-value)))))))
; 本当は make-xsignal の type に type と size が入っていて
; そこを参照するようにしたい
; そうなれば (vcd-add-vars vcd0 clk) だけでいい
(vcd-add-vars vcd0 clk "reg" 1 nil "clk")
(vcd-add-vars vcd0 i0 "reg" 1 nil "i0")
(vcd-add-vars vcd0 o0 "reg" 1 nil "o0")
(vcd-add-vars vcd0 i1 "reg" 1 nil "i1")
(vcd-add-vars vcd0 o1 "reg" 1 nil "o1")
(with-open-file (str "test3.vcd" :direction :output :if-exists :overwrite :if-does-not-exist :create)
(setf (vcd-stream vcd0) str)
(vcd-show-header vcd0)
(vcd-show-module-tb vcd0)
(vcd-show-start-dumpvars vcd0)
(propagate 17)
(set-xsignal i0 (xsignal-value o1))
(set-xsignal i1 (xsignal-value o0))
(propagate 13)
)
GTKWave で結果を見る
結果は GTKWave で見ることが出来ます。
GTKWave っていろいろできるのね
GTKWave の ユーザズガイドを見ると面白い機能があります。時間があったら使ってみようと思います。
- Appendix E: Tcl Commands.
- Read Verilog Stemsfile
will open a file requester that will ask for the name of aVerilog stemsfile. This will then launch an RTL browser and allow source codeannotation based on the primary marker position. Stems files are generated byxml2stems. Please see its manpage for syntax and more information on stemsfile generation.
- Interactive VCD
VCD files may be viewed as they are generated provided that they are written toa fifo (pipe) and are trampolined through shmidcat first (assume the simulatorwill normally generate outfile.vcd):
mkfifo outfile.vcd
cver myverilog.v &
shmidcat outfile.vcd | gtkwave -v -I myverilog.savYou can then navigate the file as simulation is running and watch it update.
リンク集
SICP の scheme の回路シミュレータを Common Lisp で作ってみた。 コンセプトをそのまま使っているので scheme ぽくなっている。 'done 返すとかね。更に開発当時 lol を読んでいたので lol にも 依存している。
Let Over Lambda のページ。素晴らしいアイデアで恐らく Ruby on Rails にヒントを得たネーミングにしたのだろうけど 流行った形跡はない。何が素晴らしかったのかをもう一度 考えてみたい(いずれ)。
VCD ファイルを書いてみようという Blog。 書いてる人は github で 10G の Ether Switch とか オレオレCPU の ZipCPU なんてのを作っているみたい。
Verilog の仕様。この中に VCD の仕様がある。
GTKWave のアノテーションのはなし。アノテーションなんだろう?
2023-7-21 11:40 JST