亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> VHDL

如何在 VHDL 中使用不纯函数

不纯函数可以读取或写入其范围内的任何信号,包括那些不在参数列表中的信号。我们说这个函数有副作用 .

我们所说的副作用的意思是,不能保证每次使用相同的参数调用函数时都会返回相同的值。如果函数可以读取不在参数列表中的信号,则返回值也可能取决于这些影子参数。此外,该函数可能正在更改未从其返回值分配的外部信号。

这篇博文是基本 VHDL 教程系列的一部分。

尽管我们可以在任何可以声明普通纯函数的地方声明不纯函数,但只有在进程中使用它们才有意义。当在我们通常声明信号的架构中声明时,在编译时没有任何信号在其范围内。因此,在架构或包中声明时,不纯函数只能做纯函数所能做的事情。

使用不纯函数的动机主要是整理代码。我们可以简单地通过将任何信号添加到参数列表中来使用纯函数来操作任何信号,但是如果参数列表变得太长,它会混淆而不是简化。

声明不纯函数的语法就是简单地写 impure function 而不是 function 声明时。泛型函数的语法参考函数教程。

运动

在上一教程中,我们通过使用计算时间延迟值的函数简化了有限状态机 (FSM) 代码。我们提供了 Minutes 和 Seconds 参数来指定我们希望将每次状态更改延迟多长时间。

如果 CounterVal 函数返回 true ,时间已过,是时候进入下一个 FSM 状态了。在同一过程中,我们还必须重置 Counter 信号,否则,该函数将无法在下一个状态下工作。计时器已经过期了。

Counter 信号将始终设置为 0 当函数返回真时。如果这发生在 CounterVal 中不是更好吗 函数而不是状态机代码中的多个位置?

在本视频教程中,我们将使用不纯函数改进上一教程中的 FSM 代码:

不纯函数 testbench 的最终代码 :

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity T22_ImpureFunctionTb is
end entity;

architecture sim of T22_ImpureFunctionTb is

    -- We are using a low clock frequency to speed up the simulation
    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

    -- The Device Under Test (DUT)
    i_TrafficLights : entity work.T22_TrafficLights(rtl)
    generic map(ClockFrequencyHz => ClockFrequencyHz)
    port map (
        Clk         => Clk,
        nRst        => nRst,
        NorthRed    => NorthRed,
        NorthYellow => NorthYellow,
        NorthGreen  => NorthGreen,
        WestRed     => WestRed,
        WestYellow  => WestYellow,
        WestGreen   => WestGreen);

    -- Process for generating clock
    Clk <= not Clk after ClockPeriod / 2;

    -- Testbench sequence
    process is
    begin
        wait until rising_edge(Clk);
        wait until rising_edge(Clk);

        -- Take the DUT out of reset
        nRst <= '1';

        wait;
    end process;

end architecture;

