如何删除VHDL中的冗余进程

时间:2019-04-13 19:12:37

标签: vhdl xilinx

很不幸,我不是VHDL的新手,而是软件开发的新手。 VHDL中功能的等效性是什么?具体来说,在下面的代码中,我需要删除四个按钮而不是一个按钮。显然,重复执行我的过程代码四次,并在每个信号后缀一个数字以使它们对于四个实例而言是唯一的,这不是专业的方法,也不是正确的方法。如何将所有这些分解为一个过程“功能”,可以将信号“传递”到该过程中,以便消除所有这些重复的代码?

----------------------------------------------------------------------------------
-- Debounced pushbutton examples
----------------------------------------------------------------------------------
library IEEE;

use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity pushbutton is
    generic(
        counter_size : integer := 19           -- counter size (19 bits gives 10.5ms with 50MHz clock)
    );
    port(
        CLK    : in  std_logic;                -- input clock
        BTN    : in  std_logic_vector(0 to 3); -- input buttons
        AN     : out std_logic_vector(0 to 3); -- 7-segment digit anodes ports
        LED    : out std_logic_vector(0 to 3)  -- LEDs
    );
end pushbutton;

architecture pb of pushbutton is
    signal flipflops0   : std_logic_vector(1 downto 0);                               -- input flip flops
    signal flipflops1   : std_logic_vector(1 downto 0);
    signal flipflops2   : std_logic_vector(1 downto 0);
    signal flipflops3   : std_logic_vector(1 downto 0);

    signal counter_set0 : std_logic;                                                  -- sync reset to zero
    signal counter_set1 : std_logic;
    signal counter_set2 : std_logic;
    signal counter_set3 : std_logic;

    signal counter_out0 : std_logic_vector(counter_size downto 0) := (others => '0'); -- counter output
    signal counter_out1 : std_logic_vector(counter_size downto 0) := (others => '0');
    signal counter_out2 : std_logic_vector(counter_size downto 0) := (others => '0');
    signal counter_out3 : std_logic_vector(counter_size downto 0) := (others => '0');

    signal button0      : std_logic;                                                  -- debounce input
    signal button1      : std_logic;
    signal button2      : std_logic;
    signal button3      : std_logic;

    signal result0      : std_logic;                                                  -- debounced signal
    signal result1      : std_logic;
    signal result2      : std_logic;
    signal result3      : std_logic;

