I am developing a state machine in VHDL and it doesn't' seem to be functioning properly. The design is shown below:
SHARED VARIABLE XM_INDEX : NATURAL RANGE 0 TO 99 := 0;
SIGNAL XM_STATE_INDICATOR : STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000000";
TYPE XM_STATE_TYPE IS (EMPTY, IDLE, POWER_UP, POWER_UP_CONFIRM,
CHANNEL_SELECT, CHANNEL_SELECT_CONFIRM, VOLUME_CHANGE,
VOLUME_CHANGE_CONFIRM, TRANSMIT_CHAR, TRANSMIT_CHAR_CONFIRM,
COMPLETED);
SIGNAL XM_CURRENT_STATE : XM_STATE_TYPE := EMPTY;
SIGNAL XM_NEXT_STATE : XM_STATE_TYPE := EMPTY;
XMStateMachineClock: PROCESS (CLK25, SYS_RST) IS
BEGIN
IF (SYS_RST = '1') THEN
XM_CURRENT_STATE <= EMPTY;
ELSIF (RISING_EDGE(CLK25)) THEN
XM_CURRENT_STATE <= XM_NEXT_STATE;
END IF;
END PROCESS XMStateMachineClock;
XMStateMachine: PROCESS (XM_CURRENT_STATE) IS
BEGIN
-- Pend on current XM state
CASE XM_CURRENT_STATE IS
-- Empty: Debug only
WHEN EMPTY =>
XM_NEXT_STATE <= IDLE;
XM_STATE_INDICATOR <= "00000001";
-- Idle: Idle state
WHEN IDLE =>
IF XM_POWER_UP = '1' THEN
XM_INDEX := 0;
XM_NEXT_STATE <= POWER_UP;
XM_STATE_INDICATOR <= "00000010";
ELSE
-- Remain in idle
XM_NEXT_STATE <= IDLE;
XM_STATE_INDICATOR <= "00000001";
END IF;
WHEN POWER_UP =>
XM_NEXT_STATE <= TRANSMIT_CHAR;
XM_STATE_INDICATOR <= "00000100";
WHEN TRANSMIT_CHAR =>
IF (XM_INDEX < 11) THEN
XM_NEXT_STATE <= TRANSMIT_CHAR_CONFIRM;
XM_STATE_INDICATOR <= "00001000";
ELSE
XM_NEXT_STATE <= COMPLETED;
XM_STATE_INDICATOR <= "00000000";
END IF;
WHEN TRANSMIT_CHAR_CONFIRM =>
XM_INDEX := XM_INDEX + 1;
XM_NEXT_STATE <= TRANSMIT_CHAR;
XM_STATE_INDICATOR <= "00000100";
WHEN COMPLETED =>
XM_NEXT_STATE <= COMPLETED;
XM_STATE_INDICATOR <= "00000000";
-- Default
WHEN OTHERS =>
END CASE;
END PROCESS XMStateMachine;
The state machine is being clocked at 25 MHz. Per my understanding, my state machine should progress between the states as follows:
However, what I see when I hook up my logic analyzer is the following:
It seems as if the state machine is only alternating between the transmit and transmit confirm states once, as opposed to the 11 times that is should, and I cannot figure out why.
答案 0 :(得分:1)
If you make XM_INDEX
a signal have an XM_INDEX_NEXT
that is latched in your XMStateMachineClock
process and then change XM_INDEX := XM_INDEX + 1
to XM_INDEX_NEXT <= XM_INDEX + 1
. I believe that this will fix your issue. XMStateMachine
will also need to be sensitive to XM_INDEX
.
答案 1 :(得分:1)
示例代码没有竞争,并且有一些机会从共享变量中查找xm_index可能会扰乱一些使用它的计划,如果有多个进程写入它。您可以注意到用户负责控制-1993共享变量中的独占访问。
通过提供完整的实体和体系结构对来创建MCVE:
library ieee;
use ieee.std_logic_1164.all;
entity xm_sm is
port (
clk25: in std_logic;
sys_rst: in std_logic;
xm_power_up: in std_logic
);
end entity;
architecture foo of xm_sm is
-- shared variable xm_index: natural range 0 to 99 := 0;
signal xm_index: natural range 0 to 99 := 0; -- CHANGED to SIGNAL
signal xm_index_nxt: natural range 0 to 99; -- ADDED
signal xm_state_indicator: std_logic_vector (7 downto 0) := "00000000";
type xm_state_type is (EMPTY, IDLE, POWER_UP, POWER_UP_CONFIRM,
CHANNEL_SELECT, CHANNEL_SELECT_CONFIRM,
VOLUME_CHANGE, VOLUME_CHANGE_CONFIRM,
TRANSMIT_CHAR, TRANSMIT_CHAR_CONFIRM,
COMPLETED);
signal xm_current_state: xm_state_type := EMPTY;
signal xm_next_state: xm_state_type := EMPTY;
begin
xmstatemachineclock:
process (clk25, sys_rst) is
begin
if sys_rst = '1' then
xm_current_state <= EMPTY;
xm_index <= 0; -- ADDED
elsif rising_edge(clk25) then
xm_current_state <= xm_next_state;
xm_index <= xm_index_nxt; -- ADDED
end if;
end process xmstatemachineclock;
xmstatemachine:
process (xm_current_state, xm_power_up) is
begin
-- pend on current xm state
case xm_current_state is
-- empty: debug only
when EMPTY =>
xm_next_state <= IDLE;
xm_state_indicator <= "00000001";
-- idle: idle state
when IDLE =>
if xm_power_up = '1' then
xm_index_nxt <= 0;
xm_next_state <= POWER_UP;
xm_state_indicator <= "00000010";
else
-- remain in idle
xm_next_state <= IDLE;
xm_state_indicator <= "00000001";
end if;
when POWER_UP =>
xm_next_state <= TRANSMIT_CHAR;
xm_state_indicator <= "00000100";
when TRANSMIT_CHAR =>
if xm_index < 11 then
xm_next_state <= TRANSMIT_CHAR_CONFIRM;
xm_state_indicator <= "00001000";
else
xm_next_state <= COMPLETED;
xm_state_indicator <= "00000000";
end if;
when TRANSMIT_CHAR_CONFIRM =>
if xm_index = 99 then -- protect again overflow -- ADDED
xm_index_nxt <= 0;
else
xm_index_nxt <= xm_index + 1; -- CHANGED
end if;
-- xm_index_nxt <= xm_index + 1;
xm_next_state <= TRANSMIT_CHAR;
xm_state_indicator <= "00000100";
when COMPLETED =>
xm_next_state <= COMPLETED;
xm_state_indicator <= "00000000";
-- default
when others =>
end case;
end process xmstatemachine;
end architecture;
这会将xm_index更改为一个信号,并包含Alden在其答案中建议的下一个值。只要只有一个进程写入它,这就可以工作。 xm_index现在也在重置期间设置为0。此外,在xm_currrent_state案例语句的TRANSMIT_CHAR_CONFIRM中,xm_index当然是防止溢出的。 xm_index(0到99)的范围可以限制为最大值(11)。这让人怀疑我们没有看到所有的设计。
添加测试平台:
library ieee;
use ieee.std_logic_1164.all;
entity xm_sm_tb is
end entity;
architecture foo of xm_sm_tb is
signal clk25: std_logic := '0';
signal sys_rst: std_logic := '0';
signal xm_power_up: std_logic := '0';
begin
DUT:
entity work.xm_sm
port map (
clk25 => clk25,
sys_rst => sys_rst,
xm_power_up => xm_power_up
);
CLOCK:
process
begin
wait for 50 ns;
clk25 <= not clk25;
if now > 3.1 us then
wait;
end if;
end process;
STIMULI:
process
begin
wait for 100 ns;
sys_rst <= '1';
wait for 100 ns;
sys_rst <= '0';
wait for 200 ns;
xm_power_up <= '1';
wait for 100 ns;
xm_power_up <= '0';
wait;
end process;
end architecture;
我们得到:
我们看到在完成之前我们会查看所有索引值。
原始代码已经成功模拟,但由于组合循环似乎没有合成到工作设计中:
XM_INDEX := XM_INDEX + 1;
其中xm_loop被一个可能的状态TRANSMIT_CHAR_CONFIRM的热状态表示锁存为锁存器使能。
在模拟中,没有xm_index的灵敏度列表会阻止加法器增加xm_index的纹波。如果xm_index已经在进程敏感性列表中,那么在达到100之后它将导致分配上的边界检查违规。(整数类型不是模块化的,它们不会包装并且不会被证明不会溢出)。
在没有看到控制台输出的综合中,我们可以假设ripply时间足以在一个时钟时间内可靠地将xm_index的值推到11以上,而不会包装到小于11。