Странное поведение конечного автомата в VHDL
Так что я недавно начал изучать VHDL как часть практики в университете. На этот раз наша задача состояла в том, чтобы создать аппарат для швартовки, на котором вы можете установить время определенным образом и использовать его в качестве обратного отсчета, вызывая сигнал тревоги, как только он достигает 0.
Начальное состояние "бездействует", если вы нажмете "keySet_in", вы можете установить минуты, нажав "keyUp_in", чтобы увеличить его, или "keyDown_in", чтобы уменьшить его. Если вы нажмете любую другую клавишу, обратный отсчет перейдет к "Пуск" и начнется со значения по умолчанию 1 минута. Есть также светодиоды для управления десятичными точками на часах и подсветкой цифр. Это не важно, и они работали, насколько я могу судить.
Из выбора минут вы можете получить первую / вторую цифру второго счетчика, нажав "keyRight_in". Вы можете увеличить / уменьшить его так же, как с помощью минут. Как только вы достигнете меньшего значения, вы начнете, если вы снова "keyRight_in". Вы всегда можете вернуться с keyLeft_in, если у вас нет минут, в этом случае он ничего не делает.
Если вы нажмете любую клавишу во время обратного отсчета, обратный отсчет прекратится и вернется в режим ожидания с предыдущим значением. Если он достигнет 0, будильник будет звучать до тех пор, пока вы не нажмете кнопку, а затем вернетесь в режим ожидания.
Теперь мы попытались реализовать мой код на FPGA сегодня, и было некоторое странное неправильное поведение, которое ни я, ни преподаватели не могли объяснить, даже если мы переписали весь код и проверяли его по крупицам. Теперь эти учителя также являются просто учениками, поэтому я предполагаю, что их знания не совершенны, и, возможно, кто-то здесь может знать больше:
Например, у нас было странное поведение (из моей памяти, возможно, не на 100% точно), что большинство кнопок не работают. При попытке увеличить / уменьшить минуты, он будет просто переключаться между 9 и 8. При попытке увеличить / уменьшить большее количество секунд, он всегда будет делать 2 шага одновременно (от 9 до 7 до 5, ...). Большинство других кнопок даже не реагировали, например, вы не можете вернуться с помощью leftKey_in. Начальное значение составляло 1 минуту. Мы также пробовали 3 разных ПЛИС.
С тех пор я подумал о 2 (маловероятных) вариантах решения проблем: 1. добавление списка чувствительности к процессу P1 2. добавление ' state <= idle; 'как начальное значение (включено в код)
Так что я знаю, что это довольно стена текста, поэтому, если кто-то все еще читает, вот код: ссылка на вставку (возникли проблемы с использованием кода в stackru, поэтому я использовал pastebin)
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use work.TIMER_PKG.ALL;
entity CONTROLLER is
port (
-- Clocks and Reset
sysclk_in : in std_logic;
clk10hz_in : in std_logic;
reset_in : in std_logic;
-- Alu
alu_instruction_out : out ALU_INSTRUCTION_TYPE;
timer_is_zero_in : in std_logic;
-- Key Controls
keyUp_in : in std_logic;
keyDown_in : in std_logic;
keyLeft_in : in std_logic;
keyRight_in : in std_logic;
keySet_in : in std_logic;
-- 7 Segment Display
disp_highlight_out : out std_logic_vector(3 downto 0);
disp_dots_out : out std_logic_vector(3 downto 0);
-- Alarm
alarm_out : out std_logic
);
end CONTROLLER;
architecture Behavioral of CONTROLLER is
type fsm_type is ( IDLE, START, WAIT10HZ, CNTDWN, TIMER_END, RESTORE,
SEL_MIN, INC_MIN, DEC_MIN,
SEL_SEC10, INC_sec10, DEC_SEC10,
SEL_SEC, INC_SEC, DEC_SEC);
signal state, next_state : fsm_type;
state <= idle;
begin
P1:process is begin
wait until rising_edge(sysclk_in);
state <= next_state;
end process;
P2:process(sysclk_in, reset_in, keyup_in, keydown_in, keyleft_in, keyright_in, keyset_in,
timer_is_zero_in) is begin
next_state <= state;
case state is
when IDLE =>
if (keyright_in or keyup_in or keydown_in or keyleft_in) = '1' then
next_state <= start;
elsif keyset_in = '1' then
next_state <= sel_min;
end if;
when Start =>
next_state <= wait10hz;
when wait10hz =>
if clk10hz_in = '1' then
next_state <= cntdwn;
end if;
when cntdwn =>
if timer_is_zero_in = '1' then
next_state <= timer_end;
elsif (keyright_in or keyup_in or keydown_in or keyleft_in or keyset_in) ='1' then
next_state <= restore;
end if;
when restore =>
next_state <= start;
when timer_end =>
if (keyright_in or keyup_in or keydown_in or keyleft_in or keyset_in) ='1' then
next_state <= idle;
end if;
when sel_min =>
if keyup_in = '1' then
next_state <= inc_min;
elsif keydown_in ='1' then
next_state <= dec_min;
elsif keyright_in ='1' then
next_state <= sel_sec10;
end if;
when sel_sec10 =>
if keyleft_in = '1' then
next_state <= sel_min;
elsif keyright_in ='1' then
next_state <= sel_sec;
elsif keyup_in = '1' then
next_state <= inc_sec10;
elsif keydown_in = '1' then
next_state <= dec_sec10;
end if;
when sel_sec=>
if keyleft_in ='1' then
next_state <= sel_sec10;
elsif keyright_in ='1' then
next_state <= start;
elsif keydown_in ='1' then
next_state <= dec_sec;
elsif keyup_in ='1' then
next_state <= inc_sec;
end if;
when inc_min =>
next_state <= sel_min;
when dec_min =>
next_state <= sel_min;
when inc_sec10 =>
next_state <= sel_sec10;
when dec_sec10 =>
next_state <= sel_sec10;
when inc_sec =>
next_state <= sel_sec;
when dec_sec =>
next_state <= sel_sec;
when others => null;
end case;
if reset_in = '1' then
next_state <= idle;
end if;
disp_highlight_out <= "1111";
disp_dots_out <= "0101";
alarm_out <= '0';
alu_instruction_out <= alu_none;
case state is
when start =>
alu_instruction_out <= alu_store;
disp_highlight_out <= "1111";
disp_dots_out <= "0101";
when idle =>
alu_instruction_out <= alu_none;
disp_highlight_out <= "1111";
disp_dots_out <= "0101";
alarm_out <= '0';
when wait10hz =>
alu_instruction_out <= alu_none;
disp_highlight_out <= "1111";
disp_dots_out <= "0101";
when cntdwn =>
alu_instruction_out <= alu_dec_timer;
disp_highlight_out <= "1111";
disp_dots_out <= "0101";
when timer_end =>
alu_instruction_out <= alu_store;
disp_highlight_out <= "1111";
disp_dots_out <= "0101";
alarm_out <= '1';
when sel_min =>
alu_instruction_out <= alu_none;
disp_highlight_out <= "1000";
disp_dots_out <= "0101";
when inc_min =>
alu_instruction_out <= alu_single_inc_min;
disp_highlight_out <= "1000";
disp_dots_out <= "0101";
when dec_min =>
alu_instruction_out <= alu_single_dec_min;
disp_highlight_out <= "1000";
disp_dots_out <= "0101";
when sel_sec10 =>
alu_instruction_out <= alu_none;
disp_highlight_out <= "0100";
disp_dots_out <= "0101";
when dec_sec10 =>
alu_instruction_out <= alu_single_dec_sec10;
disp_highlight_out <= "0100";
disp_dots_out <= "0101";
when inc_sec10 =>
alu_instruction_out <= alu_single_inc_sec10;
disp_highlight_out <= "0100";
disp_dots_out <= "0101";
when dec_sec =>
alu_instruction_out <= alu_single_dec_sec;
disp_highlight_out <= "0010";
disp_dots_out <= "0101";
when inc_sec =>
alu_instruction_out <= alu_single_inc_sec;
disp_highlight_out <= "0010";
disp_dots_out <= "0101";
when sel_sec =>
alu_instruction_out <= alu_none;
disp_highlight_out <= "0010";
disp_dots_out <= "0101";
when others =>
alu_instruction_out <= alu_none;
end case;
end process;
end Behavioral;
Надеюсь, кто-то может помочь! поздравил
1 ответ
Хотя можно реализовать двухпроцессный конечный автомат, его труднее читать и управлять им.
однако, если вы используете 2-процессы, то: - 1-й процесс синхронизируется (как и ваш P1) - 2-й процесс имеет ВСЕ используемые входы в списке чувствительности. Ваш P2 не имеет сигнала "clk_10hz_in" и NO "состояние" в списке чувствительности. НО у вас есть "sysclk_in", который не используется в качестве входных данных в комбинаторном P2, в списке чувствительности.
-> это может привести к неожиданному поведению в случае, когда разные сигналы изменяют уровни в одно и то же время (например, sysclk_in и другие сигналы).