在 VHDL 中同步计算去抖按钮按下次数

Synchronously Counting Debounced Button Presses in VHDL

以下代码是 Xilinx ISE 14.7 中的一个 VDHL 模块,它计算去抖按钮按下次数 (iXXX),测试它们是否已达到最大值,并为每个累积输入输出 "value" 到 std_logic_vector (oXXX) 将显示在 7 段 LED 上(显示多路复用器和逻辑未显示)。重置 (clrXX) 是板上的开关 (Digilent Spartan 3)。

在 XILINX ISE 中尝试综合或检查语法时,出现以下错误:

Xst:528 - Multi-source in Unit <BSO_cnt> on signal <Mcount_oOUT_s_cy<0>>; 
this signal is connected to multiple drivers.
Xst:528 - Multi-source in Unit <BSO_cnt> on signal <Mcount_oOUT_s_lut<1>>; 
this signal is connected to multiple drivers.
Xst:528 - Multi-source in Unit <BSO_cnt> on signal <Mcount_oBALL_s_cy<0>>; 
this signal is connected to multiple drivers.
Xst:528 - Multi-source in Unit <BSO_cnt> on signal <Mcount_oBALL_s_lut<1>>; 
this signal is connected to multiple drivers.
Xst:528 - Multi-source in Unit <BSO_cnt> on signal <Mcount_oBALL_s_lut<2>>; 
this signal is connected to multiple drivers.
Xst:528 - Multi-source in Unit <BSO_cnt> on signal <Mcount_oSTRIKE_s_cy<0>>; 
this signal is connected to multiple drivers.
Xst:528 - Multi-source in Unit <BSO_cnt> on signal 
    <Mcount_oSTRIKE_s_lut<1>>; this signal is connected to multiple drivers.

我该如何解决这个问题?我刚接触 VHDL,不知道从哪里开始。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity BSO_cnt is
Port ( iBALL        : in    STD_LOGIC;
       iSTRIKE      : in    STD_LOGIC;
       iOUT         : in    STD_LOGIC;
       clrBS        : in    STD_LOGIC;
       clrOUT       : in    STD_LOGIC;
       CLK          : in    STD_LOGIC;
       oBALL        : out   STD_LOGIC_VECTOR (2 downto 0);
       oSTRIKE      : out   STD_LOGIC_VECTOR (1 downto 0);
       oOUT         : out   STD_LOGIC_VECTOR (1 downto 0));
end BSO_cnt;

architecture Behavioral of BSO_cnt is

SIGNAL iBALL_s      :   STD_LOGIC;
SIGNAL iSTRIKE_s    :   STD_LOGIC;
SIGNAL iOUT_s       :   STD_LOGIC;

SIGNAL oBALL_s      :   STD_LOGIC_VECTOR(2 DOWNTO 0);
SIGNAL oSTRIKE_s    :   STD_LOGIC_VECTOR(1 DOWNTO 0);
SIGNAL oOUT_s       :   STD_LOGIC_VECTOR(1 DOWNTO 0);

Begin

oBALL       <= oBALL_s;
oSTRIKE     <= oSTRIKE_s;
oOUT        <= oOUT_s;

BALL_PROCESS: PROCESS(CLK)
BEGIN
    IF rising_edge(CLK) THEN
        IF (clrBS = '1') THEN
            oBALL_s <= (OTHERS => '0');
        ELSIF (iBALL_s /= iBALL) THEN
            IF (iBALL = '1') THEN
                iBALL_s <= iBALL;
                oBALL_s <= STD_LOGIC_VECTOR(UNSIGNED(oBALL_s) + 1);
                IF (oBALL_s = "100") THEN
                    oBALL_s     <= (OTHERS => '0');
                    oSTRIKE_s   <= (OTHERS => '0');
                END IF; 
            END IF;
        ELSE
            iBALL_s <= iBALL;
        END IF;
    END IF;
END PROCESS;

STRIKE_PROCESS: PROCESS(CLK)
BEGIN
    IF rising_edge(CLK) THEN
        IF (clrBS = '1') THEN
            oSTRIKE_s <= (OTHERS => '0');
        ELSIF (iSTRIKE_s /= iSTRIKE) THEN
            IF (iSTRIKE = '1') THEN
                iSTRIKE_s <= iSTRIKE;
                oSTRIKE_s <= STD_LOGIC_VECTOR(UNSIGNED(oSTRIKE_s) + 1);
                IF (oSTRIKE_s = "11") THEN
                    oSTRIKE_S   <= (OTHERS => '0');
                    oBALL_s     <= (OTHERS => '0');
                    oOUT_s      <= (OTHERS => '0');
                END IF;
            END IF;
        ELSE
            iSTRIKE_s <= iSTRIKE;
        END IF;
    END IF;
