帰らざるMOS回路(19) ゲートレベル回路図で論理SIMでもあるまいに。GoでVCD生成。

Joseph Halfmoon

別シリーズにてゲートレベル回路図の論理シミュレーションをやってみたらば結構よかったです。MOSトランシスタ記述も可能。昔ながらのシミュレータとはちょっと違う感じもしますが使いでが良いです。ただ「回路図レベル」です。とうに開発停止状態。そのシミュレータ出力をGo言語で変換してVCD波形ビューワーに接続してみました。

※かえらざるMOS回路 投稿順 INDEX

別シリーズ記事は以下です。デジタル信号処理のお勉強をしていたらゲートレベルの回路図で動作説明されていたので、その回路を論理シミュレーションしてみた、という流れです。

やっつけな日常(18) いまさらゲートレベル回路図で論理シミュレーションでもあるまいが

そのとき使用した論理シミュレータのWebページは以下です。HDL設計、あるいは高級言語設計主流の昨今、今更ゲートレベルの回路図を描いて論理シミュレーションする需要は少ないと思います。その中で良さげなシミュレータです。MOSトランスミッションゲートに加え、単体のMOSトランジスタもモデリング可能なので、本シリーズ再開にあたって「使える」かと思いました。

Logisim

ただし2014年10月で、開発は “suspended indefinitely” です。それでもと納得すれば上記からソフトウエアをダウンロードして動作させることが可能です。

VCD波形ビューワーに接続したい

Logisimの使いでは気にいったのですが、不満点がないわけではありません。今回テコ入れしたかったのは、波形ビューワーへの接続です。

Logisimは論理回路動作の画面上でのデモかデバッグに適するインタラクティブなシミュレータです。しかしシミュレーション結果をファイルに落とすことは出来ます。出力は以下のような「タブラー」フォーマットです。

CLK	INI	D0	D1	D2	D3
1	1	1	0	0	0
0	1	1	0	0	0
0	0	1	0	0	0
1	0	0	1	0	0
0	0	0	1	0	0

こういうテーブル形式で結果が出てくればまあ大抵のことはできますな。

一方波形ビューワーで標準的なファイルフォーマットは、Verilogなどで使われるVCDと呼ばれるフォーマットではないかと思います。VCDフォーマットに変換できればVerilog等向けの波形ビューワーでシミュレーション波形を確認できるようになる筈。

今回VCDビュワーとして使わせていただいたのは、以下のフリーの波形ビューワーです。

GTKWave

テーブル形式からVCD変換ソフト、試作その1

今回は、別件記事で生成したテーブル形式出力をGTKWaveで読み込んで表示する「だけ」を目的にして変換ソフトを試作してみました。このところ勉強中のGo言語を使用です。やっつけなので制限おおありです(今後拡張予定。)

    • 論理の階層構造などサポート無し
    • バイナリのワイヤのみのサポート、バンドル、バスなどの取扱いなし
    • 信号線の本数等制限大有り。
    • 他にもバグあるだろ~(テストしてないです。)

まあ、Go言語プログラミングの勉強にはなりました。ホントか?

package main

import (
    "bufio"
    "flag"
    "fmt"
    "os"
    s "strings"
    "time"
)

type vcdTable struct {
    titleLine []string
    dataLine  [][]string
}

func check(e error) {
    if e != nil {
        panic(e)
    }
}

func (v *vcdTable) tableReader(fname string) {
    f, err := os.Open(fname)
    check(err)
    defer f.Close()
    scanner := bufio.NewScanner(f)
    header := true
    for scanner.Scan() {
        lin := scanner.Text()
        linSlice := s.Split(lin, "\t")
        if header {
            v.titleLine = linSlice
            header = false
        } else {
            v.dataLine = append(v.dataLine, linSlice)
        }
    }
}

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 {
        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 {
            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.")
    }
}
動作確認

Go言語はビルドして実行可能なOBJに落とすこともできますが、今回はまだ「習作」なのでスクリプト形式のまま実行しています。こんな感じ。

$ go run table2VCD.go M4LFSR_SIM.LOG a.vcd

引数 M4LFSR_SIM.LOG が別シリーズ記事で作ってあったテーブル形式ファイル名で、a.vcd が生成するVCDファイル名です。

生成した a.vcd ファイルをGTKWaveで表示させてみたところが以下に。

GTKWave000

とりあえず、シミュレーションした結果を波形ビューワーで観察することは「できそうだ」(これから変換プログラムの機能追加していけば)ということで。

いつもの泥縄だな、自分。

帰らざるMOS回路(18)NMOSトランスファゲートでレベル変換 に戻る 

帰らざるMOS回路(20)今時ゲートレベル論理SIMでもあるまいに。リハビリのPWM へ進む