FPGA RGB2VGA

This is a FPGA implementation of a RGB to VGA converter using the Altera DE0-Nano board.

The Quartus II source code can be found at: source code. There is a branch for the SVGA 800x600 mode which solves the problem with missing borders.

The board can be ordered at: Osh Park


RGB Format

The CoCo3 has a RGB ouput port but it is pretty useless nowadays because it uses the old NTSC-like RGB format. The RGB was in vogue before the existence of the VGA monitors.

This format outputs video using the same NTSC multiple frequency at 60  frames per second and displaying 262.5 lines. This is often called as 15Khz mode, because it displays 15,750 lines per second. You can call it as well 240p or 480i (30fps).

With a nominal resolution of 800x262.2 pixels in overscan mode, only displaying 640x240 in the visible area, many computers adjusted their resolutions to this maximum. Some with 640x200 (coco3), and others with 512x212 (MSX2), etc.

The only way to display more vertical lines is using a trick, or flickering. In the interlace mode, the computer will display first the odd lines in first frame and the even lines on second. This way, the 262.5 would be doubled to 425 with a visual flickering.

The specs are:

 Horizontal Resolution 800 px
 Vertical Resolution 262.2 px
 Horizontal Frequency 59.94 Hz
 Vertical Frequency15.74 Khz
 Pixel Clock 14.31818 Mhz


The clock pixel is a multiple of the NTSC color burst: 3.579545 Mhz. Three analog channels R,G and B are used. Some computers have separate vertical and horizontal sync, like the Coco3, some will have a composite sync (both mixed in one wire). Some will have the composite sync mixed on G channel, called Sync On Green. Some computers have a positive sync (coco3), as some will have a negative sync (MSX). Both syncs are TTL level.

All these variants make part of the plethora of video format called by RGB. This is way some monitors like the Tandy CM-8 will work only with the coco3, and some other monitors will require a mod on the computer to accept the negative composite sync if you use it with a coco3.

There is also the RGBI, or digital RGB. CGA or Commodore 128 will display 16 colors using 4 bits: RGB and I for intensity. In this format all signals including R,G and B are TTL level.


VGA Format

The Video Graphics Array was introduced in 1987 for the IBM PS/2 models and its format was widespread as the new standard for computer video, replacing the EGA/MDA released years earlier.

This video format is progressive scan with no flickering. But for this it will require faster clock speeds.

The new specs for 640x480@60Hz are:

 Horizontal Resolution 800 px
 Vertical Resolution525 px
 Horizontal Frequency60 Hz
 Vertical Frequency31.5 Khz
 Pixel Clock 25.175 Mhz

As you can see it has the exactly double amount of lines from the previous RGB. This makes things easier for the convertor.


Building a Converter

Video converting requires dedicated integrated circuits to do the job. Since I started learning FPGA I wanted to know if would be possible to do this job using no extra IC and how good would be that.

The starting point: how could you do such converter in VHDL?

In short the VGA converter or scandoubler (15Khz to 31.5Khz) will digitize every RGB line and output it twice using the VGA clock frequency.



The basic idea is to have an ADC (analog-to-digital converter) to digitize every pixel, save it into a buffer and display it using VGA frequency. The diagram above shows the basic function of a video converter.


The DE0-Nano has an internal SDRAM of 32MB. This is way more than we need for our framebuffer. For video output we can easily build a resistor chain and use a VGA connector. But how to build a fast ADC in FPGA ? Is that possible ?


Delta-Sigma Differential Inputs

Every FPGA chip has differential input/output pins called LVDS. The LVDS is made of 2 wires (a positive and a negative pin) and they work essentially as fast voltage comparators. In short you put your analog signal into the positive port and a voltage reference in the negative. The port will return 1 if higher, 0 otherwise.



Building an ADC

We need a voltage reference to be compared against our signal and this voltage reference must be incremented every successive test. For this we can build a simple R2R DAC and let the FPGA control its voltage by  controlling the DAC output pins.

Since the SDRAM access is slow and it runs at 100Mhz speeds only we need to use a color depth that is compatible with the this speed. For this project we will use 8-bit, what gives 256 different colors using RRRGGBB notation.

Now we know that we need to built three 3-bit ADCs, well the blue component is 2-bits only but to reuse the same parts we digitize in 3-bit and discard the LSB.

Using a 3-bit DAC we need to perform 8 comparisons. So for every pixel on screen will have 8 iterations in total. The FPGA clock must run 8 times faster than 14.31Mhz then.



This shows the basic diagram of the R2R DAC connected to one LVDS port. For the RGB we need to connect the DAC to 3 different LVDS ports, one for each RGB channel. The variable resistor at the end of the chain allows us to adjust the voltage levels from 0.2 to 2.75V.


Measuring Voltages

The voltage on each channel is measured using an integrador. We iterate the DAC output from 0 to 7 generating all 8 possible voltages and for each step check each R, G and B comparators. If the signal is higher than the DAC add 1, otherwise ignore. 

At the end of the 8th iterator we have the individual voltage for each channel.

Since we need 8 iterations for each pixel this may cause some jitter or instability if we are in the edge of the pixel. That's why is important to have the clock in pair with the input pixel clock. Also adjust the input_detect module to trigger the DAC in the middle of the pixel (peak value).

Genlock

The next step is to lock the video image and capture each individual frame. This is done by the genlock. The input video is a progressive 240p video made of 262.5 lines.

Each line is separated by a HSYNC pulse. Each video frame is separated by a VSYNC pulse. The job of the genlock is to listen these signals and adjust the internal counters to generate the exact line and column that is being digitized.

