VHDL 中自行实现的 UART 总是跳过第二个字符
Self implemented UART in VHDL always skips second character
我现在正在学习 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;
移线
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 是。
最后,仿真是硬件设计的重要组成部分。为避免未初始化的引脚,请为您的电路添加一个复位,这通常是个好主意。
我现在正在学习 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;
移线
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 是。
最后,仿真是硬件设计的重要组成部分。为避免未初始化的引脚,请为您的电路添加一个复位,这通常是个好主意。