VHDL: отключение кнопок (или нет, в зависимости от обстоятельств) - PullRequest
1 голос
/ 06 мая 2020

Я прочитал другие сообщения, но не могу исправить свои. Я новичок в VHDL, поэтому уверен, что это простое решение.

Короче говоря, кнопка не работает. Код компилируется и программы битового потока. В тестовом стенде нажатия кнопок работают, но выходные светодиоды не меняются. На плате при нажатии кнопки загораются случайные светодиоды (я полагаю, из-за подпрыгивания). Согласно схеме c входные данные проходят через средства защиты от ошибок.

Может ли кто-нибудь определить проблему? И любые другие подсказки и подсказки всегда приветствуются :)

Спасибо!

EDIT1: Добавлен rise_edge (clk). Также обратите внимание, когда я нажимаю любую кнопку, в то время как она нажата, все светодиоды загораются.

Schematic

button_counter.vhd

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

entity button_counter is
    port( clk : in std_logic;
         btnU : in std_logic;
         btnD : in std_logic;
          led : out std_logic_vector (15 downto 0));
end button_counter;

architecture behavioral of button_counter is

    component debouncer is
        port(    clk : in std_logic;
                 btn : in std_logic;
             btn_clr : out std_logic);
    end component;

    signal btnU_clr : std_logic;
    signal btnD_clr : std_logic;

    begin

    debouncer_btnU : debouncer port map (clk => clk, btn => btnU, btn_clr => btnU_clr);
    debouncer_btnD : debouncer port map (clk => clk, btn => btnD, btn_clr => btnD_clr);

    process(clk)
        variable count : integer := 0;
        begin
        if (rising_edge(clk)) then
            if(btnU_clr = '1') then count := count + 1;
            elsif(btnD_clr = '1') then count := count - 1;
            end if;
            led <= std_logic_vector(to_unsigned(count, led'length));
        end if;
    end process;

end behavioral;

Debouncer.vhd

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

entity debouncer is
    port(    clk : in std_logic;
             btn : in std_logic;
         btn_clr : out std_logic);
end debouncer;

architecture behavioural of debouncer is

    constant delay : integer := 650000; -- 6.5ms
    signal count : integer := 0;
    signal btn_tmp : std_logic := '0';

    begin

    process(clk)
    begin
        if rising_edge(clk) then
            if (btn /= btn_tmp) then
                btn_tmp <= btn;
                count <= 0;
            elsif (count = delay) then
                btn_clr <= btn_tmp;
            else
                count <= count + 1;
            end if;
        end if;
    end process;

end behavioural;

button_counter_tb.vhd

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

entity button_counter_tb is
end button_counter_tb;

architecture behavioral of button_counter_tb is

signal clk_tb    : std_logic;
signal btnU_tb   : std_logic;
signal btnD_tb   : std_logic;
signal led_tb    : std_logic_vector (15 downto 0);

component button_counter
port(clk    : in std_logic; 
     btnU   : in std_logic;
     btnD   : in std_logic;
     led    : out std_logic_vector (15 downto 0));
end component;

begin

UUT: button_counter port map (clk => clk_tb, btnU => btnU_tb, btnD => btnD_tb, led => led_tb);

process
begin

btnU_tb <= '0';
btnD_tb <= '0'; 

wait for 100ns;
btnU_tb <= '1';

wait for 100ns;
btnU_tb <= '0';

wait for 100ns;
btnU_tb <= '1';

wait for 100ns;
btnD_tb <= '1';

wait for 100ns;
btnU_tb <= '0';

wait for 100ns;
btnD_tb <= '0';

end process;

end behavioral;

Ответы [ 3 ]

3 голосов
/ 07 мая 2020

После обновления вашего кода осталось несколько проблем:

  1. Часы не генерируются в тестовой среде

  2. Стимулы ( нажатия кнопок) не рассчитываются по времени в тестовой среде

  3. Средство защиты от сбоев не производит enable для отдельных часов

Чтобы облегчить моделирование для дизайна проверку , ваш дизайн был изменен, чтобы позволить более медленные часы (похоже, вы фактически используете тактовую частоту 100 МГц). Идея состоит в том, чтобы уменьшить требования к вычислениям и отображению формы сигналов.

Первые две точки рассматриваются в тестовой среде:

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

entity button_counter_tb is
end entity button_counter_tb;

architecture behavioral of button_counter_tb is
    -- NOTE: suffix _tb has been removed, it's annoying to type over and over
    signal clk:   std_logic := '0';  -- ADDED default value '0'
    signal btnU:  std_logic;
    signal btnD:  std_logic;
    signal led:   std_logic_vector (15 downto 0);

    component button_counter
        generic (                       -- ADDED generic
            CLKP:   time := 10 ns;
            DEBT:   time := 6.5 ms      -- debounce time supports different 
        );                              -- mechanical buttons/switches
        port (
            clk:    in  std_logic; 
            btnU:   in  std_logic;
            btnD:   in  std_logic;
            led:    out std_logic_vector (15 downto 0)
        );
    end component;

    constant CLKP:  time := 12.5 us; -- ADDED  just long enough to show debounce
    constant DEBT:  time := 6.5 ms;  -- ADDED
begin

CLOCK:  -- ADDED clock process
    process
    begin
        wait for CLKP/2;
        clk <= not clk;
        if now > 2 sec then    -- stop simulation
            wait;
        end if;
    end process;

UUT: 
    button_counter 
        generic map (           -- ADDED generic map
            CLKP => CLKP,
            DEBT => DEBT
        )
        port map (
            clk => clk,
            btnU => btnU,
            btnD => btnD,
            led => led
        );

-- STIMULI:
--     process
--     begin
--         btnU_tb <= '0';
--         btnD_tb <= '0';
--         wait for 100 ns;
--         btnU_tb <= '1';
--         wait for 100 ns;
--         btnU_tb <= '0';
--         wait for 100 ns;
--         btnU_tb <= '1';
--         wait for 100 ns;
--         btnD_tb <= '1';
--         wait for 100 ns;
--         btnU_tb <= '0';
--         wait for 100 ns;
--         btnD_tb <= '0';
--         wait;  -- ADDED            -- stops simulation
--     end process;
UP_BUTTON:
    process
    begin
        btnU <= '0';
        wait for 2 ms;
        btnU <= '1';   -- first button press
        wait for 0.5 ms;
        btnU <= '0';
        wait for 0.25 ms;
        btnU <= '1';
        wait for 7 ms;
        btnU <= '0';
        wait for 100 us;
        btnU <= '1';
        wait for 20 us;
        btnU <= '0';
        wait for 200 ms;
        btnU <= '1';   -- second button press
        wait for 20 us;
        btnU <= '0';
        wait for 20 us;
        btnU <= '1';
        wait for 6.6 ms;
        btnU <= '0';
        wait for 250 ms;
        btnU <= '1';    -- third button press
        wait for 20 us;
        btnU <= '0';
        wait for 20 us;
        btnU <= '1';
        wait for 6.6 ms;
        btnU <= '0';
        wait for 200 ms;
        btnU <= '1';   -- second button press
        wait for 20 us;
        btnU <= '0';
        wait for 20 us;
        btnU <= '1';
        wait for 6.6 ms;
        btnU <= '0';
        wait for 50 us;
        btnU <= '1';
        wait for 1 ms;
        btnU <= '0';
        wait;
    end process;
DOWN_BUTTON:
    process
    begin
        btnD <= '0';
        wait for 800 ms;
        btnD <= '1';   -- first button press
        wait for 0.5 ms;
        btnD <= '0';
        wait for 0.25 ms;
        btnD <= '1';
        wait for 0.5 ms;
        btnD <= '0';
        wait for 1 ms;
        btnD <= '1';
        wait for 7 ms;
        btnD <= '0';
        wait for 100 us;
        btnD <= '1';
        wait for 20 us;
        btnD <= '0';
        wait for 200 ms;
        btnD <= '1';   -- second button press
        wait for 20 us;
        btnD <= '0';
        wait for 20 us;
        btnD <= '1';
        wait for 6.6 ms;
        btnD <= '0';
        wait for 250 ms;
        wait;
    end process;
end architecture behavioral;

Суффикс _tb для имен сигналов был удален ( было больно набирать повторно).

Период тактов был выбран с отношением периода отказов к периоду clk, гарантированно позволяющего отбрасывать «отказов». Нажатие стимулирующих кнопок может быть расширено, как и симуляция, которая здесь произвольна.

Обратите внимание, что значения нажатия кнопки гарантированно охватывают один или несколько интервалов времени. Они должны допускать изменение периода часов путем изменения CLKP.

Интервал дребезга DEBT можно изменить, чтобы отразить использование различных переключателей или кнопок, включая мембранные переключатели с серьезным старением. Интервал дребезга является следствием механических характеристик конкретных переключателей или кнопок. Передача этих общих констант c обеспечивает некоторую независимость от платформы.

Третья точка решается путем изменений в противодействии:

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

entity debouncer is
    generic (                       -- ADDED GENERICS to speed up simulation
        CLKP:   time := 10 ns;
        DEBT:   time := 6.5 ms
    );
    port (
        clk:        in  std_logic;
        btn:        in  std_logic;
        btn_clr:    out std_logic
    );
end entity debouncer;

architecture behavioural of debouncer is
    -- constant delay: integer := 650000; -- 6.5ms
    constant DELAY: integer := DEBT/CLKP;
    signal count:   integer := 0;
    signal b_enab:  std_logic := '0';  -- RENAMED, WAS btn_tmp

    signal btnd0:   std_logic;      -- ADDED for clock domain crossing
    signal btnd1:   std_logic;      -- DITTO

    begin

CLK_DOMAIN_CROSS:    -- ADDED process
    process (clk)
    begin
        if rising_edge(clk) then
            btnd0 <= btn;
            btnd1 <= btnd0;
        end if;
    end process;

DEBOUNCE_COUNTER:    -- ADDED LABEL
    process (clk)
    begin
        if rising_edge(clk) then
        --     if btn /= btn_tmp then           -- REWRITTEN
        --         btn_tmp <= btn;
        --         count <= 0;
        --     elsif count = DELAY then
        --         btn_clr <= btn_tmp;
        --     else
        --         count <= count + 1;
        --     end if;
            btn_clr <= '0';       -- btn_clr for only one clock, used as enable
            if  btnd1 = '0' then  -- test for btn inactive state
                count <= 0;
            elsif count < DELAY then  -- while btn remains in active state
                count <= count + 1;
            end if;
            if count = DELAY - 1 then  -- why btn_clr '1' or 1 clock
                btn_clr <= '1';
            end if;
        end if;
    end process;
end architecture behavioural;

Дебаундер был изменен для получения домена часов значение кнопки, которое используется для сброса и включения счетчика count. Выходное имя btn_clr осталось неизменным и истинно только для одного такта и может использоваться в качестве разрешения.

CLKP и DEBT используются вместе, чтобы обеспечить более быстрое выполнение симуляции при передаче одного и того же время моделирования.

Обратите внимание, что активное состояние ввода кнопки жестко запрограммировано. Они будут подключены к контактам устройства, где можно указать полярность входа.

Изменения в button_counter передают generi c константы CLKP и DEBT средствам защиты:

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

entity button_counter is
    generic (
        CLKP:   time := 10 ns;   -- GENERIC CONSTANTS for faster simulation
        DEBT:   time := 6.5 ms   -- supports diffeent switches/buttons
    );
    port (
        clk:    in  std_logic;
        btnU:   in  std_logic;
        btnD:   in  std_logic;
        led:    out std_logic_vector (15 downto 0)
    );
end entity button_counter;

architecture behavioral of button_counter is
    component debouncer is
        generic (
            CLKP:   time := 10 ns;
            DEBT:   time := 6.5 ms
        );
        port (
            clk:        in  std_logic;
            btn:        in  std_logic;
            btn_clr:    out std_logic
        );
    end component;

    signal btnU_clr:  std_logic;
    signal btnD_clr:  std_logic;
begin

debouncer_btnU:
    debouncer
        generic map (
            CLKP => CLKP,
            DEBT => DEBT
        )
        port map (
            clk => clk,
            btn => btnU,
            btn_clr => btnU_clr
        );
debouncer_btnD:
    debouncer
    generic map (
        CLKP => CLKP,
        DEBT => DEBT
    )
        port map (
            clk => clk,
            btn => btnD,
            btn_clr => btnD_clr
        );

    process (clk)
        variable count:  integer := 0;
        begin
        if rising_edge(clk) then
            if btnU_clr = '1' then 
                count := count + 1;
            elsif btnD_clr = '1'then
                count := count - 1;
            end if;
            led <= std_logic_vector(to_unsigned(count, led'length));
        end if;
    end process;

end architecture behavioral;

И при моделировании мы теперь видим, как светодиоды подсчитывают вверх и вниз: button_counter_tb

Запуск тестовой среды и отображение различных форм сигналов позволит «увеличить масштаб» для отображения обработки сбоев в двух модулях защиты от сбоев. .

zoom in

Модификации для передачи тактового сигнала и интервала устранения дребезга через иерархию дизайна не будут строго существенными. Они облегчают моделирование, которое используется, как здесь, для подтверждения конструкции. (Стимулы, показанные в тестовом стенде, не подтверждают полностью дизайн).

При использовании общих c значений по умолчанию (с тактовой частотой 100 МГц) есть очень хорошие шансы, что проект будет работать при реализации в целевом объекте. Платформа. (Активная полярность ввода кнопок выбирается в противодействии с целью поддержки исходной реализации. Если вы подозреваете, что кнопка отскакивает при увеличении или уменьшении, вы можете увеличить значение DEBT.)

Если конкретный инструмент синтеза не может значение дескриптора типа time, переданное как общие константы c, вы можете преобразовать различные объявления CLKP и DEBT в тип integer или просто передать максимальное количество.

3 голосов
/ 06 мая 2020

Вы забываете rising_edge в своем button_counter.vhd.

 process(clk)
    variable count : integer := 0;
    begin
        if(btnU_clr = '1') then count := count + 1;
        elsif(btnD_clr = '1') then count := count - 1;
        end if;
        led <= std_logic_vector(to_unsigned(count, led'length));
 end process;

Так что исправьте это, и, возможно, это сработает (я не тестирую дизайн из-за этой очевидной ошибки):

 process(clk)
    variable count : integer := 0;
 begin
        if(rising_edge(clk)) then
            ...
        end if;
 end process;

Я не уверен, но думаю инструментальная цепочка выдаст для этого несколько предупреждений. Так что проверьте это, пожалуйста.

И ваш Testbench не содержит никаких процессов генерации тактовых импульсов, поэтому у вас не будет тактового сигнала. Может быть, это позволит вам поверить, что ваш дизайн работает (или вы забыли сигнал часов clk_tb в своем посте?).

0 голосов
/ 28 мая 2020

На вопрос дан хороший ответ, но я хотел бы выделить различные методы синхронизации и устранения неполадок.

Синхронизация

Для синхронизации можно использовать простой буфер или цепочку, что позволяет избежать создания отдельные сигналы / переменные для каждого этапа в буфере или цепочке. Общая константа c может использоваться для управления длиной цепочки (минимум 2):

signal sync_buffer: std_logic_vector(SYNC_BUFFER_MSB downto 0);  -- N-bit synchronisation buffer.
...
sync_buffer <= sync_buffer(SYNC_BUFFER_MSB - 1 downto 0) & input;

Debouncing

Для устранения дребезга, гистерезиса (причудливое слово для истории или памяти ) можно использовать для создания своего рода фильтра нижних частот, который будет подавлять нажатие и отпускание кнопки и обнаруживать фронты (как положительные, так и отрицательные) независимо от того, является ли сигнал активным высоким или активным низким. Выход будет оставаться в текущем состоянии до тех пор, пока синхронизированный вход не останется в противоположном состоянии в течение N последовательных тактовых циклов:

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

entity Debounce is
    generic
    (
        CLOCK_PERIOD   : time := 20 ns;
        DEBOUNCE_PERIOD: time := 125 ms;  -- 1/8th second as a rule of thumb for a tactile button/switch.
        SYNC_BITS      : positive := 3    -- Number of bits in the synchronisation buffer (2 minimum).
    );
    port
    (
        clock : in std_logic;
        input : in std_logic;   -- Asynchronous and noisy input.
        output: out std_logic := '0';  -- Synchronised, debounced and filtered output.
        edge  : out std_logic := '0';  -- Goes high for 1 clock cycle on either edge of synchronised and debounced input.
        rise  : out std_logic := '0';  -- Goes high for 1 clock cycle on the rising edge of synchronised and debounced input.
        fall  : out std_logic := '0'   -- Goes high for 1 clock cycle on the falling edge of synchronised and debounced input.
    );
end entity;

architecture V1 of Debounce is

    constant SYNC_BUFFER_MSB: positive := SYNC_BITS - 1;
    signal sync_buffer: std_logic_vector(SYNC_BUFFER_MSB downto 0) := (others => '0');  -- N-bit synchronisation buffer (2 bits minimum).
    alias sync_input: std_logic is sync_buffer(SYNC_BUFFER_MSB);  -- The synchronised input is the MSB of the synchronisation buffer.

    constant MAX_COUNT: natural := DEBOUNCE_PERIOD / CLOCK_PERIOD;
    signal counter: natural range 0 to MAX_COUNT := 0;  -- Specify the range to reduce number of bits that are synthesised.

begin

    assert SYNC_BITS >= 2 report "Need a minimum of 2 bits in the synchronisation buffer.";

    process(clock)
        variable edge_internal: std_logic := '0';
        variable rise_internal: std_logic := '0';
        variable fall_internal: std_logic := '0';
    begin
        if rising_edge(clock) then
            -- Synchronise the asynchronous input.
            -- MSB of sync_buffer is the synchronised input.
            sync_buffer <= sync_buffer(SYNC_BUFFER_MSB - 1 downto 0) & input;

            edge <= '0';  -- Goes high for 1 clock cycle on either edge.
            rise <= '0';  -- Goes high for 1 clock cycle on the rising edge.
            fall <= '0';  -- Goes high for 1 clock cycle on the falling edge.

            if counter = MAX_COUNT - 1 then  -- If successfully debounced, notify what happened, and reset the counter.
                output <= sync_input;
                edge <= edge_internal;  -- Goes high for 1 clock cycle on either edge.
                rise <= rise_internal;  -- Goes high for 1 clock cycle on the rising edge.
                fall <= fall_internal;  -- Goes high for 1 clock cycle on the falling edge.
                counter <= 0;
            elsif sync_input /= output then
                counter <= counter + 1;
            else
                counter <= 0;
            end if;
        end if;

        -- Edge detection.
        edge_internal := sync_input xor output;
        rise_internal := sync_input and not output;
        fall_internal := not sync_input and output;
    end process;

end architecture;

Счетчик кнопок

Во многом то же, что и другие ответы, но я Я использовал выходы rise противодействующих средств для запуска подсчета. Я также добавил пару светодиодов для визуальной обратной связи с кнопками.

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

entity ButtonCounter is
    generic
    (
        CLOCK_PERIOD   : time := 20 ns;
        DEBOUNCE_PERIOD: time := 125 ms
    );
    port
    (
        clock : in std_logic;
        btn_up: in std_logic;
        btn_dn: in std_logic;
        led_up: out std_logic;
        led_dn: out std_logic;
        leds  : out std_logic_vector(15 downto 0)
    );
end entity;

architecture V1 of ButtonCounter is

    signal count_up: std_logic;
    signal count_dn: std_logic;

    component Debounce is
        generic
        (
            CLOCK_PERIOD   : time := 20 ns;
            DEBOUNCE_PERIOD: time := 125 ms
        );
        port
        (
            clock : in std_logic;
            input : in std_logic;
            output: out std_logic;
            rise  : out std_logic
        );
    end component;

begin

    DEBOUNCE_BTN_UP:
    Debounce
    generic map
    (
        CLOCK_PERIOD    => CLOCK_PERIOD,
        DEBOUNCE_PERIOD => DEBOUNCE_PERIOD
    )
    port map
    (
        clock  => clock,
        input  => btn_up,
        output => led_up,
        rise   => count_up  -- Goes high for 1 clock cycle on the rising edge of btn_up.
    );

    DEBOUNCE_BTN_DN:
    Debounce
    generic map
    (
        CLOCK_PERIOD    => CLOCK_PERIOD,
        DEBOUNCE_PERIOD => DEBOUNCE_PERIOD
    )
    port map
    (
        clock  => clock,
        input  => btn_dn,
        output => led_dn,
        rise   => count_dn  -- Goes high for 1 clock cycle on the rising edge of btn_dn.
    );

    process(clock)
        variable counter: natural range 0 to 2 ** leds'length - 1 := 0;  -- Specify the range to reduce number of bits that are synthesised.
    begin
        if rising_edge(clock) then
            if count_up then
                counter := counter + 1;
            elsif count_dn then
                counter := counter - 1;
            end if;
            leds <= std_logic_vector(to_unsigned(counter, leds'length));
        end if;
    end process;

end architecture;

Тестовый стенд

Некоторые асинхронные и шумные кнопки ввода синхронизируются, дребезжат и фильтруются. Положительные фронты преобразованных входных сигналов запускают подсчет.

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

entity ButtonCounter_TB is
end;

architecture V1 of ButtonCounter_TB is

    constant CLOCK_PERIOD   : time := 50 ns;
    constant DEBOUNCE_PERIOD: time := 200 ns;

    signal halt_sys_clock: boolean := false;

    signal clock: std_logic := '0';
    signal btn_up: std_logic;
    signal btn_dn: std_logic;
    signal leds: std_logic_vector(15 downto 0);

    component ButtonCounter is
        generic
        (
            CLOCK_PERIOD   : time := 10 ns;
            DEBOUNCE_PERIOD: time := 125 ms
        );
        port
        (
            clock : in std_logic;
            btn_up: in std_logic;
            btn_dn: in std_logic;
            leds  : out std_logic_vector(15 downto 0)
        );
    end component;

begin

    ClockGenerator:
    process
    begin
        while not halt_sys_clock loop
            clock <= not clock;
            wait for CLOCK_PERIOD / 2.0;
        end loop;
        wait;
    end process ClockGenerator;

    Stimulus:
    process
        constant NUM_NOISE_SAMPLES: positive := 10;
        constant SWITCH_TIME: time := 2 * DEBOUNCE_PERIOD;
        variable seed1: positive := 1;
        variable seed2: positive := 1;
        variable rrand: real;
        variable nrand: natural;

        -- Performs noisy transition of sig from current value to final value.
        procedure NoisyTransition(signal sig: out std_logic; final: std_logic) is
        begin
            for n in 1 to NUM_NOISE_SAMPLES loop
                uniform(seed1, seed2, rrand);
                nrand := natural(round(rrand));
                if nrand = 0 then
                    sig <= not final;
                else
                    sig <= final;
                end if;
                wait for CLOCK_PERIOD / 5.0;
            end loop;
            sig <= final;
            wait for SWITCH_TIME;
        end;

    begin
        btn_up <= '0';
        btn_dn <= '0';
        wait for 3 ns;

        --
        -- Up Button
        --

        -- Perform 4 noisy presses and releases.
        for n in 1 to 4 loop
            NoisyTransition(btn_up, '1');
            NoisyTransition(btn_up, '0');
        end loop;

        --
        -- Down Button
        --

        -- Perform 1 noisy press and release.
        NoisyTransition(btn_dn, '1');
        NoisyTransition(btn_dn, '0');

        halt_sys_clock <= true;
        wait;
    end process;

    DUT:
    ButtonCounter
    generic map
    (
        CLOCK_PERIOD    => CLOCK_PERIOD,
        DEBOUNCE_PERIOD => DEBOUNCE_PERIOD
    )
    port map
    (
        clock  => clock,
        btn_up => btn_up,
        btn_dn => btn_dn,
        leds   => leds
    );

end architecture;

Моделирование

Simulation of ButtonCounter

...