VHDL(Xilinx 工具链)我被 "array trimming" 吓坏了

VHDL (Xilinx toolchain) I'm being scuppered by "array trimming"

我有一个包含两个文件的 VHDL 项目,我遇到了初学者的困难。

它占用系统时钟并使用30位时钟分频器(其中我只使用了少量不连续的位)来驱动原始串口模块(仅输出TX)模块吐槽周期性输出 8 位字符。

似乎在合成过程中,优化器去除了很多重要的信号,这是我没想到的。

顶级文件"Glue.vhd"...

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

entity Glue is
    port(
        clk : in std_logic;
        tx  : out std_logic;
        LED : out std_logic_vector(1 downto 0)
        );
end entity Glue;

architecture behavioural of Glue is
    signal divider : unsigned(29 downto 0);
begin
    LED(1) <= '0';

    ser_tx : entity SerialTX
    port map (
            baud_clk => divider(12),
            byte_to_transmit => std_ulogic_vector(divider(29 downto 22)),
            poke => divider(20),
            busy => LED(0),
            serial_out => tx
            );

    clocker : process(clk)
    begin
        IF(rising_edge(clk)) then
            divider <= divider + 1;
        END IF;
    end process clocker;
end architecture behavioural;

SerialTX.vhd

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity SerialTX is
    port (
        baud_clk         : in  std_logic;
        byte_to_transmit : in  std_ulogic_vector(7 downto 0); --the byte that we want to transmit
        poke             : in  std_logic;                     --a rising edge causes the byte to be sent out
        busy             : out std_logic;                     --wait for this to go low before transmiting more data
        serial_out       : out std_logic                      --the RS232 serial signal
        );
end SerialTX;

architecture behavioural of SerialTX is
    signal bit_buf                : unsigned(9 downto 0); --(STOP bit) & (8 data bits) & (START bit)
    signal internal_busy          : std_logic;
    shared variable bit_counter   : integer range 0 to 10;
begin
    busy <= internal_busy;

    busy_handler : process(poke) is
    begin
        if(rising_edge(poke)) then
            internal_busy <= '1';
        end if;
        if(bit_counter = 0) then
            internal_busy <= '0';
        end if;
    end process busy_handler;

    do_transmit : process(baud_clk) is
    begin
        if(rising_edge(baud_clk)) then 
            if((internal_busy = '1') and (bit_counter = 0)) then
                bit_counter := 10;
                bit_buf <= unsigned('1' & byte_to_transmit & '0');
            end if;

            serial_out <= bit_buf(0);
            bit_buf <= bit_buf srl 1;
            bit_counter := bit_counter - 1;
        end if;
    end process do_transmit;
end behavioural;

合成过程中的警告(请注意没有错误)如下...

WARNING:Xst:647 - Input <byte_to_transmit> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:1710 - FF/Latch <bit_buf_9> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_8> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_7> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_6> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_5> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_4> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_3> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_2> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_1> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_0> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <serial_out> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:2404 -  FFs/Latches <bit_buf<9:0>> (without init value) have a constant value of 0 in block <SerialTX>.
WARNING:Xst:1710 - FF/Latch <serial_out> (without init value) has a constant value of 0 in block <SerialTX>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:2677 - Node <divider_21> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_22> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_23> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_24> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_25> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_26> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_27> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_28> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_29> of sequential type is unconnected in block <Glue>.
WARNING:Route:455 - CLK Net:divider<20> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.
WARNING:Route:455 - CLK Net:divider<12> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.

我已经通过源代码中的连接进行了跟踪,但我找不到我正在犯的错误。我感觉我遗漏了一些作业中没有涉及的 edge/corner 个案例。

标记为“(没有初始值)”的项目我试图通过给它们默认值来纠正但无济于事。标记为"unconnected in block "的让人眼花缭乱

我必须怎么做才能满足合成器?

虽然 user1155120 在您应该指定库或进行组件绑定的意义上是正确的,但您的工具显然找到了正确的实体,因为这是您的逻辑正在优化的地方。

首先,不要使用

use IEEE.numeric_std.all;
use ieee.std_logic_unsigned.all;

在一起。您应该使用 numeric_std 除非使用遗留代码。

真正的问题有两个:

  • 你没有初始化你的计数器变量,它不正确计数。
  • 您的输入数据永远不会进入并行加载移位寄存器,因此会被修剪。

您希望计数器的起始值是多少? IIRC,未初始化的整数类型将采用其范围内的最低有效值。然而,在任何情况下最好明确开始。然而,实际上 FPGA 中没有整数——只有冷硬触发器。 0 到 10 的范围至少使用 4 位,因此使用 4 个触发器。但是,从 0 到“-1”将下溢并环绕到 15 而不是您可能期望的 10。 internal_busy 旨在成为触发器但也未初始化。我说是故意的,因为它实际上是由 bit_counter 异步重置的。这种异步重置本身是有问题的,几乎可以肯定不是你想要的:

