VHDL VGA接口

VHDL VGA interface

我一直在DE0板子上建模一个VGA接口。我有以下型号的 640x480 显示器,刷新频率为 60Hz:

主要型号:

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

ENTITY VGA is 
  PORT (clk                     :   IN   std_logic; -- demo had 2 bit vector
        vga_hs, vga_vs          :   OUT std_logic;
        vga_r, vga_g, vga_b :   OUT std_logic_vector(3 DOWNTO 0));
END ENTITY VGA;

ARCHITECTURE A1 OF VGA IS
  SIGNAL rst, clk25 :   std_logic; -- rst only resets pixel clock 
BEGIN
  SYNC1  :  ENTITY work.sync(A1)
            PORT MAP (clk25, vga_hs, vga_vs, vga_r, vga_g, vga_b);
  CLK_25 :  ENTITY work.PLL(rtl)
            PORT MAP (clk, rst, clk25);
END ARCHITECTURE A1;

同步模型:

LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;

ENTITY SYNC IS
PORT(
     clk                :   IN  std_logic;
     h_sync, v_sync     :   OUT     std_logic;
     r, g, b            :   OUT std_logic_vector(3 DOWNTO 0)
     );
END ENTITY SYNC;

ARCHITECTURE A1 OF SYNC IS
  SIGNAL h_pos: integer RANGE 0 TO 800:=0;
  SIGNAL v_pos : integer RANGE 0 TO 520:=0;
BEGIN
  TIMING :PROCESS(clk) IS
BEGIN
    IF rising_edge(clk) THEN
        IF (h_pos = 480 or v_pos = 280)  THEN -- middle of the screen is pic res/2 + (FP + sync + BP)
            r <= (OTHERS => '1');
            g <= (OTHERS => '1');
            b <= (OTHERS => '1');
        ELSE
            r <= (OTHERS => '0');
            g <= (OTHERS => '0');
            b <= (OTHERS => '0');   
        END IF;

        IF (h_pos < 800) THEN
            h_pos <= h_pos + 1;
            ELSE
                h_pos <= 1;
            IF (v_pos < 520) THEN
                v_pos <= v_pos + 1;
                ELSE
                v_pos <= 1;
            END IF; 
         END IF;


    IF (h_pos > 16 and h_pos < 112 ) THEN -- H_POS between end of FP and the end of H_SYNC
        h_sync <= '0';   -- H_SYNC needs to stay low during display
    ELSE
        h_sync <= '1';
    END IF; 

    IF (v_pos > 8 and v_pos < 10 ) THEN --V_POS between end of FP and the end of V_SYNC
        v_sync <= '0';  -- V_SYNC needs to stay low during display
    ELSE
        v_sync <= '1';
    END IF;

    IF ((h_pos > 0 and h_pos < 160) or (v_pos > 0 and v_pos < 40 )) THEN--During all of SYNC i.e FP + SYNC + BP colour signals stay low
        r <= (OTHERS => '0');
        g <= (OTHERS => '0');
        b <= (OTHERS => '0');
    END IF;
  END IF;
END PROCESS TIMING; 
END ARCHITECTURE A1;
----------Amendments made to model 09/02 13:42----------

另一个直接实例化是从 Quartus II 生成的 PLL,它似乎工作正常。谢谢 Zilmer 先生 :)。该模型编译得很好。我将它加载到 DE0 中。然后将其连接到显示器,显示器上什么也看不到。它应该在屏幕中央显示一个十字。我使用的显示器是 1920x1080 的三星显示器。这会阻止我的模型显示任何东西吗?或者我在我的模型中犯了一个明显的错误。我更改了一些标准时序值以适应 60Hz 刷新率和 25Mz clk。 谢谢 D

您的实体同步 VHDL 代码未分析。您缺少 end if 并且 h_syncv_sync 的初始值违反了子类型约束:

    signal h_pos: integer range 1 to 800 := 0;
    signal v_pos: integer range 1 to 520 := 0;

其中 01 to 8001 to 520 的范围之外。

这提出了一个问题,即您是否有另一个用于实体同步的架构,或者同步是否只是未绑定的。其中任何一个都可能给你一个错误的指示(并且你的问题中没有证明错误)。

我们可以使用测试台来演示同步在 25 MHz 时钟的模拟中的作用:

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

entity sync is
    port (
        clk:               in  std_logic;
        h_sync, v_sync:    out std_logic;
        r, g, b:           out std_logic_vector(3 downto 0)
    );
end entity sync;

