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