Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> VHDL

Tcl-gesteuerter Testbench für ein VHDL Code-Lock-Modul erstellen

Die meisten VHDL-Simulatoren bieten die Tool Command Language (Tcl) als primäre Skriptschnittstelle. Wenn Sie einen Befehl an der Simulator-Konsole eingeben, nutzen Sie Tcl, und Sie können auch Skripte schreiben, die Ihr VHDL-Design steuern und beobachten.

Dieser Artikel führt Sie durch den Bau eines selbstprüfenden Testbenchs, der Tcl – statt VHDL – zur Verifikation des Verhaltens eines einfachen Code-Lock-Moduls nutzt. Das Ergebnis ist ein klarer, wartbarer Test-Harness, der ausgeführt, modifiziert und wiederverwendet werden kann, ohne das VHDL neu zu kompilieren.

Siehe auch:

Code und ModelSim-Projektdateien unten herunterladen.

Das DUT: Ein Code-Lock-Modul in VHDL

Bevor wir in den Testbench eintauchen, schauen wir uns das Device Under Test (DUT) an. Das Modul emuliert ein einfaches Schloss, das einen Tresor öffnet, wenn die korrekte numerische Sequenz auf einem PIN-Pad eingegeben wird.

Obwohl viele ein solches Gerät als „Kombinationsschloss“ bezeichnen, ist der korrekte Begriff ein „Permutationsschloss“ – die Reihenfolge der Ziffern ist entscheidend. Zur Klarheit nennen wir es im Folgenden Code Lock.

Tcl-gesteuerter Testbench für ein VHDL Code-Lock-Modul erstellen

Die obige Abbildung zeigt einen typischen Hoteltresor. Zur Vereinfachung nutzt unser Design nur numerische Tasten und verzichtet auf CLEAR- und LOCK-Tasten.

Funktionsweise des Code-Lock-Moduls

Das Schloss startet im verriegelten Zustand. Wenn ein Benutzer vier aufeinanderfolgende Ziffern eingibt, die dem geheimen PIN entsprechen, öffnet sich das Schloss. Eine falsche Sequenz verriegelt das Gerät erneut. Dieses Verhalten wird als simpler Sequenzdetektor in VHDL implementiert.

Tcl-gesteuerter Testbench für ein VHDL Code-Lock-Modul erstellen

Das Wellenformdiagramm veranschaulicht das Timing des Moduls. Neben Takt und Reset akzeptiert das Modul zwei Eingänge: input_digit (Integer 0–9) und input_enable (std_logic). Das DUT sampelt die Ziffer an der steigenden Taktflanke, wenn Enable high ist.

Der einzige Ausgang ist unlock, ein std_logic, der nur high geht, wenn die vier aufeinanderfolgenden Ziffern dem korrekten PIN entsprechen. In diesem Beispiel ist der Passcode 1234.

Die Entity

Die folgende Entity-Definition kodiert das geheime PIN über Generics fest. Jeder Generic ist ein Integer im Bereich 0–9 und stellt eine BCD-Ziffer (Binary Coded Decimal) dar.

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;

Der deklarative Bereich

Das Modul nutzt ein einziges internes Signal – ein 4-Bit-Schieberegister, das die zuletzt eingegebenen Ziffern speichert. Wir erlauben dem Register Werte von –1 bis 9. Der spezielle Wert –1 dient dem Reset-Zustand und stellt sicher, dass ein PIN von 0000 das Schloss nicht standardmäßig öffnet.

type pins_type is array (0 to 3) of integer range -1 to 9; signal pins : pins_type;

Die Implementierung

Die Architektur umfasst eine kombinatorische Zuweisung, die unlock high treibt, wenn das Schieberegister den Generic-PIN-Werten entspricht. Da pins getaktet ist, ändert sich unlock nur an einer steigenden Taktflanke.

unlock <= '1' when pins = (pin3, pin2, pin1, pin0) else '0';

Der folgende Prozess implementiert das Schieberegister. Bei Reset werden alle Einträge auf –1 gesetzt. Wenn input_enable an einer steigenden Flanke high ist, wird die aktuelle Ziffer an Position 0 gespeichert und die vorherigen Ziffern schieben sich nach rechts.

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;

Der VHDL-Testbench

Obwohl wir Tcl für die Verifikationslogik nutzen, benötigen wir dennoch einen grundlegenden VHDL-Testbench zur Erzeugung von Takt- und Reset-Signalen. Unten steht die vollständige VHDL-Testbench-Datei.

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;

Der Tcl-Testbench

Unten steht ein Tcl-Skript, das in ModelSim läuft. Nutzen Sie Vivado oder einen anderen Simulator, sind kleinere Anpassungen nötig, da einige Befehle simulatorspezifisch sind. Siehe ModelSim Command Reference Manual für Details.

Beispielprojekt über das untenstehende Formular herunterladen.

Namensraum verwenden

