发送 16 个字符的字符串的 VHDL uart

VHDL uart which send 16 chars string

我必须在 Xilinx 上使用 vhdl 进行 UART,这将发送 16 个字符的字符串。我写了这样的代码

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.ALL;


entity uartByJackob is
    Port ( CLK, A, B, C : in  STD_LOGIC;
       RESET : in  STD_LOGIC;
       TxD, TxDOSC : out  STD_LOGIC);
end uartByJackob;

architecture Behavioral of uartByJackob is
    signal K: std_logic_vector(14 downto 0);
    signal Q: std_logic_vector(3 downto 0);
    signal CLK_Txd: std_logic;
    signal ENABLE: std_logic;
    signal QTxD: std_logic_vector(9 downto 0);
    signal DATA : STD_LOGIC_VECTOR(7 downto 0);


-- freq of clock
begin
process(CLK, RESET)
begin
if rising_edge(CLK) then
if(A = '1' and K < 10416) then
        K <= K + 1;
        CLK_Txd <= K(13);
elsif(B = '1' and K < 5208) then
        K <= K + 1;
        CLK_Txd <= K(12);
elsif(C = '1' and K < 20832) then
        K <= K + 1;
        CLK_Txd <= K(14);
else
    K <= (others => '0');
    end if;
    end if;
end process;

--counter
process(CLK_Txd, RESET, ENABLE)
 begin
if(RESET = '1' and ENABLE = '0') then
    Q <= "0000";
elsif (rising_edge(CLK_Txd)) then
    Q <= Q + 1;
end if;
end process;

--comparator
ENABLE <= '1' when (Q > 4) else '0';

--transcoder
process(Q, CLK_Txd)
begin
if (rising_edge(CLK_Txd)) then
case Q is 
 when "0001" => DATA <= x"40";
 when "0010" => DATA <= x"41";
 when "0011" => DATA <= x"42";
 when "0100" => DATA <= x"43";
 when "0101" => DATA <= x"44";
 when "0110" => DATA <= x"45";
 when "0111" => DATA <= x"46";
 when "1000" => DATA <= x"47";
 when "1001" => DATA <= x"48";
 when "1010" => DATA <= x"49";
 when "1011" => DATA <= x"50";
 when "1100" => DATA <= x"51";
 when "1101" => DATA <= x"52";
 when "1110" => DATA <= x"53";
 when "1111" => DATA <= x"54";
 when others => DATA <= x"55";
end case;
end if;
end process;

--uart
process(CLK_Txd, ENABLE, DATA)
begin
if(ENABLE = '0') then
        QTxD <= DATA & "01";    
elsif rising_edge(CLK_Txd) then
    QTxD <= '1'&QTxD(9 downto 1);
end if;
end process;
TxD <= QTxD(0);
TxDOSC <= QTxD(0);
end Behavioral;

它发送的数据与我在转码器中的数据完全无关,真的不知道为什么。您是否知道我的代码有什么问题,或者您是否有任何不同的示例说明如何使用 uart 发送您自己的 16 个字符?我想我的计数器或比较器有问题。

--编辑

感谢您的努力,我现在无法在 Xilinx 上试用您的代码,因为我正在大学里研究它。我看到您对我的代码做了很多更改。当然首先我尝试像你展示的那样做,我希望这是可以接受的,但我可能必须根据这张图片用转码器来做。

从上次我对我的代码进行此类更改开始

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.ALL;

entity uartByJackob is
 Port ( CLK, A, B, C : in  STD_LOGIC;
   RESET : in  STD_LOGIC;
   TxD, TxDOSC : out  STD_LOGIC);
end uartByJackob;

architecture Behavioral of uartByJackob is
signal K: std_logic_vector(14 downto 0);
signal Q: std_logic_vector(7 downto 0);
signal CLK_Txd: std_logic;
signal ENABLE: std_logic;
signal QTxD: std_logic_vector(7 downto 0);
signal DATA : STD_LOGIC_VECTOR(7 downto 0);
signal QPrim: std_logic_vector(3 downto 0);

begin
process(CLK, RESET)
begin
CLK_Txd <= CLK;
end process;

 process(CLK_Txd, RESET, ENABLE)
 begin
