Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Industrial programming >> VHDL

Comment créer un banc d'essai piloté par Tcl pour un module de verrouillage de code VHDL

La plupart des simulateurs VHDL utilisent le langage de commande d'outil (Tcl) comme langage de script. Lorsque vous tapez une commande dans la console du simulateur, vous utilisez Tcl. De plus, vous pouvez créer des scripts avec Tcl qui s'exécutent dans le simulateur et interagissent avec votre code VHDL.

Dans cet article, nous allons créer un banc de test d'auto-vérification qui utilise Tcl au lieu de VHDL pour vérifier qu'un module VHDL se comporte correctement.

Voir aussi :
Pourquoi vous devez apprendre Tcl
Ban de test interactif utilisant Tcl

Vous pouvez télécharger le code de cet article et du projet ModelSim en utilisant le formulaire ci-dessous.

Le DUT :un module de serrure à code en VHDL

Avant de commencer sur le banc d'essai, je vais présenter le dispositif sous test (DUT). Ce sera un module de verrouillage par code qui déverrouillera un coffre-fort lorsque nous entrerons la séquence de chiffres correcte sur un clavier NIP.

Les gens se réfèrent souvent à une serrure à code comme une serrure à combinaison . Cependant, je trouve que ce terme est inexact. Il ne suffit pas d'entrer la bonne combinaison de chiffres pour le déverrouiller. Vous devez également les saisir dans le bon ordre. À proprement parler, une serrure à combinaison est vraiment une serrure à permutation , mais appelons cela un verrouillage par code .

L'image ci-dessus montre une telle serrure à code sous la forme d'un coffre-fort d'hôtel. Pour plus de simplicité, notre exemple n'utilisera que les touches numériques, et non les boutons "CLEAR" et "LOCK".

Comment fonctionne le module de verrouillage par code

Notre module démarrera en position verrouillée, et si nous entrons quatre chiffres d'affilée qui correspondent au code PIN secret, il déverrouillera le coffre-fort. Pour le reverrouiller, nous pouvons entrer un autre numéro incorrect. Ainsi, nous devons créer un détecteur de séquence en VHDL.

La forme d'onde ci-dessus montre comment le module de verrouillage de code va fonctionner. Outre l'horloge et la réinitialisation, il existe deux signaux d'entrée :input_digit et input_enable . Le module doit échantillonner le chiffre d'entrée lorsque l'activation est "1" sur un front d'horloge montant.

Il n'y a qu'une seule sortie de ce module :le déverrouillage signal. Imaginez qu'il contrôle le mécanisme de verrouillage d'un coffre-fort ou d'un coffre-fort quelconque. Le déverrouillage le signal ne doit être « 1 » que lorsque l'utilisateur a saisi quatre chiffres consécutifs correspondant au code PIN correct. Dans cet article, nous utiliserons 1234 comme mot de passe.

L'entité

Le code ci-dessous montre l'entité du module de serrure à code. Parce que le but de ce module est d'être un exemple simple DUT pour notre testbench basé sur TCL, je code en dur le mot de passe secret en utilisant des génériques. Les quatre constantes génériques sont des décimales codées binaires (BCD) réalisées sous forme d'entiers avec une plage restreinte.

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;

Tout comme le mot de passe, le input_digit Le signal est également de type BCD. Les autres entrées et sorties sont std_logics.

La région déclarative

Ce module n'a qu'un seul signal interne :un registre à décalage qui contient les quatre derniers chiffres que l'utilisateur a tapés. Mais au lieu d'utiliser la plage BCD de 0 à 9, nous laissons les nombres aller de -1 à 9. Cela fait 11 valeurs possibles.

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

Nous devons utiliser une valeur de réinitialisation qui n'est pas un chiffre que l'utilisateur peut entrer, et c'est à cela que sert le -1. Si nous avions utilisé la plage de 0 à 9 pour les broches array, définir le mot de passe secret sur 0000 aurait initialement ouvert le coffre-fort. Avec ce schéma, l'utilisateur devra explicitement saisir quatre 0.

La mise en œuvre

En haut de la région de l'architecture, j'ai ajouté une instruction concurrente qui déverrouille le coffre lorsque les broches signal correspond aux constantes génériques. Le code ci-dessous est combinatoire, mais puisque les broches signal est cadencé, le déverrouillage le signal ne changera que sur le front montant de l'horloge.

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

