我一直在尝试设计一个RS-232接收机采用FSM方法.我承认我对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变量采取了正确的方法?如果我在任何流程之前将它们作为架构的一部分来发出信号呢?
在这方面的任何帮助都是非常感谢的!
发布于 2014-11-24 15:35:16
解决这一问题的最简单方法,同时也是生成最容易阅读的代码的方法,是移动到像这样的单进程状态机(警告:未被遵守可能包含语法错误):
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的状态转换发生在与通常采样数据相同的周期上,因此StopBitCheck中的DataIn采样将是一个周期延迟。更糟糕的是,如果您得到坏数据(DataIn/='1' in StopBitCheck),counter将仍然在16岁,StartBitCheck将始终转到StartBitCheck子句,状态机将被锁定。
再解释一下之前是怎么回事:
您的模拟会在负时钟边缘发生变化,因为组合过程中只有灵敏度列表中的时钟。组合过程的正确灵敏度列表将只包括DataIn、Enable、currentState以及您的两个变量counter和dataIndex。变量不能成为敏感列表的一部分,因为它们超出了敏感列表的范围(另外,您也不希望触发进程本身,稍后会详细介绍)。
然而,灵敏度列表基本上只是模拟器的一根拐杖。当转换到实际硬件时,进程用LUTs和触发器实现。您当前的实现永远不会合成,因为您将反馈(作为旧值的函数分配给新值的信号或变量)合并到不锁定逻辑中,从而产生组合循环。
counter和dataIndex是状态数据的一部分。状态机更容易理解,因为它们与显式状态分离,但它们仍然是状态数据的一部分。当您创建两个进程状态机(同样,我建议使用一个进程状态机,而不是两个)时,您必须将所有状态数据拆分为用于存储它的触发器(例如currentState)和生成下一个值的LUT输出(例如nextState)。这意味着对于两台进程机器,变量counter和dataIndex必须改为currentCounter、nextCounter、currentDataIndex和nextDataIndex,并将其视为状态分配。请注意,如果您选择实现更改以保持一个2进程状态机,那么上面提到的逻辑错误仍然适用。
编辑:
是的,在进入counter之前将StopBitCheck重新设置为0可能是个好主意,但您还需要考虑的是,在对最后一个数据位进行采样之后,您还需要等待完整的16个计数,然后才能转换到StopBitCheck。只是因为counter是而不是重置,所以示例只关闭一个时钟,而不是16。您可能希望修改ReadData操作以转换为StopBitCheck,因为您需要在dataIndex=7 (以及将counter重置为0)时进行如下操作:
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是存储的,所以它们是状态的一部分。如果您想要枚举每个状态,您将得到如下内容:
type states is (StartBitCheck_Counter0, StartBitCheck_counter1...最后您将得到8*16*3 = 384状态(实际上稍微少了一点,因为只有ReadData使用dataIndex,所以384中的某些状态完全是冗余状态)。毫无疑问,您可以看到,仅仅声明3个独立的信号要简单得多,因为状态数据的不同部分被不同的使用。在两个进程状态机中,人们常常忘记实际上命名为state的信号并不是唯一需要存储在时钟进程中的状态数据。
当然,在一个进程机器中,这不是一个问题,因为所有分配的东西都必须存储在触发器中(中间的组合逻辑没有在信号中列举)。另外,请注意,1-进程状态机和2-进程状态机将合成为相同的东西(假设它们都正确地实现),尽管我认为这是比较容易阅读和更难搞乱一进程机器。
我还删除了在我提供的1进程示例中维护当前状态赋值的else子句;它们在分配组合nextState时很重要,但是当没有给出新赋值时,时钟信号state将保留其旧值,而无需推断锁存器。
发布于 2014-11-23 20:54:00
第一个进程只运行在CLK上升边缘上,而第二个进程运行在两个CLK边缘上(因为每个CLK更改都运行)。
您使用两个进程,“内存状态”和“下一个状态生成器”,第一个进程必须是同步的(正如您所做的那样),而第二个进程通常是组合的,以便能够为下一个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错误的停止位接收数据模拟。

https://stackoverflow.com/questions/27086712
复制相似问题