红绿灯模块的最终代码 :

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity T22_TrafficLights is
generic(ClockFrequencyHz : integer);
port(
    Clk         : in std_logic;
    nRst        : in std_logic; -- Negative reset
    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 T22_TrafficLights is

    -- Calculate the number of clock cycles in minutes/seconds
    function CounterVal(Minutes : integer := 0;
                        Seconds : integer := 0) return integer is
        variable TotalSeconds : integer;
    begin
        TotalSeconds := Seconds + Minutes * 60;
        return TotalSeconds * ClockFrequencyHz -1;
    end function;

    -- Enumerated type declaration and state signal declaration
    type t_State is (NorthNext, StartNorth, North, StopNorth,
                        WestNext, StartWest, West, StopWest);
    signal State : t_State;

    -- Counter for counting clock periods, 1 minute max
    signal Counter : integer range 0 to ClockFrequencyHz * 60;

begin

    process(Clk) is

        -- This impure function reads and drives the Counter signal
        -- which is not on the parameter list.
        impure function CounterExpired(Minutes : integer := 0;
                                       Seconds : integer := 0)
                                       return boolean is
        begin
            if Counter = CounterVal(Minutes, Seconds) then
                Counter <= 0;
                return true;
            else
                return false;
            end if;
        end function;

    begin
        if rising_edge(Clk) then
            if nRst = '0' then
                -- Reset values
                State   <= NorthNext;
                Counter <= 0;
                NorthRed    <= '1';
                NorthYellow <= '0';
                NorthGreen  <= '0';
                WestRed     <= '1';
                WestYellow  <= '0';
                WestGreen   <= '0';

            else
                -- Default values
                NorthRed    <= '0';
                NorthYellow <= '0';
                NorthGreen  <= '0';
                WestRed     <= '0';
                WestYellow  <= '0';
                WestGreen   <= '0';

                Counter <= Counter + 1;

                case State is

                    -- Red in all directions
                    when NorthNext =>
                        NorthRed <= '1';
                        WestRed  <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= StartNorth;
                        end if;

                    -- Red and yellow in north/south direction
                    when StartNorth =>
                        NorthRed    <= '1';
                        NorthYellow <= '1';
                        WestRed     <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= North;
                        end if;

                    -- Green in north/south direction
                    when North =>
                        NorthGreen <= '1';
                        WestRed    <= '1';
                        -- If 1 minute has passed
                        if CounterExpired(Minutes => 1) then
                            State <= StopNorth;
                        end if;

                    -- Yellow in north/south direction
                    when StopNorth =>
                        NorthYellow <= '1';
                        WestRed     <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= WestNext;
                        end if;

                    -- Red in all directions
                    when WestNext =>
                        NorthRed <= '1';
                        WestRed  <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= StartWest;
                        end if;

                    -- Red and yellow in west/east direction
                    when StartWest =>
                        NorthRed   <= '1';
                        WestRed    <= '1';
                        WestYellow <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= West;
                        end if;

                    -- Green in west/east direction
                    when West =>
                        NorthRed  <= '1';
                        WestGreen <= '1';
                        -- If 1 minute has passed
                        if CounterExpired(Minutes => 1) then
                            State <= StopWest;
                        end if;

                    -- Yellow in west/east direction
                    when StopWest =>
                        NorthRed   <= '1';
                        WestYellow <= '1';
                        -- If 5 seconds have passed
                        if CounterExpired(Seconds => 5) then
                            State <= NorthNext;
                        end if;

                end case;

            end if;
        end if;
    end process;

end architecture;

我们输入run 5 min后的波形 ModelSim 控制台中的命令:

分析

从波形可以看出,我们添加了不纯函数后,模块输出保持不变。我们根本没有改变逻辑,只改变了代码。

Counter的评价 信号已从 FSM 代码移至新的不纯函数 CounterExpired . Counter <= 0; 用于清除 Counter 的行 信号也被移到了不纯函数中。

结果是更易于维护的更易读的 FSM 代码。这是主观的,但对我来说 CounterExpired(Seconds => 5)Counter = CounterVal(Seconds => 5) 更美观 .

使用不纯函数应该走多远完全取决于您和为您的服务付费的人。有些人认为应该谨慎使用它们,因为很难看清隐藏在子程序中的算法的所有原因和影响。其他人,比如我,觉得只要你的意图明确,更容易阅读的代码实际上更不容易出错。

因此,与生产模块相比,您更有可能在测试平台代码中找到不纯的函数。测试台通常比它们正在测试的模块更复杂,并且对代码正确性的要求没有 RTL 代码那么严格。

外卖

转到下一个教程 »


VHDL

  1. 我们如何使用钼?
  2. 如何在 VHDL 中创建字符串列表
  3. 如何在 VHDL 测试平台中停止仿真
  4. 如何在 VHDL 中创建 PWM 控制器
  5. 如何在 VHDL 中生成随机数
  6. 如何在 VHDL 中的进程中使用过程
  7. 如何在 VHDL 中使用函数
  8. 如何在 VHDL 中创建有限状态机
  9. 如何在 VHDL 中使用过程
  10. C 库中的 realloc() 函数:如何使用?语法和示例
  11. C 库中的 free() 函数:如何使用?通过示例学习
  12. 如何使用刀具磨床