Logique concurrente et séquentielle¶
La compétence la plus importante en VHDL est de prévoir le matériel inféré par chaque patron de code.
Logique combinatoire¶
Une sortie combinatoire dépend uniquement des entrées présentes et ne mémorise rien.
Affectation directe¶
Processus complet¶
process(all)
begin
y_o <= '0'; -- valeur par défaut
if enable_i = '1' then
y_o <= a_i xor b_i;
end if;
end process;
La valeur par défaut couvre tous les chemins. L'affectation suivante devient prioritaire lorsque la condition est vraie.
Latch accidentel¶
Quand enable_i='0', le processus demande de conserver la valeur précédente. Cette mémorisation exige un latch transparent.
Warning
Les latches ne sont pas interdits, mais ils sont rarement voulus dans un premier projet FPGA et compliquent le timing. Pour mémoriser un état, préférez un registre sur front.
Logique cadencée¶
Reset synchrone et validation¶
process(clk_i)
begin
if rising_edge(clk_i) then
if reset_i = '1' then
count_q <= (others => '0');
elsif enable_i = '1' then
count_q <= count_q + 1;
end if;
end if;
end process;
Les priorités sont reset, validation, puis conservation implicite.
Reset asynchrone¶
process(clk_i, reset_i)
begin
if reset_i = '1' then
q_o <= '0';
elsif rising_edge(clk_i) then
q_o <= d_i;
end if;
end process;
Il agit sans attendre l'horloge. Sa désactivation près d'un front peut violer les temps de recovery/removal : on préfère souvent un reset entièrement synchrone ou une assertion asynchrone avec libération synchronisée.
Validation plutôt qu'horloge créée dans la logique¶
Évitez :
slow_clock <= divider_q(25);
process(slow_clock)
begin
if rising_edge(slow_clock) then
-- ...
end if;
end process;
Cette écriture transforme un signal logique en horloge et crée un nouveau domaine.
Préférez :
process(clk_i)
begin
if rising_edge(clk_i) then
if tick_i = '1' then
-- L'état avance tout en utilisant la vraie horloge.
end if;
end if;
end process;
Sémantique des mises à jour¶
process(clk_i)
begin
if rising_edge(clk_i) then
first_q <= data_i;
second_q <= first_q;
end if;
end process;
Les deux expressions de droite utilisent les valeurs antérieures au front. Deux étages de pipeline sont correctement inférés.
Une variable permet un calcul intermédiaire immédiat :
process(clk_i)
variable next_count : unsigned(count_q'range);
begin
if rising_edge(clk_i) then
next_count := count_q;
if enable_i = '1' then
next_count := next_count + 1;
end if;
count_q <= next_count;
zero_q <= '1' when next_count = 0 else '0';
end if;
end process;
Une variable n'est pas forcément du matériel combinatoire : c'est la manière dont elle est affectée et utilisée qui détermine le circuit.
Pilotes multiples¶
Un std_logic résolu accepte techniquement plusieurs pilotes, mais deux valeurs opposées donnent X en simulation et ne conviennent généralement pas à la logique interne.
Règle pratique :
Chaque signal RTL interne appartient à un seul processus ou à une seule affectation concurrente.
Construisez un multiplexeur explicite lorsqu'il faut choisir une source.
Cycles delta¶
Une affectation sans délai est prévue pour un cycle delta ultérieur au même instant de simulation. Les cycles delta permettent aux processus concurrents de réagir sans faire avancer le temps physique.
Pour un DUT combinatoire, un petit délai non nul comme wait for 1 ns rend le test clair. Pour un DUT synchrone, alignez les vérifications sur l'horloge et tenez compte du delta suivant la mise à jour des registres.
Patrons¶
Combinatoire :
process(all)
variable result_v : unsigned(result_o'range);
begin
result_v := (others => '0');
case operation_i is
when OP_ADD => result_v := a_i + b_i;
when OP_XOR => result_v := a_i xor b_i;
when OP_PASS => result_v := a_i;
when others => null;
end case;
result_o <= result_v;
end process;
Séquentiel :
process(clk_i)
begin
if rising_edge(clk_i) then
if reset_i = '1' then
state_q <= RESET_VALUE;
else
state_q <= state_d;
end if;
end if;
end process;
Relecture¶
- Puis-je nommer les portes et registres attendus ?
- Chaque sortie combinatoire est-elle affectée sur tous les chemins ?
- Chaque état mémorisé appartient-il à un processus cadencé clair ?
- Existe-t-il plusieurs pilotes ?
- Chaque horloge est-elle distribuée par les ressources dédiées ?
- Ai-je utilisé une validation au lieu de fabriquer une horloge lente ?
- Le comportement du reset est-il défini et testé ?