Le code ci-dessous montre le processus qui lit l'entrée de l'utilisateur. Il crée un registre à décalage à partir des broches signal en décalant toutes les valeurs lorsque input_enable est '1' sur un front d'horloge montant. Le résultat est que les quatre derniers chiffres que l'utilisateur a entrés sont stockés dans les broches tableau.

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;

Le banc d'essai VHDL

Tout d'abord, nous avons toujours besoin d'un banc de test VHDL de base, même si nous utilisons Tcl pour la vérification. Le code ci-dessous montre le fichier VHDL complet. J'ai instancié le DUT et créé le signal d'horloge, mais c'est tout. A part générer l'horloge, ce banc de test ne fait rien.

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;

Le banc d'essai Tcl

Le code Tcl de cet exemple ne fonctionne qu'avec le simulateur ModelSim VHDL. Si vous souhaitez l'utiliser dans Vivado, par exemple, vous devez y apporter quelques modifications. C'est parce qu'il utilise quelques commandes spécifiques à ce simulateur. C'est un inconvénient de l'utilisation de Tcl que votre code soit verrouillé sur un fournisseur de simulateur particulier.

Pour référence, je recommande le Tcl Developer Xchange, qui couvre le langage Tcl en général, et le ModelSim Command Reference Manual, qui décrit toutes les commandes spécifiques à ModelSim.

Si vous avez installé ModelSim, vous pouvez télécharger l'exemple de projet en utilisant le formulaire ci-dessous.

Utiliser un espace de noms

La première chose que je recommande est de créer un espace de noms Tcl. C'est une bonne idée car sinon, vous risquez d'écraser involontairement les variables globales de votre script Tcl. En enveloppant tout votre code dans l'espace de noms, vous évitez ce gâchis potentiel. Nous mettrons désormais tout le code Tcl que nous écrivons dans le codelocktb espace de noms, comme indiqué ci-dessous.

namespace eval ::codelocktb {

  # Put all the Tcl code in here

}

À l'intérieur de l'espace de noms, nous devons commencer par démarrer la simulation, comme indiqué ci-dessous. Nous le faisons avec le vsim commande, suivi du nom de la bibliothèque et de l'entité du testbench VHDL. Cela chargera la simulation, mais ne l'exécutera pas. Aucun temps de simulation ne s'écoule jusqu'à ce que nous utilisions le run commande plus tard dans le script. J'aime aussi inclure une instruction If qui chargera la forme d'onde, si elle existe.

# Load the simulation
vsim work.code_lock_tb

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

Déclarer des variables d'espace de noms

Maintenant que nous avons chargé la simulation, nous pouvons commencer à interagir avec le code VHDL. Tout d'abord, je veux lire la clock_period constante et mot de passe générique dans l'environnement Tcl.

Dans le code ci-dessous, j'utilise l'examen spécifique à ModelSim commande pour lire le signal VHDL et les valeurs constantes dans Tcl. Ensuite, j'utilise les commandes de chaîne et de liste Tcl pour extraire la valeur de temps et les unités de temps. Le pinCode devient une liste des quatre chiffres que nous lisons à partir des constantes génériques.

# 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]

Notez que j'utilise un style de codage différent dans le script Tcl que dans le code VHDL. Au lieu de traits de soulignement, j'utilise une enveloppe de chameau. C'est parce que je suis le guide de style Tcl. Bien sûr, rien ne vous empêche d'utiliser le même style dans les fichiers Tcl et VHDL si c'est ce que vous préférez.

De plus, si vous avez utilisé Tcl sans espaces de noms, vous connaissez probablement le mot-clé set, qui est la manière standard de définir une variable en Tcl. Ici, j'utilise plutôt le nouveau mot-clé de variable. C'est comme une variable globale qui est liée à l'espace de noms actuel au lieu de la portée globale.

Enfin, nous déclarons une variable nommée errorCount et initialisez-le à 0, comme indiqué ci-dessous. Au fur et à mesure que la simulation progresse dans les cas de test, nous l'incrémenterons chaque fois que nous détecterons une erreur. En fin de compte, nous pouvons l'utiliser pour déterminer si le module a réussi ou échoué.

variable errorCount 0

Imprimer du texte dans ModelSim

