VHDL RS232 接收器无法与 Xilinx ISE 一起正常工作
VHDL RS232 Receiver not working correctly with Xilinx ISE
所以我有这个用于 RS232 通信的接收器代码 link,我应该发送 8 位,其中 1 个起始位“0”和一个停止位“1”,没有奇偶校验位,我以大多数方式尝试过这些代码,但模拟从未正常工作,即使有人告诉我我的问题是测试平台而不是代码,但它永远不会在 FPGA 实现上工作,我发送的第一个信号总是错误的之后的任何信号都是正确的。
下面是代码
entity Rs232Rxd is
port( Reset, Clock16x, Rxd: in std_logic;
DataOut1: out std_logic_vector (7 downto 0));
end Rs232Rxd;
architecture Rs232Rxd_Arch of Rs232Rxd is
attribute enum_encoding: string;
-- state definitions
type stateType is (stIdle, stData, stStop, stRxdCompleted);
attribute enum_encoding of statetype: type is "00 01 11 10";
signal iReset : std_logic;
signal iRxd1, iRxd2 : std_logic := '1';
signal presState: stateType;
signal nextState: stateType;
signal iClock1xEnable, iClock1x, iEnableDataOut: std_logic :='0' ;
signal iClockDiv: std_logic_vector (3 downto 0) := (others=>'0') ;
signal iDataOut1, iShiftRegister: std_logic_vector (7 downto 0):= (others=>'0');
signal iNoBitsReceived: std_logic_vector (3 downto 0):= (others=>'0') ;
begin
process (Clock16x) begin
if rising_edge(Clock16x) then
if Reset = '1' or iReset = '1' then
iRxd1 <= '1';
iRxd2 <= '1';
iClock1xEnable <= '0';
iClockDiv <= (others=>'0');
else
iRxd1 <= Rxd;
iRxd2 <= iRxd1;
end if;
if iRxd1 = '0' and iRxd2 = '1' then
iClock1xEnable <= '1';
end if;
if iClock1xEnable = '1' then
iClockDiv <= iClockDiv + '1';
end if;
end if;
end process;
iClock1x <= iClockDiv(3);
process (iClock1xEnable, iClock1x)
begin
if iClock1xEnable = '0' then
iNoBitsReceived <= (others=>'0');
presState <= stIdle;
elsif rising_edge(iClock1x) then
iNoBitsReceived <= iNoBitsReceived + '1';
presState <= nextState;
if iEnableDataOut = '1' then
iDataOut1 <= iShiftRegister;
--iShiftRegister <= (others=>'0');
else
iShiftRegister <= Rxd & iShiftRegister(7 downto 1);
end if;
end if;
end process;
DataOut1 <= iDataOut1;
process (presState, iClock1xEnable, iNoBitsReceived)
begin
-- signal defaults
iReset <= '0';
iEnableDataOut <= '0';
case presState is
when stIdle =>
if iClock1xEnable = '1' then
nextState <= stData;
else
nextState <= stIdle;
end if;
when stData =>
if iNoBitsReceived = "1000" then
iEnableDataOut <= '1';
nextState <= stStop;
else
iEnableDataOut <= '0';
nextState <= stData;
end if;
when stStop =>
nextState <= stRxdCompleted;
when stRxdCompleted =>
iReset <= '1';
nextState <= stIdle;
end case;
end process;
end Rs232Rxd_Arch;
您的问题没有 Minimal Complete and Verifiable Example。如果不编写测试平台就无法复制该问题,并且您的问题缺乏特异性(此处使用的 'signal' 和 'wrong' 不精确)。
有一些观察。
停止位后跟连续字符的起始位,没有为状态 stRxdCompleted 留出空间。当 iClock1xEnable 无效时,iNoBitsReceived 也没有设置为全“0”,这意味着采样点不是由连续字符的起始位的下降沿决定的:
这是一个大写字母'A'紧跟着一个小写字母'a',停止位紧跟着第二个字符的起始位(这是合法的)。
在第一个字符中,您看到起始位被算作一个字符位。
您还看到,当使能无效时,位计数器不会被重置,这将导致采样点漂移(并可能最终导致采样错误,具体取决于时钟差异或传输失真以及缺少同步采样点重置) .
您还看到 presState 在第一个字符的最后一个数据位期间为 stStop,但第二个字符是正确的。仔细观察,我们看到第一个字符的起始位出现在 stData 期间,而第二个字符没有出现。
当 iClock1x 停止时,状态数量和状态转换存在一个基本问题。
你不需要状态机,你有一个名为 iNoBitsReceived 的计数器,它可以存储所有状态,如果 ishiftregister 足够长以容纳开始(和可能停止)位,如果你还检测到帧错误。
在没有单独状态机的情况下将操作绑定到特定计数,并在空闲时清除位计数器:
为我们提供了一些复杂性较低的东西:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Rs232Rxd is
port (
Reset,
Clock16x,
Rxd: in std_logic;
DataOut1: out std_logic_vector (7 downto 0)
);
end entity Rs232Rxd;
architecture foo of Rs232Rxd is
signal rxd1: std_logic;
signal rxd2: std_logic;
signal baudctr: unsigned (3 downto 0);
signal ctr16x: unsigned (3 downto 0);
signal enab1xstart: std_logic;
signal enable1x: std_logic;
signal ninthbit: std_logic;
signal sampleenab: std_logic;
signal shiftregister: std_logic_vector(7 downto 0);
begin
CLOCK_DOMAIN:
process (clock16x)
begin
if rising_edge(clock16x) then
rxd1 <= rxd;
rxd2 <= rxd1;
end if;
end process;
enab1xstart <= not rxd1 and rxd2 and not enable1x;
ENABLE_1X:
process (clock16x, reset)
begin
if reset = '1' then
enable1x <= '0';
elsif rising_edge(clock16x) then
if enab1xstart = '1' then
enable1x <= '1';
elsif ninthbit = '1' then
enable1x <= '0';
end if;
end if;
end process;
SAMPLE_COUNTER:
process (clock16x, reset, ninthbit)
begin
if reset = '1' or ninthbit = '1' then
ctr16x <= (others => '0'); -- for simulation
elsif rising_edge(clock16x) then
if enab1xstart = '1' or enable1x = '1' then
ctr16x <= ctr16x + 1;
end if;
end if;
end process;
sampleenab <= not ctr16x(3) and ctr16x(2) and ctr16x(1) and ctr16x(0);
BAUD_COUNTER:
process (clock16x, reset)
begin
if reset = '1' then
baudctr <= (others => '0');
elsif rising_edge(clock16x) and sampleenab = '1' then
if baudctr = 8 then
baudctr <= (others => '0');
else
baudctr <= baudctr + 1;
end if;
end if;
end process;
NINTH_BIT: -- one clock16x period long, after baudctr changes
process (clock16x, reset)
begin
if reset = '1' then
ninthbit <= '0';
elsif rising_edge(clock16x) then
ninthbit <= sampleenab and baudctr(3) and not baudctr(2) and
not baudctr(1) and not baudctr(0);
end if;
end process;
SHIFT_REG:
process (clock16x, reset)
begin
if reset = '1' then
shiftregister <= (others => '0'); -- for pretty waveforms
elsif rising_edge(clock16x) and sampleenab = '1' then
shiftregister <= rxd2 & shiftregister(7 downto 1);
end if;
end process;
OUTREG:
process (clock16x, reset)
begin
if reset = '1' then
dataout1 <= (others => '0');
elsif rising_edge(clock16x) and ninthbit = '1' then
dataout1 <= shiftregister;
end if;
end process;
end architecture;
VHDL 基本标识符不区分大小写,名称也不是特别有启发性。以上两个波形的格式表明名称更改很方便。
如果将移位寄存器的长度延长一或两个,则可以在停止位期间检测到帧错误。更改移位寄存器长度需要对移位寄存器输出进行切片以写入数据输出。
请注意,此架构是为使用包 numeric_std 而编写的,而不是 Synopsys 包 std_logic_arith。您也没有在实体声明之前提供上下文子句。
此架构还生成启用并使用 16x 时钟而不是生成 1x 时钟。
它是在发现为纠正原始架构中的问题而进行的大量更改之后编写的。 (如有疑问,请重新开始。)
使用了这个测试平台:
library ieee;
use ieee.std_logic_1164.all;
entity rs232rxd_tb is
end entity;
architecture foo of rs232rxd_tb is
signal reset: std_logic := '0';
signal clock16x: std_logic := '0';
signal rxd: std_logic := '1';
signal dataout1: std_logic_vector (7 downto 0);
begin
DUT:
entity work.rs232rxd
port map (
reset => reset,
clock16x => clock16x,
rxd => rxd,
dataout1 => dataout1
);
CLOCK:
process
begin
wait for 3.255 us; -- 16X clock divided by 2, 9600 baud 104.16 us
clock16x <= not clock16x;
if now > 2.30 ms then
wait;
end if;
end process;
STIMULI:
process
begin
wait for 6.51 us;
reset <= '1';
wait for 13.02 us;
reset <= '0';
wait for 13.02 us;
wait for 40 us;
rxd <= '0';
wait for 104.16 us; -- start bit
rxd <= '1';
wait for 104.16 us; -- first data bit, bit 0 = '1'
rxd <= '0';
wait for 104.16 us; -- second data bit, bit 1 = '0'
rxd <= '0';
wait for 104.16 us; -- third data bit, bit 2 = '0';
wait for 104.16 us; -- fourth data bit, bit 3 = '0';
wait for 104.16 us; -- fifth data bit, bit 4 = '0';
wait for 104.16 us; -- sixth data bit, bit 5 = '0';
rxd <= '1';
wait for 104.16 us; -- seventh data bit, bit 6 = '1';
rxd <= '0';
wait for 104.16 us; -- eigth data bit, bit 7 = '0';
rxd <= '1';
wait for 104.16 us; -- stop bit ( = '1')
--wait for 104.16 us; -- idle
rxd <= '0';
wait for 104.16 us; -- start bit
rxd <= '1';
wait for 104.16 us; -- first data bit, bit 0 = '1'
rxd <= '0';
wait for 104.16 us; -- second data bit, bit 1 = '0'
rxd <= '0';
wait for 104.16 us; -- third data bit, bit 2 = '0';
wait for 104.16 us; -- fourth data bit, bit 3 = '0';
wait for 104.16 us; -- fifth data bit, bit 4 = '0';
rxd <= '1';
wait for 104.16 us; -- sixth data bit, bit 5 = '1';
wait for 104.16 us; -- seventh data bit, bit 6 = '1';
rxd <= '0';
wait for 104.16 us; -- eigth data bit, bit 7 = '0';
rxd <= '1';
wait for 104.16 us; -- stop bit ( = '1')
wait;
end process;
end architecture;
您可以看到新架构具有所有相同的基本元素,尽管时钟处理元素位于单独的处理语句中。
没有状态机进程。
通过对移位寄存器的分离输入(用于奇偶校验、两个停止位、7 个数据位等),该架构可扩展为全功能 UART 接收器。可以串行执行奇偶校验。
所以我有这个用于 RS232 通信的接收器代码 link,我应该发送 8 位,其中 1 个起始位“0”和一个停止位“1”,没有奇偶校验位,我以大多数方式尝试过这些代码,但模拟从未正常工作,即使有人告诉我我的问题是测试平台而不是代码,但它永远不会在 FPGA 实现上工作,我发送的第一个信号总是错误的之后的任何信号都是正确的。
下面是代码
entity Rs232Rxd is
port( Reset, Clock16x, Rxd: in std_logic;
DataOut1: out std_logic_vector (7 downto 0));
end Rs232Rxd;
architecture Rs232Rxd_Arch of Rs232Rxd is
attribute enum_encoding: string;
-- state definitions
type stateType is (stIdle, stData, stStop, stRxdCompleted);
attribute enum_encoding of statetype: type is "00 01 11 10";
signal iReset : std_logic;
signal iRxd1, iRxd2 : std_logic := '1';
signal presState: stateType;
signal nextState: stateType;
signal iClock1xEnable, iClock1x, iEnableDataOut: std_logic :='0' ;
signal iClockDiv: std_logic_vector (3 downto 0) := (others=>'0') ;
signal iDataOut1, iShiftRegister: std_logic_vector (7 downto 0):= (others=>'0');
signal iNoBitsReceived: std_logic_vector (3 downto 0):= (others=>'0') ;
begin
process (Clock16x) begin
if rising_edge(Clock16x) then
if Reset = '1' or iReset = '1' then
iRxd1 <= '1';
iRxd2 <= '1';
iClock1xEnable <= '0';
iClockDiv <= (others=>'0');
else
iRxd1 <= Rxd;
iRxd2 <= iRxd1;
end if;
if iRxd1 = '0' and iRxd2 = '1' then
iClock1xEnable <= '1';
end if;
if iClock1xEnable = '1' then
iClockDiv <= iClockDiv + '1';
end if;
end if;
end process;
iClock1x <= iClockDiv(3);
process (iClock1xEnable, iClock1x)
begin
if iClock1xEnable = '0' then
iNoBitsReceived <= (others=>'0');
presState <= stIdle;
elsif rising_edge(iClock1x) then
iNoBitsReceived <= iNoBitsReceived + '1';
presState <= nextState;
if iEnableDataOut = '1' then
iDataOut1 <= iShiftRegister;
--iShiftRegister <= (others=>'0');
else
iShiftRegister <= Rxd & iShiftRegister(7 downto 1);
end if;
end if;
end process;
DataOut1 <= iDataOut1;
process (presState, iClock1xEnable, iNoBitsReceived)
begin
-- signal defaults
iReset <= '0';
iEnableDataOut <= '0';
case presState is
when stIdle =>
if iClock1xEnable = '1' then
nextState <= stData;
else
nextState <= stIdle;
end if;
when stData =>
if iNoBitsReceived = "1000" then
iEnableDataOut <= '1';
nextState <= stStop;
else
iEnableDataOut <= '0';
nextState <= stData;
end if;
when stStop =>
nextState <= stRxdCompleted;
when stRxdCompleted =>
iReset <= '1';
nextState <= stIdle;
end case;
end process;
end Rs232Rxd_Arch;
您的问题没有 Minimal Complete and Verifiable Example。如果不编写测试平台就无法复制该问题,并且您的问题缺乏特异性(此处使用的 'signal' 和 'wrong' 不精确)。
有一些观察。
停止位后跟连续字符的起始位,没有为状态 stRxdCompleted 留出空间。当 iClock1xEnable 无效时,iNoBitsReceived 也没有设置为全“0”,这意味着采样点不是由连续字符的起始位的下降沿决定的:
这是一个大写字母'A'紧跟着一个小写字母'a',停止位紧跟着第二个字符的起始位(这是合法的)。
在第一个字符中,您看到起始位被算作一个字符位。
您还看到,当使能无效时,位计数器不会被重置,这将导致采样点漂移(并可能最终导致采样错误,具体取决于时钟差异或传输失真以及缺少同步采样点重置) .
您还看到 presState 在第一个字符的最后一个数据位期间为 stStop,但第二个字符是正确的。仔细观察,我们看到第一个字符的起始位出现在 stData 期间,而第二个字符没有出现。
当 iClock1x 停止时,状态数量和状态转换存在一个基本问题。
你不需要状态机,你有一个名为 iNoBitsReceived 的计数器,它可以存储所有状态,如果 ishiftregister 足够长以容纳开始(和可能停止)位,如果你还检测到帧错误。
在没有单独状态机的情况下将操作绑定到特定计数,并在空闲时清除位计数器:
为我们提供了一些复杂性较低的东西:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Rs232Rxd is
port (
Reset,
Clock16x,
Rxd: in std_logic;
DataOut1: out std_logic_vector (7 downto 0)
);
end entity Rs232Rxd;
architecture foo of Rs232Rxd is
signal rxd1: std_logic;
signal rxd2: std_logic;
signal baudctr: unsigned (3 downto 0);
signal ctr16x: unsigned (3 downto 0);
signal enab1xstart: std_logic;
signal enable1x: std_logic;
signal ninthbit: std_logic;
signal sampleenab: std_logic;
signal shiftregister: std_logic_vector(7 downto 0);
begin
CLOCK_DOMAIN:
process (clock16x)
begin
if rising_edge(clock16x) then
rxd1 <= rxd;
rxd2 <= rxd1;
end if;
end process;
enab1xstart <= not rxd1 and rxd2 and not enable1x;
ENABLE_1X:
process (clock16x, reset)
begin
if reset = '1' then
enable1x <= '0';
elsif rising_edge(clock16x) then
if enab1xstart = '1' then
enable1x <= '1';
elsif ninthbit = '1' then
enable1x <= '0';
end if;
end if;
end process;
SAMPLE_COUNTER:
process (clock16x, reset, ninthbit)
begin
if reset = '1' or ninthbit = '1' then
ctr16x <= (others => '0'); -- for simulation
elsif rising_edge(clock16x) then
if enab1xstart = '1' or enable1x = '1' then
ctr16x <= ctr16x + 1;
end if;
end if;
end process;
sampleenab <= not ctr16x(3) and ctr16x(2) and ctr16x(1) and ctr16x(0);
BAUD_COUNTER:
process (clock16x, reset)
begin
if reset = '1' then
baudctr <= (others => '0');
elsif rising_edge(clock16x) and sampleenab = '1' then
if baudctr = 8 then
baudctr <= (others => '0');
else
baudctr <= baudctr + 1;
end if;
end if;
end process;
NINTH_BIT: -- one clock16x period long, after baudctr changes
process (clock16x, reset)
begin
if reset = '1' then
ninthbit <= '0';
elsif rising_edge(clock16x) then
ninthbit <= sampleenab and baudctr(3) and not baudctr(2) and
not baudctr(1) and not baudctr(0);
end if;
end process;
SHIFT_REG:
process (clock16x, reset)
begin
if reset = '1' then
shiftregister <= (others => '0'); -- for pretty waveforms
elsif rising_edge(clock16x) and sampleenab = '1' then
shiftregister <= rxd2 & shiftregister(7 downto 1);
end if;
end process;
OUTREG:
process (clock16x, reset)
begin
if reset = '1' then
dataout1 <= (others => '0');
elsif rising_edge(clock16x) and ninthbit = '1' then
dataout1 <= shiftregister;
end if;
end process;
end architecture;
VHDL 基本标识符不区分大小写,名称也不是特别有启发性。以上两个波形的格式表明名称更改很方便。
如果将移位寄存器的长度延长一或两个,则可以在停止位期间检测到帧错误。更改移位寄存器长度需要对移位寄存器输出进行切片以写入数据输出。
请注意,此架构是为使用包 numeric_std 而编写的,而不是 Synopsys 包 std_logic_arith。您也没有在实体声明之前提供上下文子句。
此架构还生成启用并使用 16x 时钟而不是生成 1x 时钟。
它是在发现为纠正原始架构中的问题而进行的大量更改之后编写的。 (如有疑问,请重新开始。)
使用了这个测试平台:
library ieee;
use ieee.std_logic_1164.all;
entity rs232rxd_tb is
end entity;
architecture foo of rs232rxd_tb is
signal reset: std_logic := '0';
signal clock16x: std_logic := '0';
signal rxd: std_logic := '1';
signal dataout1: std_logic_vector (7 downto 0);
begin
DUT:
entity work.rs232rxd
port map (
reset => reset,
clock16x => clock16x,
rxd => rxd,
dataout1 => dataout1
);
CLOCK:
process
begin
wait for 3.255 us; -- 16X clock divided by 2, 9600 baud 104.16 us
clock16x <= not clock16x;
if now > 2.30 ms then
wait;
end if;
end process;
STIMULI:
process
begin
wait for 6.51 us;
reset <= '1';
wait for 13.02 us;
reset <= '0';
wait for 13.02 us;
wait for 40 us;
rxd <= '0';
wait for 104.16 us; -- start bit
rxd <= '1';
wait for 104.16 us; -- first data bit, bit 0 = '1'
rxd <= '0';
wait for 104.16 us; -- second data bit, bit 1 = '0'
rxd <= '0';
wait for 104.16 us; -- third data bit, bit 2 = '0';
wait for 104.16 us; -- fourth data bit, bit 3 = '0';
wait for 104.16 us; -- fifth data bit, bit 4 = '0';
wait for 104.16 us; -- sixth data bit, bit 5 = '0';
rxd <= '1';
wait for 104.16 us; -- seventh data bit, bit 6 = '1';
rxd <= '0';
wait for 104.16 us; -- eigth data bit, bit 7 = '0';
rxd <= '1';
wait for 104.16 us; -- stop bit ( = '1')
--wait for 104.16 us; -- idle
rxd <= '0';
wait for 104.16 us; -- start bit
rxd <= '1';
wait for 104.16 us; -- first data bit, bit 0 = '1'
rxd <= '0';
wait for 104.16 us; -- second data bit, bit 1 = '0'
rxd <= '0';
wait for 104.16 us; -- third data bit, bit 2 = '0';
wait for 104.16 us; -- fourth data bit, bit 3 = '0';
wait for 104.16 us; -- fifth data bit, bit 4 = '0';
rxd <= '1';
wait for 104.16 us; -- sixth data bit, bit 5 = '1';
wait for 104.16 us; -- seventh data bit, bit 6 = '1';
rxd <= '0';
wait for 104.16 us; -- eigth data bit, bit 7 = '0';
rxd <= '1';
wait for 104.16 us; -- stop bit ( = '1')
wait;
end process;
end architecture;
您可以看到新架构具有所有相同的基本元素,尽管时钟处理元素位于单独的处理语句中。
没有状态机进程。
通过对移位寄存器的分离输入(用于奇偶校验、两个停止位、7 个数据位等),该架构可扩展为全功能 UART 接收器。可以串行执行奇偶校验。