Skip to main content

VGA Video card - Updated specifications

·8 mins

In my previous post on the VGA card I covered the specifications of the video display processor as it was at the time, the text, bitmap, tile and sprite modes with hardware accelerated drawing. Since then the design has been through some internal refactoring and I’ve managed to add a few new features along the way, so it’s time to update the specifications.

The original FPGA design had grown organically as features were added and was starting to strain the Spartan-6, using around 99% of the slice LUTs on the device. Rather than trying to keep patching it, I did a staged refactor of the whole design. Working out where the resources were being used and adding a cleaner modular VHDL base with well defined interfaces between the blocks, explicit clock domain crossings, a proper SRAM arbiter, and each subsystem owning its own registers. The same board and the same feature set but a much cleaner codebase that leaves room to expand and a better starting point for a redesign of the board.

What’s changed since the last post #

The main changes between the previous version and what’s running now:

  • Higher output resolutions. The card is no longer fixed at 640x480. Text and bitmap modes can now output at 640x480, 800x600 or 1024x768, all at 60Hz.
  • Text mode scales with resolution. The original 80x30 and 80x60 modes now extend up to 128x48 and 128x96 characters at 1024x768. Text cells have also moved from block RAM into video SRAM, accessed through a base pointer.
  • A new Atari ST style planar bitmap mode. Alongside the existing RGB565 and chunky palette modes there is now a 4-plane 16-colour planar format, matching the ST’s low resolution bitmap layout. This was added trying to get EmuTOS running better and will be a separate post with more information on that.
  • One unified tile engine. The four fixed tile layers (two 16x16, two 8x8) have been merged into a single shared tile-line engine. All four layers now have an identical register layout and each one can be configured at runtime for 8x8 or 16x16 tiles.
  • Programmable tile map sizes. Tile maps are no longer fixed at 512x512 pixels — each layer can select a map width and height of 32, 64, 128 or 256 tiles independently.
  • Programmable layer priority and transparency. Layer ordering used to be fixed with layers drawn 1-4 in sequence. Each layer now has a 4-bit compositor priority and its own programmable transparent palette index, so layers can be reordered freely and slotted between sprites.
  • Per-column Y scrolling. Alongside the existing per-line X scroll, each tile layer can now enable a per-column vertical scroll table — every tile column gets its own Y offset, Mega Drive style, for column waves and vertical parallax effects.
  • A better drawing engine. Draw commands now go through a command FIFO so the CPU can queue up work without waiting for each primitive to finish. The engine gained burst writes to SRAM, XOR and transparent draw modes, a pattern RAM, a clipping window, and a 1-bpp pattern expansion command for fast monochrome-to-colour blits (very useful for font rendering in EmuTOS).
  • A hardware cursor sprite. A dedicated cursor overlay, separate from the text cursor and the sprite engine, so the mouse pointer no longer costs a sprite or a redraw.
  • Programmable line interrupt. The card can raise an interrupt at a chosen scanline, in addition to the vblank interrupt.
  • Scanline effects and text overlay. For a retro CRT feel the output can darken alternate lines (black, 50% or 25% brightness), and text mode can be overlaid on top of bitmap or tile output.

There were some challenges and surprising wins when implementing this. Splitting the registers into a more modular system helped a little but making many of them write-only freed up some resources. Most were read/write from early development as a way to verify contents and generally were not used for reading.

The drawing mode was cleaned up, there was a lot of repeated code so it was unified where possible. The initial attempt at a command FIFO for the drawing mode was using a FIFO based in distributed RAM as it required a 171-bit width to store the commands. This meant that it was stored in distributed RAM and took too many resources so dropped the triangle rendering to see how it worked. A surprising change here was moving the FIFO into BRAM rather than distributed RAM freed up a lot of resource and allowed the triangle drawing code. Surprising as I didn’t expect the FPGA to be able to optimize a wide BRAM so well. That change also allowed the FIFO depth to be increased to 256.

A real big win in resources and a factor that allowed extending the bitmap modes was the tile rendering. There was 4 instances of the tile engine code being created, two fixed at 16x16 and two at 8x8. These have now been merged into a single instance that handles all 4 tile layers. It also had the benefit of making the tile layers more flexible, each tile layer can now be selectable as 16x16 or 8x8 in size. While the combined tile renderer is more complex, it only takes roughly half the resources of the 4 instance version.

100x37 text mode
Demonstration of the new higher resolution text modes

Updated Specifications #

Here are the specifications for the updated version of the card.

Text Mode #

Text mode renders at the selected physical resolution with an 8x16 or 8x8 font:

