In-process procedures benutten voor schonere VHDL FSM-ontwerpen
In VHDL kan een procedure externe signalen aansturen zolang het signaal in zijn bereik blijft. Terwijl een procedure gedeclareerd in een architectuur geen toegang heeft tot externe signalen – omdat die signalen niet zichtbaar zijn tijdens compilatie – kan een procedure gedefinieerd binnen een proces vrij lezen en schrijven naar elk signaal dat het proces kan zien.
Het gebruik van in-process procedures is een effectieve manier om herhalende logica in toestandsmachines of complexe protocollen op te schonen. Door gemeenschappelijke acties – zoals een getijde toestandovergang – in een procedure te encapsuleren, verminder je boilerplate-code, verbeter je leesbaarheid en maak je de bedoeling van elke stap kristalhelder.
Bijvoorbeeld, in een verkeerslichtcontroller, het hernoemen van operaties naar ChangeState(ToState, Minutes, Seconds) vertelt de lezer precies wat de code doet zonder in de onderliggende variabelentoewijzingen te duiken.
Praktische oefening
In een eerdere tutorial gebruikten we een impure functie om een Counter-signaal aan te sturen en toestandsveranderingen te bepalen. We konden de State-toewijzing echter niet in die functie verplaatsen omdat VHDL een returnwaarde vereist voor elke functie-aanroep. In plaats daarvan past een procedure – zonder returnwaarde – perfect voor dit gebruik.
Hieronder staat de complete testbench en module die een verkeerslicht-FSM aangedreven door een in-process procedure demonstreren.
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;
Verkeerslicht-module
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; begin 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;
Golfform
De simulatie blijft identiek aan de oorspronkelijke FSM – alleen de code is beter onderhoudbaar en begrijpelijker geworden.
Analyse
Door de timing- en toestandsveranderingslogica in één procedure te verplaatsen, behielden we het gedrag van de module terwijl we de leesbaarheid aanzienlijk verbeterden. De procedure zorgt voor consistente implementatie waar ook immer deze aangeroepen wordt.
Belangrijkste opmerkingen
- Een procedure binnen een proces heeft toegang tot elk signaal dat zichtbaar is voor dat proces.
- Herhalende logica in procedures encapsuleren verbetert codehelderheid en onderhoudbaarheid.
VHDL
- Freesmes Slijper Technieken Beheersen: Een Praktische Gids
- Hoe molybdeen de moderne industrie vormt: toepassingen, voordelen en leveringsdynamiek
- Inleiding tot VHDL: Uw Eerste AND-poort Bouwen
- VHDL Procedure Voorbeeld: Een Standard Logic Vector Incrementeren
- Dynamische string-lijsten in VHDL: Best Practices & Voorbeeld
- Impure functies in VHDL gebruiken: FSM-leesbaarheid en onderhoudbaarheid verbeteren
- VHDL-functies onder de knie krijgen: een praktische gids voor efficiënt ontwerp
- Procedures in VHDL gebruiken: je ontwerp vereenvoudigen met herbruikbare code
- Een betrouwbare timer in VHDL bouwen: klokcycli tellen tot uren
- Een klokgetriggerd proces in VHDL bouwen: een praktische gids
- VHDL port map instantiation onder de knie krijgen: een praktische gids