VGA 控制信号如何在 Verilog/HDL 中工作?

How do VGA control signals work in Verilog/HDL?

我是一个FPGA初学者,对VGA的控制方式不太理解 信号与VGA交互,如何正确生成VGA控制 更复杂规格的信号(使用 Verilog):

  1. 貌似水平同步和垂直同步信号的低频部分 对应于从一行到下一行的过渡,以及过渡 分别从右下角到左上角。但 那些低信号是 VGA "controlled" 吗?做那些低信号 "tell"VGA切换到下一行还是跳回起点?

  2. 后门廊和前门廊是否满足设置时间和 保持时间?仅在 VGA 时序 table 中给出门廊的持续时间 所需的最小值(即我可以将门廊设置得比 在时间给定值 table?)

  3. 这是最让我困惑的:从两个连续的 水平和垂直同步信号,VGA 到底是怎么知道的 (或者我知道)它在屏幕上显示的确切 point 吗?如果计时 是唯一的因素,这是否意味着我必须非常注意 完美匹配 RPG 控制信号和水平同步和垂直同步信号?如果 答案是肯定的,那么我的问题就更多了,我只能 以我的作业为例进行描述:

在这个作业中,我们使用FPGA(Spartan 入门板)的内置50MHz 时钟,VGA 的像素指定为800x600。帧率指定为 72Hz。

VGA时序table给出了以下同步信号结构:

水平同步: | 0---后沿(104)---103 | 104---显示(800)---903 | 904---前廊(16)---919 | 920__pulse宽度(120)__1039|

垂直同步: | 0---后廊(23)---22 | 23---显示(600)---622 | 623---前廊(37)---659 | 660__pulse宽度(6)__665|

这似乎意味着对于屏幕上一行中的每个像素,我花费 1 时钟周期(20ns)就可以了。但是,对于许多复杂的情况,我会 想要根据像素在屏幕上的位置来控制像素的颜色 也许还有很多附加条件,例如 当前位置与另一个位置、某些状态等。现在,如果像素的位置取决于水平和垂直像素计数器,并且颜色进一步取决于位置,我如何及时匹配它们?另外,如果我的逻辑必须花费超过 20ns 来决定像素的颜色怎么办?这会完全破坏 VGA 屏幕上的图像吗?

实际上我已经完成了作业,但我发现我的代码很乱 我完全不知道它为什么起作用:

例如,作业的一部分要求我们向星星展示 每 0.5 秒改变一次颜色。我的实现看起来像:

//----------------pixel counters----------------
always@(posedge CLK or posedge RESET) begin
    if(RESET) h_count <= 11'd 0;
    else if (h_count >= 11'd 1039) h_count <= 11'd 0;
    else h_count <= h_count + 11'd 1;
end

always@(posedge CLK or posedge RESET) begin
    if(RESET) v_count <= 10'd 0;
    else if (v_count >= 10'd 665) v_count <= 10'd 0;
    else if (h_count == 11'd 1039) v_count <= v_count + 10'd 1;
    else v_count <= v_count;
end

//----------------h- and v- sync----------------
always@(posedge CLK or posedge RESET) begin 
  if(RESET) VGA_HSYNC <= 1'b 0;
  else VGA_HSYNC <= (h_count >= 11'd 0) & (h_count <= 11'd 919);
end

always@(posedge CLK or posedge RESET) begin 
  if(RESET) VGA_VSYNC <= 1'b 0;
  else VGA_VSYNC <= (v_count >= 10'd 0) & (v_count <= 10'd 659);
end

//----------------a frame counter and a flag----------------
always@(posedge CLK or posedge RESET) begin
    if(RESET) frame_count <= 6'd 0;
    else if (frame_count >= 6'd 49) frame_count <= 6'd 0;
    else if(v_count == 10'd 665) frame_count <= frame_count + 6'd 1;
    else frame_count <= frame_count;
end

always@(posedge CLK or posedge RESET) begin
    if(RESET) color_flag <= 1'd 0;
    else if (frame_count == 6'd 49) color_flag <= ~(color_flag);
    else color_flag <= color_flag;
end

//----------------RGB control----------------
always@(posedge CLK or posedge RESET) begin
    if(RESET) VGA_RGB <=  3'b 000;
    else if(display) begin //display is high when counters in valid range
        casez({tree, star, snow})  //these are outputs from submodules that decides the "shapes" of objects on screen
            3'b ??1: VGA_RGB <= 3'b 111;  //white snow
            3'b ?10: VGA_RGB <= (color_flag) ? (3'b 110) : (3'b 111);
                //switching between yellow and white
            3'b 100: VGA_RGB <= 3'b 010;  //green tree
            default: VGA_RGB <= 3'b 001;  //blue background
    endcase
  end
  else VGA_RGB <= 3'b 000;  //for transitions
end

在我看来,我的h_count和v_count直接决定了我的VGA_HSYNC和VGA_VSYNC。但是我的VGA_RGB至少依赖于color_flag,进一步依赖于frame_count,而frame_count又依赖于h_count和v_count。这不应该导致几个时钟周期的延迟吗?该代码是可综合的,并且确实生成了我想要显示的内容。但是我的 VGA_RGB 到底是如何及时与 VGA_HSYNC 和 VGA_VSYNC 同步的???我是不是想多了这个问题?还是我只是幸运?我错过了什么?

您的大部分问题都可以用以下事实来解释:VGA 是一种模拟标准,最初设计用于 运行 模拟设备。 h-sync 和 v-sync 脉冲简单地触发它们各自的偏转线圈回到它们的起始位置,而前廊和后廊说明了光束在屏幕的可见部分之外开始和结束的事实。

关于准确性,它比以前更好:在 years-gone-by 中向模拟显示器发送定时错误的 VGA 信号实际上可能会损坏它。现在,LCD 等的驱动电路能够以任意刷新率检测和显示各种分辨率,这一切都发生了变化。根据我的经验,尽管您拥有的余地比您想象的要小,并且在很大程度上取决于显示设备的品牌和型号……他们中的许多人甚至在他们的用户手册中列出了略有不同的时间!如果您想确保您的电路能够在您的测试仪 运行 所在的设备上正常工作,那么您应该尽可能准确地坚持 "official" 分辨率之一。

就 per-pixel 时序而言,800x600x72Hz 信号需要 50MHz 时钟,因此您有 20nS 的时间来获取像素数据(也就是说我不是 100% 确定您的同步和门廊时间是正确的,它们与 VGA Timings 处的时间不同,我自己过去曾成功使用过)。如果 20nS 还不够,那么您需要使用多个电路,例如一个用于奇数像素,另一个用于偶数。或者,您可以实施管道阶段,例如对于您在周期 X 输出的像素,您在周期 X-1 从 ROM 读取它的值,并计算要在周期 X-2 读取的地址。我过去使用的另一个技巧是 double-buffer 使用 2 行 RAM 显示,即在任何给定的行上,您直接从一个缓冲区中绘制,同时写入下一个缓冲区;这使您有更多时间来渲染每一行,但通常会增加渲染和显示之间单独时钟域的额外复杂性。