VHDL RS-232 接收器 [英] VHDL RS-232 Receiver
问题描述
我一直在尝试采用 FSM 方法设计 RS-232 接收器.我承认我对 VHDL 没有很全面的理解,所以我一直在编写代码并边走边学习.但是,我相信此时我已经碰壁了.
I have been trying to design an RS-232 receiver taking an FSM approach. I will admit that I do not have a very well-rounded understanding of VHDL, so I have been working on the code on the fly and learning as I go. However, I believe I've hit a brick wall at this point.
我的问题是我的代码中有两个进程,一个用于触发下一个状态,另一个用于执行组合逻辑.我的代码如下:
My issue is that I have two processes in my code, one to trigger the next state and the other to perform the combinational logic. My code is as follows:
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;
无论出于何种原因,根据我的模拟,我的流程似乎不同步.虽然事情只应该发生在时钟的上升沿,但我在下降沿发生了转换.此外,似乎事情并没有根据计数器的值而改变.
For whatever reason, based on my simulations, it seems that my processes are out of sync. Although things are only supposed to occur at the rising edge of the clock, I have transitions occurring at the falling edge. Furthermore, it seems like things are not changing according to the value of counter.
在我的所有模拟中,启用输入都很高.然而,这只是为了保持简单,它最终将被馈送到 153,600 波特发生器的输出(波特发生器将连接到启用输入).因此,我只希望在我的波特率发生器高时改变事情.否则,什么都不做.我的代码是否采用了正确的方法?
The Enable input is high in all of my simulations. However, this is just to keep it simple for now, it will eventually be fed the output of a 153,600 Baud generator (the Baud generator will be connected to the Enable input). Hence, I only want things to change when my Baud generator is high. Otherwise, do nothing. Am I taking the right approach for that with my code?
如果有帮助,我可以提供模拟的屏幕截图.我也不确定我是否在我的流程敏感性列表中包含了正确的变量.另外,我是否对我的 counter 和 dataIndex 变量采取了正确的方法?如果我在任何流程之前将它们作为架构的一部分来制作信号会怎样?
I can supply a screenshot of my simulation if that would be helpful. I am also not sure if I am including the correct variables in my process sensitivity list. Also, am I taking the right approach with my counter and dataIndex variables? What if I made them signals as part of my architecture before any of my processes?
对此的任何帮助将不胜感激!
Any help on this would be very much so appreciated!
推荐答案
解决这个问题的最简单方法,同时也产生最容易阅读的代码,是移动到像这样的单进程状态机(警告:不是合规可能包含语法错误):
The easiest way to fix this, while also producing the easiest to read code, would be to move to a 1-process state machine like so (warning: not complied may contain syntax errors):
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;
请注意,虽然这不再包含语言问题,但逻辑中仍然存在奇怪的问题.
Note that while this no longer contains language issues, there are still odd things in the logic.
在 counter
为 16 之前,您不能进入状态 StopBitCheck
,因为状态转换在 counter < 的 elsif 中.16
.因此,if 计数器 <
无法访问.StopBitCheck
中的 16
You cannot enter the state StopBitCheck
until counter
is 16 because the state transition is in an elsif of counter < 16
. Therefore, the if counter < 16
in StopBitCheck
is unreachable.
另请注意,状态转换到 StopBitCheck
发生在您通常采样数据的同一周期,因此 StopBitCheck
中的 DataIn
采样> 会晚一个周期.更糟糕的是,如果您曾经获得过错误数据(StopBitCheck
中的DataIn/='1'
),counter
仍然是 16,StartBitCheck
将始终转到 else 子句,并且状态机将锁定.
Also note that the state transition to StopBitCheck
happens on the same cycle that you would normally sample data, so the sampling of DataIn
within StopBitCheck
will be a cycle late. Worse, were you ever to get bad data (DataIn/='1'
in StopBitCheck
), counter
would still be at 16, StartBitCheck
would always go to the else clause, and the state machine would lock up.
关于之前错误的更多解释:
Some more explanation on what was wrong before:
您的模拟在负时钟边沿发生了变化,因为您的组合过程只有敏感度列表上的时钟.组合过程的正确敏感性列表将仅包括 DataIn
、Enable
、currentState
和您的两个变量,counter
和 dataIndex
.变量不能成为您的敏感度列表的一部分,因为它们超出了敏感度列表的范围(您也不想自行触发您的流程,稍后会详细介绍).
Your simulation has things changing on the negative clock edge because your combinatorial process only has the clock on the sensitivity list. The correct sensitivity list for the combinatorial process would include only DataIn
, Enable
, currentState
, and your two variables, counter
and dataIndex
. Variables can't be a part of your sensitivity list because they are out of scope for the sensitivity list (also you do not want to trigger your process off of itself, more on that in a moment).
然而,敏感度列表大多只是模拟器的拐杖.当转换为真正的硬件时,过程在 LUT 和触发器中实现.您当前的实现永远不会综合,因为您将反馈(根据旧值分配新值的信号或变量)合并到非时钟逻辑中,从而产生组合循环.
The sensitivity list is, however, mostly just a crutch for simulators. When translated to real hardware, processes are implemented in LUTs and Flip Flops. Your current implementation will never synthesize because you incorporate feedback (signals or variables that get assigned a new value as a function of their old value) in unclocked logic, producing a combinatorial loop.
counter
和 dataIndex
是状态数据的一部分.状态机更容易理解,因为它们从显式状态中分离出来,但它们仍然是状态数据的一部分.当你制作一个两进程状态机时(同样,我推荐 1 个进程状态机,而不是 2 个)你必须将所有状态数据拆分成用于存储它的触发器(例如currentStatecode>) 和生成下一个值的 LUT 的输出(例如
nextState
).这意味着对于您的两个流程机器,变量 counter
和 dataIndex
必须改为 currentCounter
、nextCounter
、currentDataIndex
和 nextDataIndex
并像您的状态分配一样对待.请注意,如果您选择实施更改以保留 2 进程状态机,则该行上面提到的逻辑错误仍将适用.
counter
and dataIndex
are part of your state data. The state machine is simpler to understand because they are split off from the explicit state, but they are still part of the state data. When you make a two process state machine (again, I recommend 1 process state machines, not 2) you must split all state data into Flip Flops used to store it (such as currentState
) and the output of the LUT that generates the next value (such as nextState
). This means that for your two process machine, the variables counter
and dataIndex
must instead become currentCounter
, nextCounter
, currentDataIndex
, and nextDataIndex
and treated like your state assignment. Note that if you choose to implement changes to keep a 2-process state machine, the logic errors mentioned above the line will still apply.
是的,在进入 StopBitCheck
之前将 counter
重置回 0 可能是个好主意,但您还需要考虑到您正在等待完整的 16 个计数之后在转换到 StopBitCheck
之前,对最后一个数据位进行采样.只是因为 counter
not 重置,样本仅关闭一个时钟而不是 16.您可能需要修改您的 ReadData
操作当 dataIndex=7(以及将 counter
重置为 0)时,在采样最后一位时转换到 StopBitCheck
,如下所示:
Yes, resetting the counter
back to 0 before moving into StopBitCheck
might be a good idea, but you also need to consider that you are waiting the full 16 counts after sampling the last data bit before you even transition into StopBitCheck
. It is only because counter
is not reset that the sample is only off by one clock instead of 16. You may want to modify your ReadData
action to transition to StopBitCheck
as you sample the last bit when dataIndex=7 (as well as reset counter
to 0) like so:
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
,所以它们是状态的一部分.如果你想枚举每一个状态,你会有类似的东西:
A pure state machine only stores a state. It generates its output purely from the state, or from a combination of the state and the inputs. Because counter
and dataIndex
are stored, they are part of the state. If you wanted to enumerate every single state you would have something like:
type states is (StartBitCheck_Counter0, StartBitCheck_counter1...
你最终会得到 8*16*3 = 384 个状态(实际上稍微少一些,因为只有 ReadData
使用 dataIndex
,所以 384 个状态中的一些是完全冗余的状态).毫无疑问,您可以看到,声明 3 个单独的信号要简单得多,因为状态数据的不同部分的使用方式不同.在双进程状态机中,人们常常忘记实际上名为 state
的信号并不是需要存储在时钟进程中的唯一状态数据.
and you would end up with 8*16*3 = 384 states (actually somewhat less because only ReadData
uses dataIndex
, so some of the 384 are wholly redundant states). As you can no doubt see, it is much simpler to just declare 3 separate signals since the different parts of the state data are used differently. In a two process state machine, people often forget that the signal actually named state
isn't the only state data that needs to be stored in the clocked process.
当然,在 1 进程机器中,这不是问题,因为分配的所有内容都必须存储在触发器中(中间组合逻辑未在信号中枚举).另外,请注意,1-process 状态机和 2-process 状态机将合成相同的东西(假设它们都正确实现),尽管我认为相对更容易阅读并且更难搞砸一台单进程机器.
In a 1-process machine, of course, this isn't an issue because everything that is assigned is by necessity stored in flip flops (the intermediary combinational logic isn't enumerated in signals). Also, do note that 1-process state machines and 2-process state machines will synthesize to the same thing (assuming they were both implemented correctly), although I'm of the opinion that is comparatively easier to read and more difficult to mess up a 1-process machine.
我还删除了在我提供的 1-process 示例中维护当前状态分配的 else 子句;它们在分配组合 nextState
时很重要,但时钟信号 state
将保持其旧值,只要它没有被赋予新分配而不推断锁存器.
I also removed the else clauses that maintained the current state assignment in the 1-process example I provided; they are important when assigning a combinational nextState
, but the clocked signal state
will keep its old value whenever it isn't given a new assignment without inferring a latch.
这篇关于VHDL RS-232 接收器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!