Le bloc process : liste de sensibilité, instructions séquentielles, et patterns combinatoire/séquentiel.
Un process décrit le comportement d'une partie du circuit avec des instructions séquentielles. Il fonctionne en concurrence avec les autres blocs de l'architecture et peut décrire une logique combinatoire ou une logique à état.
Un process avec liste de sensibilité s'exécute une première fois au démarrage de la simulation, puis attend un événement sur un signal de cette liste.
Pour une logique combinatoire, liste tous les signaux lus :
-- Tous les signaux lus dans le process doivent être dans la listeprocess(i_a, i_b, i_sel)begin if i_sel = '0' then w_mux <= i_a; else w_mux <= i_b; end if;end process;
En oublier un peut faire diverger simulation et synthèse. L'inférence d'un latch est un autre problème : elle apparaît lorsqu'une sortie doit conserver sa valeur parce qu'elle n'est pas affectée sur tous les chemins.
Pour un registre sur front, liste l'horloge et, s'il existe, le reset asynchrone :
-- Uniquement l'horloge (et le reset asynchrone si utilisé)process(i_clk)begin if rising_edge(i_clk) then r_q <= i_d; end if;end process;
VHDL-2008 permet de calculer la sensibilité avec process(all) :
-- Capture automatiquement tous les signaux lusprocess(all)begin w_result <= i_a + i_b;end process;
Choisir un modèle de process
Ce process combinatoire décode l'opération et affecte la sortie dans tous les cas. Son label p_alu aide à le retrouver :
architecture rtl of alu is signal w_result : std_logic_vector(7 downto 0);begin p_alu : process(i_op, i_a, i_b) begin case i_op is when "00" => w_result <= i_a AND i_b; when "01" => w_result <= i_a OR i_b; when "10" => w_result <= i_a XOR i_b; when others => w_result <= (others => '0'); end case; end process p_alu; o_result <= w_result;end architecture rtl;
Le modèle suivant enregistre les valeurs sur un front et utilise un reset synchrone :
p_reg : process(i_clk)begin if rising_edge(i_clk) then if i_rst = '1' then r_data <= (others => '0'); r_valid <= '0'; r_counter <= (others => '0'); else r_data <= i_data; r_valid <= i_valid; r_counter <= r_counter + 1; end if; end if;end process p_reg;
Les affectations de signaux restent différées, même si les instructions du process sont exécutées dans l'ordre.
Exprimer les conditions et les répétitions
if / elsif / else teste des conditions successives :
process(i_clk)begin if rising_edge(i_clk) then if i_rst = '1' then r_state <= IDLE; elsif i_enable = '1' then r_state <= ACTIVE; else r_state <= r_state; -- inutile mais explicite end if; end if;end process;
case choisit une branche selon une valeur :
process(i_clk)begin if rising_edge(i_clk) then case r_state is when IDLE => if i_start = '1' then r_state <= RUNNING; end if; when RUNNING => if r_done = '1' then r_state <= IDLE; end if; when others => r_state <= IDLE; end case; end if;end process;
Toutes les valeurs possibles doivent être couvertes. when others regroupe celles qui ne sont pas nommées ; il n'est pas obligatoire lorsque les choix couvrent déjà tout le type. Sa présence ne garantit pas la récupération matérielle d'une FSM après un encodage illégal.
Une boucle peut décrire plusieurs opérations dans une même activation. En synthèse, une boucle à bornes fixes est généralement déroulée en logique, sans ajouter un cycle par itération :
process(i_clk) variable v_sum : unsigned(15 downto 0);begin if rising_edge(i_clk) then v_sum := (others => '0'); for i in 0 to 7 loop if w_data(i) = '1' then v_sum := v_sum + 1; end if; end loop; r_popcount <= v_sum(3 downto 0); end if;end process;
Pour éviter une mémoire involontaire en combinatoire, couvre toutes les branches ou donne une valeur par défaut :
-- MAUVAIS : toutes les branches ne couvrent pas toutes les sortiesprocess(i_sel, i_a, i_b)begin if i_sel = '0' then w_out <= i_a; end if; -- i_sel = '1' → w_out non assigné → latch inféré !end process;-- BON : valeur par défaut + couverture complèteprocess(i_sel, i_a, i_b)begin w_out <= i_a; -- valeur par défaut if i_sel = '1' then w_out <= i_b; end if;end process;