busy_handler : process(poke) is -- <-- wrong sensitivity list
begin
    if(rising_edge(poke)) then
        internal_busy <= '1';
    end if;
    if(bit_counter = 0) then   -- <-- not clocked
        internal_busy <= '0';
    end if;
end process busy_handler;

您混合了时钟代码和组合代码。您的模拟将关闭,因为虽然 internal_busy 的安宁在硬件中是并行的,但您的模拟工具将仅显示 poke.

变化的更新
-- can never happen, because bit_counter = 0 means internal_busy is forced to '0'
if((internal_busy = '1') and (bit_counter = 0)) then
    bit_counter := 10;
    bit_buf <= unsigned('1' & byte_to_transmit & '0');
end if;

如果没有这个逻辑,bit_buf 确实会是 9'h000(Xilinx 工具假设的值——您没有给出任何值!)。这就是以下几行试图告诉您的内容:

WARNING:Xst:1710 - FF/Latch <bit_buf_9> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_8> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_7> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_6> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_5> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_4> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_3> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_2> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_1> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_0> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1710 - FF/Latch <serial_out> (without init value) has a constant value of 0 in block <SerialTX>. This FF/Latch will be trimmed during the optimization process.
  1. bit_buf_9 始终为零,将被硬零(逻辑或结构)替换。因此它的触发器是多余的,可以修剪。
  2. bit_buf_{8..0} 被确定为等同于其他一些触发器(此处:bit_buf_9)。但是,那个人字拖被修剪了,因此,这些人字拖也将被修剪。
  3. bit_buf_9 供稿 serial_out,所以它也被修剪了。

其余错误信息:

WARNING:Xst:647 - Input <byte_to_transmit> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:2677 - Node <divider_21> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_22> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_23> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_24> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_25> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_26> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_27> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_28> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_29> of sequential type is unconnected in block <Glue>.
  1. byte_to_transmit 未使用且不是顶级块的一部分,不会被保留。
  2. divider(29 downto 22) 连接到 byte_to_transmit。由于该端口将被删除,因此可以优化掉最后使用的触发器 divider(20) 之后的所有触发器。

时钟警告:

WARNING:Route:455 - CLK Net:divider<20> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.
WARNING:Route:455 - CLK Net:divider<12> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.
  1. divider(20)divider(12)用作时钟。
  2. divider(20)divider(12) 由逻辑生成:一个或多个切片内的 LUT 和触发器。根据 FPGA 的不同,有一些可以反馈到时钟路由资源。
  3. 在您的情况下,工具没有找到在时钟路由资源上路由这些信号的方法,并且必须使用通常为逻辑信号保留的资源,因此偏移可能过大。

可以通过 1. 为两个模块使用公共时钟 clk 和 2. 使用 pokebaud_clk 作为时钟启用来避免这些时钟错误:

clk_en_p: process (clk) is
begin

if (rising_edge(clk)) then
    if (clk_en = '1') then
        my_signal <= assignment;
    end if;
end if;
end process clk_en_p;

总而言之,您应该按照 Brian Drummond 的建议编写测试平台并在综合之前模拟您的设计。

在编写实现之前编写部分测试平台通常会有所帮助,因为这会迫使您在实际编写之前考虑组件的接口以及您期望它如何反应。

您可以模拟您的设计,它处于复杂性的边缘,比猜测它在 FPGA 中综合和加载时为什么不起作用要容易。也可以做成simpler.

可以去掉 bit_counter 因为你有一个 10 位的 bit_buf 移位寄存器。

这样做的方式是,您设置一个默认模式,该模式被识别为 'empty',并且该模式是由于移出 bit_buf 而构建的。

除了以 'left' 移入“0”移出的人工产物外,不会出现该模式。当停止位位于最右边的位置(“0000000001”)时,移位寄存器移入“0”并停止。右边位置的'1'保持传输空闲标记。

空闲模式的想法来自于您对 srl 的使用,它在调试您的原始设计时注意到,它的左边填充了“0”。 'learning' 你为什么这样设计的增量变化的数量变得令人望而却步。它导致查看基本原理并描述基于您的实现。

为了开始模拟您的设计,对 Glue 进行了一些更改,包括分频的初始值,以便增量起作用,并移动 poke 和 byte_to_transmit 的分频器分接器,以将模拟时间缩短为以下为 40 毫秒的邻域。

