【问题标题】:Self implemented UART in VHDL always skips second characterVHDL 中自行实现的 UART 总是跳过第二个字符
【发布时间】:2016-05-22 10:19:10
【问题描述】:

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

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

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

以下代码将 10 MHz 时钟分频以每秒约 38 400 位发送,我也尝试以每秒 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;

【问题讨论】:

  • 你的模拟测试台呢?
  • 我尝试使用 ModelSim,但 tx_pin 始终未初始化,即使经过数百万个时钟周期(我还将 1s 延迟更改为几分之一秒)。我正在寻找另一个问题的解决方案。您所说的模拟测试台到底是什么意思?
  • 在提供输入信号(clk 等)并监控其输出(理想情况下,报告错误但可以等待)的测试台(没有端口的 VHDL 实体)中实例化您的 UART。在此处发布该文件。没有测试平台的模拟是一种艰难的方式。直接在硬件中调试是真的困难的方法。哦,还有 std_logic_arith 库。

标签: vhdl fpga intel-fpga


【解决方案1】:

移动线

txIdle <= idle;

来自进程外的 TX.vhd。信号在过程结束后获取它们的新值。

例如:

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

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

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

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

另外,正如 Brian 提到的,不要使用 std_logic_arith,尤其是在使用 numeric_std 时。它们相互冲突(有些工具会处理它)并且 std_logic_arith 不是 IEEE 标准,而 numeric_std 是。

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

【讨论】:

  • 在发现 ModelSim 非常有用后,我对代码进行了很多更改。这给我指出了一些错误,我发现这个问题与两个周期的空闲信号有关,也是未初始化的值。但是,我找不到你知道的解释。经过代码修改,尽量避免出现两个周期的空闲信号,波形如下:i.imgur.com/c0J1mEG.png 所以应该没问题,可惜在硬件上现在字符串开头出现了双H问题。我会处理一段时间并通知你。
  • 我的想法用完了,波形是这样的,还有“你好,黑暗我的老朋友!”收到的字符串 - 双首字母:i.imgur.com/7ysiatV.png UART.vhd:pastebin.com/bmkV2brBTX.vhd:pastebin.com/v4cAs1wc
  • 我运行了 SignalTap,它确认了问题:imgur.com/eb0SYC0(有一个字错误,应该是“反转”而不是“反转”)
  • 你在模拟中遇到了同样的问题。第一个字符被发送两次,最后一个字符从不发送。问题是 tx 模块在idle 为 1 时读取输入,并且 uart 模块会在被 tx 读取后更新数据发送周期。我建议在发送字符之间切换start,以便start 和数据在idle 被断言后的周期内更新。
  • 没错,我按照你说的做了,而且效果很好——谢谢
猜你喜欢
  • 2016-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
  • 1970-01-01
  • 2014-07-20
  • 2018-11-08
相关资源
最近更新 更多