Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Industrial programming >> VHDL

Hoe maak je een Tcl-gestuurde testbench voor een VHDL-codeslotmodule?

De meeste VHDL-simulators gebruiken de Tool Command Language (Tcl) als hun scripttaal. Wanneer u een opdracht typt in de console van de simulator, gebruikt u Tcl. Bovendien kunt u met Tcl scripts maken die in de simulator worden uitgevoerd en met uw VHDL-code communiceren.

In dit artikel zullen we een zelfcontrolerende testbench maken die Tcl in plaats van VHDL gebruikte om te controleren of een VHDL-module zich correct gedraagt.

Zie ook:
Waarom je Tcl moet leren
Interactieve testbench met Tcl

Je kunt de code van dit artikel en het ModelSim-project downloaden met behulp van het onderstaande formulier.

De DUT:een codeslotmodule in VHDL

Voordat we op de testbank beginnen, zal ik het te testen apparaat (DUT) presenteren. Het wordt een codeslotmodule die een kluis ontgrendelt wanneer we de juiste cijferreeks op een pincode invoeren.

Mensen verwijzen vaak naar een codeslot als een combinatieslot . Ik vind deze term echter onjuist. Het is niet voldoende om de juiste combinatie van cijfers in te voeren om het te ontgrendelen. Je moet ze ook in de juiste volgorde invoeren. Strikt genomen is een combinatieslot eigenlijk een permutatieslot , maar laten we het een codeslot noemen .

De afbeelding hierboven toont zo'n codeslot in de vorm van een hotelkluis. Voor de eenvoud gebruikt ons voorbeeld alleen de cijfertoetsen en niet de knoppen "CLEAR" en "LOCK".

Hoe de codeslotmodule werkt

Onze module start in de vergrendelde positie en als we vier cijfers achter elkaar invoeren die overeenkomen met de geheime pincode, wordt de kluis ontgrendeld. Om het opnieuw te vergrendelen, kunnen we een ander, onjuist nummer invoeren. We moeten dus een sequentiedetector maken in VHDL.

De bovenstaande golfvorm laat zien hoe de codeslotmodule gaat werken. Afgezien van de klok en reset, zijn er twee ingangssignalen:input_digit en input_enable . De module zal het invoercijfer bemonsteren wanneer de activering '1' is op een stijgende klokflank.

Er is maar één uitgang van deze module:de ontgrendeling signaal. Stel je voor dat het het vergrendelingsmechanisme van een kluis of een soort kluis bestuurt. De ontgrendeling signaal mag alleen '1' zijn als de gebruiker vier opeenvolgende cijfers heeft ingevoerd die overeenkomen met de juiste pincode. In dit artikel gebruiken we 1234 als toegangscode.

De entiteit

