Testbenches et ordonnancement
Appliquer des stimuli propres, vérifier automatiquement les sorties et comprendre quand les registres changent.
Un testbench n'est pas du RTL
Le banc de test n'a pas de ports. Il instancie le circuit, crée l'horloge, applique les entrées et décide si le résultat est correct.
`timescale 1ns/1ps
module edge_counter (
input wire i_clk,
input wire i_reset,
input wire i_enable,
output reg [3:0] o_count
);
always @(posedge i_clk) begin
if (i_reset)
o_count <= 4'd0;
else if (i_enable)
o_count <= o_count + 1'b1;
end
endmodule
module tb_edge_counter;
reg i_clk;
reg i_reset;
reg i_enable;
wire [3:0] o_count;
edge_counter dut (
.i_clk (i_clk),
.i_reset (i_reset),
.i_enable (i_enable),
.o_count (o_count)
);
initial i_clk = 1'b0;
always #5 i_clk = ~i_clk;
task expect_count;
input [3:0] expected;
begin
if (o_count !== expected) begin
$display("FAIL at %0t: expected %0d, got %0d",
$time, expected, o_count);
$finish;
end
end
endtask
initial begin
i_reset = 1'b1;
i_enable = 1'b0;
repeat (2) @(posedge i_clk);
@(negedge i_clk);
expect_count(0);
i_reset = 1'b0;
i_enable = 1'b1;
repeat (3) @(posedge i_clk);
@(negedge i_clk);
expect_count(3);
i_enable = 1'b0;
repeat (2) @(posedge i_clk);
@(negedge i_clk);
expect_count(3);
$display("PASS");
$finish;
end
endmoduleLe test change les commandes au front descendant et vérifie aussi au front descendant. Elles sont donc stables avant le prochain front actif et les affectations non bloquantes ont le temps d'être appliquées avant la lecture.
Comprendre un pas de simulation
Plusieurs événements peuvent porter le même temps de simulation. Au front montant :
- les blocs sensibles au front sont réveillés ;
- le membre droit des affectations non bloquantes est évalué ;
- les nouvelles valeurs sont appliquées plus tard dans le même pas de temps.
Un test qui lit une sortie exactement dans la même région que le front peut donc observer l'ancienne valeur. Ajouter des délais arbitraires partout masque le problème sans établir une méthode solide. Il vaut mieux choisir clairement les fronts de stimulation et de vérification.
Affectations dans le testbench
Les signaux de stimulus sont généralement pilotés avec des affectations bloquantes. Les registres du DUT restent décrits avec des affectations non bloquantes. Cette séparation réduit les courses entre le test et le circuit.
Pour comparer des signaux qui peuvent contenir x ou z, === et !== rendent ces valeurs visibles. Avec ==, le résultat de la comparaison peut lui-même devenir inconnu.
Attendre un événement utile
@(posedge i_clk) attend un front. wait(o_done) attend qu'une condition devienne vraie. repeat (N) répète une action un nombre connu de fois. Un délai comme #10 avance d'une durée absolue définie par timescale.
Les attentes liées au protocole sont souvent plus robustes que les délais fixes. Par exemple, attendre o_valid résiste à un changement de latence mieux qu'attendre exactement quatre périodes.
Rendre le test auto-vérifiant
Un test utile connaît le résultat attendu et échoue tout seul. Il doit indiquer au minimum le scénario, la valeur attendue et la valeur reçue.
Les formes d'onde restent utiles pour comprendre une panne, mais elles ne doivent pas être le seul moyen de savoir si cent cas sont passés.
Prévoir les limites
Pour un compteur ou une FIFO, tester seulement quelques valeurs normales ne suffit pas. Il faut aussi couvrir le reset, le rebouclage, le plein, le vide, les demandes simultanées et les commandes maintenues plusieurs cycles.
À retenir
- Le testbench crée le temps et les stimuli, il n'est pas synthétisé.
- Une affectation non bloquante met à jour sa cible plus tard dans le même pas de simulation.
- Stimuler et vérifier sur des fronts bien choisis évite les courses.
===et!==détectent explicitementxetz.- Un test doit annoncer seul son succès ou son échec.
📝 Tester mes connaissances - Quiz du chapitre