【问题标题】:VHDL beginner - what's going wrong wrt to timing in this circuit?VHDL初学者 - 这个电路的时序出了什么问题?
【发布时间】:2014-04-25 14:03:09
【问题描述】:

我对 VHDL 和硬件设计非常陌生,想知道是否有人可以告诉我我对遇到的以下问题的理解是否正确。

我一直在为 Nexys4 板开发一个简单的 BCD-to-7 段显示驱动程序——这是我的 VHDL 代码(去掉了标题)。

entity BCDTo7SegDriver is
    Port ( CLK : in STD_LOGIC;
           VAL : in STD_LOGIC_VECTOR (31 downto 0);
           ANODE : out STD_LOGIC_VECTOR (7 downto 0);
           SEGMENT : out STD_LOGIC_VECTOR (6 downto 0));

   function BCD_TO_DEC7(bcd : std_logic_vector(3 downto 0))
       return std_logic_vector is
   begin
       case bcd is
           when "0000" => return "1000000";
           when "0001" => return "1111001";
           when "0010" => return "0100100";
           when "0011" => return "0110000";
           when others => return "1111111";
       end case;
   end BCD_TO_DEC7;
end BCDTo7SegDriver;

architecture Behavioral of BCDTo7SegDriver is
    signal cur_val : std_logic_vector(31 downto 0);
    signal cur_anode : unsigned(7 downto 0) := "11111101";
    signal cur_seg : std_logic_vector(6 downto 0) := "0000001";
begin

process (CLK, VAL, cur_anode, cur_seg)
begin
    if rising_edge(CLK) then
        cur_val <= VAL;
        cur_anode <= cur_anode rol 1;
        ANODE <= std_logic_vector(cur_anode);
        SEGMENT <= cur_seg;
    end if;

    -- Decode segments
    case cur_anode is
        when "11111110" => cur_seg <= BCD_TO_DEC7(cur_val(3 downto 0));
        when "11111101" => cur_seg <= BCD_TO_DEC7(cur_val(7 downto 4));
        when "11111011" => cur_seg <= BCD_TO_DEC7(cur_val(11 downto 8));
        when "11110111" => cur_seg <= BCD_TO_DEC7(cur_val(15 downto 12));
        when "11101111" => cur_seg <= BCD_TO_DEC7(cur_val(19 downto 16));
        when "11011111" => cur_seg <= BCD_TO_DEC7(cur_val(23 downto 20));
        when "10111111" => cur_seg <= BCD_TO_DEC7(cur_val(27 downto 24));
        when "01111111" => cur_seg <= BCD_TO_DEC7(cur_val(31 downto 28));
        when others => cur_seg <= "0011111";
    end case;
end process;
end Behavioral;

现在,起初我尝试从约束文件中定义的板时钟天真地驱动这个电路:

## Clock signal
##Bank = 35, Pin name = IO_L12P_T1_MRCC_35,                 Sch name = CLK100MHZ
set_property PACKAGE_PIN E3 [get_ports clk]                         
    set_property IOSTANDARD LVCMOS33 [get_ports clk]
    create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]

这让我在七段显示器上看起来几乎是垃圾输出 - 看起来每个解码的数字都被叠加到每个数字位置。基本上,如果正在解码的值的第 3 位到第 0 位是“0001”,则显示器连续显示 8 个 1,而不是 00000001(但不完全是 - 其他段被点亮但看起来更暗)。

将时钟放慢到更合理的值就可以了,电路按照我的预期工作。

当我查看详细说明给我的信息时(我使用的是 Vivado 2014.1),它给了我一个电路,其中 VAL 连接到 8 个并行 RTL_ROM(每个 RTL_ROM 解码 4 位输入)。这些 ROM 的输出被馈送到 RTL_MUX,cur_anode 的值被用作选择器。 RTL_MUX 的输出馈送到 cur_val 寄存器;然后将 cur_val 和 cur_anode 寄存器链接到输出。

那么,考虑到这一点,电路的哪个部分无法处理时钟速率?根据我的阅读,我觉得这与我可能需要添加的时间限制有关;我的想法是否正确?