La commande puts est le moyen standard d'afficher du texte sur la console en Tcl. Mais cette méthode fonctionne de manière malheureuse dans ModelSim. La version Windows fait ce que vous attendez; il imprime la chaîne sur la console. Dans la version Linux, en revanche, le texte est sorti dans le shell à partir duquel vous avez démarré ModelSim, et non dans la console au sein de l'interface graphique.

L'image ci-dessous montre ce qui se passe lorsque nous tapons les puts commande dans la console ModelSim. Il apparaît dans la fenêtre du terminal derrière. Pire encore, si vous avez démarré ModelSim à l'aide d'un raccourci sur le bureau, vous ne verrez jamais la sortie car le shell est masqué.

Il existe des solutions de contournement pour modifier le comportement des puts commande. Vous pouvez, par exemple, le redéfinir (Oui ! Vous pouvez le faire en Tcl) et le faire fonctionner sur les deux plates-formes. Mais un moyen plus simple d'imprimer le texte sur la console sous Linux et Windows consiste à utiliser l' écho spécifique à ModelSim. commande.

Nous utiliserons la procédure Tcl personnalisée ci-dessous pour imprimer le texte. Et ce faisant, nous préparons également le message avec l'heure de simulation actuelle. Dans ModelSim, vous pouvez toujours l'obtenir en utilisant le $now variable globale.

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

Simulation pour N cycles d'horloge

Le DUT est un module cadencé, ce qui signifie que rien ne se passe entre les fronts d'horloge montants. Par conséquent, nous voulons simuler par étapes en fonction de la durée d'un cycle d'horloge. La procédure Tcl ci-dessous utilise la clockPeriod et timeUnits variables que nous avons précédemment extraites du code VHDL pour y parvenir.

proc runClockCycles { count } {
  variable clockPeriod
  variable timeUnits

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

La procédure prend un paramètre :count . Nous le multiplions par la longueur d'une période d'horloge pour obtenir la durée de N cycles d'horloge. Enfin, nous utilisons le ModelSim run commande pour simuler précisément aussi longtemps.

Vérification d'une valeur de signal de Tcl

Dans ModelSim, nous pouvons lire un signal VHDL à partir de Tcl en utilisant le examine commande. Le code ci-dessous montre la procédure Tcl que nous utilisons pour lire une valeur de signal et vérifier qu'elle est comme prévu. Si le signal ne correspond pas à expectedVal paramètre, nous imprimons un message désagréable et incrémentons le errorCount variables.

proc checkSignal { signalName expectedVal } {
  variable errorCount

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

Tester une séquence PIN

La sortie du module de serrure à code dépend non seulement des entrées actuelles mais aussi de leurs valeurs précédentes. Par conséquent, la vérification des sorties doit avoir lieu au moins après l'envoi de quatre chiffres au DUT. Ce n'est qu'alors que le signal de déverrouillage doit passer de « 0 » à « 1 » si le code PIN est correct.

La procédure Tcl ci-dessous utilise la force de ModelSim mot-clé pour changer les signaux VHDL de Tcl. Le -dépôt passer à la force signifie que ModelSim changera la valeur, mais laissera un autre pilote VHDL en prendre le contrôle plus tard, bien qu'aucune autre entité ne contrôle les entrées DUT dans notre testbench.

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

Le tryPin la procédure utilise notre printMsg procédure pour informer de ce qu'il fait, du code PIN qu'il saisit et s'il s'agit du bon code d'accès. Il utilise également les runClockCycles procédure à exécuter pendant exactement une période d'horloge, tout en manipulant les entrées du DUT pour simuler un utilisateur saisissant un code PIN.

Enfin, il utilise le checkSignal procédure pour vérifier que le DUT se comporte comme prévu. Comme je l'ai déjà expliqué, le checkSignal la procédure imprimera un message d'erreur et incrémentera le errorCount variable si le déverrouillage le signal ne correspond pas à la valeur attendue.

Cas de test et statut de fin

Dans le code Tcl ci-dessus, nous avons commencé la simulation, et nous avons défini un tas de variables et de procédures, mais nous n'avons pas du tout simulé. La simulation est toujours à 0 ns. Aucun temps de simulation ne s'est écoulé.

Vers la fin de notre espace de noms personnalisé, nous commençons à appeler les procédures Tcl. Comme indiqué dans le code ci-dessous, nous commençons par courir pendant dix cycles d'horloge. Après cela, nous relâchons la réinitialisation et vérifions que le déverrouillage la sortie a la valeur attendue de '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)"
}

