今時ノスタルジックなゲートレベルのシミュレーションにハマりつつあります。前回はPWM回路を動かしてみましたが、素のSIM環境には不満が残りました。最低限の「テストベンチみたいなもの」がないと「シミュレーションやった感」が無いよな~と。あくまで「やった感」自己満足の世界ですが、そこが大事じゃないかと。
※かえらざるMOS回路 投稿順 INDEX
※ゲートレベルのシミュレーションに使わせていただいておりますツールは以下のものです。
このツールは論理回路のお勉強用に画面上でインタラクティブにロジック動作をおいかけるためのツールです。論理検証に使うようなツールではありません。遅延もなく、HiZもなしです。
なかなか良く出来たUIで感銘を受けました。しかし残念なことに2014年10月で開発は “suspended indefinitely” です。上記からソフトウエアをダウンロードして動作させることはまだ可能でした(Windows10上で使用中)
ベスト・ベンチ「もどき」
回路図に階層を持たせる機能はあるので、
-
- 最上位層にテストベンチ本体と被テスト回路(DUT)のインスタンスを置く
- 被テスト回路(DUT)の最上位は1つのセルとして入出力ピンを持たせ、このレベルで全体シミュレーションをする
以下では中央上部の「8ビットPWM」という箱の中に前回作ったPWM回路がDUTとしておかれており、その外側でシミュレーションを制御しているのが「テストベンチ」部分です。
テストベンチ「もどき」部分に設けた機能は以下のとおりです。
-
- 1相クロック CLK
- RESET信号生成器
- シミュレーションステップのカウンタ(16ビット)
- シミュレーション期間LED
- テストベクタ供給用ROM
1のクロックは「普通のエッジトリガの回路」用に1相の単純なクロックです。本シリーズでは、過去記事でノスタルジックな2相ノンオーバラップ・クロックをとりあげとりましたが、とりあえずシミュレータになれるまでは封印と。
2はRESET信号(回路図上ではRST、ハイ・アクティブ)を生成するためのテストベンチ回路です。現状、CLK信号がアクティブになるとRST信号も確定し、CLKが3発でるまでRSTはアクティブです。その間に必要な初期化をしてね、という感じです。勿論、テストベンチなので必要に応じてもっと長期間にするとかもアリです。
3はシミュレーション期間内での「時間位置」を知るためのカウンタです。CLKでインクリメント。とりあえず16ビットとしたので65535カウントまでOK。
4はシミュレーション期間中か終了しているかを示すLEDです。青点灯でRUN期間中。赤点灯で終了です。インタラクティブにステップ実行したり、所定の速さ(0.25Hz~4kHz)でRUNさせる機能はあるのですが、所定のクロック数バッチ実行したら終了、という機能が見当たりませんでした。そこで所定のクロック数を与えてLEDを点灯させることにいたしました。赤が点灯したら自分でクロックを止め、シミュレーション結果のLOGをクローズする必要があります。
5は、DUTによっては必要となる外部入力ベクトルを設定するためのメモリです。今回は未使用です。またそのうち。
DUT(Device Under Test)
今回は前回作ったPWM回路をDUT化してみました。こんな感じ。
シミュレーション結果
テストベンチの最上位層からシミュレーションし、5つの信号/バンドルをタブラー・フォーマットのファイルに出力してみました。ファイルの頭の方はこんな感じです。
CLK RST SIMCOUNT PWMOUT_PIN PWMCOUNTER_PIN 0 1 0000 0000 0000 0000 0 0000 0000 1 1 0000 0000 0000 0001 0 0000 0000 0 1 0000 0000 0000 0001 0 0000 0000 1 1 0000 0000 0000 0010 0 0000 0000 0 1 0000 0000 0000 0010 0 0000 0000 1 1 0000 0000 0000 0011 0 0000 0001 0 1 0000 0000 0000 0011 0 0000 0001 1 0 0000 0000 0000 0100 0 0000 0010 0 0 0000 0000 0000 0100 0 0000 0010 1 0 0000 0000 0000 0101 0 0000 0011 0 0 0000 0000 0000 0101 0 0000 0011
VCD波形ビューワーへの接続
今回から、ビット幅のある信号(バス、バンドル、レジスタ等)を表示できるようにしたので、Go言語による変換スクリプトを改造しました。とっても「ベタ」なプログラムなので、解読はしやすい?
package main import ( "bufio" "flag" "fmt" "os" s "strings" "time" ) type vcdTable struct { titleLine []string nBits []int dataLine [][]string } func check(e error) { if e != nil { panic(e) } } func getBits(arg []string) ([]string, []int) { nItem := len(arg) tempS := make([]string, nItem) tempI := make([]int, nItem) for idx := 0; idx < nItem; idx++ { if len(arg[idx]) > 1 { work := s.Replace(arg[idx], " ", "", -1) tempS[idx] = work tempI[idx] = len(work) } else { tempS[idx] = arg[idx] tempI[idx] = 1 } } return tempS, tempI } func (v *vcdTable) tableReader(fname string) { f, err := os.Open(fname) check(err) defer f.Close() scanner := bufio.NewScanner(f) header := true bitHeader := false for scanner.Scan() { lin := scanner.Text() linSlice := s.Split(lin, "\t") if header { v.titleLine = linSlice header = false bitHeader = true } else { sLis, iLis := getBits(linSlice) if bitHeader { v.nBits = iLis bitHeader = false } v.dataLine = append(v.dataLine, sLis) } } } func (v *vcdTable) vcdWriter(fname string, modname *string) { f, err := os.Create(fname) check(err) defer f.Close() w := bufio.NewWriter(f) wN := []string{"!", "\"", "#", "$", "%", "&", "'", "(", ")"} w.WriteString("$date\n") now := time.Now() w.WriteString("\t") w.WriteString(fmt.Sprint(now.Weekday())) w.WriteString(" ") w.WriteString(fmt.Sprint(now.Month())) w.WriteString(" ") w.WriteString(fmt.Sprint(now.Day())) w.WriteString(" ") w.WriteString(fmt.Sprint(now.Hour())) w.WriteString(":") w.WriteString(fmt.Sprint(now.Minute())) w.WriteString(":") w.WriteString(fmt.Sprint(now.Second())) w.WriteString(" ") w.WriteString(fmt.Sprint(now.Year())) w.WriteString("\n") w.WriteString("$end\n") w.WriteString("$version\n") w.WriteString("\tLogisim\n") w.WriteString("$end\n") w.WriteString("$timescale\n") w.WriteString("\t100ns\n") w.WriteString("$end\n") w.WriteString("$scope module ") w.WriteString(*modname) w.WriteString(" $end\n") for idx, fil := range v.titleLine { if v.nBits[idx] > 1 { w.WriteString("$var wire ") w.WriteString(fmt.Sprint(v.nBits[idx])) w.WriteString(" ") w.WriteString(wN[idx]) w.WriteString(" ") w.WriteString(fil) w.WriteString(" [") w.WriteString(fmt.Sprint(v.nBits[idx])) w.WriteString(":0] ") w.WriteString(" $end\n") } else { w.WriteString("$var wire 1 ") w.WriteString(wN[idx]) w.WriteString(" ") w.WriteString(fil) w.WriteString(" $end\n") } } w.WriteString("$upscope $end\n") w.WriteString("$enddefinitions $end\n") w.WriteString("#0\n") w.WriteString("$dumpvars\n") for linN, linBody := range v.dataLine { w.WriteString("#") w.WriteString(fmt.Sprint((linN + 1) * 100)) w.WriteString("\n") for idx, fil := range linBody { if v.nBits[idx] > 1 { w.WriteString("b") w.WriteString(fil) w.WriteString(" ") w.WriteString(wN[idx]) w.WriteString("\n") } else { w.WriteString(fil) w.WriteString(wN[idx]) w.WriteString("\n") } } } w.Flush() } func main() { opt1 := flag.Bool("OPT", false, "a bool") optFile := flag.String("FIELD", "", "a file name") modName := flag.String("MOD", "DUT", "a module name") flag.Parse() fmt.Println("OPT: ", *opt1) fmt.Println("FIELD: ", *optFile) fmt.Println("MOD: ", *modName) fnameSlice := flag.Args() fmt.Println("fnameSlice: ", fnameSlice) if len(fnameSlice) > 1 { fmt.Println("Table filename: ", fnameSlice[0]) fmt.Println("VCD filename: ", fnameSlice[1]) var vt vcdTable vt.tableReader(fnameSlice[0]) vt.vcdWriter(fnameSlice[1], modName) fmt.Println(vt.titleLine) fmt.Println(vt.dataLine[0]) } else { fmt.Println("Two File names needed.") } }
タブラーフォーマットのファイル TABLE.tbl から VCDフォーマットのファイル dut.vcd を生成するには以下のようにします。
$ go run table2VCD.go TABLE.tbl dut.vcd
さてフリーのVCDビューワーの定番 gtkwave で波形を観察したところが以下に。
テストベンチ作るので1回使ってしまったです。