VHDL中自实现的UART总是跳过第二个字符

时间:2016-05-21 12:00:00

标签: vhdl fpga intel-fpga

我正在学习VHDL,我尝试实现UART(1个起始位,8个数据位,1个停止位)来定期发送硬编码字符串。

一切都按预期工作 - 我每1秒收到一次字符串。但是,没有第二个角色。

无论字符串有多长,它都是字符。我在示波器上检查了这个事实,这个特殊字符没有波形。 1个起始位,8位表示第一个字符,停止位,起始位和8位表示第三个字符,而不是第二个字符。

下面的代码是10 MHz时钟分频,每秒发送~38 400 bit,我也试过每秒9600位,这两个问题都是一样的。

我正在使用Altera MAX10开发板:http://maximator-fpga.org/

简短视频如何运作: https://gfycat.com/JoyousIlliterateGuillemot

UART.vhd:

LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    use ieee.numeric_std.ALL;
    use ieee.std_logic_arith.all;

entity UART is
    port (
        clk_10mhz: in STD_LOGIC;
        txPin: out STD_LOGIC
    );
end entity;

architecture Test of UART is
    signal txStart: STD_LOGIC;
    signal txIdle: STD_LOGIC;
    signal txData: STD_LOGIC_VECTOR(7 downto 0);

    component TX is
        port (
            clk_in: in STD_LOGIC;
            start: in STD_LOGIC;
            data: in STD_LOGIC_VECTOR(7 downto 0);
            tx: out STD_LOGIC;
            txIdle: out STD_LOGIC
        );
    end component TX;


begin
    process (clk_10mhz, txIdle) 
        variable clkDividerCounter : integer range 0 to 10000000;
        variable textToSend : string(1 to 31) := "Hello darkness my old friend!" & CR & LF; 
        variable currentCharacterIndex : integer range 0 to 31;
    begin       
        if (rising_edge(clk_10mhz)) then
            if (clkDividerCounter < 10000000) then
                clkDividerCounter := clkDividerCounter + 1;
            else
                clkDividerCounter := 0;
                currentCharacterIndex := 1;
            end if;


            if (txIdle = '1' and currentCharacterIndex > 0) then
                txData <= CONV_STD_LOGIC_VECTOR(character'pos(textToSend(currentCharacterIndex)),8);
                txStart <= '1';

                if (currentCharacterIndex < 31) then
                    currentCharacterIndex := currentCharacterIndex + 1;
                else
                    currentCharacterIndex := 0;
                    txStart <= '0';
                end if;
            end if;
        end if;
    end process;


    u1: TX port map (clk_10mhz, txStart, txData, txPin, txIdle);
end Test;

TX.vhd:

LIBRARY ieee;
    USE ieee.std_logic_1164.all;
    use ieee.numeric_std.ALL;

entity TX is
    port (
        clk_in: in STD_LOGIC;
        start: in STD_LOGIC;
        data: in STD_LOGIC_VECTOR(7 downto 0);

        tx: out STD_LOGIC;
        txIdle: out STD_LOGIC
    );
end entity;

architecture Test of TX is
    signal idle: STD_LOGIC;
begin
    process (clk_in) 
        variable bitIndex : integer range 0 to 9;
        variable clkDividerCounter : integer range 0 to 260;

        variable dataFrame : STD_LOGIC_VECTOR(9 downto 0);
        variable dataFrameCurrentIndex : integer range 0 to 9;
    begin
        if (rising_edge(clk_in)) then
            if (start = '1' and idle = '1') then
                dataFrame(0) := '0';
                dataFrame(8 downto 1) := data;
                dataFrame(9) := '1';

                dataFrameCurrentIndex := 0;

                idle <= '0';
            end if;

            if (idle = '0') then                
                if (clkDividerCounter < 260) then
                    clkDividerCounter := clkDividerCounter + 1;
                else
                    if (dataFrameCurrentIndex <= 9) then
                        tx <= dataFrame(dataFrameCurrentIndex);
                        dataFrameCurrentIndex := dataFrameCurrentIndex + 1;
                    else
                        idle <= '1';
                    end if;

                    clkDividerCounter := 0;
                end if;
            end if; 

            txIdle <= idle; 
        end if;
    end process;
end Test;

1 个答案:

答案 0 :(得分:1)

移动线

txIdle <= idle;
来自TX.vhd之外的进程。在过程结束后,信号将获取新值

例如:

idle <= '0';
txIdle <= idle;

如果在进程内执行了两个语句,txIdle'1'时,会将idle设置为'1'。您应该注意到这意味着txIdle连续两个周期'1'会导致currentCharacterIndex在开始时增加两次。

请注意,与信号相反,变量在遇到赋值语句时会获取新值,而不会像信号那样在过程结束时获取。

虽然你的代码对初学者来说并不是那么糟糕,但我建议你在开始学习VHDL时只使用信号。用变量搞错,或描述次优或破坏的实现要容易得多。

另外,正如Brian所说,不要使用std_logic_arith,尤其是在使用numeric_std时。它们相互冲突(虽然有些工具处理它),std_logic_arith不是IEEE标准,而numeric_std是。

最后,仿真是硬件设计的关键部分。为避免未初始化的引脚,请在电路中添加一个复位,这通常是一个好主意。