VGA Bitmap - red-bote/VHDL_Demos GitHub Wiki
This presentation uses the example from rams_20c.vhd to load image data from a .bmp file and initialize the memory array of the BRAM.
Image File Preparation
The .bmp file is converted to the data-file format, which is a 32-bit binary series represented as readable ASCII.
See previous presentations of VGA sync controller and loading image data from external file.
A bitmap file can be encoded as 32-bit RGBx (24-bits + 8-bits alpha channel). Normally the image attributes and size are extracted from the bitmap header, but to simplify things the header is ignored. Header size can be expected 70 bytes. To confirm the example bitmap of 32x16 RGBx using shell commands:
$ ls -l rgb.bmp
-rwxrwxr-x 1 xubuntu xubuntu 2118 Dec 4 19:34 rgb.bmp
$ file rgb.bmp
rgb.bmp: PC bitmap, Adobe Photoshop with alpha channel mask, 32 x 16 x 32
$ echo 2118-32*16*4 | bc -l
70
If imagemagick is installed, the identify
command is available to provide information about the file.
The following command reads the bitmap file, skips the header and dumps the image data to the rams data file in the expected format:
dd if=rgb.bmp bs=1 skip=70 | xxd -b -c 4 | cut -c11-45 | sed 's/\ //g' \
> vga_bitmap.srcs/sources_1/imports/rams/rams_20c.data
$ head vga_bitmap.srcs/sources_1/imports/rams/rams_20c.data
11111111111111111111111100000000
11111111111111111111111100000000
11111111111111111111111100000000
11111111111111111111111100000000
00000000000000001111111100000000
00000000000000001111111100000000
00000000000000001111111100000000
00000000000000001111111100000000
00000000000000001111111100000000
00000000000000001111111100000000
Note: requires Reset Behavioral Simulation or Reset Synthesis Run in order to completely "rebuild" the circuit so that alterations to the external data are propagated into the data array of the RAM.
Pixel Generator
The hw_image_generator.vhd file from the VGA tutorial is modified with a crude address generator that interfaces with the data ROM and displays the image tile in a fixed location.
- pixel_clk added as input port to entity
- pix_address and pix_data signals added to architecture
- p_pix_address process provides the address to clock data out of the RAM
- u_vram added to generate pix_data
- red green and blue signals from pix_data
Note pix_address is 6-bits which is the size of the ROM (64-bytes) so only 2 lines of the 32x16 pixel image are stored.
ENTITY hw_image_generator IS
GENERIC(
pixels_y : INTEGER := 16; --vertical extent of image
pixels_x : INTEGER := 16); --horizontal extent of image
PORT(
pixel_clk : IN STD_LOGIC;
.
.
.
END hw_image_generator;
ARCHITECTURE behavior OF hw_image_generator IS
signal pix_address : unsigned(5 downto 0) := (others => '0'); -- initialize for simulation
signal pix_data : std_logic_vector(31 downto 0);
signal column_r : unsigned(9 downto 0);
BEGIN
p_pix_address : process(pixel_clk, row, column)
begin
if rising_edge(pixel_clk) then
if(row < pixels_y AND column < pixels_x) THEN
pix_address <= pix_address + 1;
elsif (row >= pixels_y AND column >= pixels_x) OR (row = 0 AND column = 0) then
pix_address <= (others => '0');
end if;
column_r <= to_unsigned(column, column_r'length);
end if;
end process p_pix_address;
u_vram : entity work.rams_20c
port map(
clk => pixel_clk,
we => '0',
addr => std_logic_vector(pix_address),
din => (others => '0'),
dout => pix_data);
PROCESS(disp_ena, row, column_r, pix_data)
BEGIN
IF(disp_ena = '1') THEN --display time
-- IF(row < pixels_y AND column < pixels_x) THEN
IF(row < pixels_y AND column_r < pixels_x) THEN
red <= pix_data(15 downto 8);
green <= pix_data(23 downto 16);
blue <= pix_data(31 downto 24);
ELSE
red <= (OTHERS => '0');
green <= (OTHERS => '0');
blue <= (OTHERS => '0');
END IF;
ELSE --blanking time
red <= (OTHERS => '0');
green <= (OTHERS => '0');
blue <= (OTHERS => '0');
END IF;
END PROCESS;
END behavior;
Simulation and Timing Analysis
Test image size 16 pixels wide with columns colored for identification in simulation trace.
Simulation trace shows relationship of data to the column address. Data from ROM is delayed 1 clock cycle relative to expect column for display.
Registered column_r delays the column signal by 1 clock.