if(ENABLE = '0') then
    Q <= "00000000";
elsif (rising_edge(CLK_Txd)) then
    Q <= Q + 1;
end if;
end process;

ENABLE <= '1' when (Q <= 255) else '0';
process(Q(7 downto 4))
begin
case Q(7 downto 4) is 
 when "0000" => DATA <= x"40";
 when "0001" => DATA <= x"41";
 when "0010" => DATA <= x"42";
 when "0011" => DATA <= x"43";
 when "0100" => DATA <= x"44";
 when "0101" => DATA <= x"45";
 when "0110" => DATA <= x"46";
 when "0111" => DATA <= x"47";
 when "1000" => DATA <= x"48";
 when "1001" => DATA <= x"49";
 when "1010" => DATA <= x"50";
 when "1011" => DATA <= x"51";
 when "1100" => DATA <= x"52";
 when "1101" => DATA <= x"53";
 when "1110" => DATA <= x"54";
 when "1111" => DATA <= x"55";
 when others => DATA <= x"56";
 end case;
 end process;

process(CLK_Txd, ENABLE, DATA)
begin
if(ENABLE = '1') then
     QTxD <= DATA;  
elsif rising_edge(CLK_Txd) then
    QTxD <= '1'&QTxD(7 downto 1);
end if;
end process;
TxD <= QTxD(0);
TxDOSC <= QTxD(0);
end Behavioral;

据此,我将 MSB 发送到代码转换器,将 LSB 发送到比较器,但我的程序始终将 x"40" 发送到 DATA,并且它可能与您正在谈论的这个计数器相关联。

这是我的模拟效果。我对此很不高兴,因为我没有足够的 vhdl 技能来自己做。我希望你能帮助我重建我的项目。在模拟上它看起来不错我不知道它在 Xilinx 上看起来如何。

Can you show me a piece of code? - Stefan

向 Adrian Adamcyzk 的代码 (Altera FPGA hardware (has an issue) vs ModelSim simulation (ok) - self implemented UART) 提供 link 的全部目的是提供一个带有位(波特)计数器和触发器的示例,用于控制发送消息一次。

这是 Jackob 的修改:

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity uartbyjackob is
    port ( 
        clk, a, b, c:   in  std_logic;
        reset:          in  std_logic;
        txd, txdosc:    out std_logic
    );
end entity uartbyjackob;

architecture foo of uartbyjackob is
    -- signal k:       unsigned(14 downto 0); -- FOR simulation
    -- note if k were used in simulation it would require initialization
    signal q:       unsigned (3 downto 0);   -- WAS std_logic_vector
    signal clk_txd: std_logic;
    signal enable:  std_logic;
    signal qtxd:    std_logic_vector(9 downto 0);
    -- signal data:    std_logic_vector(7 downto 0);
    -- added:
    signal bdcnt:   unsigned (3 downto 0);
    signal ldqtxd:  std_logic;
    signal davl:    std_logic;

    type data_lut is array (0 to 15) of std_logic_vector (7 downto 0);
    constant data:  data_lut := (
            x"40", x"41", x"42", x"43", x"44", x"45", x"46", x"47",
            x"48", x"49", x"50", X"51", x"52", X"53", x"54", x"55"
        );
    signal datalut: std_logic_vector (7 downto 0); -- FOR SIMULATION visibility
begin
    -- -- freq of clock  -- NOTE k never in known binary state for simulation
    -- process (clk, reset)
    -- begin
    --     if rising_edge(clk) then
    --         if a = '1' and k < 10416 then
    --                 k <= k + 1;
    --                 clk_txd <= k(13);
    --         elsif b = '1' and k < 5208 then
    --                 k <= k + 1;
    --                 clk_txd <= k(12);
    --         elsif c = '1' and k < 20832 then
    --                 k <= k + 1;
    --                 clk_txd <= k(14);
    --         else
    --             k <= (others => '0');
    --         end if;
    --     end if;
    -- end process;

    clk_txd <= clk;     -- SHORTENS SIMULATION