SerialTx 的实例化使用选定的名称,模拟器不隐式包含 use work.all; 上下文项,有时在综合工具中提供。

使用添加的测试平台标记您的设计,没有 bit_counter:

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

entity SerialTX is
    port (
        baud_clk:          in  std_logic;
        byte_to_transmit:  in  std_logic_vector(7 downto 0); -- before -2008
        poke:              in  std_logic;
        busy:              out std_logic;
        serial_out:        out std_logic
    );
end entity SerialTX;

architecture foo of SerialTX is
    -- signal bit_buf:         unsigned(9 downto 0);
    constant BB_IDLE:       std_logic_vector (9 downto 0) := "0000000001";
    signal bit_buf:         std_logic_vector (9 downto 0) := BB_IDLE;
    -- signal internal_busy:   std_logic;
    signal poke_reg:        std_logic_vector (0 to 2) := "000";
    signal start_tx:        std_logic;

    -- shared variable bit_counter:    integer range 0 to 10;
begin
    -- busy <= internal_busy;
poke_filt:
    process (baud_clk)  -- translate poke to baud_clk domain
    begin
        if rising_edge (baud_clk) then
            poke_reg <= poke & poke_reg (0 to 1);
        end if;
    end process;

    -- front edge of poke in baud_clk_domain:
    start_tx <= poke_reg(0) and poke_reg(1) and not poke_reg(2);

-- busy_handler:
--     process (poke) is
--     begin
--         if rising_edge (poke) then
--             internal_busy <= '1';
--         end if;
--         if bit_counter = 0 then
--             internal_busy <= '0';
--         end if;
--     end process busy_handler;

busy_handler:  
    process (baud_clk)
    begin
        if rising_edge (baud_clk) then
            if start_tx = '1' and bit_buf = BB_IDLE then
                busy <= '1';
            elsif bit_buf = BB_IDLE then
                busy <= '0';
            end if;
        end if;
    end process;

do_transmit:
    process (baud_clk)
    begin
        if rising_edge(baud_clk) then 
            if start_tx = '1'  and bit_buf = BB_IDLE then
                bit_buf <=  '1' & byte_to_transmit & '0';
            elsif bit_buf /= BB_IDLE then
                -- bit_buf <= bit_buf srl 1; 
                -- srl UNDEFINED in package std_logic_1164
                bit_buf <= '0' & bit_buf(9 downto 1); -- shift right one
            end if;                                   -- zero fill
        end if;
    end process;

-- do_transmit:
--     process (baud_clk)
--     begin
--         if rising_edge(baud_clk) then
--             if internal_busy = '1' and bit_counter = 0 then
--                 bit_counter := 10;
--                 bit_buf <= unsigned ('1' & byte_to_transmit & '0');
--             end if;
--             serial_out <= bit_buf(0);
--             bit_buf <= bit_buf srl 1;
--             bit_counter := bit_counter - 1;
--         end if;
--     end process do_transmit;

    serial_out <= bit_buf(0); -- ADDED, no 11th flip flop

end architecture;

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

entity Glue is
    port (
        clk:  in  std_logic;
        tx:   out std_logic;
        LED:  out std_logic_vector(1 downto 0)
    );
end entity Glue;

architecture behavioural of Glue is
    signal divider:  unsigned(29 downto 0) := (others => '0'); -- init val
begin
    LED(1) <= '0';

ser_tx:  
    entity work.SerialTX  -- ADDED work prefix to make selected name
        port map (
            baud_clk => divider(12),
         -- byte_to_transmit => std_ulogic_vector(divider(29 downto 22)),
            byte_to_transmit => std_logic_vector(divider(25 downto 18)),
            poke => divider(17), -- WAS divider(20), for simulation
            busy => LED(0),
            serial_out => tx
        );
clocker:  
    process (clk)
    begin
        if rising_edge(clk) then
            divider <= divider + 1;
        end if;
    end process clocker;
end architecture behavioural;

library ieee;
use ieee.std_logic_1164.all;

entity glue_tb is
end entity;

architecture fum of glue_tb is
    signal clk:     std_logic := '0';
    signal tx:      std_logic;
    signal led:     std_logic_vector(1 downto 0);
begin
DUT:
    entity work.glue
        port map (
            clk => clk,
            tx => tx,
            led => led
        );
CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        if now > 40 ms then
            wait;
        end if;
    end process;
end architecture;

运行 40 毫秒给出:

byte_to_transmit 值 05 的特写:

您可能注意到 srl 未在包 std_logic_1164 中提供,并且已被替换为使用 std_ulogic_vector,std_logic_vector 在 -2008 年成为它的子类型。

