我一直在尝试设计采用FSM方法的RS-232接收器。我承认我对VHDL没有非常全面的了解,所以我一直在研究代码并随时学习。但是,我相信此时我已经碰到了一堵砖墙。
我的问题是我的代码中有两个进程,一个用于触发下一个状态,另一个用于执行组合逻辑。我的代码如下:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity ASyncReceiverV4 is
Port ( DataIn : in STD_LOGIC;
Enable : in STD_LOGIC;
CLK : in STD_LOGIC;
BadData : out STD_LOGIC;
DataOut : out STD_LOGIC_VECTOR (7 downto 0));
end ASyncReceiverV4;
architecture Behavioral of ASyncReceiverV4 is
type states is (StartBitCheck, ReadData, StopBitCheck);
signal currentState, nextState : states;
begin
process(CLK)
begin
if rising_edge(CLK) then
currentState <= nextState;
end if;
end process;
process(CLK)
variable counter : integer := 0;
variable dataIndex : integer := 0;
begin
case currentState is
when StartBitCheck =>
if Enable = '1' then
if (DataIn = '0' and counter < 8) then
counter := counter + 1;
elsif (DataIn = '0' and counter = 8) then
BadData <= '0';
nextState <= ReadData;
counter := 0;
else
nextState <= StartBitCheck;
end if;
end if;
when ReadData =>
if Enable = '1' then
if counter < 16 then
counter := counter + 1;
elsif (counter = 16 and dataIndex < 8) then
DataOut(dataIndex) <= DataIn;
counter := 0;
dataIndex := dataIndex + 1;
elsif dataIndex = 8 then
dataIndex := 0;
nextState <= StopBitCheck;
else
nextState <= ReadData;
end if;
end if;
when StopBitCheck =>
if Enable = '1' then
if DataIn = '1' then
if counter < 16 then
counter := counter + 1;
nextState <= StopBitCheck;
elsif counter = 16 then
counter := 0;
nextState <= StartBitCheck;
end if;
else
DataOut <= "11111111";
BadData <= '1';
nextState <= StartBitCheck;
end if;
end if;
end case;
end process;
end Behavioral;
无论出于何种原因,根据我的模拟,似乎我的流程不同步。虽然事情只发生在时钟的上升沿,但我在下降沿发生了转换。而且,似乎事情并没有根据反击的价值而改变。
我的所有模拟中的启用输入都很高。但是,这只是为了保持现在的简单,最终将输出153,600波特率发生器的输出(波特率发生器将连接到使能输入)。因此,当波特发生器很高时,我只想改变一些事情。否则,什么也不做。我用我的代码采取了正确的方法吗?
如果有帮助,我可以提供模拟的屏幕截图。我也不确定我是否在我的过程敏感性列表中包含了正确的变量。另外,我是否采用了我的计数器和dataIndex变量的正确方法?如果我在任何进程之前将它们作为我的架构的一部分发出信号怎么办?
对此的任何帮助都非常感激!
答案 0 :(得分:1)
修复此问题的最简单方法是生成最容易读取的代码,这样就可以转移到1进程状态机(警告:未编译可能包含语法错误):
entity ASyncReceiverV4 is
Port ( DataIn : in STD_LOGIC;
Enable : in STD_LOGIC;
CLK : in STD_LOGIC;
BadData : out STD_LOGIC;
DataOut : out STD_LOGIC_VECTOR (7 downto 0));
end ASyncReceiverV4;
architecture Behavioral of ASyncReceiverV4 is
type states is (StartBitCheck, ReadData, StopBitCheck);
signal state : states := StartBitCheck;
signal counter : integer := 0;
signal dataIndex : integer := 0;
begin
process(CLK)
begin
if rising_edge(CLK) then
case state is
when StartBitCheck =>
if Enable = '1' then
if (DataIn = '0' and counter < 8) then
counter <= counter + 1;
elsif (DataIn = '0' and counter = 8) then
BadData <= '0';
state <= ReadData;
counter <= 0;
end if;
end if;
when ReadData =>
if Enable = '1' then
if counter < 16 then
counter <= counter + 1;
elsif (counter = 16 and dataIndex < 8) then
DataOut(dataIndex) <= DataIn;
counter <= 0;
dataIndex <= dataIndex + 1;
elsif dataIndex = 8 then
dataIndex <= 0;
state <= StopBitCheck;
end if;
end if;
when StopBitCheck =>
if Enable = '1' then
if DataIn = '1' then
if counter < 16 then
counter <= counter + 1;
elsif counter = 16 then
counter <= 0;
state <= StartBitCheck;
end if;
else
DataOut <= "11111111";
BadData <= '1';
state <= StartBitCheck;
end if;
end if;
end case;
end if;
end process;
end Behavioral;
请注意,虽然这不再包含语言问题,但逻辑中仍有一些奇怪的事情。
在StopBitCheck
为16之前,您无法输入状态counter
,因为状态转换位于counter < 16
的elsif中。因此,if counter < 16
中的StopBitCheck
无法访问。
另请注意,状态转换为StopBitCheck
的时间与您通常采样数据的周期相同,因此DataIn
内StopBitCheck
的采样时间较晚。更糟糕的是,你是否曾经得到错误的数据(DataIn/='1'
中的StopBitCheck
),counter
仍然是16,StartBitCheck
总是会转到else子句,而州机器会锁定。
之前对错误的更多解释:
您的模拟在负时钟边沿上发生了变化,因为组合过程只有灵敏度列表上的时钟。组合过程的正确敏感度列表仅包括DataIn
,Enable
,currentState
以及您的两个变量counter
和dataIndex
。变量不能成为灵敏度列表的一部分,因为它们超出了灵敏度列表的范围(你也不想触发你自己的过程,稍后会更多地介绍它)。
然而,灵敏度列表主要只是模拟器的拐杖。当转换为真实硬件时,过程在LUT和触发器中实现。您当前的实现将永远不会合成,因为您在非时钟逻辑中包含反馈(信号或变量,这些信号或变量被赋予新值作为其旧值的函数),从而产生组合循环。
counter
和dataIndex
是您的州数据的一部分。状态机更容易理解,因为它们与显式状态分离,但它们仍然是状态数据的一部分。当您创建一个双进程状态机(同样,我建议使用1个进程状态机,而不是2个)时,必须将所有状态数据拆分为用于存储它的触发器(例如currentState
)以及生成下一个值的LUT的输出(例如nextState
)。这意味着对于您的两台流程计算机,变量counter
和dataIndex
必须改为currentCounter
,nextCounter
,currentDataIndex
和nextDataIndex
以及对待你的州任务。请注意,如果您选择实施更改以保留2进程状态机,则该行上方提到的逻辑错误仍将适用。
编辑:
是的,在进入counter
之前将StopBitCheck
重置为0可能是一个好主意,但您还需要考虑在对最后一个数据位进行采样之后等待全部16个计数你甚至过渡到StopBitCheck
。只是因为counter
不重置,示例只关闭一个时钟而不是16个。您可能希望修改ReadData
操作以转换为{{1在dataIndex = 7(以及将StopBitCheck
重置为0)时最后一位采样,如下所示:
counter
纯状态机仅存储状态。它仅从状态或从状态和输入的组合生成其输出。由于存储了 elsif (counter = 16) then
DataOut(dataIndex) <= DataIn;
counter <= 0;
if (dataIndex < 7) then
dataIndex <= dataIndex + 1;
else
dataIndex <= 0;
state <= StopBitCheck;
end if;
end if;
和counter
,因此它们是州的一部分。如果你想枚举每个州,你会有类似的东西:
dataIndex
并且你最终会得到8 * 16 * 3 = 384个状态(实际上有点少,因为只有 type states is (StartBitCheck_Counter0, StartBitCheck_counter1...
使用ReadData
,所以384中的一些是完全冗余状态)。毫无疑问,由于状态数据的不同部分的使用方式不同,因此简单地声明3个单独的信号要简单得多。在双进程状态机中,人们经常忘记实际命名为dataIndex
的信号不是需要存储在时钟进程中的唯一状态数据。
我还删除了在我提供的1进程示例中维护当前状态分配的else子句;它们在分配组合state
时非常重要,但只要未给出新的分配而没有推断锁存器,时钟信号nextState
将保留其旧值。
答案 1 :(得分:-1)
第一个过程仅在CLK上升沿运行,而第二个过程在CLK边沿运行(因为每次CLK变化都会运行)。
你使用2个进程,“内存状态”和“下一个状态构建器”,第一个必须是同步的(正如你所做的那样),第二个通常是组合的,能够及时产生下一个状态下一个CLK有效边沿。
但是在你的情况下,第二个过程也需要在每个CLK脉冲上运行(因为变量计数器和索引),因此解决方案可以是在上升沿运行第一个,在下降沿运行第二个(如果你的硬件支持这个)。
以下是您的代码,我希望它有用。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity ASyncReceiverV4 is
Port ( DataIn : in STD_LOGIC;
Enable : in STD_LOGIC;
CLK : in STD_LOGIC;
BadData : out STD_LOGIC := '0';
DataOut : out STD_LOGIC_VECTOR (7 downto 0) := "00000000");
end ASyncReceiverV4;
architecture Behavioral of ASyncReceiverV4 is
type states is (StartBitCheck, ReadData, StopBitCheck);
signal currentState : states := StartBitCheck;
signal nextState : states := StartBitCheck;
begin
process(CLK)
begin
if rising_edge(CLK) then
currentState <= nextState;
end if;
end process;
process(CLK)
variable counter : integer := 0;
variable dataIndex : integer := 0;
begin
if falling_edge(CLK) then
case currentState is
when StartBitCheck =>
if Enable = '1' then
if (DataIn = '0' and counter < 8) then
counter := counter + 1;
elsif (DataIn = '0' and counter = 8) then
BadData <= '0';
nextState <= ReadData;
counter := 0;
else
nextState <= StartBitCheck;
end if;
end if;
when ReadData =>
if Enable = '1' then
if counter < 15 then
counter := counter + 1;
elsif (counter = 15 and dataIndex < 8) then
DataOut(dataIndex) <= DataIn;
counter := 0;
dataIndex := dataIndex + 1;
elsif dataIndex = 8 then
dataIndex := 0;
nextState <= StopBitCheck;
else
nextState <= ReadData;
end if;
end if;
when StopBitCheck =>
if Enable = '1' then
if DataIn = '1' then
if counter < 15 then
counter := counter + 1;
nextState <= StopBitCheck;
elsif counter = 15 then
counter := 0;
nextState <= StartBitCheck;
end if;
else
DataOut <= "11111111";
BadData <= '1';
nextState <= StartBitCheck;
end if;
end if;
end case;
end if;
end process;
end Behavioral;
这是一个错误的停止位接收数据模拟的0x55