VHDL RS-232 接收器 [英] VHDL RS-232 Receiver

查看:84
本文介绍了VHDL RS-232 接收器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试采用 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:

您的模拟在负时钟边沿发生了变化,因为您的组合过程只有敏感度列表上的时钟.组合过程的正确敏感性列表将仅包括 DataInEnablecurrentState 和您的两个变量,counterdataIndex.变量不能成为您的敏感度列表的一部分,因为它们超出了敏感度列表的范围(您也不想自行触发您的流程,稍后会详细介绍).

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.

counterdataIndex 是状态数据的一部分.状态机更容易理解,因为它们从显式状态中分离出来,但它们仍然是状态数据的一部分.当你制作一个两进程状态机时(同样,我推荐 1 个进程状态机,而不是 2 个)你必须将所有状态数据拆分成用于存储它的触发器(例如currentStatecode>) 和生成下一个值的 LUT 的输出(例如 nextState).这意味着对于您的两个流程机器,变量 counterdataIndex 必须改为 currentCounternextCountercurrentDataIndexnextDataIndex 并像您的状态分配一样对待.请注意,如果您选择实施更改以保留 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;

纯状态机只存储一个状态.它纯粹从状态或从状态和输入的组合中生成输出.因为存储了 counterdataIndex,所以它们是状态的一部分.如果你想枚举每一个状态,你会有类似的东西:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