Nous pourrions essayer les 10 000 codes PIN différents, mais cela prendrait un temps considérable. La simulation pilotée par Tcl est beaucoup plus lente qu'un banc de test VHDL pur. Le simulateur doit démarrer et s'arrêter beaucoup, et cela prend beaucoup de temps. Par conséquent, j'ai décidé de ne vérifier que les cas d'angle.

Nous appelons tryPin quatre fois, avec les codes PIN :0000, 9999, le code PIN correct et les chiffres du code PIN correct dans l'ordre inverse. J'imagine que c'est une erreur facile à faire lors de la création d'une serrure à code, de ne regarder que la combinaison, et non l'ordre des chiffres.

Enfin, à la toute fin du code Tcl, mais toujours dans l'espace de noms, nous vérifions le errorCount variable et imprime un "Test :OK" ou un "Échec du test".

Exécuter le banc d'essai

Et maintenant vient la partie amusante :exécuter le testbench. Je préfère utiliser la commande source Tcl, comme indiqué ci-dessous, mais vous pouvez également utiliser le do spécifique à ModelSim commande. En fait, les fichiers ModelSim DO ne sont en réalité que des fichiers Tcl avec un suffixe différent.

source code_lock/code_lock_tb.tcl

Dans la version finale de mon code, il n'y a pas d'erreurs. La liste ci-dessous montre la sortie d'une simulation réussie. Le script Tcl nous informe de ce qu'il fait, et nous pouvons voir que toutes les lignes de message ont un horodatage. C'est notre printMsg procédure au travail. Enfin, le testbench s'arrête et affiche "Test :OK".

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

Cependant, je veux vous montrer à quoi cela ressemble lorsque le DUT échoue à un test. Pour ce faire, j'ai créé une erreur dans le module de verrouillage de code. J'ai remplacé la vérification de pin1 avec pin2 de sorte que le DUT ignore la pin1 évaluer. C'est une faute de frappe facile à faire, comme indiqué dans le code ci-dessous.

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

Lorsque nous exécutons maintenant le testbench, vous pouvez voir dans la liste ci-dessous que le défaut est détecté. Et enfin, le testbench imprime "Test :Échec" avec le nombre d'erreurs.

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)

Réflexions finales

J'ai créé de nombreux bancs de test basés sur Tcl dans ma carrière, mais mon opinion à leur sujet est quelque peu partagée.

D'une part, vous pouvez faire des choses intéressantes qui ne sont pas possibles avec VHDL seul. Par exemple, le banc de test interactif. C'est aussi bien que vous puissiez changer le testbench sans avoir à recompiler. Et enfin, la vérification utilisant un langage très différent peut être avantageuse. Il faudrait faire la même erreur dans deux technologies différentes pour qu'elle passe inaperçue, et c'est peu probable.

D'un autre côté, il y a aussi des inconvénients. Les bancs de test basés sur Tcl sont beaucoup plus lents que leurs homologues VHDL. Un autre problème important est le verrouillage du fournisseur. Il est impossible de créer un banc de test Tcl entièrement portable, alors qu'un banc de test VHDL peut fonctionner sur n'importe quel simulateur capable.

Et la dernière raison pour laquelle les bancs de test Tcl ne valent peut-être pas la peine est le langage lui-même. Il n'a pas de grandes fonctionnalités pour prévenir les erreurs de programmation, et le débogage d'un problème Tcl est difficile. Ce n'est pas un langage intuitif ni indulgent comme Python ou Java.

Cependant, il sert de langage de liaison entre VHDL et le monde du logiciel. Et parce que la plupart des outils FPGA, pas seulement les simulateurs, prennent en charge Tcl, je recommande fortement de l'apprendre.

Ces pensées ne sont que mes opinions. Dites-moi ce que vous en pensez dans la section des commentaires !


VHDL

  1. Comment créer une liste de chaînes en VHDL
  2. Comment arrêter la simulation dans un testbench VHDL
  3. Comment créer un contrôleur PWM en VHDL
  4. Comment créer un tampon circulaire FIFO en VHDL
  5. Banc de test interactif utilisant Tcl
  6. Comment créer un banc d'essai d'auto-vérification
  7. Comment créer une liste chaînée en VHDL
  8. Comment utiliser une fonction en VHDL
  9. Comment installer un simulateur et un éditeur VHDL gratuitement