Tcl駆動テストベンチでVHDLコード・ロック・モジュールを検証する
ほとんどのVHDLシミュレーターは、Tool Command Language(Tcl)を主要なスクリプト・インターフェースとして公開しています。シミュレーター・コンソールでコマンドを入力するとき、あなたはTclを使用しており、VHDL設計を駆動・監視するスクリプトを書くこともできます。
この記事では、シンプルなコード・ロック・モジュールの振る舞いを、VHDLではなくTclを使って検証する自己チェック型テストベンチの構築手順を説明します。その結果、VHDLを再コンパイルせずに実行、変更、再利用可能な明確で保守しやすいテスト・ハーネスが得られます。
関連記事:
コードとModelSimプロジェクト・ファイルは以下からダウンロードできます。
DUT: VHDLコード・ロック・モジュール
テストベンチに入る前に、テスト対象デバイス(DUT)を確認しましょう。このモジュールは、PINパッドで正しい数字シーケンスが入力されると金庫を開錠するシンプルなロックをエミュレートします。
多くの人はこのデバイスを「コンビネーション・ロック」と呼びますが、正確には「順列ロック」です。数字の順序が重要だからです。明確にするため、この記事ではコード・ロックと呼びます。

上図は典型的なホテルの金庫です。簡略化のため、数字キーのみを使用し、CLEARとLOCKボタンは省略しています。
コード・ロック・モジュールの動作
ロックはロック状態で開始します。ユーザーが秘密のPINと一致する4桁の連続した数字を入力するとロックが開きます。間違ったシーケンスを入力すると再ロックされます。この動作はVHDLでシンプルなシーケンス検出器として実装されます。
波形はモジュールのタイミングを示しています。クロックとリセットに加え、モジュールは2つの入力を受け取ります: input_digit(0〜9の整数)とinput_enable(std_logic)。DUTはイネーブルがハイのとき、立上がりクロック・エッジで数字をサンプリングします。
出力はunlockのみで、4桁連続の数字が正しいPINと一致したときのみハイになります。この例ではパスコードは1234です。
エンティティ
以下のエンティティ定義では、ジェネリックで秘密のPINをハードコードしています。各ジェネリックは0〜9の整数で、BCD(2進化十進)数字を表します。
entity code_lock is generic (pin0, pin1, pin2, pin3 : integer range 0 to 9); port ( clk : in std_logic; rst : in std_logic; input_digit : in integer range 0 to 9; input_enable : in std_logic; unlock : out std_logic ); end code_lock;
宣言領域
モジュールは単一の内部信号、直近の入力数字を格納する4ビット・シフト・レジスタを使用します。レジスタは-1〜9の値を保持できます。特別な値-1はリセット状態用で、PINが0000の場合にデフォルトでロックが解除されないようにします。
type pins_type is array (0 to 3) of integer range -1 to 9; signal pins : pins_type;
実装
アーキテクチャには、シフト・レジスタがジェネリックPIN値と一致したときにunlockをハイに駆動する組み合わせ割り当てが含まれます。pinsはクロック同期なので、unlockは立上がりクロック・エッジでのみ変化します。
unlock <= '1' when pins = (pin3, pin2, pin1, pin0) else '0';
以下のプロセスでシフト・レジスタを実装します。リセット時、すべてのエントリを-1に設定。input_enableが立上がりエッジでハイのとき、現在の数字を位置0に格納し、前の数字を右にシフトします。
PINS_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then pins <= (others => -1); else if input_enable = '1' then pins(0) <= input_digit; pins(1 to 3) <= pins(0 to 2); end if; end if; end if; end process;
VHDLテストベンチ
検証ロジックにTclを使用しますが、クロックとリセット信号を生成する基本的なVHDLテストベンチは必要です。以下が完全なVHDLテストベンチ・ファイルです。
library ieee; use ieee.std_logic_1164.all; entity code_lock_tb is end code_lock_tb; architecture sim of code_lock_tb is constant clk_hz : integer := 100e6; constant clock_period : time := 1 sec / clk_hz; signal clk : std_logic := '1'; signal rst : std_logic := '1'; signal input_digit : integer range 0 to 9; signal input_enable : std_logic := '0'; signal unlock : std_logic; begin clk <= not clk after clock_period; DUT : entity work.code_lock(rtl) generic map (1,2,3,4) port map ( clk => clk, rst => rst, input_digit => input_digit, input_enable => input_enable, unlock => unlock ); end architecture;
Tclテストベンチ
以下はModelSim内で実行されるTclスクリプトです。Vivadoや他のシミュレーターを使用する場合、一部コマンドがシミュレーター固有のため微調整が必要です。詳細はModelSimコマンド・リファレンス・マニュアルを参照してください。
サンプル・プロジェクトは以下のフォームからダウンロードできます。
名前空間の使用
スクリプトを名前空間でカプセル化すると、グローバル変数との偶発的な衝突を防止できます。すべてのTclコードはcodelocktb名前空間内に配置します。
namespace eval ::codelocktb { # すべてのTclコードをここに配置 }
シミュレーションの初期化
vsimでシミュレーションを開始し、存在すれば波形ファイルを読み込みます。
# シミュレーションをロード vsim work.code_lock_tb # 波形ファイルがあればロード if {[file exists wave.do]} { do wave.do }
VHDL値をTclにインポート
ModelSimのexamineコマンドを使用して、clock_period定数とPINジェネリックを読み取ります。以下のスニペットは数値と時間単位を抽出します。
variable clockPeriod [examine clock_period] variable clockPeriod [string trim $clockPeriod "{}"] variable timeUnits [lindex $clockPeriod 1] variable clockPeriod [lindex $clockPeriod 0] variable pinCode [examine dut.pin0 dut.pin1 dut.pin2 dut.pin3]
errorCount変数も初期化して失敗を追跡します。
variable errorCount 0
タイムスタンプ付き出力
echoコマンドは、すべてのプラットフォームでModelSimコンソールにメッセージを表示します。以下のプロシージャは、各メッセージに現在のシミュレーション時間をプレフィックスします。
proc printMsg { msg } { global now variable timeUnits echo $now $timeUnits: $msg }
指定クロック・サイクル数の実行
DUTがクロック・センシティブなため、時間をクロック周期の倍数で進めます。
proc runClockCycles { count } { variable clockPeriod variable timeUnits set t [expr {$clockPeriod * $count}] run $t $timeUnits }
信号値のチェック
checkSignalプロシージャは、VHDL信号を期待値と比較し、異なればエラーを報告します。
proc checkSignal { signalName expectedVal } { variable errorCount set val [examine $signalName] if {$val != $expectedVal} { printMsg "ERROR: $signalName=$val (expected=$expectedVal)" incr errorCount } }
PIN入力のシミュレーション
このプロシージャは、ユーザーが4桁のPINを入力する様子をシミュレートするためにDUT入力を駆動します。4桁すべてを適用した後、unlock出力をチェックします。
proc tryPin { digits } { variable pinCode set pinStatus "incorrect" if { $digits == $pinCode } { set pinStatus "correct" } printMsg "Entering $pinStatus PIN code: $digits" foreach i $digits { force input_digit $i -deposit force input_enable 1 -deposit runClockCycles 1 force input_enable 0 -deposit runClockCycles 1 } if { $pinStatus == "correct" } { checkSignal unlock 1 } else { checkSignal unlock 0 } }
テスト・シーケンスと結果報告
短い初期化期間後、スクリプトはリセットを解除し、いくつかのテスト・ケースを実行します: 2つのコーナー・ケース(0000と9999)、正しいPIN、逆順のPINシーケンス。その後、総合結果を報告します。
runClockCycles 10 force rst '0' -deposit runClockCycles 1 printMsg "Checking reset value" checkSignal unlock 0 tryPin {0 0 0 0} tryPin {9 9 9 9} tryPin $pinCode tryPin [lreverse $pinCode] if { $errorCount == 0 } { printMsg "Test: OK" } else { printMsg "Test: Failure ($errorCount errors)" }
テストベンチの実行
ModelSim内で以下のコマンドを実行してスクリプトを実行します:
source code_lock/code_lock_tb.tcl
成功時の出力例(各メッセージにタイムスタンプが付与されます):
VSIM> source code_lock/code_lock_tb.tcl # vsim # 110 ns: Checking reset value # 110 ns: Entering incorrect PIN code: 0 0 0 0 # 190 ns: Entering incorrect PIN code: 9 9 9 9 # 270 ns: Entering correct PIN code: 1 2 3 4 # 350 ns: Entering incorrect PIN code: 4 3 2 1 # 430 ns: Test: OK
DUTに欠陥がある場合(例: unlock割り当てのタイポ)、スクリプトは失敗とエラー数を報告します。
VSIM> source code_lock/code_lock_tb.tcl # vsim # 110 ns: Checking reset value # 110 ns: Entering incorrect PIN code: 0 0 0 0 # 190 ns: Entering incorrect PIN code: 9 9 9 9 # 270 ns: Entering correct PIN code: 1 2 3 4 # 350 ns: ERROR: unlock=0 (expected=1) # 350 ns: Entering incorrect PIN code: 4 3 2 1 # 430 ns: Test: Failure (1 errors)
まとめ
キャリアを通じて多数のTclベース・テストベンチを書いてきました。独自の利点があります: インタラクティブなテスト、テスト・ハーネスを再コンパイルなしで変更可能、設計とテストベンチ両方のバグを防ぐ追加の検証レイヤー。
しかしトレードオフもあります。Tclスクリプトは純粋なVHDLテストベンチより著しく遅く、言語が脆弱で、エラーを早期に検出しにくく、Pythonなどの高級言語よりデバッグが直感的ではありません。
これらの欠点にもかかわらず、TclはFPGAツールとシミュレーションの事実上の接着言語です。Xilinx Vivado、Intel Quartus、Mentor Graphics Questaを扱うなら、Tclをマスターすることは報われます。コメント欄で経験を共有してください。
VHDL
- VHDLで文字列リストを作成する: ベスト・プラクティスと実装例
- VHDLテストベンチ・シミュレーションを優雅に終了させる方法
- VHDLでPWMコントローラを実装: 設計、シミュレーション、FPGAデモ
- ブロックRAMにおける堅牢なVHDLリングバッファFIFOの設計
- Tclベースのインタラクティブテストベンチでハードウェア検証を強化
- VHDLで自己検証テストベンチを構築する実践ガイド
- VHDLで保護型とアクセスポインタを用いた動的リンクリスト実装
- VHDLにおける不純関数の活用:FSMの可読性と保守性向上
- VHDL関数マスター:効率的な設計のための実践ガイド
- VHDLで有限ステートマシンを設計:信号機の実践例
- 無料VHDLシミュレータ&エディタ導入ガイド:学生向けステップバイステップ