DAVL_FF:                -- DATA_AVAILABLE to send
    process (clk_txd, reset)
    begin
        if reset = '1' then
            davl <= '0'; 
        elsif rising_edge (clk_txd) then
            if q = 15 and bdcnt = 9 then  -- a JK FF equivalent
                davl <= '0';
            elsif q = 0 then
                davl <= '1';             -- one clock holderover from reset
            -- else
                -- davl <= davl;
            end if;
        end if;
    end process;

    -- process(clk_txd, reset, enable)
    --  begin
    -- if reset = '1' and enable = '0' then
    --     q <= "0000";
    -- elsif rising_edge(clk_txd) then
    --     q <= q + 1;
    -- end if;
    -- end process;

QCNT:
    process (clk_txd, reset)
    begin
        if reset = '1' then
            q <= (others => '0');
        elsif rising_edge (clk_txd) then
            if enable = '1' then
                q <= q + 1;
            end if;
        end if;
    end process;

BAUD_COUNTER:
    process (clk_txd, reset)
    begin
        if reset = '1' then
            bdcnt <= (others => '0');
        elsif rising_edge (clk_txd) then
            if davl = '0' or bdcnt = 9 then
                bdcnt <= (others => '0');  
            else 
                bdcnt <= bdcnt + 1;
            end if;
        end if;
    end process;

    -- comparator
    -- enable <= '1' when (q > 4) else '0';

    enable <= '1' when bdcnt = 9 and davl = '1'  and q /= 15 else
              '0'; 
    -- q latches at 15;

    ldqtxd <= '1' when bdcnt = 9 and davl = '1' else
              '0';

    datalut <= data(to_integer(q));  -- FOR SIMULATION VISIBILITIY

    --transcoder
    -- process(q, clk_txd)
    -- begin
    --     if rising_edge(clk_txd) then
    --         case q is
    --             when "0001" => data <= x"40";
    --             when "0010" => data <= x"41";
    --             when "0011" => data <= x"42";
    --             when "0100" => data <= x"43";
    --             when "0101" => data <= x"44";
    --             when "0110" => data <= x"45";
    --             when "0111" => data <= x"46";
    --             when "1000" => data <= x"47";
    --             when "1001" => data <= x"48";
    --             when "1010" => data <= x"49";
    --             when "1011" => data <= x"50";
    --             when "1100" => data <= x"51";
    --             when "1101" => data <= x"52";
    --             when "1110" => data <= x"53";
    --             when "1111" => data <= x"54";
    --             when others => data <= x"55";
    --         end case;
    --     end if;
    -- end process;

    -- uart
    -- process (clk_txd, enable, data)
    -- begin
    --     if enable = '0' then
    --         qtxd <= data & "01";
    --     elsif rising_edge(clk_txd) then
    --         qtxd <= '1' & qtxd(9 downto 1);
    --     end if;
    -- end process;

TX_SHIFT_REG:
    process (clk_txd, reset)  -- shift regiseter Tx UART
    begin
        if reset = '1' then
            qtxd <= (others => '1');  -- output mark by default
        elsif rising_edge (clk_txd) then
            if  ldqtxd = '1' then
                qtxd <= '1' & data(to_integer(q)) & '0'; 
                 -- STOP & Data(q) 7 downto 0 & START , a MUX and expansion
            else
                qtxd <= '1' & qtxd(9 downto 1); -- shift out;
            end if;
        end if;
    end process;

    txd <= qtxd(0);

    txdosc <= qtxd(0);

end architecture foo;

library ieee;
use ieee.std_logic_1164.all;

entity uartbyjackob_tb is
end entity;

architecture foo of uartbyjackob_tb is
    signal clk:     std_logic := '0';
    signal reset:   std_logic := '0';
    signal txd:     std_logic;
begin
    DUT:
    entity work.uartbyjackob
        port map ( 
            clk => clk, -- clk_txd driven by clk
            a   =>  'X', 
            b   => 'X', 
            c   => 'X',     -- a, b, c aren't used
            reset => reset,
            txd => txd,
            txdosc => open
        );
CLOCK:
    process
    begin
        wait for 52.35 us;
        clk <= not clk;
        if now > 20000 us then
            wait;
        end if;
    end process;
STIMULUS:
    process
    begin
        wait for 104.7 us;
        reset <= '1';
        wait for 104.7 us;
        reset <= '0';
        wait;
    end process;
