FPGA Test Card


The project implements a Test Card picture and shows it on a VGA output. It is similar to Philips PM5544 (see reference picture) and is used by TV stations all over the globe. The project includes color fields, some text and a working clock and is implemented in verilog  and synthesized into a Xilinx FPGA. My aim is to get a version of this test card that looks close to the one used by the Swedish television. (yea, I’m a swede..)

 
Original Swedish test picture based on Philips PM5544.

I started out the project by separating the picture into 2 elements, squared patterned border and Circle structure. Because I didn’t want to start by implementing the circle, I chose to start with a simpler rectangle with the same width and height as the circle. By coincidence, it turns out that this rectangle picture also exists as a standard called Philips PM5538.

My first initial output was something like this:


The implementation was mostly done as vectors/border values in Verilog code and not by the use of a bitmaps only. The reason for me to create this core based on Verilog code instead of showing a static bitmap was to experiment to see if relatively advanced graphics can be represented by using logical elements within an FPGA. It can be compared to a vector engine, but all vectors have static values.

A separate goal of the project was NOT to cache or save anything, thus every point in the picture shall be calculated on-the-fly. This has been violated by ONE single bit in the Pythagoras core.



SECTIONS WITHIN CIRCLE/RECTANGLE

The internal sections of the circle was created using a 12-part if/else clause that are triggered by different sections of the screen using the current Y-coordinate.



BACKGROUND GRID

The background grid is created using a 36 pixels grid, both horizontally and vertically. The reference picture for PM5544 shows that 17 squares horizontally and 13 squares vertically shall be shown as backdrop for the middle circle. As the VGA-resolution used here is 640*480 (640/17=37,6 and 480/13=36,9) a grid size of 36 pixels looks good.

BITMAPS

Even though most of the test card picture was implemented using vectors, this is not the case with texts and numbers/digits. The best matching font I’ve found for the channel name and country specifier was a font called Amarillo. By using the nice and free “Photoshop Clone” called Paint.Net I’ve rendered the strings “FPGA” and “GÄVLE” and reworked the height/width ratio of them manually to suit this application. These layouts was then saved as pictures using the .BMP file format.

The best matching font I’ve found for the number characters was a font called Zürich. The same procedure as mentioned above was performed on every character separately.
These BMP bitmap files was then converted to pure verilog source code using a C# application written solely for this purpose. Reused C# source code classes from former projects was used to implement the conversion application.

By using a simple verilog source format, the mono-colored picture can actually still be seen and changed as ’0′ and ’1′ within the verilog source code. I though this was a nice feature for simplicity. Down below is an example of this, the digit “1″ as it is seen within the verilog source code.

9'd226: ClockDataOutput = 20'b00000000000000000000;
9'd227: ClockDataOutput = 20'b00000000011111100000;
9'd228: ClockDataOutput = 20'b00000000111111100000;
9'd229: ClockDataOutput = 20'b00000011111111100000;
9'd230: ClockDataOutput = 20'b00001111111111100000;
9'd231: ClockDataOutput = 20'b00001111111111100000;
9'd232: ClockDataOutput = 20'b00001111111111100000;
9'd233: ClockDataOutput = 20'b00001100011111100000;
9'd234: ClockDataOutput = 20'b00000000011111100000;
9'd235: ClockDataOutput = 20'b00000000011111100000;
9'd236: ClockDataOutput = 20'b00000000011111100000;
9'd237: ClockDataOutput = 20'b00000000011111100000;
9'd238: ClockDataOutput = 20'b00000000011111100000;
9'd239: ClockDataOutput = 20'b00000000011111100000;
9'd240: ClockDataOutput = 20'b00000000011111100000;
9'd241: ClockDataOutput = 20'b00000000011111100000;
9'd242: ClockDataOutput = 20'b00000000011111100000;
9'd243: ClockDataOutput = 20'b00000000011111100000;
9'd244: ClockDataOutput = 20'b00000000011111100000;
9'd245: ClockDataOutput = 20'b00000000011111100000;
9'd246: ClockDataOutput = 20'b00000000011111100000;
9'd247: ClockDataOutput = 20'b00000000011111100000;
9'd248: ClockDataOutput = 20'b00000000011111100000;
9'd249: ClockDataOutput = 20'b00000000011111100000;
9'd250: ClockDataOutput = 20'b00000000011111100000;
9'd251: ClockDataOutput = 20'b00000000011111100000;
9'd252: ClockDataOutput = 20'b00000000011111100000;
9'd253: ClockDataOutput = 20'b00000000011111100000;
9'd254: ClockDataOutput = 20'b00000000011111100000;
9'd255: ClockDataOutput = 20'b00000000011111100000;
9'd256: ClockDataOutput = 20'b00000000011111100000;
9'd257: ClockDataOutput = 20'b00000000011111100000;

