首页 文章

VHDL初学者 - 这个电路的时序有什么问题?

提问于
浏览
3

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

我一直在为Nexys4板制作一个简单的BCD到7段显示驱动程序 - 这是我的VHDL代码( Headers 被剥离) .

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的电路(每个解码4位输入) . 这些ROM的输出被送入RTL_MUX,cur_anode的值被用作选择器 . RTL_MUX的输出提供cur_val寄存器;然后将cur_val和cur_anode寄存器链接到输出 .

那么,考虑到这一点,电路的哪个部分无法处理时钟速率?根据我的阅读,我觉得这与我可能需要添加的时序约束有关;我在想正确的方向吗?

3 回答

  • 2

    您的计时报告是否表明您有计时问题?在我看来,你只是非常快速地浏览分段值 . 无论您为更高的时钟速度设计得多好,每个时钟周期都会旋转 cur_anode ,因此您的显示会相应改变 . 如果您的时钟太快,显示器的变化将比人类能够读取的速度快得多 .

    其他一些建议:

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

    • cur_seg 上的初始化赢得't really do anything, as it'总是由您的进程驱动(组合) . 这不是问题 - 只是想确保你知道 .

  • 2

    那么这有两个部分 .

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

    通过从瞬态电流(段需要时间到上升)切换到稳态电流来增加段的亮度(当驱动段比其固有驱动频率慢时,电流会达到所需的水平) . 因此亮度增加 .

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

  • 2

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

    带有ANODE环形计数器和多个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 for digit 将得到优化,增量器小于全加器 .

相关问题