发送16个字符串的VHDL uart

时间:2017-02-07 13:54:12

标签: vhdl xilinx

我必须在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;

它发送的数据与我在转码器中完全没有关联,并且真的不知道为什么。你有任何想法我的代码有什么问题,或者你有任何不同的例子如何发送自己的16个字符与uart?我想我的计数器或比较器出了问题。

- 编辑

为了你的努力,我现在无法在Xilinx上尝试你的代码,而是在我的大学工作。我看到你在我的代码中做了很多改动。当然首先我尝试像你展示的那样做,我希望这是可以接受的,但我可能必须根据这张图片用转码器来做。
enter image description here

从上次我改变了我的代码

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,它可以与你正在谈论的这个计数器连接。

有我的模拟效果。我对这种方式感到不满,因为我没有足够的技能来自己做这件事。我希望你能帮助我重建我的项目。在模拟它看起来不错,我不知道它在Xilinx上的样子。

enter image description here

1 个答案:

答案 0 :(得分:1)

  
    

你能告诉我一段代码吗? - 斯特凡

  

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

这是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位,数据(q)(0)...数据(q(7),停止位。使能或ldqtxd将与停止位同时发生。

只有一个可观察的回退到这个实现,如果在移位寄存器中的值未完成加载时复位,则会导致接收器出现帧错误。 davl变为false后,请勿将其重置10次。

模拟以9600波特clk_txd显示,字符背靠背。

它的触发器数量少于原始触发器(忽略k)。没有与QTxD( - 8 FF)和bdcnt(+ 4)加davl(+ 1)分开的数据寄存器。有两个比较(优化为两个)bdcnt = 9,q =,/ = 9.这些可以单独表示,因此在合成过程中不需要优化。

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

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

运行测试平台给出:

uartbyjackob_tb_foo.png

其中有一个添加的信号datalut,用于显示在ldqtxd之后移出的值。

更改后制作q计数器(7 downto 0)

我们仍然从波形中看到它不起作用。

这是由启用和移位寄存器引起的。

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

要传输的数据的顺序是空格(起始位),数据(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;

评论表:

-- 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 downto 0))。在下面的波形中,q显示为十六进制以匹配:

uart2_fixed_8bit_shift_reg.png

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