-1

我正在尝试用 VHDL 创建一个 VGA 驱动程序。

我要 640x480 @ 60 Hz,所以我需要 25 MHz 和 31.5 KHz 时钟。divider_h过程由 50 MHz 时钟驱动,产生 25 MHz 时钟。在 25 MHz 时钟h_counter的每个刻度上,进程递增h_sync,当它达到某个值 ( H_FRONT + H_SYNC - 1) 时,divider_v进程被触发并clock_v在短时间内设置为 1。

Quartus II 的时序分析失败并出现警告:Can't meet minimum setup and hold requirements clock along 1 path(s)编译报告中的时钟保持部分指出,MSBv_counter是罪魁祸首,最小松弛时间为 -0.050 ns。第二低的松弛时间是 0.218 ns,这很好。

我尝试将 long 1 状态用于clock_v短暂的 0 状态,并且最小松弛时间已增加到 -0.019 ns,这仍然是不可接受的。

据我了解,保持时钟问题意味着输入在正确处理之前已更改,因此我尝试使 1 和 0 出现大致相同的时间段。令我惊讶的是,超过 40 条路径因相同的错误而变为红色。

注释掉v_clock进程可以解决问题。

为什么 MSB 的松弛时间比其他位高得多?我能做些什么来解决这个问题?

这是我的代码:

library ieee;
use ieee.std_logic_1164.all;

library Famikon;
use Famikon.Types.all;
use Famikon.VgaNames.all;


entity VgaDriver is
    port (
        -- inputs
        clock_50: in STD_LOGIC; -- 50 MHz
        reset: in bit; -- '1' resets
        r, g, b: in screen_pixel; -- bit_vector subtype

        -- outputs
        vga_x, vga_y: out hw_coord; -- integer subtype
        vga_drawing: out bit;
        hw_r, hw_g, hw_b: out hw_pixel; -- bit_vector subtype
        vga_hsync, vga_vsync: out bit;
        vga_blank, vga_clock: out bit
    );
end entity;


architecture Arch of VgaDriver is
    signal clock_h, clock_v: std_logic;
    signal h_counter, v_counter: vga_counter; -- integer subtype
    signal h_coord, v_coord: hw_coord; -- integer subtype
    signal h_active, v_active: bit;
begin

    -- some irrelevant code --

    h_active <= '1' when (h_counter >= H_BLANK) else '0';
    v_active <= '1' when (v_counter >= V_BLANK) else '0';


    divider_h: process (clock_50) begin
        if (rising_edge(clock_50)) then
            clock_h <= not clock_h;
        end if;
    end process;


    divider_v: process (h_counter) begin
        if (h_counter /= H_FRONT + H_SYNC - 1) then
            clock_v <= '0';
        else
            clock_v <= '1';
        end if;
    end process;


    h_clock: process (clock_h, reset) begin
        if (rising_edge(clock_h)) then
            if (reset = '1') then
                h_counter <= 0;
            elsif (h_counter = H_TOTAL - 1) then
                h_counter <= 0;
            else
                h_counter <= h_counter + 1;
            end if;
        end if;
    end process;


    v_clock: process (clock_v, reset) begin
        if (rising_edge(clock_v)) then
            if (reset = '1') then
                v_counter <= 0;
            elsif (v_counter = V_TOTAL - 1) then
                v_counter <= 0;
            else
                v_counter <= v_counter + 1;
            end if;
        end if;
    end process;


end architecture;

失败路径的详细信息:

  • 从:v_counter[9]
  • 至:v_counter[9]
  • 从时钟:clock
  • 时钟:clock
  • 所需的保持关系:0.000 ns
  • 所需最短 P2P 时间:0.618 ns
  • 实际缩短 P2P 时间:0.568 ns
  • 最小松弛:-0.050 ns

v_counter是一个bit_vector(9 downto 0)。当我在这条路径上使用Locate in Design时,Quartus 指向v_clock进程的第一行(一个带有rising_edge(clock_v))。

还有一个关于涟漪时钟的警告,其中提到了所有h_counterclock_h和三个门控时钟Equal0Equal0~1Equal0~0.

4

2 回答 2

2

因为您没有包含您的 Famikon 库包,所以有人必须查找VGA时序。(您的代码示例不是最小、完整和可验证的示例)。

为了让您的设计进行分析,Jenning 需要添加以下内容:

