VGA Image Sync - red-bote/VHDL_Demos GitHub Wiki

This demo expands on the previous guide that discussed extracting data from bitmap a file to generate an image on a VGA display.

In this guide the image is extracted from the bitmap data and converted to VHDL by a conversion utility hex2rom. The synchronization of the image with the scan signal is compensated in this project by registering the video control signals.

Converting Test Image

hex2rom is distributed with the T80 CPU core from OpenCores.org but possibly found other places.

Compile with gcc and copy into $PATH. If there's a compile error try adding header for cstring

hex2rom.cpp:630:8: error: ‘strcmp’ was not declared in this scope
  630 |    if (strcmp(argv[1], "-b"))

< #include <cstring>

The bitmap file is converted to 32-bit to facilitate import into VHDL:

The bitmap file has a 70-byte information header that is stripped off so that just the RGB data is converted to a VHDL file. The file attributes of image size and data format are assumed and the header would interfere with aligning the data correctly into 32-bit words in the VHDL file.

# strip .bmp header and write to intermediate file
dd if=rgb.bmp  bs=1 skip=70 of=rgb.dat
# convert to a VHDL file
hex2rom -b rgb.dat bmp_rom   10l32s  >  rtl_project.srcs/sources_1/new/bitmap.vhd

The arguments used with hex2rom are:

-b option is specified the file is read as a binary file

The format string has the format AEDOS where:
  A = Address bits (10)
  E = Endianness, l or b (l)
  D = Data bits (32)
  O = ROM type: (s) for synchronous ROM

Verification of Mode Timings

The following table is based on Tiny VGA 640 x 480 @ 60 Hz but the times are measured in simulation and the length in pixels calculated based on a VGA clock frequency of 25 Mhz.

On the Basys 3 board, the VGA pixel clock period for 640x480@60Hz is 40 ns (100Mhz / 4 or 10ns * 4) which approximates correct frequency of 25.175 MHz.

To measure a time interval in Vivado simulation, click wave trace at measurement start point, hold shift and then click wave trace at end point.

Horizontal Timing

Scanline Part Time Pixels Comment
Visible 25.6 us 640 25.6us / 0.04us = 640
Fp 640.0 ns 16 640ns / 40ns = 16
Bp 1920.0 ns 48 1920ns / 40ns = 48
Sync Pulse 3.84 us 96 3.84us / .04us = 96
1 Line 32.0 us 800 32us / 0.040us = 800

Vertical Timing

Frame Part Time Lines Comment
Visible 16.8 ms 525 16800us / 32us = 525
Fp 320.0 us 10 320.0us / 32us = 10 (Fp starts on line 479 at RE of column 0)
Bp 1056.0 us 33 1056u / 32us = 33 (Bp ends on line 479 at FE of column 639)
Sync Pulse 64.0 us 2 64.0us / 32.0us = 2

Synchronizing VGA Display to Data

Analysis of the video signal timing in the previous guide showed that when data is displayed from a synchronous memory the data is delayed by a clock cycle. The new CRT controller module registers hsync and vsync to delay the video frame synchronization by 1 pixel clock cycle to compensate for the clock cyle taken for reading the pixel value from memory.

entity crtc is
    Port ( clk_vga : in STD_LOGIC;
           reset_l : in STD_LOGIC;
           o_video_on : out STD_LOGIC;
           o_hsync : out STD_LOGIC;
           o_vsync : out STD_LOGIC;
           o_red : out STD_LOGIC_VECTOR (3 downto 0);
           o_green : out STD_LOGIC_VECTOR (3 downto 0);
           o_blue : out STD_LOGIC_VECTOR (3 downto 0));
end crtc;

architecture Behavioral of crtc is
    signal vga_col : integer;
    signal vga_row : integer;
    signal hsync : std_logic;
    signal vsync : std_logic;
    signal video_on : std_logic;
    signal r_hsync : std_logic;
    signal r_vsync : std_logic;
    signal r_video_on : std_logic;
    signal RGB : std_logic_vector(23 downto 0);
begin

    u_vga_control : entity work.vga_controller
        GENERIC map (
            h_pulse  => 96,
            h_bp     => 48,
            h_pixels => 640,
            h_fp     => 16,
            h_pol    => '0',
            v_pulse  => 2,
            v_bp     => 33,
            v_pixels => 480,
            v_fp     => 10,
            v_pol    => '0')
        port map(
            pixel_clk => clk_vga, -- 25 Mhz
            reset_n => reset_l,
            h_sync => hsync,
            v_sync => vsync,
            disp_ena => video_on,
            column => vga_col, -- pixel_x,
            row => vga_row, -- pixel_y,
            n_blank => open,
            n_sync => open
        );

    u_image_gen_rom : entity work.rom_image
    port map(
        --disp_ena => disp_ena,
        clk => clk_vga, 
        row => vga_row,
        column => vga_col,
        red => RGB(23 downto 16),
        green => RGB(15 downto 8),
        blue => RGB(7 downto 0)
    );

    -- register the control signals to sync them with the RAM data
    p_video_sync : process(clk_vga)
    begin
        if rising_edge(clk_vga) then
            r_video_on <= video_on;
            r_hsync <= hsync;
            r_vsync <= vsync;
        end if;
    end process p_video_sync;

    -- gate the video data from the ROM only if within a valid image time
    p_video_enable : process(r_video_on, RGB)
    begin
        if r_video_on = '1' then
            o_red <= RGB(23 downto 20);
            o_green <= RGB(15 downto 12);
            o_blue <= RGB(7 downto 4);
        else
            o_red <= (others => '0');
            o_green <= (others => '0');
            o_blue <= (others => '0');
        end if;
    end process p_video_enable;

    o_video_on <= r_video_on;
    o_hsync <= r_hsync;
    o_vsync <= r_vsync;

end Behavioral;
⚠️ **GitHub.com Fallback** ⚠️