De onderstaande code toont de entiteit van de codeslotmodule. Omdat het doel van deze module is om een ​​eenvoudig DUT-voorbeeld te zijn voor onze TCL-gebaseerde testbench, codeer ik de geheime toegangscode hard met behulp van generieke middelen. De vier generieke constanten zijn binair gecodeerde decimalen (BCD's) gerealiseerd als gehele getallen met een beperkt bereik.

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;

Net als de toegangscode, is de input_digit signaal is ook een BCD-type. De andere in- en uitgangen zijn std_logics.

De declaratieve regio

Deze module heeft maar één intern signaal:een schuifregister dat de vier laatste cijfers bevat die de gebruiker heeft ingetypt. Maar in plaats van het BCD-bereik van 0 tot 9 te gebruiken, laten we de getallen van -1 tot 9 gaan. Dat zijn 11 mogelijke waarden.

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

We moeten een resetwaarde gebruiken die geen cijfer is dat de gebruiker kan invoeren, en daar is de -1 voor. Als we het bereik 0 tot 9 hadden gebruikt voor de pinnen array, het instellen van de geheime toegangscode op 0000 zou in eerste instantie de kluis hebben geopend. Bij dit schema moet de gebruiker expliciet vier nullen invoeren.

De implementatie

Bovenaan de architectuurregio heb ik een gelijktijdige instructie toegevoegd die de kluis ontgrendelt wanneer de pins signaal komt overeen met de generieke constanten. De onderstaande code is combinatorisch, maar aangezien de pinnen signaal is geklokt, de ontgrendeling signaal verandert alleen op de stijgende flank van de klok.

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

De onderstaande code toont het proces dat de gebruikersinvoer leest. Het maakt een schuifregister van de pinnen signaal door alle waarden te verschuiven wanneer input_enable is '1' op een stijgende klokflank. Het resultaat is dat de vier laatste cijfers die de gebruiker heeft ingevoerd, worden opgeslagen in de pinnen array.

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;

De VHDL-testbank

Allereerst hebben we nog steeds een basis VHDL-testbench nodig, ook al gebruiken we Tcl voor de verificatie. De onderstaande code toont het volledige VHDL-bestand. Ik heb de DUT geïnstantieerd en het kloksignaal gemaakt, maar dat is alles. Afgezien van het genereren van de klok, doet deze testbank niets.

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;

De Tcl-testbank

De Tcl-code in dit voorbeeld werkt alleen met de ModelSim VHDL-simulator. Als je het bijvoorbeeld in Vivado wilt gebruiken, moet je er enkele wijzigingen in aanbrengen. Dat komt omdat het een paar commando's gebruikt die specifiek zijn voor deze simulator. Het is een nadeel van het gebruik van Tcl dat uw code wordt vergrendeld door een bepaalde simulatorverkoper.

Ter referentie raad ik de Tcl Developer Xchange aan, die de Tcl-taal in het algemeen behandelt, en de ModelSim Command Reference Manual, die alle ModelSim-specifieke opdrachten beschrijft.

Als je ModelSim hebt geïnstalleerd, kun je het voorbeeldproject downloaden via het onderstaande formulier.

Een naamruimte gebruiken

Het eerste dat ik aanbeveel, is om een ​​Tcl-naamruimte te maken. Dat is een goed idee, want anders kunt u onbedoeld globale variabelen uit uw Tcl-script overschrijven. Door al uw code in de naamruimte te verpakken, vermijdt u die potentiële puinhoop. We plaatsen alle Tcl-code die we vanaf nu schrijven in de codelocktb naamruimte, zoals hieronder weergegeven.

namespace eval ::codelocktb {

  # Put all the Tcl code in here

}

Binnen de naamruimte moeten we beginnen met het starten van de simulatie, zoals hieronder weergegeven. Dat doen we met de vsim commando, gevolgd door de bibliotheek en entiteitsnaam van de VHDL-testbench. Dat laadt de simulatie, maar voert deze niet uit. Er gaat geen simulatietijd voorbij totdat we de run . gebruiken commando verderop in het script. Ik voeg ook graag een If-instructie toe die de golfvorm laadt, als deze bestaat.

# Load the simulation
vsim work.code_lock_tb

# Load the waveform
if {[file exists wave.do]} {
  do wave.do
}

Naamruimtevariabelen declareren

Nu we de simulatie hebben geladen, kunnen we beginnen met de interactie met de VHDL-code. Eerst wil ik de clock_period . lezen constante en generieke toegangscode in de Tcl-omgeving.

In de onderstaande code gebruik ik het ModelSim-specifieke onderzoek commando om VHDL-signaal en constante waarden in Tcl te lezen. Vervolgens gebruik ik Tcl-tekenreeks en lijstopdrachten om de tijdwaarde en tijdseenheden te extraheren. De pincode variabele wordt een lijst van de vier cijfers die we aflezen van de generieke constanten.

# Read the clock period constant from the VHDL TB
variable clockPeriod [examine clock_period]

# Strip the braces: "{10 ns}" => "10 ns"
variable clockPeriod [string trim $clockPeriod "{}"]

# Split the number and the time unit
variable timeUnits [lindex $clockPeriod 1]
variable clockPeriod [lindex $clockPeriod 0]

# Read the correct PIN from the VHDL generics
variable pinCode [examine dut.pin0 dut.pin1 dut.pin2 dut.pin3]

Merk op dat ik een andere codeerstijl gebruik in het Tcl-script dan in de VHDL-code. In plaats van onderstrepingstekens gebruik ik kameelomhulsel. Dat komt omdat ik de Tcl-stijlgids volg. Natuurlijk houdt niets je tegen om dezelfde stijl te gebruiken in de Tcl- en VHDL-bestanden als je daar de voorkeur aan geeft.

Als u Tcl zonder naamruimten hebt gebruikt, kent u waarschijnlijk het sleutelwoord set, wat de standaardmanier is om een ​​variabele in Tcl te definiëren. Hier gebruik ik in plaats daarvan het nieuwere variabele trefwoord. Het is als een globale variabele die is gekoppeld aan de huidige naamruimte in plaats van aan het globale bereik.

Ten slotte declareren we een variabele met de naam errorCount en initialiseer het naar 0, zoals hieronder weergegeven. Naarmate de simulatie de testgevallen doorloopt, verhogen we deze elke keer dat we een fout detecteren. Uiteindelijk kunnen we het gebruiken om te bepalen of de module geslaagd of mislukt is.

variable errorCount 0

Tekst afdrukken in ModelSim

Het puts-commando is de standaardmanier om tekst naar de console af te drukken in Tcl. Maar deze methode werkt op een ongelukkige manier in ModelSim. De Windows-versie doet wat je zou verwachten; het drukt de string af naar de console. In de Linux-versie daarentegen wordt de tekst uitgevoerd in de shell waar je ModelSim van startte, en niet in de console in de GUI.

De afbeelding hieronder laat zien wat er gebeurt als we de puts . typen commando in de ModelSim-console. Het verschijnt in het terminalvenster erachter. Erger nog, als je ModelSim hebt gestart met een snelkoppeling op het bureaublad, zul je de uitvoer nooit zien omdat de shell verborgen is.

Er zijn oplossingen om het gedrag van de puts . te veranderen opdracht. Je kunt het bijvoorbeeld opnieuw definiëren (Ja! Dat kan in Tcl) en het op beide platforms laten werken. Maar een eenvoudigere manier om de tekst naar de console af te drukken in zowel Linux als Windows, is door de ModelSim-specifieke echo te gebruiken. commando.

We gebruiken de onderstaande aangepaste Tcl-procedure om tekst af te drukken. En terwijl we dit doen, voegen we het bericht ook toe aan de huidige simulatietijd. In ModelSim kun je het altijd krijgen met de $now globale variabele.

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

Simuleren voor N klokcycli

De DUT is een geklokte module, wat betekent dat er niets gebeurt tussen de stijgende klokflanken. Daarom willen we in stappen simuleren op basis van de duur van een klokcyclus. De onderstaande Tcl-procedure gebruikt de clockPeriod en timeUnits variabelen die we eerder uit de VHDL-code hebben gehaald om dat te bereiken.

proc runClockCycles { count } {
  variable clockPeriod
  variable timeUnits

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

De procedure heeft één parameter nodig:count . We vermenigvuldigen het met de lengte van één klokperiode om de duur van N klokcycli te krijgen. Ten slotte gebruiken we de ModelSim run commando om precies zo lang te simuleren.

Een signaalwaarde van Tcl controleren

In ModelSim kunnen we een VHDL-signaal van Tcl lezen met behulp van de examine opdracht. De onderstaande code toont de Tcl-procedure die we gebruiken om een ​​signaalwaarde te lezen en te controleren of deze is zoals verwacht. Als het signaal niet overeenkomt met de expectedVal parameter, drukken we een vervelend bericht af en verhogen de errorCount variabel.

proc checkSignal { signalName expectedVal } {
  variable errorCount

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

Een pincode testen

De output van de codeslotmodule hangt niet alleen af ​​van de huidige inputs, maar ook van hun vorige waarden. Daarom moet het controleren van de uitgangen minstens gebeuren na het verzenden van vier cijfers naar de TU Delft. Alleen dan mag het ontgrendelingssignaal veranderen van '0' naar '1' als de pincode correct is.

De onderstaande Tcl-procedure gebruikt de ModelSim kracht trefwoord om VHDL-signalen van Tcl te wijzigen. De -aanbetaling schakel over naar de kracht sleutelwoord betekent dat ModelSim de waarde zal wijzigen, maar later een andere VHDL-driver de controle laat overnemen, hoewel er geen andere entiteiten zijn die de DUT-invoer in onze testbench controleren.

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

De tryPin procedure maakt gebruik van onze printMsg procedure om te informeren over wat het doet, welke pincode het invoert en of het de juiste toegangscode is. Het gebruikt ook de runClockCycles procedure om precies één klokperiode te lopen, terwijl de DUT-invoer wordt gemanipuleerd om een ​​gebruiker te simuleren die een pincode invoert.

Ten slotte gebruikt het het checkSignal procedure om te controleren of de TU Delft zich gedraagt ​​zoals verwacht. Zoals ik al heb uitgelegd, is de checkSignal procedure zal een foutmelding afdrukken en de errorCount . verhogen variabele als de ontgrendelen signaal komt niet overeen met de verwachte waarde.

Testgevallen en eindstatus

In de bovenstaande Tcl-code zijn we begonnen met de simulatie en hebben we een aantal variabelen en procedures gedefinieerd, maar we hebben helemaal geen simulatie gedaan. De simulatie staat nog steeds op 0 ns. Er is geen simulatietijd verstreken.

Tegen het einde van onze aangepaste naamruimte beginnen we de Tcl-procedures aan te roepen. Zoals weergegeven in de onderstaande code, beginnen we met tien klokcycli. Daarna laten we de reset los en controleren we of de ontgrendeling output heeft de verwachte waarde van '0'.

runClockCycles 10

# Release reset
force rst '0' -deposit
runClockCycles 1

# Check reset value
printMsg "Checking reset value"
checkSignal unlock 0

# Try a few corner cases
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)"
}

We zouden alle 10000 verschillende pincodes kunnen proberen, maar dat zou veel tijd kosten. Tcl-gestuurde simulatie is veel langzamer dan een pure VHDL-testbench. De simulator moet veel starten en stoppen en dat kost veel tijd. Daarom heb ik besloten om alleen hoekgevallen te controleren.

We noemen tryPin vier keer, met de pincodes:0000, 9999, de juiste pincode en de cijfers van de juiste pincode in omgekeerde volgorde. Ik kan me voorstellen dat dit een makkelijke fout is om te maken bij het maken van een codeslot, om alleen naar de combinatie te kijken en niet naar de volgorde van de cijfers.

Eindelijk, helemaal aan het einde van de Tcl-code, maar nog steeds binnen de naamruimte, controleren we de errorCount variabele en druk een “Test:OK” of een “Test Mislukt” af.

De testbank draaien

En nu komt het leuke gedeelte:de testbank draaien. Ik gebruik liever het Tcl source commando, zoals hieronder getoond, maar je kunt ook het ModelSim-specifieke do gebruiken opdracht. ModelSim DO-bestanden zijn eigenlijk gewoon Tcl-bestanden met een ander achtervoegsel.

source code_lock/code_lock_tb.tcl

In de definitieve versie van mijn code zijn er geen fouten. De onderstaande lijst toont de output van een succesvolle simulatie. Het Tcl-script informeert ons over wat het doet en we kunnen zien dat alle berichtregels een tijdstempel hebben. Dat is onze printMsg procedure op het werk. Ten slotte stopt de testbank en drukt "Test:OK" af.

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

Ik wil je echter laten zien hoe het eruit ziet als de TU Delft een toets niet haalt. Om dat te doen, heb ik een fout gemaakt in de codeslotmodule. Ik heb de controle van pin1 . vervangen met pin2 zodat de DUT de pin1 . negeert waarde. Het is een makkelijke typfout, zoals te zien is in de onderstaande code.

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

Wanneer we nu de testbench uitvoeren, kunt u aan de onderstaande lijst zien dat de fout is gedetecteerd. En tot slot drukt de testbench "Test:Failure" af, samen met het aantal fouten.

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)