For exevy pixel clock tick, the HCOUNT is incremented. It goes from 0 to 799 for each line. After the HSYNC, the HCOUNT is zeroed and the VCOUNT is incremented. The VCOUNT goes from 0 to 262. After the VSYNC both HCOUNT and VCOUNT are zeroed and a new frame start again.

Since we need to perform 8 tests each pixel, our HCOUNT goes instead from 0 to 6400. The current pixel column is HCOUNT/8.

We also want to discard the front and back porches, so the digitization occurs only in the 640x240 window. This reduces memory bandwidth and optimizes the resolution to a 640x480 VGA monitor, avoiding pixelation.



Line Buffers

SDRAMs cannot be accessed directly like Static RAMs. You need to set the command, row strobe, column strobe and then you have your data. This could take up to 6 clock cycles, but we need to access our pixel data instantly.

For this we create two line buffers in internal RAM. Once the line is finished and our genlock is blanked between back/sync/front porch we send the entire line to the SDRAM using burst mode. After the line is sent, we can start again reusing the same line buffer.

We need two line buffers, one for the incoming line and other for the output line.

The line buffers can be easily implemented using Altera's ALTRAM modules. Just creating 2 dual port/dual clock RAM buffers of 1024 bytes each.in

The same method applies to the output line, while blanking the new line is being read.


Framebuffer

The entire framebuffer is stored into the SDRAM one line each time. The SDRAM module will read and from from/to the line buffers performing the transfer. Once its finished it raises an ack wire.

The SDRAM module operates on same clock speed of the genlock (114Mhz) and it writes and reads a line in burst mode.

Since we write and read the framebufffer so fast we don't need to perform any refresh. In fact a refresh routine would stall the operation and affect the operation.


VGA outuput

The video is output by the vga out module that reads one line each time and displays it twice. The analog video is generated using resistors in paralel for each color bit: 680Ohms, 1K, 2K for bits 2,1 an 0.


Schematics:



Board:




Additional Features

In the final project I added an composite monitor input into the green channel. Changing the FPGA code this allows us to support Apple II composite signal and even display artifact colors for it.


Bill Of Materials

1x TRIMMER 10K OHM 0.2W PC PIN
3x RES 75 OHM 1/4W 1% AXIAL
8x RES 1K OHM 1/4W 5% CARBON FILM
4x RES 2K OHM 1/4W 5% CARBON FILM
2x RES 47 OHM 1/4W 5% CARBON FILM
3x RES 750 OHM 1/4W 5% CARBON FILM
3x RES 1K5 OHM 1/4W 5% CARBON FILM
3x RES 3K OHM 1/4W 5% CARBON FILM
1x CONN HEADER 10POS .100 STR TIN (COCO3 HEADER MALE)
1x CONN HD D-SUB RCPT R/A 15POS (VGA OUTPUT CONNECTOR FEMALE)
1x SWITCH SPST GOLD 8 SEC 50V (DIP SWITCH 8-POSITION)
2x CON HEADER FEMALE 40POS (FEMALE HEADER 40 POSITIONS, 2 ROWS)


Optional for Composite monochrome (Apple2) or Non-coco3 computers, RGBI: 

1x LM1881N/NOPB-ND IC VIDEO SYNC SEPARATOR 8-DIP
1x RES 680K OHM 1/4W 5% CARBON FILM
2x CAP CER 0.1UF 100V 10% RADIAL
3x 3M9447-ND CONN HEADER VERT SGL 2POS GOLD (JUMPER HEADER)
1x CONN JACK RCA R/A METAL ORG PCB (RCA COMPOSITE INPUT)
1x CONN HD D-SUB RCPT R/A 15POS (RGB VGA-LIKE INPUT CONNECTOR FEMALE)

Important: See Modification below


Assembling Instructions

The stacking header go under the board. They will be soldered on top. All the other components remains on top. The board is plugged on top of the
Altera DE0 Nano. Follow the GPIO0 and GPIO1 orientations.


Modifications

I have changed some resistor for the VGA DAC. Please, replace the values as below. And add a new 3K0 as show below. All values are in Ohms. R7 and R8 are mixed together to compose the blue output so there is no difference if you connect the new resistor to R7 or R8.



RGBI

Optionally you can modify the board to support RGBI to be used with Tandy 1000, Commodore 128 or Spectrum 128+.  You just add 2x 1K0 resistor for the Bright channel. Make you cable to output the Bright through the RGB input pin 4. WARNING: The R,G and B lines don't have voltage dividers, instead they have a 75R pull down. Connecting it directly could damage the Altera permanently. Use a 150R resistor on the three lines in your cable for the R, G and B lines. The Bright line is already safe by this change. The RGBI can be switched by the switch RGBI




Usage

The 3 jumpers are used to drive the composite video to the sync separator and also to drive the separated sync signals to the H and V syncs. If you use it with an Apple II composite, short all 3 jumpers, if using a RGB computer with composite sync, short only J2 and J3 and use the RGB IN port. When using a computer with separated H and V, use the coco3 port.

The DIP switches changed in the last github versions. The last dip switches are for:

 SHRINK
Shrink the image to show 720x240. Normal mode is 640x240. Some computers need the borders to be shown.
 OFFSET
Use this jumper to use alternate offset (centering)
 DEINTERLACEDe-interlaces odd-even fields. For flicker free images on Amiga
 APPLE ][For Apple II it will make the display black and white instead of green
 ARTIFACT INVERSEreverse the fake colors (coco3)
 ARTIFACTTo use fake colors on Coco2 or to reconstruct Apple 2 colors (use with Apple2 switch as well)
 RGBIUse the Bright input as RGBI. Note the R,G and B are still analog and need a proper trimmer adjust for the better image.
 SCANLINETurns scanlines on/off


Assembled board



New board with 3-bit blue and RGBI input:



Images





Comments