【问题讨论】:

    标签: vhdl fpga


    【解决方案1】:

    您的计时报告是否表明您有计时问题?在我看来,您只是在极快地滚动细分值。无论您如何设计更高的时钟速度,您都会在每个时钟周期旋转cur_anode,因此您的显示将相应地改变。如果你的时钟太快,显示的变化会比人类阅读的快得多。

    其他一些建议:

    • 您应该将单个进程拆分为单独的时钟和非时钟进程。并不是说你正在做的事情最终不会合成(显然),而是它非常规,并且可能会导致意想不到的结果。

    • 您在cur_seg 上的初始化不会真正做任何事情,因为它总是由您的进程驱动(组合地)。这不是问题 - 只是想确保您知道。

    【讨论】:

    • 事实上,输出信号的变化速度会比电路板自身传输到显示器的速度更快。用示波器探测显示器的引脚可能会很有教育意义。
    • @Yann:我在计时报告中看到以下内容,这些消息是否相关? 6. checking no_output_delay --------------------------- There are 15 ports with no output delay specified. (HIGH) There are 0 ports with no output delay but user has a false path constraint There are 0 ports with no output delay but has a clock definition.
    • 不是真的;输出延迟用于微调相对于时钟的反应,特别是满足外部电路的保持要求。在这种情况下您不需要它,因为显示中没有状态;充其量您可以将其用于亚微秒级调整,以将线路中的延迟与显示器相匹配。与显示器的合理切换速率相比,转换时间非常快(粗略的经验法则,眼睛可以感知 400Hz;在这种情况下,任何超过 400Hz*阳极*2=6.4kHz 的东西都是射频干扰功率浪费),所以所有的基于 FPGA 的时序噪声很小。
    • “您应该将您的单个进程拆分为单独的时钟和非时钟进程。并不是说您正在做的事情最终不会合成(显然),而是它非常规......”我不同意,这根本不是非常规的。我和其他许多人一直这样做。 (充其量)有 50:50 的分摊比例是“最佳”
    • @Martin:很有趣——我很少看到一个进程有上面写的时钟部分和组合部分。这样做有什么好处?
    【解决方案2】:

    这有两个部分。

    您的分段显得如此模糊,因为您基本上是以 1/8 占空比运行它们的速度比分段反应时间要快(您更改的每个时钟脉冲都点亮了哪个分段,然后您停止驱动它在下一个脉冲)。

    通过从瞬态电流(段需要时间来加速)切换到稳态电流(当您以低于其固有驱动频率的速度驱动段时,更长的时间段可让电流达到所需水平,从而增加段变亮的时间) )。因此亮度增加。

    关于您的代码的另一件事。您可能知道这一点,但是当您在那里锁定时钟时,标记为 cur_anode 的变量是高级的,实际上代表 NEXT 阳极。您还可以分别将 ANODE 和 SEGMENT 锁存到当前的阳极和段。只是指出 cur_anode 可能是用词不当(并且令人困惑,因为它通常是 NEXT 的)。

    【讨论】:

    • 啊,亮度对我来说很有意义。我在电路上看到的真正让我困惑的是,当我运行快速时钟版本并将其输入值 1 时,而不是在显示屏上显示“00000001”(即使是微弱的),它显示的是“11111111”;就好像一个输出分布在多个时钟周期上。当我放慢时钟时,对电路使用相同的输入时,我会在显示屏上看到预期的“00000001”。
    • 还有,忘了说,谢谢解答!我将更深入地了解时间报告。它提到了一些关于具有未指定输出延迟的引脚的事情。我还有一些工作要做(但这很有趣,所以耶):)
    • 这与其说是关于 FPGA 时序报告,不如说是驱动分段的延迟。举个例子,拿一个 LED 并使用电路板驱动它(通过晶体管)。编写一些简短的代码,允许您控制 FPGA 的占空比和频率输出。通过一些高频和一些低频工作。低频(任意尝试 30Hz->15k)是 LED 可以响应的。除此之外,电路的性质会阻止更高的频率(LED 无法反应),并且您的 LED 会变暗或反应不规律。它的行为或多或少类似于低通滤波器。
    【解决方案3】:

    请记住 Paul Seeb 和 fru1bat 对时钟速度的回答、Paul 对 NEXT 阳极的评论、fru1bat 关于分离时钟和非时钟进程的建议以及您注意到您有 8 个 ROM,还有其他架构。

    带有用于阳极和多个 ROM 的环形计数器的架构恰好是速度的最佳选择,因为不需要 Paul 和 fru1bat 说明。相反,您可以针对区域进行优化。

    因为时钟速度要么是外部的,要么是通过添加定期提供的使能来控制的,因此在区域优化中没有解决:

    architecture foo of BCDTo7SegDriver is
        signal digit:   natural range 0 to 7;            -- 3 bit binary counter
        signal bcd:     std_logic_vector (3 downto 0);   -- input to ROM
    begin
    
    UNLABELED:
        process (CLK) 
        begin
            if rising_edge(CLK) then
    
                if digit = 7 then       -- integer/unsigned "+" result range 
                    digit <= 0;         -- not tied to digit range in simulation
                else
                    digit <= digit + 1;
                end if;
    
            SEGMENT_REG:
                SEGMENT <= BCD_TO_DEC7(bcd);  -- single ROM look up
    
            ANODE_REG:
                for i in ANODE'range loop
                    if digit = i then
                        ANODE(i) <= '0';
                    else
                        ANODE(i) <= '1';
                    end if;
                end loop;
            end if;        
        end process;
    
    BCD_MUX:    
        with digit select 
            bcd <= VAL(3 downto 0)   when 0,
                   VAL(7 downto 4)   when 1,
                   VAL(11 downto 8)  when 2,
                   VAL(15 downto 12) when 3,
                   VAL(19 downto 16) when 4,
                   VAL(23 downto 20) when 5,
                   VAL(27 downto 24) when 6,
                   VAL(31 downto 28) when 7;
    
    end architecture;
    

    这用一个 32 位寄存器 (cur_val)、一个 8 位环形计数器 (cur_anode) 和函数 BCD_TO_DEC7 隐含的 ROM 的七个副本换取一个三位二进制计数器。

    事实上,关于您是否应该使用单独的顺序(时钟)和组合(非时钟)进程的争论有点让人想起 Liliput 和 Blefuscu 为 Endian-ness 开战。

    由于不共享敏感度列表,单独的进程通常执行效率更高。您还可以注意到,所有并发语句都有进程或块语句等效项。在这个设计中也没有任何东西可以特别利用变量来实现更有效的模拟,同时暗示一个单一的过程。 (XST 不支持共享变量)。

    我还没有验证这是否会合成,但在阅读了 XST 用户指南的 14.1 版本后认为应该可以。如果不是,您可以将 digit 转换为长度为 3 的 std_logic_vector。

    digit+ 1 将得到优化,增量器小于全加器。

    【讨论】:

    • 谢谢!关于时钟或非时钟分离,当我进入更复杂的设计时,我正在考虑转向像这样的 2-process 风格,从阅读这篇文章来看似乎是有道理的:http://www.gaisler.com/doc/vhdl2proc.pdf
    猜你喜欢
    • 2020-10-17
    • 2022-11-14
    • 2015-11-22
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 2018-06-14
    相关资源
    最近更新 更多