For  now, the bitmaps used are synthesized into the FPGA using gates as the current structures does not map the source code to internal FPGA memory (BlockRAM). The channel name (FPGA) is one case-structure of verilog code and the country specifier (GÄVLE) in another case-structure.

DIGITAL ON-SCREEN CLOCK

The clock is implemented as 8 separate case-structures of verilog code, 6 digits and 2 colons like this: “0″,”1″,”:”,”2″,”3″,”:”,”4″,”5″. These are mapped using an if/else clause.
The actual digits shown are mapped using 6 different 4-bit values. A kind of Binary-Coded-Decimal system is used to step seconds, minutes and hours correct.
A separate timer is used that divides the 50 MHz physical clock signal input down to 1 Hz and used to count seconds and keep track of time.

CIRCLE/PYTHAGORAS CORE

The circle form used is implemented using Pythagoras theorem in real-time and do not use any table or other type of trick to get the circle form.  In the current implementation, the timing of this core only supports up to 100 MHz which translates to about 4 clock cycles per pixel. To be able to pin-point an exact pixel being inside or outside the specified circle, the algorithm would need up to 8 clock cycles. This means that it takes the current algorithm about 1-2 pixel clocks to finish.
As the timing isn’t really met, artifacts can be seen on the border of the circle.



I see this as a “feature “and there are several ways around it (caching/pipelining/pre-calculating). I leave to the reader of this article to try this out. By storing the previous decision bit and using as default value of the Pythagoras core (inside or outside the circle), the core actually works pretty good.

HARDWARE


As the test card picture uses more than the 3-bit (8) colors available in the standard VGA-output of the MicroBlaze Starter Kit, I was forced to use an custom VGA-output. The one used is a 12-bit, 4096 color R2R VGA-output that I’ve included in my own extension board, Arcade Extender.
The core uses a 50 MHz clock external to the FPGA and included in the MicroBlaze Starter Kit. By using this clock as input both a 25 MHz pixel clock and a 100 MHz Pythagoras core clock is created. The 25 Mhz pixel clock is used to create a VGA output of 640*480@60Hz which normally ALL VGA-compatible monitors/TV’s can show.



INPUT/OUTPUT


Input uses 3 switches and 1 push-button. The push-button is a reset button for seconds to set the value “00″. Switch[0] is used to alternate between PM5538 (Rectangle) and PM5544 (Circle). Switch[1] is used to setup hour and Switch[2] is used to setup minutes. By enabling any of the setup switches, this values will increase together with seconds. By disabling the switches normal clock running mode is used.

RESULTS


Here are some pictures and a video of the on-going work and the finished test card FPGA application. I’m quite satisfied with the result.

Early picture, only rectangle implemented and the Grey smooth bars
under the middle cross still missing
The Grey smooth bars added!

Here is the first working circle form! Due to the Pythagoras core taking 3-4 pixels to finish the calculation of 1 pixel it looks hollow.A small problem can also be seen as the extra parts at the edges of the screen, but these are easy to solve.

Yea, it still taking 3-4 pixels to finish but I'm remembering the last pixels result
and defaulting to that if we didn't finish the calculation in time.

A magnification of the first bitmaps in use. In this picture numbers is shown as
bitmaps but the actual clock has not been implemented so it is just static numbers.
The END result! All text in place, all different fields implemented
and a working settable clock also implemented. 


Here is a picture of the result and the hardware creating the picture!


SOURCES


Download the complete verilog source for the project here:
SOURCE VERSION 2011-xx-xx

Download the help C# source to convert .BMP bitmap files to verilog source code suitable for the project:
SOURCE VERSION 2011-xx-xx

FURTHER WORK

Ideas for extensions to this work would be:
- Composite PAL/NTSC TV output as an alternative to the VGA output. This would actually make this core work for the purposes the original  test card was created for.
- Optimize the usage of bitmap to make them synthesize into FPGA BlockRAM resources.
- NTP/Ethernet setting of the digital clock
- Make a perfect circle algorithm, either by optimizing the supplied algorithm or implementing a completely new one. Another extension would use some filtering/anti-alias between the inside/outside of the circle to make it even smoother.

4 kommentarer:

Unknown sa...

This is Michael McGarrah from the Seagate forums and the BlackArmor toolchain. You definitely have the hardware credit. Neat project.

Magnus sa...

Thanks Michael, I'll try to add some more of my projects to the Blog for others to read about. Real-time graphics is one of my favorite subjects so this is part of most projects.

Hopefully I can be helpful is some way with the toolchain or at least in packaging some nice tools for the NAS110 if we managed to get the toolchain going.

djrm sa...

Greetings Michael, lovely project. I would like to incorporate it into my own fpga display panel test, is the source available please. Kind regards, David

Magnus sa...

Hello David! Yeah I'll find the code. I actually did a perfect circle and 100% working Antialias between foreground and background. Send me an email at magnus(at)wedmark(dot)se