Laatste gedachten

Ik heb in mijn carrière veel op Tcl gebaseerde testbanken gemaakt, maar mijn mening hierover is enigszins verdeeld.

Aan de ene kant kun je toffe dingen doen die met VHDL alleen niet mogelijk zijn. Bijvoorbeeld de interactieve testbench. Het is ook leuk dat je de testbench kunt wijzigen zonder opnieuw te hoeven compileren. En tot slot kan verificatie met een heel andere taal voordelig zijn. Je zou dezelfde fout in twee verschillende technologieën moeten maken om onopgemerkt te blijven, en dat is onwaarschijnlijk.

Aan de andere kant zijn er ook enkele nadelen. De op Tcl gebaseerde testbanken zijn magnitudes langzamer dan hun VHDL-tegenhangers. Een ander belangrijk probleem is vendor lock-in. Het is onmogelijk om een ​​volledig draagbare Tcl-testbench te maken, terwijl een VHDL-testbench op elke geschikte simulator kan draaien.

En de laatste reden waarom Tcl-testbanken misschien niet de moeite waard zijn, is de taal zelf. Het heeft geen geweldige functies om programmeerfouten te voorkomen, en het debuggen van een Tcl-probleem is moeilijk. Het is geen intuïtieve of vergevingsgezinde taal zoals Python of Java.

Het dient echter een doel als een lijmtaal tussen VHDL en de softwarewereld. En omdat de meeste FPGA-tools, niet alleen simulators, Tcl ondersteunen, raad ik je ten zeerste aan om het te leren.

Deze gedachten zijn slechts mijn meningen. Vertel me wat je denkt in het commentaargedeelte!


VHDL

  1. Een lijst met strings maken in VHDL
  2. Simulatie stoppen in een VHDL-testbench
  3. Een PWM-controller maken in VHDL
  4. Hoe maak je een ringbuffer FIFO in VHDL
  5. Interactieve testbank met Tcl
  6. Hoe maak je een zelfcontrolerende testbank aan
  7. Een gekoppelde lijst maken in VHDL
  8. Een onzuivere functie gebruiken in VHDL
  9. Een functie gebruiken in VHDL
  10. Een eindige-toestandsmachine maken in VHDL
  11. Hoe een VHDL-simulator en -editor gratis te installeren