architecture a1 of sync is
    signal h_pos: integer range 1 to 800 := 1; -- was := 0;
    signal v_pos: integer range 1 to 520 := 1; -- was := 0;
begin
timing:  
    process (clk) is
        begin
        if rising_edge(clk) then
            if h_pos = 480 or v_pos = 280  then -- middle of the screen
                r <= (others => '1');
                g <= (others => '1');
                b <= (others => '1');
            else
                r <= (others => '0');
                g <= (others => '0');
                b <= (others => '0');   
            end if;

            if h_pos < 800 then
                h_pos <= h_pos + 1;
                else
                    h_pos <= 1;
                if v_pos < 520 then
                    v_pos <= v_pos + 1;
                    else
                    v_pos <= 1;
                end if; 
                if h_pos > 16 and h_pos < 112  then 
                    h_sync <= '0';   -- h_sync low during display
                else
                    h_sync <= '1';
                end if; 

                if v_pos > 8 and v_pos < 10  then 
                    v_sync <= '0';  -- v_sync low during display
                else
                    v_sync <= '1';
                end if;

                if (h_pos > 1 and h_pos < 160) or 
                   (v_pos > 1 and v_pos < 40 ) then -- black during blanking
                    r <= (others => '0');
                    g <= (others => '0');
                    b <= (others => '0');
                end if;
            end if;
        end if;  -- added misssing end if
    end process timing; 
end architecture a1;

library ieee;
use ieee.std_logic_1164.all;

entity sync_tb is
end entity;

architecture foo of sync_tb is
    signal clk:     std_logic := '0';
    signal h_sync:  std_logic;
    signal v_sync:  std_logic;
    signal r, g, b: std_logic_vector (3 downto 0);
begin
DUT:
    entity work.sync 
        port map (
            clk => clk,
            h_sync => h_sync,
            v_sync => v_sync,
            r => r,
            g => g,
            b => b
        );
CLOCK:
    process
    begin
        wait for 20 ns;  -- clock period 25 MHz = 40 ns;
        clk <= not clk;
        if now > 20 ms then  -- one frame time plus a bit
            wait;
        end if;
    end process;
end architecture;

现在我们来解决问题:

我们注意到的第一件事是 h_sync 是错误的。另请注意,v_sync 似乎约为 16.667 毫秒(1/60 秒)。

我们可以添加 h_pos 和 v_pos 计数器,这样我们就可以查看 h_sync,我们知道 h_pos 计数器是 运行 v_sync 我们可以在 60 Hz 的右邻域中看到。

所以我选择了错误的地方添加end if。更正也将操作计数器与其输出操作分开(h_sync、v_sync 和 r、g、b)。

timing:  
    process (clk) is
        begin
        if rising_edge(clk) then
            if h_pos = 480 or v_pos = 280  then -- middle of the screen
                r <= (others => '1');
                g <= (others => '1');
                b <= (others => '1');
            else
                r <= (others => '0');
                g <= (others => '0');
                b <= (others => '0');   
            end if;

            if h_pos < 800 then
                h_pos <= h_pos + 1;
            else
                h_pos <= 1;
                if v_pos < 520 then
                    v_pos <= v_pos + 1;
                else
                    v_pos <= 1;
                 end if;  
            end if;  -- separate the counters for what they produce
            -- HSYNC
            if h_pos > 16 and h_pos < 112  then 
                h_sync <= '0';   -- h_sync low during display
            else
                h_sync <= '1';
            end if; 
            -- VSYNC
            if v_pos > 8 and v_pos < 10  then 
                v_sync <= '0';  -- v_sync low during display
            else
                v_sync <= '1';
            end if;
            -- BLANKING
            if (h_pos > 1 and h_pos < 160) or 
               (v_pos > 1 and v_pos < 40 ) then
                r <= (others => '0');
                g <= (others => '0');
                b <= (others => '0');
            end if;
        end if;
    end process timing; 

我们现在 h_sync:

注意我们现在也可以看到垂直消隐间隔。

放大我们可以看到 v_pos 280:

处有一条水平白线

沿着 h_pos 480 (+1) 处的垂直线:

这看起来可能有用。

我可能想做的唯一设计更改是在 h_pos = 0 开始线条的可见部分,在 v_pos = 0 开始框架的可见部分。这将允许其他东西来寻址像素以写入帧缓冲区,而无需添加额外的像素计数器或不必进行偏移算术。 (对于 x 和 y 轴,像素寻址通常从 0 开始)。