In-Prozess-Prozeduren für saubere VHDL-FSM-Entwürfe nutzen
In VHDL kann eine Prozedur externe Signale treiben, solange diese in ihrem Gültigkeitsbereich liegen. Eine im Architekturkörper deklarierte Prozedur hat keinen Zugriff auf externe Signale (zur Compile-Zeit nicht sichtbar), aber eine innerhalb eines Prozesses definierte Prozedur kann alle Signale lesen und schreiben, die der Prozess sieht.
In-Prozess-Prozeduren eignen sich hervorragend, um wiederkehrende Logik in Zustandsautomaten oder komplexen Protokollen zu kapseln. Durch Einkapselung gemeinsamer Aktionen – z. B. eines zeitgesteuerten Zustandsübergangs – reduzieren Sie Boilerplate-Code, verbessern Lesbarkeit und machen die Absicht jedes Schritts deutlich.
Beispiel: In einem Ampelsteuerung-Controller wird ChangeState(ToState, Minutes, Seconds) aufgerufen – der Leser erkennt sofort die Aktion, ohne Variablenzuweisungen zu analysieren.
Praxisbeispiel
In einem früheren Tutorial nutzten wir eine impure function, um einen Counter zu steuern und Zustandswechsel zu bestimmen. Die State-Zuweisung konnte nicht in die Funktion verlagert werden, da Funktionen immer einen Rückgabewert benötigen. Eine Prozedur – ohne Rückgabewert – passt perfekt.
Unten folgt der vollständige Testbench und das Modul für eine Ampel-FSM, getrieben von einer In-Prozess-Prozedur.
Testbench
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T23_ProcedureInProcessTb is
end entity;
architecture sim of T23_ProcedureInProcessTb is
constant ClockFrequencyHz : integer := 100; -- 100 Hz
constant ClockPeriod : time := 1000 ms / ClockFrequencyHz;
signal Clk : std_logic := '1';
signal nRst : std_logic := '0';
signal NorthRed : std_logic;
signal NorthYellow : std_logic;
signal NorthGreen : std_logic;
signal WestRed : std_logic;
signal WestYellow : std_logic;
signal WestGreen : std_logic;
begin
i_TrafficLights : entity work.T23_TrafficLights(rtl)
generic map(ClockFrequencyHz => ClockFrequencyHz)
port map (
Clk => Clk, nRst => nRst,
NorthRed => NorthRed, NorthYellow => NorthYellow, NorthGreen => NorthGreen,
WestRed => WestRed, WestYellow => WestYellow, WestGreen => WestGreen
);
Clk <= not Clk after ClockPeriod / 2;
process is
begin
wait until rising_edge(Clk);
wait until rising_edge(Clk);
nRst <= '1';
wait;
end process;
end architecture;
Ampel-Modul
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity T23_TrafficLights is
generic(ClockFrequencyHz : integer);
port(
Clk : in std_logic;
nRst : in std_logic;
NorthRed : out std_logic;
NorthYellow : out std_logic;
NorthGreen : out std_logic;
WestRed : out std_logic;
WestYellow : out std_logic;
WestGreen : out std_logic
);
end entity;
architecture rtl of T23_TrafficLights is
type t_State is (NorthNext, StartNorth, North, StopNorth, WestNext, StartWest, West, StopWest);
signal State : t_State;
signal Counter : integer range 0 to ClockFrequencyHz * 60;
process(Clk) is
procedure ChangeState(ToState : t_State; Minutes : integer := 0; Seconds : integer := 0) is
variable TotalSeconds : integer;
variable ClockCycles : integer;
begin
TotalSeconds := Seconds + Minutes * 60;
ClockCycles := TotalSeconds * ClockFrequencyHz - 1;
if Counter = ClockCycles then
Counter <= 0;
State <= ToState;
end if;
end procedure;
begin
if rising_edge(Clk) then
if nRst = '0' then
State <= NorthNext;
Counter <= 0;
NorthRed <= '1'; NorthYellow <= '0'; NorthGreen <= '0';
WestRed <= '1'; WestYellow <= '0'; WestGreen <= '0';
else
NorthRed <= '0'; NorthYellow <= '0'; NorthGreen <= '0';
WestRed <= '0'; WestYellow <= '0'; WestGreen <= '0';
Counter <= Counter + 1;
case State is
when NorthNext =>
NorthRed <= '1'; WestRed <= '1';
ChangeState(StartNorth, Seconds => 5);
when StartNorth =>
NorthRed <= '1'; NorthYellow <= '1'; WestRed <= '1';
ChangeState(North, Seconds => 5);
when North =>
NorthGreen <= '1'; WestRed <= '1';
ChangeState(StopNorth, Minutes => 1);
when StopNorth =>
NorthYellow <= '1'; WestRed <= '1';
ChangeState(WestNext, Seconds => 5);
when WestNext =>
NorthRed <= '1'; WestRed <= '1';
ChangeState(StartWest, Seconds => 5);
when StartWest =>
NorthRed <= '1'; WestRed <= '1'; WestYellow <= '1';
ChangeState(West, Seconds => 5);
when West =>
NorthRed <= '1'; WestGreen <= '1';
ChangeState(StopWest, Minutes => 1);
when StopWest =>
NorthRed <= '1'; WestYellow <= '1';
ChangeState(NorthNext, Seconds => 5);
end case;
end if;
end if;
end process;
end architecture;
Wellenform
Die Simulation verhält sich identisch zum ursprünglichen FSM – nur der Code ist wartbarer und verständlicher.
Analyse
Durch Auslagern der Timing- und Zustandswechsel-Logik in eine einzige Prozedur bleibt das Verhalten erhalten, während die Lesbarkeit drastisch steigt. Die Prozedur garantiert konsistente Implementierung an allen Aufrufstellen.
Kernpunkte
- Eine Prozedur innerhalb eines Prozesses kann auf alle Signale zugreifen, die der Prozess sieht.
- Wiederkehrende Logik in Prozeduren zu kapseln, erhöht Code-Klarheit und Wartbarkeit.
VHDL
- Einführung in VHDL: Ihr erstes AND-Gate bauen
- VHDL Procedure Beispiel: Inkrementieren eines Standard Logic Vectors
- Wie Molybdän die moderne Industrie formt: Nutzung, Vorteile und Versorgungsdynamik
- String-Listen in VHDL erstellen: Best Practices & Beispiel
- Impure Functions in VHDL nutzen: FSM-Lesbarkeit und Wartbarkeit steigern
- VHDL-Funktionen meistern: Praktischer Leitfaden für effizientes Design
- Prozeduren in VHDL: Wiederverwendbaren Code für einfachere Designs
- Zuverlässigen Timer in VHDL bauen: Taktzyklen bis Stunden zählen
- Getakteten Prozess in VHDL erstellen: Praktischer Leitfaden
- Schleiftechniken für Werkzeugschleifmaschinen meistern: Praktischer Leitfaden