Daniel Hall
Daniel Hall

Reputation: 854

VHDL VGA interface

I've been modelling a VGA interface on the DE0 board. I have the following model for a 640x480 display which refreshes at 60Hz:

Main model:

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;

Sync model:

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----------

The other direct instantiation is for PLL generated from Quartus II it seems to work fine.. thank you Mr Zilmer :). The model compiles fine. I load it into the DE0. Then connect this to a monitor and get nothing on the display. It should display a cross in the centre of the screen. The display I am using is a Samsung with 1920x1080. Would this stop my model from displaying anything? Or have I made a obvious mistake in my model. I have changed some of the standard timing values to fit a 60Hz refresh with 25Mz clk. Thanks D

Upvotes: 0

Views: 803

Answers (1)

user1155120
user1155120

Reputation:

Your VHDL code for entity sync doesn't analyze. You are missing an end if and your initial values for h_sync and v_sync violate the subtype constraint:

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

Where 0 is outside the bounds of 1 to 800 or 1 to 520.

This raises the question of whether or not you have another architecture for entity sync, or whether sync is simply unbound. Either of which might give you an incorrect indication (and the error isn't demonstrated in your question).

We can use a testbench to demonstrate what sync does in simulation with a 25 MHz clock:

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;

And now we get to troubleshoot:

sync_tb_1.png

The first thing we notice is h_sync is wrong. Also note that v_sync appears to be around 16.667 ms (1/60th of a second).

We can add the h_pos and v_pos counters so we can look at h_sync, we do know the h_pos counter is running to get a v_sync we can see in the right neighborhood of 60 Hz.

So I picked the wrong place to add the end if. Correcting that also separates operating the counters from operating on their outputs (h_sync, v_sync and 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; 

We have h_sync now:

sync_tb_2.png

Notice we can see the vertical blanking interval now too.

Zooming in we can see there a horizontal white line at v_pos 280:

sync_tb_3.png

Along with a vertical line at h_pos 480 (+1):

sync_tb_4.png

And this looks like it just might work.

The only design change I might be tempted to make would be to start the visible portion of the line at h_pos = 0 and the the visible portion of the frame at v_pos = 0. This would allow something else to address pixels for writing in a frame buffer without adding additional pixel counters or having to do offset arithmetic. (Pixel addressing usually starts at 0 for x and y axis).

Upvotes: 4

Related Questions