Output resolution 8x16 font 8x8 font
640x480 80x30 80x60
800x600 100x37 100x75
1024x768 128x48 128x96
  • Hardware cursor
  • Hardware controlled text blinking
  • 16 foreground colours from a user defined palette
  • 16/8 background colours — only 8 available when blink mode is enabled, otherwise the full 16
  • Custom 8x16 or 8x8 fonts loaded through the font RAM registers
  • Text cells stored in video SRAM through a base pointer, so multiple text screens can be kept in memory and switched instantly

Palette support #

There is a 1024-entry RGB565 palette shared by the bitmap, tile, sprite and text paths. Each mode selects a bank within the full palette range, allowing a wide range of colours across layers.

Bitmap mode #

  • Full-resolution mode at 640x480, 800x600 or 1024x768
  • Low-resolution pixel-doubled mode at 320x240, 400x300 or 512x384
  • RGB565 mode (16-bit, 65,536 colours)
  • Palette modes
    • 8-bpp 256 colours
    • 4-bpp 16 colours
    • 2-bpp 4 colours
    • 1-bpp 2 colours
    • 4-plane planar 16 colours (Atari ST format)
  • Sprites can be displayed over bitmap modes
  • Framebuffer pointer latched at vblank for tear-free double buffering
  • Per-line X scroll through the line-scroll table

The one bandwidth limit: 1024x768 at full resolution can’t sustain 16-bpp, so the hardware clamps that combination to 8-bpp.

The planar mode stores each group of 16 pixels as four consecutive 16-bit plane words, exactly matching the Atari ST’s bitmap layout — which means ST screen data can be copied straight into VRAM and displayed.

Tile Mode #

There are 4 tile layers, all driven by a single shared tile-line engine. Every layer has the same capabilities and register layout:

  • Independent enable per layer
  • Runtime selection of 8x8 or 16x16 tiles per layer
  • Programmable map size per layer — width and height each selectable from 32, 64, 128 or 256 tiles
  • Independent tile graphics and tile map base pointers
  • Independent per-pixel X/Y scrolling
  • Per-line X scroll from a shared or per-layer line-scroll table
  • Per-column Y scroll from a per-layer table — each tile column (8 or 16 pixels wide) can carry its own vertical offset, in the style of the Mega Drive’s column scroll
  • 16 colours per tile, selected from 8 16-colour palettes via the tile map entry
  • Selectable palette bank per layer from 8 palette banks
  • Tile flipping on both X and Y axes
  • Programmable transparent palette index per layer
  • Programmable 4-bit compositor priority per layer — layers and sprites mix in any order

Tilemap output currently runs in the 320x240 logical pipeline and is displayed through 640x480 timing.

Sprites #

  • 256 sprites available
  • 16x16 pixel sprites, 4-bpp
  • 48 visible sprites per scanline
  • Flipping on X and Y axis
  • 4-bit per-sprite priority, composited against the tile layer priorities
  • 16 colours per sprite, index 0 always transparent
  • 16 palette banks for 256 sprite colours
  • Sprites can be displayed over bitmap or tilemap output, operating in 320x240 resolution

Hardware cursor #

A dedicated hardware cursor sprite sits above everything else — a small 3-colour pattern with its own position registers, separate from both the text cursor and the sprite engine. It exists so a mouse pointer costs nothing: no sprite slot used and no software redraw needed.

Hardware accelerated drawing #

The drawing engine operates directly on VRAM in RGB565 format. Commands are pushed into a hardware FIFO, so the CPU can queue a batch of primitives and carry on with other work while the engine drains the queue.

Drawing operations:

  • Filled rectangle
  • Bresenham line drawing (with fast horizontal and vertical paths)
  • Filled triangle
  • 1-bpp pattern expansion — expands monochrome bitmaps to two colours, ideal for fast text and glyph rendering

Memory operations:

  • Memory fill
  • Linear memory copy
  • 2D memory copy / blit

Draw modes and state:

  • Solid, XOR and transparent draw modes
  • 16-bit repeating patterns and a dedicated pattern RAM
  • Programmable clipping window

Interrupts and effects #

  • Vertical blank interrupt
  • Programmable line interrupt — raised at the start of a chosen scanline, for raster split effects and timing without polling
  • Scanline effect modes — alternate lines black, 50% or 25% brightness for a CRT look
  • Text overlay mode — text composited over bitmap or tilemap output, handy for HUDs and debug output
  • Output blanking with sync kept active

What’s next #

The Spartan-6 has been pushed about as far as it makes sense to. The current architecture was deliberately structured so its types and interfaces can widen rather than restructure, and the next step is a version 2 board: a Spartan-7 with 32-bit SRAM, 8-8-8 RGB output and HDMI. That will be a clean start on new hardware — but everything above now forms a solid, well organised foundation to build it from.