END PROCESS;

OUT_PROCESS: PROCESS(CLK)
BEGIN
    IF rising_edge(CLK) THEN
        IF (clrOUT = '1') THEN
            oOUT_s <= (OTHERS => '0');
        ELSIF (iOUT_s /= iOUT) THEN
            IF (iOUT = '1') THEN
                iOUT_s <= iOUT;
                oOUT_s <= STD_LOGIC_VECTOR(UNSIGNED(oOUT_s) + 1);
                IF (oOUT_s = "11") THEN
                    oOUT_s      <= (OTHERS => '0');
                    oBALL_s     <= (OTHERS => '0');
                    oSTRIKE_s   <= (OTHERS => '0');
                END IF;
            END IF;
        ELSE
            iOUT_s <= iOUT;
        END IF;
    END IF;
END PROCESS;

end Behavioral;

您的问题可能与您从多个进程驱动信号(oSTRIKE_soBALL_soOUT_s)有关。将此视为短路(当不同进程不同意该值时,您期望什么值?)。

为什么编译和/或模拟时不报错?

作为大多数不了解多驱动问题、解析函数和解析类型的VHDL的人(也就是很多VHDL程序员甚至老师),你总是使用STD_LOGIC具有内置解析函数的类型,用于计算多个驾驶过程的结果值。它有效(通常给出很多 X 值)......仅用于模拟。如果您使用了未解析的类型 (STD_ULOGIC),您将在编译或模拟时出错,因为这些类型不支持多驱动器情况。

为什么在合成中不起作用?

您的合成器尝试将您的设计映射到目标 (Spartan FPGA) 的可用硬件资源上。 机会 你的目标没有配备可用于实现你的伪造设计的三态缓冲器。所以合成器会产生错误。我写 chance 是因为,如果它是另一个能够实现此目标的目标,您可以对其进行编程并...将其熔断,这要归功于您漂亮的短路。有些事情可能会在你迟到之前阻止你,但你永远不知道......毕竟你很幸运。

那怎么办?

  1. 如果您没有设计合理使用多个驱动器的东西,请不要使用 STD_LOGICSTD_LOGIC_VECTOR。也就是说,在任何时候,所有进程都以 'Z'(高阻抗)驱动信号,但至多驱动强值('0''1')的信号除外。在大多数情况下,您不想这样做,您必须使用 STD_ULOGICSTD_ULOGIC_VECTOR。如果你的老师坚持要你使用 STD_LOGICSTD_LOGIC_VECTOR,请发给我。
  2. 从您处理所有情况(重置、状态...)的单个进程驱动任何信号。

终于明白了!问题是多个进程将值分配给同一个信号。这就是我纠正错误的方式:

注意标志信号 {Bmax_s、Smax_s 和 Omax_s}。一个进程使用它们向其他进程指示它已达到其最大计数。然后,每个时钟周期,每个进程使用一个或多个标志来决定是否清除,或者在OUT_PROCESS递增的情况下,其输出信号and/or更新自己的标志。

现在每个信号只由一个进程分配!

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity BSO_cnt is
    Port ( inBALL       : in  STD_LOGIC;
           inSTRIKE     : in  STD_LOGIC;
           inOUTS       : in  STD_LOGIC;
           inclrBS      : in  STD_LOGIC;
           inclrOUT     : in  STD_LOGIC;
           CLK          : in  STD_LOGIC;
           outBALL      : out  STD_LOGIC_VECTOR (1 downto 0);
           outSTRIKE    : out  STD_LOGIC_VECTOR (1 downto 0);
           outOUTS      : out  STD_LOGIC_VECTOR (1 downto 0));
end BSO_cnt;

architecture Behavioral of BSO_cnt is
    --inputs
    SIGNAL inBALL_s     :   STD_LOGIC;
    SIGNAL inSTRIKE_s   :   STD_LOGIC;
    SIGNAL inOUTS_s     :   STD_LOGIC;
    --outputs
    SIGNAL outBALL_s    :   STD_LOGIC_VECTOR(1 DOWNTO 0);
    SIGNAL outSTRIKE_s  :   STD_LOGIC_VECTOR(1 DOWNTO 0);
    SIGNAL outOUTS_s    :   STD_LOGIC_VECTOR(1 DOWNTO 0);
    --max count flags
    SIGNAL maxBALL      :   STD_LOGIC;
    SIGNAL maxSTRIKE    :   STD_LOGIC;
    SIGNAL maxOUTS      :   STD_LOGIC;

