S3ESK VGA Core
Tips on using VGA in the CS hardware labs
- The '4:3 in Wide' button on the lab monitors can be used to stop the image stretching to an incorrect aspect ratio.
- If the image flickers or does not line up correctly with the edges of the screen, try using the 'Auto' button while the display is filled with non-black graphics that touch all four edges of the screen (the test images should work for this).
- If the monitor complains that the resolution is unsupported, double check that your pixel clock value matches the resolution you've selected (see the table below). The lab monitors should correctly identify the three valid resolutions available with the core as: 640x480@61Hz, 800x600@60Hz and 1024x768@60Hz.
Adding the core to your design
- First, copy the 'embs_vga_v1_00_a' folder from the code repository/archive to the 'pcores' directory within the root of your XPS project.
- Load your project in XPS
- Select Project | Rescan User Repositories.
- Select the 'IP Catalog' tab on the left-hand side, and expand 'Project Local PCores' then 'EMBS'. You should see a component called 'VGA Core'.
- Double click this component and answer 'Yes' then 'OK' (the sections below on resolution and pixel width explain how the settings here can be changed, but for now the defaults are fine).
- Select the 'System Assembly View' tab then 'Bus Interfaces'.
- Find the
embs_vga_0
component and expand it by clicking the + - It has two bus connections, MPLB (master PLB) and SPLB (slave PLB). Use the drop down boxes to connect these both to
mb_plb
. - Click the 'Addresses' tab and click the 'Generate Addresses' button. (A yellow icon on its own in the top right)
- Click the 'Ports' tab and expand the
embs_vga_0
component by clicking the + - Select the
Red
,Green
,Blue
,HSync
andVSync
connections (notPixel_Clk
), right click and select 'Make External' from the menu. The 'Connected Port' column for each signal should automatically fill in with a value starting withExternal Ports::embs_vga_0_
followed by the signal name and_pin
. This connects the five VGA signals to external ports (it routes them to outside the FPGA). - Now select Hardware | Launch Clock Wizard
- A dialogue may appear telling you "The following clock ports are editable...". Click OK.
- Find the
embs_vga_0
component and set itsPixel_Clk
frequency to 40.000000 MHz (you may have to type this if it is not in the drop-down list). This value will be different if a different output resolution is chosen (see below). - Click Validate Clocks. It should say "Clock Validations were successful!".
- Click OK. Click OK on the "Please make sure..." box that appears.
We have finished connecting up the VGA component inside the FPGA.
The final thing we need to do it to tell the tools which external pins on the FPGA to use for the Red
, Green
, Blue
, HSync
and VSync
signals, and to relax the timing constraints between the PLB and pixel clocks (so the static timing analysis phase doesn't moan at us).
- Open your project's User Constraints File by double-clicking 'UCF File' in the 'Project Files' list of the 'Project' window in XPS (alternatively, open
<your XPS project>/data/system.ucf
in a text editor). - Scroll to the bottom of the file and copy and paste the following into it:
- The
NET
constraints tell the tools where each signal should be routed and the logic level type to use; e.g. connectembs_vga_0_Red_pin
to pinH14
on the FPGA chip, which is routed on the board to the red pin of the VGA socket, and use TTL (5V) logic signals. - The
TIMESPEC
constraint tells to tools to allow up to 10ns of difference between the bus and pixel clock domains used by the core, which run at different speeds - this is fine as the data is buffered using asynchronous FIFOs within the core.
- The
- Save and close the UCF file.
- Click Hardware | Generate Bitstream.
- Re-export your design to SDK.
- In SDK, right click on your BSP and select Board Support Package Settings.
- Select Drivers.
embs_vga_0
should be in the list. If it is not, close and re-open SDK. - Set the driver of
embs_vga_0
togeneric
and click OK.
Core options
The following options can be changed from the peripheral settings in XPS (accessed by double clicking the instance of the core in your project).
Output resolution
The EMBS VGA core supports three display resolutions: 640x480, 800x600 and 1024x768, all running at approximately 60Hz (i.e. the image is refreshed 60 times per second). The resolution must be set in XPS before synthesis. In order to change the resolution of the core, the desired value must be selected and the correct clock speed for 'Pixel_Clk' set in the Clock Wizard.
Clock speeds are:
Resolution | Pxl_Clk Frequency (MHz) |
---|---|
640x480 | 25.000000 |
800x600 | 40.000000 |
1024x768 | 65.000000 |
If you have the wrong combination of clock speed and resolution, your project will either fail to meet timing constraints during synthesis or the monitor will complain that the VGA signal is unsupported.
Pixel data width in DDR
The core can be set to read either 4 or 8 bits from memory for each pixel (i.e. two or one pixels per byte). This does not affect the image output (as each pixel always has only 3 bits of data), but does affect how data should be written to the frame buffer from the MicroBlaze. Keeping the core set to 8 bits-per-pixel is the easiest option, as each pixel can be individually written in DDR. If changed to 4 bits-per-pixel, two pixels (one byte) must be written at the same time (or a byte read, one pixel changed, then re-written). The advantages of using 4 bits-per-pixel are a smaller frame buffer and lower PLB and DDR utilisation, however the awkwardness of writing two pixels at once means that 8 bits-per-pixel is more practical unless there are speed or bandwidth concerns.
A note on memory bandwidth
While a VGA core set to 1024x768 resolution at 8 bits-per-pixel will function fine if it is the only part of the system accessing DDR, it uses a lot of memory and PLB bandwidth. If your system is experiencing delays accessing the PLB or DDR, or the graphics output has problems, try reducing the resolution or bits-per-pixel values. As a rough guide to relative PLB and memory utilisation, the following table shows the number of bytes read from DDR per second for each configuration.
Resolution | Bits per pixel | Bytes read per second |
---|---|---|
640x480 | 4 | 9,216,000 |
640x480 | 8 | 18,432,000 |
800x600 | 4 | 14,400,000 |
800x600 | 8 | 28,800,000 |
1024x768 | 4 | 23,592,960 |
1024x768 | 8 | 47,185,920 |
ChipScope debug ports
One of the choices in the peripheral options is to include ChipScope debug ports. This adds several extra output ports to the core for monitoring internal signals using ChipScope Pro software, and should be left set to 'FALSE' unless ChipScope is being used to debug the core's operation.
Using the EMBS VGA core
The VGA core designates a given chunk of DDR memory as a video frame buffer. It reads the frame from this memory and draws it on the screen. The MicroBlaze can write graphics data into this memory for the VGA core to draw. The first location in the frame buffer corresponds to the top-left of the image, with subsequent locations moving right until the end of a line, then moving to the far left of the line below. At the end of each frame, the read location will reset to the start of the frame buffer. The size of the frame buffer varies depending on the core settings used, as shown in the following table.
Resolution | Bits per pixel | Decimal size (bytes) | Hex size (bytes) |
---|---|---|---|
640x480 | 4 | 153,600 | 0x25800 |
640x480 | 8 | 307,200 | 0x4B000 |
800x600 | 4 | 240,000 | 0x3A980 |
800x600 | 8 | 480,000 | 0x75300 |
1024x768 | 4 | 393,216 | 0x60000 |
1024x768 | 8 | 786,432 | 0xC0000 |
The VGA core includes two software-accessible registers:
XPAR_EMBS_VGA_0_BASEADDR
- Frame Buffer Base Address RegisterXPAR_EMBS_VGA_0_BASEADDR + 1
- Control Register
Important: the frame buffer location must be set to a valid address in DDR before the core is enabled, otherwise graphics may not be aligned correctly on the display.
Setting the frame buffer location
The first register sets the location in DDR that the core reads the frame buffer from.
For example, to set the frame buffer at the start of DDR, you can use the following C code:
*((volatile unsigned int *) XPAR_EMBS_VGA_0_BASEADDR) = XPAR_DDR_SDRAM_MPMC_BASEADDR;
The location of the frame buffer can be changed at any time by writing a different value to the register.
Controlling the core
Writing a 1 to the control register will enable graphics output from the core, and writing a 0 will disable it.
The default state is disabled, so before the core will output graphics it must be enabled by setting the control register to 1:
*((volatile unsigned int *) XPAR_EMBS_VGA_0_BASEADDR + 1) = 1;
When the core is disabled it will not read any data from DDR, and it will hold the three colour output signals low and the two sync signals high.
Pixel format
The core uses a three-bit pixel value to produce a total of eight possible colours. This limitation is due to the way the FPGA board is wired up, with only a digital output connected to each of the red, green and blue VGA pins instead of an analogue one.
The lower three bits of each 4- or 8-bit pixel are the values of red, green and blue, as follows.
8-bit pixel width
'Byte 1 | Byte 2 | Byte 3 | Byte 4 00000RGB|00000RGB|00000RGB|00000RGB Px1 Px2 Px3 Px4
4-bit pixel width
'Byte 1 | Byte 2 | Byte 3 | Byte 4 0RGB0RGB|0RGB0RGB|0RGB0RGB|0RGB0RGB Px1 Px2 Px3 Px4 Px5 Px6 Px7 Px8
So, for example the following C code sets four pixels (starting at location 'x') to red, green, blue and white respectively, for either 4 or 8 bits-per-pixel.
// For 8 bits-per-pixel *((volatile unsigned char *) x) = 0b00000100; // Red pixel *((volatile unsigned char *) x + 1) = 0b00000010; // Green pixel *((volatile unsigned char *) x + 2) = 0b00000001; // Blue pixel *((volatile unsigned char *) x + 3) = 0b00000111; // White pixel // For 4 bits-per-pixel *((volatile unsigned char *) x) = 0b01000010; // Red and green pixels *((volatile unsigned char *) x + 1) = 0b00010111; // Blue and white pixels
The available eight colours are:0b0000
or 0x0
: Black0b0001
or 0x1
: Blue0b0010
or 0x2
: Green0b0011
or 0x3
: Cyan0b0100
or 0x4
: Red0b0101
or 0x5
: Magenta0b0110
or 0x6
: Yellow0b0111
or 0x7
: White
Example code
The following two examples can be used to test the EMBS VGA core.
Example 1: Display image from UART
The following C program can be used to copy an image received over the UART to the frame buffer. This can be useful to test that the core is set up correctly.
To transmit a binary image (see below) over the UART, the following command can be used in a Linux terminal (make sure all other screen sessions to the serial port have been closed):
cat image.bin > /dev/ttyS0
Where 'image.bin' is the name of the image file downloaded below. The following images can be used for each resolution and pixel width setting:
Resolution | Bits per pixel | Image |
---|---|---|
640x480 | 4 | vgatest640x4.bin |
640x480 | 8 | vgatest640x8.bin |
800x600 | 4 | vgatest800x4.bin |
800x600 | 8 | vgatest800x8.bin |
1024x768 | 4 | vgatest1024x4.bin |
1024x768 | 8 | vgatest1024x8.bin |
Example 2: Generating patterns in C
The following C program generates several test patterns and displays them using the VGA core. It will progress to the next pattern whenever it receives a byte over the UART (the easiest way to do this is to open a screen session and press any key to advance). It also prints out its current status over the UART.
The file contains functions for 4- and 8-bit output, and allows the screen resolution to be set, so make sure the constants at the top of the file are correct before use.
FPGA Resource Usage
On a Spartan-3E xc3s500e (the FPGA that is on the Starter Board), Xilinx ISE estimates the core usage at:
Logic Utilisation | Used | Available | Utilisation |
---|---|---|---|
Number of Slices | 373 | 4,656 | 8% |
Number of Slice Flip Flops | 437 | 9,312 | 4% |
Number of 4 input LUTs | 528 | 9,312 | 5% |
Number of bonded IOBs | 0 | 232 | 0% |
Number of BRAMs | 1 | 20 | 5% |