begin

    -- Make sure Mercury BaseBoard 7-Seg Display is disabled (anodes are pulled high)
    AN <= (others => '1');

    -- Feed buttons into debouncers
    button0 <= BTN(0);
    button1 <= BTN(1);
    button2 <= BTN(2);
    button3 <= BTN(3);

    -- Start or reset the counter at the right time
    counter_set0 <= flipflops0(0) xor flipflops0(1);
    counter_set1 <= flipflops1(0) xor flipflops1(1);
    counter_set2 <= flipflops2(0) xor flipflops2(1);
    counter_set3 <= flipflops3(0) xor flipflops3(1);

    -- Feed LEDs from the debounce circuitry
    LED(0) <= result0;
    LED(1) <= result1;
    LED(2) <= result2;
    LED(3) <= result3;

    -- Debounce circuit 0
    process (CLK)
    begin
        if (CLK'EVENT and CLK = '1') then
            flipflops0(0) <= button0;
            flipflops0(1) <= flipflops0(0);
            if (counter_set0 = '1') then                  -- reset counter because input is changing
                counter_out0 <= (others => '0');
            elsif (counter_out0(counter_size) = '0') then -- stable input time is not yet met
                counter_out0 <= counter_out0 + 1;
            else                                         -- stable input time is met
                result0 <= flipflops0(1);
            end if;
        end if;
    end process;

    -- Debounce circuit 1
    process (CLK)
    begin
        if (CLK'EVENT and CLK = '1') then
            flipflops1(0) <= button1;
            flipflops1(1) <= flipflops1(0);
            if (counter_set1 = '1') then                  -- reset counter because input is changing
                counter_out1 <= (others => '0');
            elsif (counter_out1(counter_size) = '0') then -- stable input time is not yet met
                counter_out1 <= counter_out1 + 1;
            else                                         -- stable input time is met
                result1 <= flipflops1(1);
            end if;
        end if;
    end process;

    -- Debounce circuit 2
    process (CLK)
    begin
        if (CLK'EVENT and CLK = '1') then
            flipflops2(0) <= button2;
            flipflops2(1) <= flipflops2(0);
            if (counter_set2 = '1') then                  -- reset counter because input is changing
                counter_out2 <= (others => '0');
            elsif (counter_out2(counter_size) = '0') then -- stable input time is not yet met
                counter_out2 <= counter_out2 + 1;
            else                                         -- stable input time is met
                result2 <= flipflops2(1);
            end if;
        end if;
    end process;

    -- Debounce circuit 3
    process (CLK)
    begin
        if (CLK'EVENT and CLK = '1') then
            flipflops3(0) <= button3;
            flipflops3(1) <= flipflops3(0);
            if (counter_set3 = '1') then                  -- reset counter because input is changing
                counter_out3 <= (others => '0');
            elsif (counter_out3(counter_size) = '0') then -- stable input time is not yet met
                counter_out3 <= counter_out3 + 1;
            else                                         -- stable input time is met
                result3 <= flipflops3(1);
            end if;
        end if;
    end process;

end pb;

2 个答案:

答案 0 :(得分:1)

VHDL具有函数,但函数调用是表达式,而不是某些编程语言中的语句或表达式语句。函数调用总是返回类型的值,而表达式不能代表设计层次结构的一部分。

请考虑其他子程序类过程,它们是语句。

不使用以下过程,也可以简化去抖动器的过程和相关的声明:

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

entity pushbutton is
    generic (
        counter_size:  integer := 19 -- The left bound of debounce counters
    );
    port (
        clk:     in  std_logic;
        btn:     in  std_logic_vector(0 to 3);
        an:      out std_logic_vector(0 to 3);
        led:     out std_logic_vector(0 to 3)
    );
end entity pushbutton;

architecture pb1 of pushbutton is
    -- There are two flip flops for each of four buttons:
    subtype buttons is std_logic_vector(0 to 3);
    type flip_flops is array (0 to 1) of buttons;
    signal flipflops:   flip_flops;
    signal counter_set: std_logic_vector(0 to 3);
    use ieee.numeric_std.all; 
    type counter is array (0 to 3) of
                        unsigned(counter_size downto 0);
    signal counter_out: counter := (others => (others => '0'));
begin
    an <= (others => '1');
    counter_set <= flipflops(0) xor flipflops(1);

DEBOUNCE:
    process (clk)
    begin
        if rising_edge (clk) then
            flipflops(0) <= btn;
            flipflops(1) <= flipflops(0);
            for i in 0 to 3 loop
                if counter_set(i) = '1' then
                    counter_out(i) <=  (others => '0');
                elsif counter_out(i)(counter_size) = '0' then
                    counter_out(i) <= counter_out(i) + 1;
                else
                    led(i) <= flipflops(1)(i);
                end if;
            end loop;
        end if;
    end process;
end architecture pb1;

将设计规范的一部分移至过程中

architecture pb2 of pushbutton is
    -- There are two flip flops for each of four buttons:
    subtype buttons is std_logic_vector(0 to 3);
    type flip_flops is array (0 to 1) of buttons;
    signal flipflops:   flip_flops;
    signal counter_set: std_logic_vector(0 to 3);
    use ieee.numeric_std.all;
    type counter is array (0 to 3) of
                        unsigned(counter_size downto 0);
    signal counter_out: counter := (others => (others => '0'));
    procedure debounce (
    -- Can eliminate formals of mode IN within the scope of their declaration:
        -- signal counter_set:    in  std_logic_vector (0 to 3);
        -- signal flipflops:      in  flip_flops;
        signal counter_out:    inout counter;
        signal led:             out std_logic_vector(0 to 3)
        ) is
    begin
        for i in 0 to 3 loop
            if counter_set(i) = '1' then
                counter_out(i) <=  (others => '0');
            elsif counter_out(i)(counter_size) = '0' then
                counter_out(i) <= counter_out(i) + 1;
            else
                led(i) <= flipflops(1)(i);
            end if;
        end loop;
    end procedure;
begin
    an <= (others => '1');
    counter_set <= flipflops(0) xor flipflops(1);

DEBOUNCER:
    process (clk)

    begin
        if rising_edge (clk) then
            flipflops(0) <= btn;
            flipflops(1) <= flipflops(0);
            -- debounce(counter_set, flipflops, counter_out, led);
            debounce (counter_out, led);
        end if;
    end process;
end architecture pb2;

此处该过程用作顺序语句的集合,并且不保存任何代码行。

顺序过程调用对于隐藏重复的混乱很有用。通过合并声明和使用loop语句,已经消除了混乱情况。在设计输入工作,代码维护工作和用户可读性之间存在一种平衡行为,这也可能会受到编码样式的影响。编码样式也受暗示硬件的RTL构造的影响。

将时钟评估移到过程中将需要过程调用是一个并发语句,类似于实例化,您已经拥有了。您应该在体系结构主体中或在使用循环语句时将声明为块声明性项目的信号合并在一起似乎并不值得。

请注意,resultbutton声明已被删除。同样,对计数器使用软件包numeric_std和类型unsigned可以防止无意中将其分配给具有相同子类型的其他对象。计数器值被视为无符号数字,而例如counter_set则不被视为无符号数字。

也有一个独立的计数器,用于每个输入的去抖操作,就像原始操作一样。如果没有独立的计数器,则重复清除单个计数器时,某些事件可能会因独立输入而丢失。

此代码尚未通过仿真验证,缺少测试平台。借助实体,两种架构都可以进行分析和阐述。

除了在for循环中找到的可从函数调用中受益的顺序语句外,这里似乎没有其他任何东西。由于函数调用返回 a 值,因此该值的类型要么需要是复合的(这里是记录类型),要么需要针对每个分配目标分为单独的函数调用。

还有一个generate语句,它可以将零个或多个声明和并发语句(此处为一个进程)的副本详细说明为带有块声明项的块语句。仅在详细块中使用的任何信号都可以是块声明项。

architecture pb3 of pushbutton is
begin
DEBOUNCERS:
    for i in btn'range generate
        signal flipflops:       std_logic_vector (0 to 1);
        signal counter_set:     std_logic;
        signal counter_out:     unsigned (counter_size downto 0) := 
                                                            (others => '0');
    begin
        counter_set <= flipflops(0) xor flipflops(1);
DEBOUNCE:
        process (clk)
        begin
            if rising_edge (clk) then
                flipflops(0) <= btn(i);
                flipflops(1) <= flipflops(0);
                if counter_set = '1' then
                    counter_out <=  (others => '0');
                elsif counter_out(counter_size) = '0' then
                    counter_out <= counter_out + 1;
                else
                    led(i) <= flipflops(1);
                end if;
            end if;
        end process;
    end generate;
end architecture pb3;

附录

OP指出了上述代码中的错误,这是由于在合成体系结构pb2时缺乏仿真和抽象所隐藏的复杂性。防反跳时间为10.5毫秒(50 MHz时钟),通用名称(counter_size)实际上也是计数器的左边界(使用无符号类型作为无符号二进制计数器)。

上面的代码中已纠正了错误(在同步器中,两个按钮各有两个触发器),并简单地加入了OP关于计数器的命名约定。

OP在注释中的综合错误与要求在分配声明的左手或右手每个元素上都有一个匹配元素有关。

如果不进行代码合成(OP会这样做),则无法进行错误模拟。因为找到分配flipflops(0)的特定错误的唯一必要条件是时钟,所以可以编写一个简单的测试平台:

use ieee.std_logic_1164.all;
entity pushbutton_tb is
end entity;

architecture fum of pushbutton_tb is
    signal clk:     std_logic := '0';
    signal btn:     std_logic_vector (0 to 3);
    signal an:      std_logic_vector(0 to 3);
    signal led:     std_logic_vector(0 to 3);
begin
CLOCK:
    process
    begin
        wait for 0.5 ms;
        clk <= not clk;
        if now > 50 ms then
            wait;
        end if;
    end process;

DUT:
    entity work.pushbutton (pb2)
        generic map (
            counter_size =>  4  -- FOR SIMULATION
        )
        port map (
            clk => clk,
            btn => btn,
            an => an,
            led => led
        );

STIMULUS:
    process
    begin
        btn <= (others => '0');
        wait for 20 ms;
        btn(0) <= '1';
        wait for 2 ms;
        btn(1) <= '1';
        wait for 3 ms;
        btn(2) <= '1';
        wait for 6 ms;
        btn(3) <= '1';
        wait;
    end process;
end architecture;

更正后的代码和一个测试台,用以演示在模拟过程中分配中没有匹配的元素错误。

对两种体系结构都提供了仿真,结果相同。

该通用方法用于在测试台中使用1毫秒的时钟来减小反跳计数器的大小(以避免使用50 MHz时钟事件的仿真时间不会增加叙述内容)。

这是第一个架构的模拟输出:

pushbutton_tb_pb2

此处的警告是,应该对设计进行仿真。一类VHDL语义错误条件只能在运行时(或综合时)进行检查。

添加了用于减少“统一”代码的抽象,否则执行相同的操作会引入此类错误。

使用设计层次结构中的名称,generate语句不会出现该问题:

pushbutton_tb_generate.jpg

在generate语句中找到的并发语句和声明将在generate语句隐含的任何已生成的块语句中复制。每个block语句代表设计层次结构的一部分。

在设计复杂性和用于调试的波形显示组织之间需要权衡。

无论如何,应该模拟依赖于隐藏重复细节的设计描述。这里有两个引用,用于在选定名称中使用的生成参数i,容易遭受与忽略参数替换的范围相同的错误。

答案 1 :(得分:0)

多位去抖动电路可能看起来像这样:

library IEEE;
use     IEEE.std_logic_1164.all;
use     IEEE.numeric_std.all;

use     work.Utilities.all;


entity Debouncer is
    generic (
        CLOCK_PERIOD_NS  : positive := 10;
        DEBOUNCE_TIME_MS : positive := 3;

        BITS             : positive
    );
    port (
        Clock  : in  std_logic;

        Input  : in  std_logic_vector(BITS - 1 downto 0);
        Output : out std_logic_vector(BITS - 1 downto 0) := (others => '0')
    );
end entity;

architecture rtl of Debouncer is
begin
    genBits: for i in Input'range generate
        constant DEBOUNCE_COUNTER_MAX  : positive := (DEBOUNCE_TIME_MS * 1000000) / CLOCK_PERIOD_NS;
        constant DEBOUNCE_COUNTER_BITS : positive := log2(DEBOUNCE_COUNTER_MAX);

        signal DebounceCounter         : signed(DEBOUNCE_COUNTER_BITS downto 0) := to_signed(DEBOUNCE_COUNTER_MAX - 3, DEBOUNCE_COUNTER_BITS + 1);

    begin
        process (Clock)
        begin
            if rising_edge(Clock) then
                -- restart counter, whenever Input(i) was unstable within DEBOUNCE_TIME_MS
                if (Input(i) /= Output(i)) then
                    DebounceCounter <= DebounceCounter - 1;
                else
                    DebounceCounter <= to_signed(DEBOUNCE_COUNTER_MAX - 3, DebounceCounter'length);
                end if;

                -- latch input bit, if input was stable for DEBOUNCE_TIME_MS
                if (DebounceCounter(DebounceCounter'high) = '1') then
                    Output(i) <= Input(i);
                end if;
            end if;
        end process;
    end generate;
end architecture;

它希望用户提供一个频率(以毫秒为单位的周期)和一个去抖动时间(以毫秒为单位),而不是计数器的大小。

引用的包实现了log2函数。