Bancs de test auto-vérifiants¶
Un banc auto-vérifiant transforme la spécification en code et provoque un échec dès que le comportement réel diffère du comportement attendu.
Test exhaustif¶
Lorsque l'espace d'entrée est petit, essayez toutes les combinaisons.
for a_value in 0 to 15 loop
for b_value in 0 to 15 loop
for cin_value in 0 to 1 loop
a_s <= to_unsigned(a_value, a_s'length);
b_s <= to_unsigned(b_value, b_s'length);
if cin_value = 0 then
cin_s <= '0';
else
cin_s <= '1';
end if;
wait for 1 ns;
expected := a_value + b_value + cin_value;
actual := to_integer(unsigned'(carry_s & sum_s));
assert actual = expected
report "adder mismatch"
severity failure;
end loop;
end loop;
end loop;
Un additionneur 4 bits ne représente que 16 × 16 × 2 = 512 cas : l'exhaustivité est idéale.
Fonction de référence¶
Exprimez la règle mathématique, indépendamment de la structure du RTL :
function model_saturating_add(
a_value : natural;
b_value : natural;
maximum : natural
) return natural is
begin
if a_value + b_value > maximum then
return maximum;
else
return a_value + b_value;
end if;
end function;
Le modèle doit rester simple, lisible et distinct de l'implémentation.
Scoreboard tenant compte des cycles¶
Pour un pipeline, conservez les résultats attendus jusqu'à leur cycle de sortie :
cycle N : appliquer A, calculer A attendu, l'empiler
cycle N+1 : appliquer B, calculer B attendu, l'empiler
cycle N+L : dépiler A attendu et comparer la sortie A
À latence fixe, un tableau décalé suffit. Pour une latence variable, une file transactionnelle avec identifiants est préférable.
expected_pipe(0) := model(input_s);
for stage in 1 to LATENCY loop
expected_pipe(stage) := expected_pipe(stage - 1);
end loop;
if output_valid_s = '1' then
assert output_s = expected_pipe(LATENCY)
severity failure;
end if;
Transactions¶
Pilote, moniteur et scoreboard peuvent échanger une transaction cohérente plutôt que plusieurs valeurs indépendantes.
Tester d'abord les limites¶
Incluez toujours :
- zéro et un ;
- minimum et maximum représentables ;
- maximum−1 et limites de signe ;
- opérandes égaux ;
- transitions de retenue, emprunt et overflow ;
- reset en activité ;
- changements de validation ;
- transactions successives ;
- longues périodes d'inactivité.
Les erreurs arithmétiques et de compteur se trouvent souvent aux frontières.
Invariants¶
Un invariant doit être vrai en permanence :
invariant_check : process(clk_s)
begin
if rising_edge(clk_s) then
assert not (read_enable_s = '1' and empty_s = '1')
report "Read attempted while FIFO empty"
severity failure;
end if;
end process;
Certaines propriétés sont des hypothèses sur l'environnement plutôt que des garanties du DUT. Nommez-les clairement.
Tests pseudo-aléatoires reproductibles¶
- Employer une graine explicite.
- L'afficher au début.
- Afficher l'indice de la transaction défaillante.
- Transformer toute graine révélant un bogue en test dirigé permanent.
L'aléatoire ne remplace ni les limites ni la couverture.
Couverture simple¶
if saw_overflow then
overflow_cases := overflow_cases + 1;
end if;
assert overflow_cases > 0
report "Coverage hole: overflow never exercised"
severity failure;
Pour une FSM, comptez états et transitions. Pour un protocole, comptez commandes, réponses, erreurs et backpressure.
Exemple exécutable¶
Le dossier examples/adder contient un additionneur générique, un test exhaustif de 512 cas, un test par fichier et un lanceur PowerShell XSim.