poke 被过滤到 baud_clk 域并生成一个时钟周期事件,将 internal_busy 设置为“1”,随后在空闲时设置为“0”在 bit_buf.

中再次检测到模式

注意 busy 为 10 个时钟(起始位、8 个数据位和停止位)并且 serial_out 的第 11 个触发器已被丢弃。

这是使用 ghdl, binaries can be downloaded here. The waveform displays are from gtkwave 模拟的。两者均根据 GPL 获得许可。

VHDL 具有一组特定的语言结构,用于推断时序逻辑 - 存储器、寄存器和锁存器,是可用于综合的语言子集的一部分。符合条件的 VHDL 的超集或子集通常由综合供应商提供支持,这些供应商可能还包括基于其目标平台芯片的限制。资格和支持的结构可在综合供应商文档中找到。 VHDL 语言本身是在 IEEE Std 1076-2008 中定义的,尽管仅对 -1993 或 -2002 修订版提供普遍支持。

您会发现其中一件事是共享变量已成为受保护的类型,可以通过方法进行访问,确保对共享资源的独占访问,并且偶然变得没有资格进行综合。

Am I right in thinking that your 3-bit poke_reg system means that the poke high pulse duration must be longer than 3 baud_clk edges in order to be correctly detected? Is it possible to make the poke signal edge-triggered? – Wossname 13 hours ago

将适用于短于或等于三个 baud_clk 间隔的戳(发送)事件。

在SerialTx中不用busy(internal_busy),实际传输时只覆盖baud_clk个周期(bauds)的间隔。

可以创建一个安全的忙信号(两个触发器的异或)并且在 poke(发送)上升沿和 internal_busy 结束之间的整个间隔内工作:

architecture fum of SerialTX is
    constant BB_IDLE:       std_logic_vector (9 downto 0) := "0000000001";
    signal bit_buf:         std_logic_vector (9 downto 0) := BB_IDLE;
    signal poke_event:      std_logic := '0'; -- Added
    signal internal_busy:   std_logic := '0'; -- Re-Added
    signal poke_reg:        std_logic_vector (0 to 1) := "00";
    signal start_tx:        std_logic;
    signal end_event:       std_logic := '0'; -- Added
begin

    busy <= poke_event xor end_event;  -- ADDED, was FF output

pokeevent:
    process (poke)
    begin
        if rising_edge(poke) then
            poke_event <= not poke_event;
        end if;
    end process;
poke_edge:
    process (baud_clk)  -- translate poke to baud_clk domain
    begin
        if rising_edge (baud_clk) then
            poke_reg <= poke_event & poke_reg (0);
        end if;
    end process;

    -- front edge of poke in baud_clk_domain:
    start_tx <= poke_reg(0) xor poke_reg(1); -- CHANGED, when not equal

endevent:
    process (baud_clk)
    begin
        if rising_edge (baud_clk) then
            if internal_busy = '1' and bit_buf = BB_IDLE then
                end_event <= not end_event;
            end if;
        end if;
    end process;

busy_handler:  -- CHANGED
    process (baud_clk)
    begin
        if rising_edge (baud_clk) then
            if start_tx = '1' and bit_buf = BB_IDLE then
                internal_busy <= '1';
            elsif bit_buf = BB_IDLE then
                internal_busy <= '0';
            end if;
        end if;
    end process;

do_transmit:
    process (baud_clk)
    begin
        if rising_edge(baud_clk) then 
            if start_tx = '1'  and bit_buf = BB_IDLE then
                bit_buf <=  '1' & byte_to_transmit & '0';
            elsif bit_buf /= BB_IDLE then
                bit_buf <= '0' & bit_buf(9 downto 1);
            end if;
        end if;
    end process;

    serial_out <= bit_buf(0);

end architecture;

这给出:

注意internal_busy信号用于判断busy后沿何时出现(区分bit_buf中的idle pattern是否发送)。

poke信号已通过修改Glue缩短,证明poke(发送)的宽度可以是任意的:

architecture behavioural of Glue is
    signal divider:  unsigned(29 downto 0) := (others => '0'); -- init val
    signal poke:    std_logic := '0';  -- ADDED
begin
    LED(1) <= '0';

    poke <= divider(17), '0' after 10 us;  -- ADDED
ser_tx:  
    entity work.SerialTX  -- ADDED work prefix to make selected name
        port map (
            baud_clk => divider(12),
            -- byte_to_transmit => std_ulogic_vector(divider(29 downto 22)),
            byte_to_transmit => std_logic_vector(divider(25 downto 18)),-- sim
            poke => poke, -- WAS divider(20), for simulation
            busy => LED(0),
            serial_out => tx
        );

(不,表示新信号 poke 上单次触发的复杂波形不符合合成条件。)