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