VGA Tile Map - red-bote/VHDL_Demos GitHub Wiki
Display an image using alpha-numeric characters, demonstrates a video RAM using tile-mapping.
Image Preparation
A bitmap image is loaded into a block memory simulating a tile-RAM. The data does not generate individual colored pixels but instead is used as indices into a character-generator ROM. The character ROM should also have 256 elements. An 8-bit bitmap is scaled to 80 x 60 pixels to fit a VGA display (640/8 x 480/8 tiles). Used Windows Paint for this, tried converting from GIMP but it converted to 4-bit index color and wouldn't convert to 8-bit color for some reason.
- Clip source image to 960 x 720 (1.33 aspect ratio)
- Scale image to 80 x 60
- Flip vertical (bitmap files encode image data upside-down)
- Save as 256 color bitmap
Determine the data offset (4 byte word at 000Ah)
0000000 42 4d f6 16 00 00 00 00 00 00 36 04 00 00 28 0
file
command may give this info. This one has a header length of 0x0436==1078
$ file redbote_80x60_8bpp_test.bmp
redbote_80x60_8bpp_test.bmp: PC bitmap, Windows 3.x format, 80 x 60 x 8, image size 4800, cbSize 5878, bits offset 1078
An 8-bit bitmap formatted this way uses an 8-bit index into a color-table of 256*4==1024 bytes. Color table offset is 0x36 (54) so 1024+54==1078.
Convert bitmap file to binary and then to VHDL. Note skip argument to dd
. If skip is not used and bitmap data includes header, the VHDL code must have logic to read past the bitmap header using the offset determined in previous step.
dd if=redbote_80x60_8bpp_test.bmp of=tempvram.bin bs=1 skip=1077 > tempvram.bin
hex2rom -b tempvram.bin char_vram 14l8s > vga_tilemap.srcs/sources_1/imports/vga/char_vram.vhd
Clocks
The mmcm clock is really slow to simulate for some reason. The VGA clock is derived from a clock divider. The clock is implemented by a 4-bit up-counter with synchronous reset. An unsigned adder is imported from xstug_examples.
begin
u_adder : entity work.adders_1
generic map (
WIDTH => 4)
port map (
A => "0001",
B => count_reg,
SUM => adder_out
);
p_clock_reg : process(C)
begin
if rising_edge(C) then
if CLR = '1' then
count_reg <= "0000";
else
count_reg <= adder_out;
end if;
end if;
end process p_clock_reg;
Q <= count_reg;
end Behavioral;
With the input clock of 100 Mhz, the clock divider bit-1 is tapped for divide-by-4 yielding a 25 Mhz reference suitable for the VGA display timing 640 x 480 @ 60Hz.
u_clk : entity work.clock_div
port map (
C => clk,
CLR => reset,
Q => clk_cnt -- out std_logic_vector(3 downto 0)
);
vga_clk <= clk_cnt(1);
If the synthesis schematic is opened it can be seen that the tool has implemented a BUFG on the clock divider output to stabilize the signal.
Character ROM
Character Generator provides a demonstration of a CRT controller with a character ROM. Synchronization of signals and data also applies here.
Tile RAM Address Generator
A dual-port Block RAM stores the character codes to be displayed. The address generator converts the raster scan location into the corresponding address in the video RAM. A workable approach is to implement the address calculation in a VHDL process with explicit multiplication and division:
process (pixel_r, pixel_c)
variable v_addr : integer;
begin
v_addr := pixel_r / 8 * 80 + pixel_c / 8; -- 80 column wide display
address <= std_logic_vector(to_unsigned(v_addr, address'length));
end process;
The synthesis result exhibits a fairly elaborate circuit that includes a DSP block.
Tile RAM Video RAM
A simulated tile-RAM is created with the static image data encode in the ROM.
Prepare the test image and instantiate the VRAM.
u_vram : entity work.char_vram
port map (
Clk => clk,
A => vram_addr,-- in std_logic_vector(13 downto 0);
D => vram_data -- out std_logic_vector(7 downto 0)
);
Create new VRAM address generator module:
entity vram_addr_gen is
Port ( clk : in STD_LOGIC;
row : in STD_LOGIC_VECTOR (9 downto 0);
col : in STD_LOGIC_VECTOR (9 downto 0);
address : out STD_LOGIC_VECTOR (13 downto 0));
end vram_addr_gen;
Tile RAM Operation
The components of the tile-mapped display are:
- VRAM address generator: calculate tile address from current raster-scan coordinate
- Tile RAM: video RAM organized as array of codes corresponding to the character tile in every tile location on the display
- Character generator ROM: bitmaps of each tile that can be displayed
- Pixel shift generator: selects from the character ROM the pixel that should appear at the current raster-scan coordinate