begin

    --assign signals to module outputs
    outBALL     <=  outBALL_s;
    outSTRIKE   <=  outSTRIKE_s;
    outOUTS     <=  outOUTS_s;

    --count ball pushes & update ball counter
    BALL_PROCESS: PROCESS(CLK)
    BEGIN   
        --wait for postive edge of clock
        IF rising_edge(CLK) THEN
            --check for clear ball/strike switch 
            IF (inclrBS = '1') THEN
                outBALL_s   <=  (OTHERS =>  '0');
            --check for any max count flags 
            ELSIF (maxSTRIKE = '1') OR (maxBALL = '1') OR (maxOUTS = '1') THEN
                --clear ball count
                outBALL_s   <=  (OTHERS =>  '0');
                --clear ball max count flag
                maxBALL     <=  '0';
            --check for change of ball button state
            ELSIF (inBALL_s /= inBALL) THEN
                --if pressed 
                IF inBALL = '1' THEN
                    inBALL_s        <=  inBALL;
                    -- check for walk
                    IF (outBALL_s = "11") THEN
                        --set flag
                        maxBALL <=  '1';
                    ELSE
                        --increment ball counter
                        outBALL_s   <=  STD_LOGIC_VECTOR(UNSIGNED(outBALL_s) + 1);
                    END IF;
                --if released   
                ELSE
                    inBALL_s    <=  inBALL;
                END IF;             
            END IF;         
        END IF;     
    END PROCESS;

    --Count strike pushes, and update strike counter
    STRIKE_PROCESS: PROCESS(CLK)
    BEGIN   
        --wait for positive edge of clock
        IF rising_edge(CLK) THEN
            --check for clr ball/strike switch
            IF inclrBS = '1' THEN
                outSTRIKE_s <=  (OTHERS =>  '0');
            --check for any max counts reached & update strike count
            ELSIF (maxSTRIKE = '1') OR (maxBALL = '1') OR (maxOUTS = '1') THEN
                outSTRIKE_s <=  (OTHERS =>  '0');
                --reset flag
                maxSTRIKE   <=  '0';
            --check for strike button state change
            ELSIF (inSTRIKE_s /= inSTRIKE) THEN
                --if pressed increment strike count
                IF inSTRIKE = '1' THEN
                    inSTRIKE_s      <=  inSTRIKE;
                    --check for strikeout 
                    IF (outSTRIKE_s = "10") THEN
                        --set flag
                        maxSTRIKE   <=  '1';
                    ELSE
                        --increment strike count
                        outSTRIKE_s     <=  STD_LOGIC_VECTOR(UNSIGNED(outSTRIKE_s) + 1);
                    END IF;
                --if released   
                ELSE
                    inSTRIKE_s  <=  inSTRIKE;
                END IF;             
            END IF;
        END IF; 
    END PROCESS;

    --Count out pushes, and update out counter
    OUT_PROCESS: PROCESS(CLK)
    BEGIN
        --wait for positive clock edge
        IF rising_edge(CLK) THEN
            --check for clear out switch
            IF inclrOUT = '1' THEN
                outOUTS_s   <=  (OTHERS =>  '0');
            --check for max out count
            ELSIF maxOUTS = '1' THEN
                outOUTS_s   <=  (OTHERS =>  '0');
                --clear flag
                maxOUTS     <=  '0';
            --check for strikeout
            ELSIF maxSTRIKE = '1' THEN
                --check for max outs
                IF (outOUTS_s = "10") THEN
                    --set max outs flag
                    maxOUTS <=  '1';
                ELSE
                    --increment outs
                    outOUTS_s   <=  STD_LOGIC_VECTOR(UNSIGNED(outOUTS_s) + 1);
                END IF;
            --check if button has changed states
            ELSIF (inOUTS_s /= inOUTS) THEN
                --if pressed
                IF inOUTS = '1' THEN
                    inOUTS_s    <=  inOUTS;
                    --check for change of inning
                    IF (outOUTS_s = "10") THEN
                        maxOUTS <=  '1';
                    ELSE
                        --increment outs
                        outOUTS_s   <=  STD_LOGIC_VECTOR(UNSIGNED(outOUTS_s) + 1);
                    END IF;
                --if released
                ELSE
                    inOUTS_s    <=  inOUTS;
                END IF;
            END IF;
        END IF;
    END PROCESS;

end Behavioral;

下面是测试台的模拟输出图像,该测试台重置并增加球和击球数,直到发生 3 次击球...

ISim TestBench

对我的第一个 VHDL 项目来说还不错 :D