Die Kapselung des Skripts in einen Namensraum verhindert unbeabsichtigte Kollisionen mit globalen Variablen. Der gesamte Tcl-Code residiert im codelocktb-Namensraum.

namespace eval ::codelocktb { # Gesamter Tcl-Code hier }

Simulation initialisieren

Wir starten die Simulation mit vsim und laden die Wellenform-Datei, falls vorhanden.

# Simulation laden vsim work.code_lock_tb # Wellenform laden, falls vorhanden if {[file exists wave.do]} { do wave.do }

VHDL-Werte in Tcl importieren

Mit ModelSims examine-Befehl lesen wir die clock_period-Konstante und die PIN-Generics. Der folgende Ausschnitt extrahiert den numerischen Wert und die Zeiteinheit.

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]

Wir initialisieren auch eine errorCount-Variable zur Fehlerverfolgung.

variable errorCount 0

Ausgabe mit Zeitstempel

Der echo-Befehl zeigt Nachrichten in der ModelSim-Konsole auf allen Plattformen an. Die folgende Prozedur präfixiert jede Nachricht mit der aktuellen Simulationszeit.

proc printMsg { msg } { global now variable timeUnits echo $now $timeUnits: $msg }

Für angegebene Anzahl Taktzyklen laufen

Da das DUT taktsensitiv ist, schreiten wir die Zeit in Vielfachen der Taktperiode voran.

proc runClockCycles { count } { variable clockPeriod variable timeUnits set t [expr {$clockPeriod * $count}] run $t $timeUnits }

Signalwerte prüfen

Die checkSignal-Prozedur vergleicht ein VHDL-Signal mit einem Erwartungswert und meldet einen Fehler bei Abweichung.

proc checkSignal { signalName expectedVal } { variable errorCount set val [examine $signalName] if {$val != $expectedVal} { printMsg "ERROR: $signalName=$val (expected=$expectedVal)" incr errorCount } }

PIN-Eingabe simulieren

Diese Prozedur steuert die DUT-Eingänge, um einen Benutzer zu simulieren, der einen 4-stelligen PIN eingibt. Sie prüft den unlock-Ausgang, nachdem alle vier Ziffern angelegt wurden.

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 } }

Testsequenz und Ergebnisbericht

Nach kurzer Initialisierungsphase gibt das Skript den Reset frei und führt mehrere Testfälle aus: zwei Randfälle (0000 und 9999), den korrekten PIN und die umgekehrte PIN-Sequenz. Anschließend berichtet es das Gesamtergebnis.

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)" }

Testbench ausführen

Führen Sie das Skript mit folgendem Befehl in ModelSim aus:

source code_lock/code_lock_tb.tcl

Ein erfolgreicher Lauf erzeugt eine Ausgabe ähnlich der folgenden, mit Zeitstempeln vor jeder Nachricht.

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

Enthält das DUT einen Defekt (z. B. einen Tippfehler in der unlock-Zuweisung), meldet das Skript den Fehler und die Fehleranzahl.

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)

Abschließende Gedanken

In meiner Karriere habe ich zahlreiche Tcl-basierte Testbenches geschrieben. Sie bieten einzigartige Vorteile: interaktives Testen, die Möglichkeit, den Test-Harness ohne Neukompilierung zu modifizieren, und eine zusätzliche Verifikationsschicht, die vor Bugs sowohl im Design als auch im Testbench schützt.

Es gibt jedoch Trade-offs. Tcl-Skripte laufen spürbar langsamer als reine VHDL-Testbenches, und die Sprache kann fragil sein – Fehler sind früh schwer zu erkennen, und Debugging ist weniger intuitiv als in höheren Sprachen wie Python.

Trotz dieser Nachteile bleibt Tcl die de-facto Klebesprache für FPGA-Tooling und Simulation. Wenn Sie mit Xilinx Vivado, Intel Quartus oder Mentor Graphics Questa arbeiten, wird die Beherrschung von Tcl sich auszahlen. Teilen Sie Ihre Erfahrungen gerne in den Kommentaren unten.

VHDL

  1. String-Listen in VHDL erstellen: Best Practices & Beispiel
  2. VHDL-Testbench-Simulation sauber beenden
  3. PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo
  4. Ein robuster VHDL-Ringpuffer-FIFO im Block-RAM entwerfen
  5. Hardware-Verifikation mit Tcl-basiertem interaktivem Testbench verbessern
  6. Selbstprüfenden VHDL-Testbench erstellen – Praxisleitfaden
  7. Dynamische verkettete Liste in VHDL mit Protected Types und Access-Pointern
  8. Impure Functions in VHDL nutzen: FSM-Lesbarkeit und Wartbarkeit steigern
  9. VHDL-Funktionen meistern: Praktischer Leitfaden für effizientes Design
  10. Kostenloser VHDL-Simulator & Editor: Ein Leitfaden für Studierende