package Types is
    subtype screen_pixel is bit_vector (3 downto 0);
    subtype hw_coord     is natural range 0 to 1023;
    subtype hw_pixel     is bit_vector (3 downto 0);
    subtype vga_counter  is natural range 0 to 1023;
end package;
package VgaNames is
    constant H_FRONT:   natural range 0 to 1023 := 16;
    constant H_SYNC:    natural range 0 to 1023 := 96;
    constant H_BACK:    natural range 0 to 1023 := 48;
    constant H_BLANK:   natural range 0 to 1023 := H_FRONT + H_SYNC + H_BACK;
    constant H_VISIBLE: natural range 0 to 1023 := 640;
    constant H_TOTAL:   natural range 0 to 1023 := H_BLANK + H_VISIBLE;

    constant V_FRONT:   natural range 0 to 1023 := 10;
    constant V_SYNC:    natural range 0 to 1023 := 2;
    constant V_BACK:    natural range 0 to 1023 := 33;
    constant V_BLANK:   natural range 0 to 1023 := V_FRONT + V_SYNC + V_BACK;
    constant V_VISIBLE: natural range 0 to 1023 := 480;
    constant V_TOTAL:   natural range 0 to 1023 := V_BLANK + V_VISIBLE;
end package;

您还注意到,首先列出消隐间隔:

h_active <= '1' when (h_counter >= H_BLANK) else '0';
v_active <= '1' when (v_counter >= V_BLANK) else '0';

您要么对 vga_x 和 vga_y 进行偏移运算,要么使用单独的计数器。这可以通过在 h_counter 和 v_counter 的 0 处开始显示的非空白部分来解决。

然而,这不对 vcount[9] 设置时间问题负责。

还有其他问题。

divider_v: process (h_counter) begin
    if (h_counter /= H_FRONT + H_SYNC - 1) then
        clock_v <= '0';
    else
        clock_v <= '1';
    end if;
end process;

这看起来像是负责纹波时钟。您正在尝试通过组合逻辑生成 clock_v 。出于某些正当原因,您不应该这样做 - h_counter 位和用于识别比较的门之间可能存在非对称延迟,即使使用 LUT 也有多个级别的组合逻辑,并且路由延迟可能不匹配。您的clock_v 是否有效取决于布局的变幻莫测,并且它希望被注册(可能与clock_h 一起)。

回答者知道目标设备的供应商是谁以及实际的警告和错误消息非常有帮助。您可能会发现修复 clock_v 就足够了,但可能还不够,在这种情况下,我认为 Kevin 表示缺乏适当地约束您的设计。

最近在堆栈交换问题上出现了一种较旧的 VHDL VGA 时序生成器(请参阅VHDL VGA 同步电路)。它来自 Pong P. Chu 的书“FPGA PROTOTYPING BY VHDL Examples, XILINX SPARTAN-3 Version”的第 12 章,文件名为 list_ch12_01_vga_sync.vhd,可在作者的配套网站上的代码清单 zip 下载中找到。正如 Brian Drummond 指出的那样,对于那些不愿意使用编辑器搜索或打印输出的人来说,将水平和垂直计数器分布在三个流程语句中会更难阅读(原始设计规范的视图对于某些口味来说太平淡,并且似乎有太多流程)。

Brian 有一个额外的答案,它提供了一个组合过程的示例,并显示了垂直计数器计数器在水平计数器中的嵌套。它还显示从“0000000000”(0) 开始的非空白水平和垂直间隔。您可能会注意到 Brian 还评论了 h_active 和 v_active 未注册的等效信号。

于 2014-11-23T20:16:35.050 回答
2

如果您提供了失败路径的起点和终点,将会有所帮助。看起来主要问题是您正在生成不同的时钟并且存在跨域时序问题或建立不同域之间关系的不完整约束。大概reset来自clock_50域,不应该直接在其他两个域中使用。更强大的解决方案将使用具有同步使能的单个时钟来获得与clock_v和相同的效果clock_h

当需要生成时钟时,您不应尝试在 FPGA 架构中构建它们,而应使用板载 PLL/DLL 来管理它们。这样可以更好地控制域之间的偏差,并且时序分析器将更好地处理这种情况。每个域都需要有自己的复位信号,该信号与自己的时钟同步。

v_active注册并将h_active比较逻辑与它们输入的任何内容分开也是一个好主意。另外,您正在使用同步重置,因此无需将它们放在敏感度列表中。

于 2014-11-23T00:05:40.920 回答