end architecture;

已修改模型以加快仿真速度,忽略波特率时钟发生器。

添加了一个触发器 (davl) 用于启用 UART 运行。增加了一个波特(位)计数器 bdcnt。

我更改了加载到 QTxD 中的开始、停止和数据值的顺序,因此首先出现开始位,然后是 8 个数据位和停止位。

您可以从左到右读取TxD 起始位,data(q)(0) ... data(q(7), 停止位。启用或ldqtxd 将与停止同时发生位。

此实现只有一个可观察到的缺陷,如果您在移位寄存器中的值尚未完成加载时重置,则会导致接收器出现帧错误。 davl 变为假后,不要将其重置为 10 个波特率。

模拟显示为 9600 波特 clk_txd,字符背对背输出。

它的人字拖比原来的少(忽略 k)。没有与 QTxD(- 8 FF)加 bdcnt(+ 4)加 davl(+ 1)分开的数据寄存器。有两个比较(优化为两个)bdcnt = 9, q =, /= 9。它们可以单独表示,因此在综合过程中不需要优化。

我更改了查找 table 样式,个人喜好问题也是将计数器更改为无符号类型并仅使用包 numeric_std 进行算术运算的借口。

小测试台同样不期望 k 计数器生成波特时钟。

运行 测试平台给出:

在 ldqtxd 之后添加的信号数据显示被移出的值。

更改后使 q 计数器(7 减为 0)

我们仍然从你的波形中看出它不起作用。

这是由于启用和移位寄存器。

如果您使用一个高四位索引输出字符的计数器,您的字符将在计数器低四位索引的 16 clk_txd 次中的 10 次中传输。剩余时钟时间 TxD 应为“1”(RS-232 术语中的空闲线标记)。

传输数据的顺序是space(起始位)、数据(0)到数据(7)和一个标记(停止位)。 (在 TxD 上从左到右显示)。

对于模拟,不使用 k 计数器。我把它包括在下面注释掉了。

为了正确模拟,我做了几处更改。这些包括同步加载包含 QTxD 的移位寄存器、同步清除 QTxD 的最右边位以提供全宽度和每 16 个时钟 (clk_txd) 发生一次的移动使能。启用之前是一个新的起始位清除,并且两者都被偏移以防止它在复位期间发生,这会导致任何接收器的第一个字符出现帧错误。

模拟是使用我在上面提供的相同测试平台完成的。

您的新代码的更改显示在注释中:

architecture behavioral of uartbyjackob is
    -- signal k:    std_logic_vector(14 downto 0);
    signal q:       unsigned (7 downto 0); -- std_logic_vector(7 downto 0);
    signal clk_txd: std_logic;
    signal enable:  std_logic;
    signal qtxd:    std_logic_vector(7 downto 0); 
    -- using an 8 bit shift register requires a method of outputting a 
    -- synchronous start bit (the width is important for receive framing)
    -- and synchronous stop bit
    signal data:    std_logic_vector(7 downto 0);
    signal qprim:   std_logic_vector(3 downto 0);
    signal clear:   std_logic;           -- synchronous clear for start bit

begin
    -- let's keep this here for when you put it the FPGA
    -- -- freq of clock  -- NOTE k never in known binary state for simulation
    -- process (clk, reset)
    -- begin
    --     if rising_edge(clk then
    --         if a = '1' and k < 10416 then
    --                 k <= k + 1;
    --                 clk_txd <= k(13);
    --         elsif b = '1' and k < 5208 then
    --                 k <= k + 1;
    --                 clk_txd <= k(12);
    --         elsif c = '1' and k < 20832 then
    --                 k <= k + 1;
    --                 clk_txd <= k(14);
    --         else
    --             k <= (others => '0');
    --         end if;
    --     end if;
    -- end process;

    process (clk) -- , reset)
    begin
        clk_txd <= clk;  -- if simply a concurrent assignment statement this
    end process;         -- would look similar to the elaborated equivalent 
                         -- process. The difference, no sensitivity list and
                         -- an explict wait on clk statement at the end. 
                         -- This process wants to be removed and replaced by
                         -- the above commented out process for synthesis

    process (clk_txd, reset) -- , reset, enable) -- enable a reset?
    begin
        -- if enable = '0' then 
        if reset = '1' then     -- puts q counter in known state for simulation
            q <= "00000000";
        elsif rising_edge(clk_txd) then
            if q /= 255 then    -- stop after sending once
                q <= q + 1;
            end if;
        end if;
    end process;

    -- enable <= '1' when q <= 255 else '0';  -- this appears incorrect

    enable <= '1' when q(3 downto 0) = "0010" else
              '0';
    clear  <= '1' when q(3 downto 0) = "0001" else
              '0';

    -- USING ONE COUNTER requires some clocks output MARKS 
    -- (idle bits) each 16 clocks. It requires the load (enable)
    --  occur once every 16 clocks.

    -- q(3 downto 0) is selected for enable to prevent outputting spaces
    -- TxD during reset (q is reset to all '0's). This would cause a receive
    -- framing error.

    process (q(7 downto 4))
    begin
        case q(7 downto 4) is 
            when "0000" => data <= x"40";
            when "0001" => data <= x"41";
            when "0010" => data <= x"42";
            when "0011" => data <= x"43";
            when "0100" => data <= x"44";
            when "0101" => data <= x"45";
            when "0110" => data <= x"46";
            when "0111" => data <= x"47";
            when "1000" => data <= x"48";
            when "1001" => data <= x"49";
            when "1010" => data <= x"50";
            when "1011" => data <= x"51";
            when "1100" => data <= x"52";
            when "1101" => data <= x"53";
            when "1110" => data <= x"54";
            when "1111" => data <= x"55";
            when others => data <= x"56";
        end case;
    end process;

    process (clk_txd) -- , enable, data)  -- synchronous enable and clear
    begin
        -- if enable = '1' then  -- this appears incorrect
        --    qtxd <= data; 
        if reset = '1' then
            qtxd <= (others => '1'); -- outputs mark after reset 
        elsif rising_edge(clk_txd) then
            if  clear = '1' then     -- synchronous clear for start bit
                qtxd(0) <= '0';
            elsif enable = '1' then         -- synchronous load
                qtxd <= data;
            else
                qtxd <= '1' & qtxd(7 downto 1); -- shift right
            end if;
        end if;
    end process;

    -- the synchronous load prevents the first start bit from being stretched
    -- q(3 downto 0) the following in hex notation
    -- q(3 downto 0) = 2 is the start bit
    --               = 3 is data(0)
    --               ...
    --               = A is data(7)  
    --               = B is the stop bit
    --               = C - 1 are mark (idle) bits (q(3 downto 0) rolls over)
    --               = 1 enable occurs loading qtxd
    --
    -- The offset is caused by synchronous load (1 clk_txd) and the load point 
    -- (q(3 downto 0) = 1 in enable term).
    --
    -- The load point wants to occur in the first 6 counts of q(3 downto 0) to
    -- insure a trailing mark when q is stopped.
    --
    -- q(3 downto 0) = 1 is selected for enable to prevent spurious spaces 
    -- during reset from causing a receive framing error.

    txd <= qtxd(0);
    txdosc <= qtxd(0);

end architecture behavioral;

评论table:

-- the synchronous load prevents the first start bit from being stretched
-- q(3 downto 0) the following in hex notation
-- q(3 downto 0) = 2 is the start bit
--               = 3 is data(0)
--               ...
--               = A is data(7)  
--               = B is the stop bit
--               = C - 1 are mark (idle) bits (q(3 downto 0) rolls over)
--               = 1 enable occurs loading qtxd
--
-- The offset is caused by synchronous load (1 clk_txd) and the load point 
-- (q(3 downto 0) = 1 in enable term).
--
-- The load point wants to occur in the first 6 counts of q(3 downto 0) to
-- insure a trailing mark when q is stopped.
--
-- q(3 downto 0) = 1 is selected for enable to prevent spurious spaces 
-- during reset from causing a receive framing error.

告诉您在哪里可以找到数据位(q(7 到 0))所选字符。在下面的波形中,q 显示为十六进制以匹配:

您会发现修复后传输的第一个字符是 0x40,第二个是 0x41,...