Showing posts with label Teletext. Show all posts
Showing posts with label Teletext. Show all posts

Saturday, November 23, 2024

Insertomatic 6000 Part 4: Finishing up

Summary of the Insertomatic 6000

The Insertomatic 6000 is a device which generates six analogue TV channels for distribution around a domestic tv system, two of which are derived from external sources such as a PVR or digital TV receiver, and four of which are generated by Raspberry Pis. All six channels can carry modern enthusiast teletext services containing a range of artwork and information.

List of hardware features:

  • One Raspberry Pi 3 + three Pi Zero 2 Ws fully networked using USB Gadget
  • 6 analogue RF channels in total
  • ...4 of which are connected directly to the composite video outputs of the Raspberry Pis
  • ...2 of which are connected to the two VBIT-Pis, which insert teletext data into the VBI of external composite video sources
  • Each VBIT-Pi takes an external composite video source (such as that from a PVR) and adds a teletext service to the vertical blanking interval of the picture; the teletext service can be set independently from the service which is on the corresponding Pi's own output
  • A controller board which switches the audio channel of the two VBIT-Pi channels to an auxiliary input when the external video input is switched off, and which allows the three Pi Zero channels to be switched between their own audio and the audio of the Pi 3 under control of each Pi Zero
  • Audio selector switches, which select media playback on each of the four Raspberry Pis
  • LCD, which displays information about the playing media, and allows the modulator channels to be conveniently changed
  • A USB port on the front panel, which makes it possible to take full control of the system and run any program that can run on a Raspberry Pi

A short video demonstrating the TV channels generated by the Insertomatic 6000. Five modern enthusiast teletext services are currently carried. The four Raspberry Pi channels are presently showing slideshows but I hope to add visualisers for the audio in the future.

Safe shutdown

I decided it would be useful to incorporate a shutdown function into the Pi Pico-based controller so that all the Pis could easily be safely shut down without having to log into each Pi in turn and run sudo poweroff as I had been doing so far. I used this Sparkfun example to make the Pis shut down or reboot depending on the length of the pulse received on an input pin, and added some menu options to the controller which enable the user to issue a shutdown or reboot command and holds an output pin low for the correct amount of time.

A DuPont wire harness was made which connected pin 6 on each of the four Pis together and to a single I/O pin on the Pi Pico.

I found a flaw in the Sparkfun code which was that when attempting to issue a shutdown command, execution of the script does not terminate immediately, so if the pin goes high before the script has been terminated by the system shutdown, the restart command will be executed as well, and this overrides the earlier shutdown command, resuting in some of the Pis turning back on. I added a quit command immediately below the call to the shutdown function to resolve this problem.

Noise

My fear with this problem was that the wired construction simply resulted in poor grounds and that there would be no way around it except for creating a massive PCB with everything on it to provide a good ground plane. The fact that adding a single ground wire between a Pi and the modulators made little difference supported this view, but fortunately it wasn't necessary. All the Pis needed individual ground wires to the modulators, and once this was done, the picture quality was improved dramatically to the extent that it was now almost perfect.

Animation showing the improved noise

Thermals

The Insertomatic 6000 now has a lot of stuff inside a case with very poor airflow, so I was concerned that the Pis would get too hot, especially when used for something more than just internet radio.

A slot was cut in the base acrylic sheet to hold a fan and some holes were drilled in the bottom panel to the left of the Pi 3 to encourage air to flow across the small heatsink on the Pi 3.

Cutout for the fan cut by the laser cutter
Fan installed in the hole

The 35mm fan runs at high speed directly from a 5V power supply and makes a lot of noise. Whilst very effective, the noise and the 4W of power consumption were unacceptable, so diodes were added in series until a low level of noise was achieved without affecting its effectiveness very much, and the 2W power consumption was better if still a little disappointing.

Temperature of the Pi 3 measured using vcgencmd measure_temp with all Pis powered up and running the Slideomatic software and internet radio:

  • Case closed, no fan: 46.2°C
  • Case open, no fan: 44.0°C
  • Case closed, fan at full speed: 33.2°C
  • Case closed, fan at low speed: 36.5°C

With the Pi 3 running stress for 15 minutes (using the command shown in Part 3) but conditions otherwise the same:

  • Case closed, no fan: 72.0°C, soft temperature throttling activated
  • Case closed, fan at full speed: 49.9°C
  • Case closed, fan at low speed: 49.9°C

vcgencmd get_throttled was added to the for loop from where stress was run to allow monitoring of the throttling status too.

With the Pi 3 under load, the difference between the fan running fast or slow is eliminated which is surprising, but the results at least show that having the fan lowers the temperatures significantly. However, even at the lower speed, the fan is still audible when the room is silent.

I considered adding a simple MOSFET circuit which allows the Raspberry Pi to control the fan via PWM; a Python script could then automatically turn the fan on when a certain temperature is reached or even use PWM to vary the fan speed and possibly drive it at an even lower speed when idle. However, I didn't want to risk introducing another place where noise could get into the pictures, so I decided to keep things relatively simple and add an extra AliExpress buck converter board which would only power the fan; this reduced the fan's impact on mains power consumption to just 0.5W, and the voltage adjustment potentiometer could be used to adjust the voltage to a point where the fan was very quiet but would still reliably start, and this happened to be at 1.82V. The temperature of the Pi 3 running stress was then 55.8°C and idle 38.6°C, a little warmer than when the fan was spinning faster but still much better than no fan at all, so I think it's sufficient to just have the fan running at this speed all the time and not bother adding a speed control.

Fan power supply added to the stripboard

Keeping all services up to date

The vbit2 package includes a script which keeps the currently enabled service up to date. In the case of the two Pi Zeros which each have a VBIT-Pi, it would be useful if when a different service is inserted using the VBIT-Pi to that inserted in the Pi's own composite video output, it is also kept up to date. The script was modified to update every installed service rather than just the enabled one, which works well. Unused services should be uninstalled to avoid updating unused services.

And that wraps up all the work on the hardware of the Insertomatic 6000. However, it's a project that will never be completely done, because there's always room for future improvements. One idea I'd like to implement at some point is visualisers for the audio playback instead of static images in the slideshow.

Code links

Slideomatic

Pi Pico RF Modulator and LCD controller

Customised version of teletext-update.py

Saturday, October 12, 2024

Insertomatic 6000 Part 3: Mechanical and wiring improvements

Mechanical design

The biggest part of this phase would be the mechanical design, as a number of issues had been identified and were to be improved upon.

Depth of the case

The case is slightly too deep to fit in the rack cabinet that I'd like to put it in, and as it should be possible to make everything fit in a shorter case, the case needed to be shortened.

The first step was to deal with the extruded side pieces of the case. Cutting these is easy using the metal bandsaw at the Hackspace, which achieved a perfectly straight cut in seconds. The threads for the screws were then tapped using the correct size taps.

Metal bandsaw
Extruded case offcuts

The next step was to cut the top and bottom panels, This was much trickier because the panels wouldn't fit in the metal bandsaw. They can't be cut with the laser cutter either, not even with multiple passes, but the laser cutter can at least cut a line in the paint which serves as a guide for manual hacksawing. This was done, and the results weren't very nice, as just a millimetre of deviation from the line shows up very clearly once the case is assembled, but it's acceptable. A hole was drilled in each panel for the screw which goes into the metal crosspiece near the front of the cabinet.

The internal sheet of acrylic which holds all the PCBs was also redesigned to make the parts fit more efficiently following the decision to mount the Pi Zeros on top of the VBIT-Pis.

View of the interior from above

Front panel

The design had a few imperfections; the cutout for the pushwheel switches was too big, leaving visible gaps above and below the switches. The cutouts for the three buttons weren't keyed, so the buttons could be rotated. The depth of the front panel made it difficult to plug anything into the USB port. It was also decided that wiring up the LEDs to the activity LEDs on the Pis would be too risky so the arrangement for those was also changed, reducing the number of holes needed. A new front panel was cut with all these changes made.

The LED holders are of a type which are normally secured to the panel using a nut, but in this case the diameter of the holes cut by the CNC were such that it was possible to tap a thread using an M8x0.7mm tap then screw the holders into the panel. This was just as well because the metal and laser cut panels together would have been too deep to get access to the thread of the holders to secure them with nuts.

View of the Insertomatic 6000 from the front

Electrical wiring

While working on the project, it had become clear that using "DuPont Wire" to connect all the parts together was not so convenient after all. The solution to this problem would come in the form of custom wiring harnesses, which would be assembled from IDC ribbon cable, DuPont-syle housings with the right number of pins for each connector, and proper crimps which would be crimped on by hand. This enabled each cable to be made to the exact length required, making the wiring much neater, and all the connectors being the correct size for their connectors make assembly and disassembly much easier with less chance of error.

Hackspace crimping tools

Here is one of the wiring harnesses that I made up:

Picture of one wiring harness

Controller

Channel memory

Obviously it's highly inconvenient for the modulator channels to be forgotten every time the Insertomatic 6000 is turned off, so adding this feature to the Pi Pico-based modulator controller was important.

As is common with these newfangled 32-bit microcontrollers, unlike PICs and AVRs, the Pi Pico doesn't have any EEPROM, and it is necessary to write to the FLASH instead. The main difference is that flash can only be erased a whole sector at a time, which effectively means that all data has to be written in one go rather than updating it whenever each individual setting is changed to avoid making excessive write cycles. This is solved by having a dedicated option in the menu to save the settings when the user is ready. The cycle life is quite high (10,000) and there isn't a need to change the settings often, so it was decided that it would be acceptable to not bother with any wear levelling.

A channel banks feature was added, which makes it possible to save several sets of channels for the modulators and switch between them easily. It could be used to quickly reconfigure the modulator channels when moving the Insertomatic 6000 from where it is normally installed to other places such as teletext exhibitions.

Now Playing info display

I used the Raspberry Pi Foundation's serial PIO example and expanded it to work on four pins on the Pico. I added some code to poll the FIFOs of these PIOs and process the data into some arrays for the cyclic display on the LCD. I then learnt how to control the serial port on the Pi 3 and Zeros so that data could be sent to the Pi Pico; a little bit of code was added to slideomatic.py to process the data returned by the mpc command and send this to the Pico. The data from the Pis is stored and displayed in a cycle.

Animation of the now playing info display

Power supply

The inductors on the AliExpress buck modules got rather uncomfortably hot even when the Pis were all idle, and the linear regulator the for the modulators also got very hot despite only having a 7V input voltage, and it was decided that this could be detrimental to the long term reliability of the design once the box was closed up. It was decided that a second regulator would be added and each one would only be connected to two of the Pis; the result of this was that the inductors only get slightly warm when the Pis are idle, and only get very warm under a prolonged stress test using the stress command on all the Pis simultaneously. The 7805 regulator was changed to an LDO regulator so the input voltage for that could be reduced to under 6V, and it was mounted on one of the extruded edge pieces of the case for heat sinking, and as a result, this regulator now remains cold.

Power consumption

The following command was used to stress-test the Pis:

for ((i = 0 ; i < 60 ; i++ )); do stress -c 4 -t 60s ; vcgencmd measure_temp ; done

Measured with an inexpensive plug-in power meter, the total power consumption is listed below. From 240V to 5V the power supplies are 75% efficient, which doesn't seem great but as the conversion to 5V is done in two stages, this is still equivalent to 87% per stage. 240V to 5V power supplies tend to be less efficient than those with higher output voltages, so the gain of changing to one of these would not be significant even if that didn't introduce noise issues.

  • 15.0W - all Pis idle
  • 24.0W - all Pis running the stress test
  • 0.1W - when all loads are disconnected from the mains power supply

The power consumption is quite important because the total energy consumption adds up to quite a lot over time when the device is left running 24 hours a day as is the intention. For comparison:

  • 4.0W - Single Pi 5 at idle
  • 20.0W - Ivy Bridge office desktop at idle with no spinning hard drives
  • 7.0W - Ivy Bridge laptop at idle

Next steps

The main part of the project is done, but there's still some work to be done...

The picture quality on some of the channels is still quite disappointing, particularly the channels derived from the external AV inputs. I will experiment with extra ground paths between the modulators and Pis, and find a way to disconnect the +5V power lines between the Pi 3 and three Pi Zeros, which are not helpful when the Pi Zeros have their owm direct connection to the power supply board; this will also enable the second 5V switching supply to be dedicated to the two Pi Zeros with the VBIT-Pis, allowing the lone Pi Zero to be paired with the Pi 3 on the first 5V switching supply.

Animation of the noise

Hopefully it won't come to the point of having to design a large PCB almost as big as the acrylic sheet to fit all the components and provide a nice ground plane, but at this point this could be done and it wouldn't add a massive amount of cost in the grand scheme of things.

Finally, once the Insertomatic 6000 has been installed in its rack, all that's left is to find some interesting alternative software to run on the Pi 3. It's possible to switch between the Slideomatic screen and the terminal by connecting a USB keyboard and typing Ctrl+Alt+F1 or Ctrl+Alt+F2, so the Pi 3 can be used for other things before being set back to the Slideomatic screen. Perhaps some BBC Micro and ZX Spectrum ROMs for software originally distributed via Telesoftware could be used to create a "Ceefax Game Pass" service!

Monday, May 30, 2022

Experimenting with RF modulators part 2: Sky IO Link and control via a Raspberry Pi

Following on from Experimenting with a programmable RF Modulator (making a controller for it and testing harmonics), I have continued my work on this topic with these goals in mind:

  • Make a neater VBIT-Pi assembly by integrating the modulator, VBIT-Pi hat and Raspberry Pi into a single unit
  • Control a Sky IO Link

Raspberry Pi control

The Raspberry Pi already has I2C support, and it is fairly straightforward to use it from the command line and in a Python script. In my application, the Raspberry Pi is already being used with a VBIT-Pi hat, an open-source hardware teletext generator which adds teletext data to an external video signal, which has two ICs on it connected to the I2C bus. Fortunately, the modulator IC's I2C address is different to the two ICs, so it's simply a case of wiring up the I2C pins to the same pins used on the VBIT-Pi.

The i2cdetect command can be used to test that all the I2C devices are connected:

pi@raspberrypi:~ $ i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n]
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- 25 -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- 44 -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- 65 -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

The modulator can then be tested from the command line with a command like this: i2cset -y 1 0x65 0x80 0x10 0x25 0xF4 i (this will set the output to C21)

I modified the VBIT-Pi to remove the composite video output, since that was now wired into the modulator, and add an audio input for the modulator instead. I also added a micro USB port (wired to a GPIO pin via a resistive divider) with the intention of connecting that to the USB port on the set top box that will supply the video signal, so that the modulator is only active when the set top box is on.

RF modulator mounted on the Pi

I wrote the script to control it in Python. To keep things simple, I only implemented the maths for UHF channels, even though the modulator supports VHF channels.

When pressed into service, the Pi can be set up to run the script at startup. During testing, it can be started from the command line with python3 ./rf-mod.py and terminated with Ctrl+C. If you have no need for the external enable, then all the code concerning the GPIO interrupt can be removed.

# RF modulator example code
# This example controls an MC44BS373CA RF modulator
# from a Raspberry Pi. The GPIO set as an input
# can be used to enable and disable the RF modulator
# on demand, and if connected to the USB port on a
# set top box, will automatically turn the modulator
# on when the set top box is on.

import time

import smbus
# Set I2C bus number (may vary according to the Pi used)
bus = smbus.SMBus(0)

import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)

# GPIO 23 set up as input.
GPIO.setup(23, GPIO.IN)

device_address = 0x65
# Set desired channel here (UHF only)
desired_channel = 38
# Set to 1 to enable test pattern or 0 for normal behaviour
test_screen_enable = 0
desired_frequency = (desired_channel - 21) * 4 * 8 + 1885
desired_n = (desired_frequency << 2) & 0x3ffc
desired_n_h = (desired_n >> 8) | (test_screen_enable << 6)
desired_n_l = desired_n & 0xff
config_values = [0x80, 0x10, desired_n_h, desired_n_l]

def configureModulator(gpioChannel):
        #gpioChannel only needed for setting up event handler; it doesn't do anything
        try:
                time.sleep(1)
                bus.write_i2c_block_data(device_address, config_values[0], config_values[1:4])
                print("RF Modulator configured: C", desired_channel, " System I")
        except:
                print("I2C Bus Error (Modulator probably not powered)")

#Configure now just in case modulator is already powered
configureModulator(0)

try:
        GPIO.add_event_detect(23, GPIO.RISING, callback=configureModulator, bouncetime=2000)
except KeyboardInterrupt:
        GPIO.cleanup()       # clean up GPIO on CTRL+C exit

#Loop forever - should not be resource intensive
while True:
        time.sleep(1000)

GPIO.cleanup()           # clean up GPIO on normal exit

This works very well and reliably. The output of the modulator is fed into the house AV distribution system, making it available at every TV point in the house. In retrospect, it may be more useful to use the set top box signal to control a multiplexer which switches the video input between the set top box and Raspberry Pi's composite video out, instead of turning the modulator off. The Pi could then be used to display an "in vision" service or other video content when the set top box is turned off.

Sky IO Link

The Sky IO Link is an RF modulator in a small box which was introduced alongside later Sky+HD boxes where the built-in modulator was removed to reduce costs. This modulator is still made today (2022) by various manufacturers and sold at a low price (~£10), which makes it a great choice for any project requiring a modulator.

Photo of IO Link

The modulator is similar to the ones that were previously built into the Sky boxes, but with an Abilis modulator IC rather than the Freescale one used previously, but the IC claims to be a drop-in replacement, so it should work with the code I've already developed.

The short cable is terminated with a 10-way mini DIN connector, and probing around suggests the following pinout: (wire colours on my unit shown)

IO Link Pinout

The VBIT-Pi's open-source PCB layout could be modified to include the mating connector, which would allow neat and tidy integration with the Pi.

10-way mini DIN PCB connector wiring

For testing, I wired the connector up to the Pi's GPIO header and connected the video input to the Pi's video output:

  • 4 (Power supply) - 2 (5V)
  • 5 (GND) - 9 (GND)
  • 6 (SCL) - 5 (GPIO 1 / SCL 0)
  • 7 (SDA) - 3 (GPIO 0 / SDA 0)

The SCL and SDA pins require pull-up resistors, and should be pulled up to 3.3V (not 5V as shown in my photo, but I got away with that bodge).

I ran my Python script from earlier (changing the SMBUS port to 0 for the original Pi in use here) and it worked perfectly.

IO Link connected to Pi and TV

Tuesday, January 5, 2021

VHDL Teletext: Overview of the project and the FPGA Teletext hardware

Photo of the FPGA Teletext board in action

Project objective

The aim of this project was to build a fully-featured Teletext decoder using an FPGA. The open-source firmware was written in VHDL. The completed device is the world's first standalone Teletext decoder with an HDMI output, but initially,I implemented a VGA output to learn the basics of generating video from an FPGA.

A brief introduction to Teletext

Teletext was introduced in Europe in 1970s as a system to display text and simple graphics on television sets. A signal contained within the Vertical Blanking Interval (VBI) (of the composite video signal) is decoded by (typically) a custom ASIC in the TV set, which displays the page requested by the user when it is received. World System Teletext (WST) became very popular in Europe and remains in use today (2021) in countries including Germany, Italy, and Ireland. Teletext was also launched in the United States, where it was much less successful, and didn't last very long.

Teletext most commonly carried information for human use such as news, but in the past it also carried programs for computers to use - called Telesoftware.

In the 1990s, Level 2.5 teletext was introduced. It allows Teletext artists to use more colours and more complex graphics. Level 2.5 is not widely supported by television sets, but VHDL Teletext will include some Level 2.5 decoding ability.

Teletext survives despite the switchover from analogue to digital television; the data originally carried in the Vertical Blanking Interval of the analogue picture is now carried as part of the DVB-S signal, and is encoded into the composite video output of most satellite receivers when tuned to an appropriate channel.

Outside of broadcast, Teletext is used as an art form. The Teletext signals can be generated by a Raspberry Pi using vbit2. TEEFAX, started by Peter Kwan in 2016, is one such service that can be run from a Raspberry Pi.

Development process

I've documented key parts of the project development process in detail in my blog:

  1. Driving a VGA monitor from an FPGA - I gained an understanding of the basics of FPGA video generation by displaying simple test patterns on a VGA monitor.
  2. Page display including Text and Graphics - A basic implementation of the Teletext display generator, incorporating most Level 1 features and using RAM pre-loaded with a fixed page for testing purposes.
  3. Data recovery, processing and page extraction - Created the logic for recovering the data from the composite video signal, identifying packets belonging to the requested page, and writing them to dual-port RAM.
  4. A sidestep post - programming the FPGA using a MAX 10 FPGA - The FPGA Teletext board (details of the board are below in this blog entry) can be programmed using a Raspberry Pi, offering an alternative to those who do not have or wish to procure a USB Blaster. The Raspberry Pi is a popular generator of Teletext signals, so it is incredibly useful to be able to reprogram the FPGA using it.
  5. Generating a high resolution HDMI/DVI output from an FPGA using Double Data Rate I/O - Added the logic to produce HDMI signals at resolutions up to 1280x720.

VHDL-Teletext firmware features

  • Open-source firmware GitHub
  • Level 1.0 teletext decoder with single-page memory
  • Selected Level 2.5 and Level 3.5 features implemented
  • Resolution selection via DIP switches: 720x576 or 800x600 at 50Hz or 60Hz (1280x720 available via firmware branch, and 640x480 also possible with minor code modifications)
  • Alpha Black enable switch for selecting between adherence to the original specification or compatibility with certain modern teletext services
  • Power-on page number selection via DIP switches
  • Mix function enables/disables the display of background colours (note that this board does not display the video portion of the composite video signal over HDMI)
  • Subpage selection
  • Reveal button displays and hides "concealed" page content
  • Page selection via number keys, up/down buttons, and Fastext keys
  • Level 2.5/3.5 store function for applying enhancements to Level 1.0 pages
  • Parameterised design enables design tweaks without any programming knowledge

The hardware: the FPGA Teletext PCB

An FPGA development board designed for the application of decoding WST (World System Teletext) and displaying it on a HDMI monitor/TV. The board is programmed with my open-source VHDL-Teletext firmware and all that's needed to complete the setup is a micro USB power supply, HDMI or DVI monitor, and a source of teletext data with composite video out such as a Raspberry Pi running VBIT2 or a satellite receiver. 50/60Hz output and choice of 576p and 800x600 resolutions for compatibility with international TVs.

FPGA Teletext PCB

Development board hardware features

  • 10M08SCE144C8 FPGA
  • HDMI output with arbitrary resolution support* with proper current-mode logic level shifter IC
  • Composite video input with high-speed comparator for WST sampling
  • Micro USB power supply connection - typical current when running teletext decoder firmware is 100mA
  • 3.3V 2A switching regulator (plenty of power for external electronics) and 2.5V LDO regulator
  • 3-channel programmable oscillator
  • 16-way DIP switch
  • 20 buttons (including detachable keypad)
  • Three I/O headers with 23 I/O pins
  • Footprint for optional 512K SRAM for development purposes (footprint can alternatively be used as extra I/O)
  • Footprint for optional video processor IC (SAA7113) (this could be used to implement full composite video to HDMI conversion)

* Resolution support tested up to 1280x720. The master branch of the firmware includes support for 720x576 and 800x600 resolutions, and the 720p mode uses the feature/720p branch.

Closeup of FPGA on the FPGA Teletext PCB

The board is suitable for all manner of FPGA projects. The Altera MAX 10 FPGA has 8k logic elements which is enough for complex hobby FPGA projects. In addition to HDMI connectivity and the built-in keypad and buttons, 23 I/O are connected to headers to allow connection to other devices, providing the potential for diverse applications such as a Telesoftware decoder or a replica of an old microcomputer.

Why did you make it?

I made it because I wanted to create a Teletext decoder which does not depend on obsolete components and has a high-quality, modern, video output connection.

The unique combination of hardware features needed made existing FPGA development boards unsuitable. Limited stocks of old obsolete decoder chips are still available but these only have low-quality composite video outputs and usually only work with PAL TVs.

What makes it special?

This is the world's only standalone teletext decoder with HDMI output.

Hardware for sale

I have assembled a small number of boards by hand and they are up for sale below. If there is serious demand for this PCB, then I will consider getting a proper production run done, and I'll offer a self-assembly kit too. Please comment below on my blog if these are of interest.

Click here to buy the VHDL Teletext PCB on Tindie (fully assembled) - ships from the UK (though all pricing is in USD on Tindie) - please remember that this is currently a very low volume product so I cannot achieve the low pricing of high-volume consumer gear.

Also available on eBay for those who would encounter high fees from their bank/card provider for paying in USD on Tindie or who wish to use the eBay Global Shipping Programme - eBay link.

Monday, December 21, 2020

Generating a high resolution HDMI/DVI output from an FPGA using Double Data Rate I/O (VHDL Teletext Decoder Part 5)

Introduction

I've already shown how to add a VGA output to an FPGA, but a HDMI/DVI output is sometimes more useful, as they offer better compatibility with modern displays, and a much higher bit depth (8 bits of colour per channel) is possible without needing to add extra I/O pins for each bit like you would with VGA. I've implemented a HDMI output as part of the VHDL Teletext project, and the main benefit for that project is that the additional colours supported by Level 2.5 teletext can be generated by the FPGA.

HDMI/DVI are very closely related to VGA, the timings are essentially the same, and it is in fact possible to generate VGA and HDMI/DVI from the same internal signals simultaneously. There is very little difference between HDMI and DVI, but DVI is video only, whereas HDMI usually carries audio as well. Generating audio will not be covered in this article, but any HDMI display should be able to accept a signal without audio, so the implementation in this article can be used for both and the only thing that differs is the choice of connector (the electronics are identical). The interface will be referred to as DVI from here on.

How does HDMI/DVI differ from VGA?

DVI is essentially the digital version of VGA. The main features are:

  • Analogue video channels are replaced by TMDS-encoded digital channels
  • Sync pulses are replaced by special codes that never appear in the active video area
  • Timings can be the same as VGA - timings are broadly categorised as "TV timings" and "PC timings"
  • Current-mode signalling is used on the hardware level
  • Data clock is 10 times the pixel clock, so a high clock speed is needed, but I'll address this issue by using Double Data Rate I/O

This diagram explains how the control signals work for a DVI video signal. Notice how the diagram is very similar to the equivalent diagram for a VGA interface.

Diagram showing the control signals for a DVI video signal

"TV timings" vs "PC timings"

Early HDMI TVs usually only support TV timings, and computer monitors and later TVs usually support all timings (computer monitors may not support interlaced timings, but those are outside the scope of this article). Examples of TV timings are 576p (720x576@50Hz), 720p (1280x720@50Hz and 1280x720@60Hz) and 1080p (1920x1080@50Hz and 1920x1080@60Hz) - 720x576@60Hz is not a proper TV timing, and will not be accepted by most older HDMI TVs. Examples of PC timings are 800x600 and 1280x1024, and PC monitors will often accept any clock frequency you make up as long as the refresh rate is within a certain range such as 50Hz to 80Hz. The most common values for the vertical and horizontal front porch, sync, and back porch for your chosen resolution can be found online (e.g. VESA Monitor Timing Standard).

An additional complication that affects TVs is the RGB Range, which can be "Full range" (0 to 255) or "Limited range" (16 to 235). TVs normally use "Limited range", but unless accurate colour is important, this generally doesn't matter for FPGA applications, especially text-only ones. Limited range was originally used for broadcast applications to avoid clipping analogue signals containing overshoots when they are converted to digital, which could introduce artefacts such as ringing if the digital signal is converted back to analogue.

Timings (EDID) can be read from the connected monitor over an I2C connection. The FPGA Teletext board includes the necessary connections to do this, but this hasn't been implemented in the firmware.

The TMDS signal

Digital video data is encoded using a scheme called TMDS (Transition-minimized differential signaling). It is a similar concept to 8B10B encoding, except the goal is to reduce the number of transitions rather than increase them.

The details of the algorithm are contained within the DVI specification, so I won't explain that in great detail. The algorithm is designed to be relatively easy to implement in programmable logic and in ASICs.

Another detail is that current-mode signalling is employed, whereas FPGAs usually have LVDS outputs, which use voltage-mode signalling. A limited selection of FPGAs have CML outputs, but for those which don't, a converter IC such as the PTN3366 can be used. Using LVDS directly will probably work at low resolutions using a short cable, but I wanted to do it properly on the FPGA Teletext PCB, so I used the PTN3366.

The code

The code is made up of these main parts:

  • Port definitions: the input for each channel is 8 bits, but if the input signal uses fewer bits, you can simply duplicate the bits to generate 8 bits for each channel. VHDL Teletext offers the user choice of two resolutions, so a selector input is provided for that. Pixel clock and high-speed clocks (5x pixel clock) are required.
  • Counter: controls the active area and pixels
  • Sync generator: determines whether the HSync, VSync, both, or no sync control codes should be used according to the counter values and resolution selection
  • TMDS encoders: three identical encoders for the three channels
  • Shifter: serialises the TMDS data using the high-speed clock

High-speed serialiser

The shifter is the part which differs from most HDMI implementations - the shifter is a shift register which shifts two bits per clock cycle, and two bits are extracted from each shift register to feed to the FPGA pins - this is for the Double Data Rate I/O.

DDR shift register block diagram

Shifting two bits per clock cycle allows the high-speed clock to be halved compared to the conventional method, which allows a dramatic increase in the maximum resolution without breaking the FPGA's maximum clock speed limit (402.5MHz for the PLL output on the MAX 10). This allows an increase in the maximum resolution from 800x600 to 1280x720. Even if the higher resolutions are not required, this still makes timing closure much easier, so is well worth doing regardless of what resolution you want to use. The impact on power consumption is minimal.

The values in the datasheet under "True LVDS Transmitter Timing Specifications" should also be considered. In the case of the VHDL Teletext board, the FPGA has a limit of 640Mbps, and 1280x720@50Hz is under this limit, but 1280x720@60Hz is above this limit. 1280x720@60Hz is under the limit of the fastest speed grade, which is more expensive. In practice, 1280x720@60Hz works fine on the slower speed grade, but this may not hold true at temperature extremes! The data rate in Mbps is 10 times the pixel clock, even when using DDRIO.

Implementation on the MAX 10 FPGA

On the MAX 10, the GPIO Lite IP core is used to implement Double Data Rate I/O. On other FPGAs, a different IP core may be used, such as the ALTDDIO IP core. The IP core updates the I/O pin on both edges of CLK_VIDEO_BIT, with either bit 0 or bit 1 from the TMDS encoder's shift register selected depending on whether the edge was a rising edge or a falling edge.

Take care when specifying the input pins. On the GPIO Lite IP core, the correct order (from high to low) is the four upper bits (CLK_OUT(1), R_OUT(1)...) followed by the four lower bits (CLK_OUT(0), R_OUT(0)...). This wasn't clearly explained in the documentation and caught me out the first time!

Example output on real hardware

Captured using the FPGA Teletext PCB:

Screenshot of 1280x720 signal generated by the FPGA

The hardware

The FPGA Teletext PCB, shown below with the numeric keypad removed, is a custom-designed PCB containing all the necessary hardware to decode the teletext data in a composite video signal (from a Raspberry Pi running vbit2 or from a satellite receiver) and display it on a HDMI or DVI display. The firmware could be used on other FPGA boards if the necessary external hardware is added.

Photo of the FPGA Teletext PCB in action displaying a Teefax page

Please see the project overview for more details of the hardware, including links to buy.

Sunday, December 20, 2020

How to program Intel FPGAs over a network using OpenOCD on the Raspberry Pi (VHDL Teletext Decoder Part 4)

Introduction

Often, the need to program an FPGA without the proper programmer arises because programming needs to be carried out remotely, by a customer, or on a one-off basis where the cost of the proper programmer cannot be justified. Most of the guides online cover Xilinx FPGAs, with little attention given to Altera, and I felt that an Altera/Intel FPGA programming guide was necessary, so I've decided to write this entry which explains how to program a MAX 10 FPGA using a Raspberry Pi.

These instructions are aimed at the FPGA Teletext board, but are valid for any MAX 10 FPGA board.

The method described here will use OpenOCD. No external components are needed, and the programming can be done over an Ethernet connection. It has been tested on a first-gen Raspberry Pi and should be compatible with every model.

Programming the Flash with OpenOCD is slower than using the USB Blaster, and it takes a few minutes.

Setup

The first part of the procedure is identical to Adafruit's guide to programming microcontrollers using a Raspberry Pi. Connect your Raspberry Pi to your network and follow all of the instructions to compile and install OpenOCD. I would suggest setting up a connection to the Raspberry Pi using SSH if you have not already done this as it will make controlling the Pi much easier.

On the Raspberry Pi, create a new file called openocd.cfg using nano or another text editor, in your home directory, with the following contents:

source [find interface/raspberrypi-native.cfg]
transport select jtag
# The expected-id should be changed if you are using an FPGA other than the 10M08, you can find the IDs online
jtag newtap 10m08 tap -expected-id 0x31820dd -irlen 10
jtag_rclk 10000

Wire the Raspberry Pi up to the FPGA Teletext PCB's programming header using jumper wires. Use 100-ohm series resistors to protect the I/O against mishaps if you're not confident.

TCK TMS TDI TDO
Raspberry Pi header pin number 23 22 19 21
Raspberry Pi GPIO number 11 25 10 9
Altera/FPGA Teletext Decoder
programming header pin number
1 5 9 3

Don't forget to connect GND on the FPGA Teletext PCB (pins 2 or 10 on the programming header) to the Raspberry Pi.

FPGA-Teletext board connected to Raspberry Pi using JTAG connections

Programming the MAX 10 FPGA using the Raspberry Pi

On your computer, in Quartus, open the relevant programming file in the Programmer (POF for Flash programming), then go to File > Create/Update > Create JAM, JBC, SVF or ISC File. Make sure the file type is set to SVF, the operation is set to Program, and the speed set to something low like 1.0 MHz (the default of 25MHz is too fast for reliable operation with jumper wires). Move this file over to the Pi using PSCP.

Theoretically it should be possible to generate an SVF file from a SOF file and just program the SRAM, but I wasn't able to get this to work - programming would complete but the Flash (yes, the flash) would be erased.

Running this command will program the FPGA with the new design:

openocd -f openocd.cfg -c init -c "svf teletext-decoder.svf" -c shutdown

The new design should now be running on the FPGA.

If the Flash programming fails partway through the procedure, check that your jumper wires are making a good connection, then try lowering the clock speed when generating the SVF file. It would fail partway through the programming every time at the default frequency (25MHz) but it worked first time when I used 1MHz - the speed may need to be lower if using very long jumper wires.

Conclusion

This is a useful method for programming FPGAs with a Raspberry Pi. I see it as being especially useful for FPGA-based Pi Hats, where the FPGA can be permanently connected to the relevant pins on the Hat's connector, and also useful for products like the FPGA Teletext Decoder board where users are likely to own a Raspberry Pi but not a USB Blaster.

Next: Generating a high resolution HDMI/DVI output from an FPGA using Double Data Rate I/O

An alternative?

An alternative that's worth investigating: JTAG network programmer for Altera Quartus Prime. This should allow programming straight from the Quartus programmer and allow the use of SignalTap. When I tried it on an original Raspberry Pi, it was detected by Quartus, but the Raspberry Pi would freeze up completely whenever I tried to use it. I will experiment with it again on a newer model Raspberry Pi some time.

When trying that method, note that the pins on the header which need to be connected to the FPGA are different to the OpenOCD method, and after copying nw_jtag_srv to the Raspberry Pi, be sure to change its permissions using chmod 777 nw_jtag_srv.

Saturday, July 11, 2020

VHDL Teletext Decoder Part 3: Data recovery, processing and page extraction

In Part 2, I designed a display controller which can display a 40 character by 25 line screen of text and graphics as used by Teletext (and Mode 7 on the BBC Micro). The display controller reads from a dual-port RAM, which means it can be used in any FPGA project where data display and basic graphics are required. In the case of a teletext decoder, the VBI data needs to be extracted from the composite video signal, error correction needs to be applied, and then the data relating to the currently selected page number needs to be loaded into the dual-port RAM so that it is displayed by the display controller.

RTL block diagram

The teletext decoder section of the design has been broken up into four modules:

  • TXT_DATA_RECOVERY: Detects the start of a packet (line) of teletext data and extracts the serial data
  • TXT_DESERIALISER: Converts the serial data to parallel
  • TXT_DATA_PROCESSOR: Performs error correction on the data and determines the page number and subcode of each packet received
  • TXT_MEMORY_CONTROLLER: Writes packets from the selected page number to the dual-port RAM

Parallel vs serial transmission schemes

Not to be confused with serial and parallel data, these terms refer to the order in which pages are transmitted in the VBI (Vertical Blanking Interval).

In a parallel mode scheme, a few lines from one page in a particular magazine are transmitted followed by a few lines from a page from another magazine and so on. This approach was favoured by UK broadcasters when teletext was broadcast here and is still used by TEEFAX. This scheme can be identified from the display of page numbers only from the current magazine in the rolling header when waiting for a page.

In a serial mode scheme, all packets transmitted belong to the page number and magazine of the last header packet transmitted. This approach is favoured by international broadcasters, and is identified by page numbers from every magazine being displayed in the rolling header.

More detailed information about these schemes is described in Annex B of ETSI EN 300 706.

TXT_DATA_RECOVERY

This module detects the start sequence of the packet then forwards the serial data onto the next module. The start of the packet is shown below:

To simplify the design and avoid the need for a high-speed ADC, the packet is simply digitally sampled, which means the module only has a single digital input. A high-speed comparator is the only active semiconductor required, although the user may have to adjust the trimmer pot RV1 when switching sources.

Composite video input and sampling circuit

The comparator converts the analogue signal into a digital signal suitable for the FPGA:

The data recovery module, along with the rest of the teletext decoder section, runs at 27.750MHz, four times the data bit rate. A SI5351 programmable clock source is used to generate this unusual frequency with great accuracy along with the unique video clock required by the VGA controller for each resolution.

There are four sampled bits per data bit (known as 4x oversampling), and the bit rate has a very tight tolerance so there is no need to worry about clock recovery, which simplifies the design a lot. A shift register is continuously fed with the digital data provided by the comparator and a packet is detected once the start sequence is detected in the shift register. One out of every four bits will be sampled during the inter-bit transition, so the comparison is performed using only three sampled bits in every data bit, then the data sent to the next module is recovered from the middle sampled bit of each subsequent data bit.

Start pattern in teletext waveform

The colour burst is not sampled, nor is the video line number counted. These are not necessary for decoding, and the only consequence is the decoder will be capable of receiving anything transmitted otherwise in accordance with the specification which may be rejected by a commercial decoder.

TXT_DESERIALISER

This module converts the recovered data from serial to parallel. The FRAME_VALID_OUT signal indicates the start and end of each packet received to the next module.

TXT_DATA_PROCESSOR

This is the most complex module in the teletext decoder.

Page number detection

The two main types of packet are page header and normal packets. There's also non-displayable packets, but those will be explained later.

Teletext packet types

Every packet contains the magazine number, which is the leftmost digit of the three-digit page number, however, only the header packet contains the remaining two digits of the page number and the subcode. When a parallel transmission scheme is used, pages from different magazines may be transmitted on different lines in the same video frame, which means that the data processor must remember the page number and subcode of the last transmitted header packet for each of the 8 magazines. This is stored in the PAGE_AND_SUBCODE_CACHE whenever a header packet is received and retrieved whenever a packet other than a header packet is received so that the correct page and subcode is always sent to the next module along with the decoded data.

Packet processing

Hamming coding is used to protect certain data such as the page number and subcode to improve the chances of the header packet being decoded correctly in cases of poor reception. This reduces the chance of an incorrect page being loaded on top of the displayed page when a subsequent header packet is incorrectly determined to have the selected page number. Unfortunately, Hamming code dramatically increases the number of bits taken up by each piece of data, so it is only possible to use it for the most important data, and the display data is only protected with a simple parity check with no error correction capability because it is less critical that every display byte is correctly decoded. It should be noted that correct decoding of every single display byte is expected when the signal quality is good and there is no interference.

The parity checking and Hamming decoding is carried out by sub-modules (VHDL components) in TXT_DATA_PROCESSOR.

TXT_MEMORY_CONTROLLER

This module selects the appropriate address in the dual-port RAM to write each packet belonging to the specified page number to, then forwards the packet data on to the dual-port RAM.

It also controls the display of the status line. The last 8 characters should be continuously updated regardless of the magazine of the header row, whilst the rest of the header row should only be updated when waiting for a newly-selected page number ("search mode"), in which case only header rows from the current magazine should be displayed when the pages are transmitted using the parallel transmission mode, and header rows from all magazines should be displayed when pages are transmitted using the serial transmission mode.

This module also extracts the editorial links (also known as Fastext) for the four coloured buttons and the index button from the relevant non-displayable packet.

The 8 characters in the top left corner of the display are called the status characters. These are controlled by the keypad controller, and are used to display the page number and subcode number. The memory controller monitors these characters and updates the dual-port RAM whenever they change. The UPCOMING_FRAME_IN signal ensures that page content updates (which must happen in real-time) are prioritised over updates to the status characters.

The REQ_SUBCODE_SPEC_IN signal is set when the user requires a specific sub-page to be displayed. When the Time-Text button is pressed, the keypad controller sets the REQ_SUBCODE_SPEC_IN signal and the memory controller will then only display pages where both the page number and subpage match the requested values, otherwise the subpage is ignored and all subpages from the current page will be displayed as they are received.

Screenshot of the project in operation

The project can decode pages generated by VBIT2 running on a Raspberry Pi.

Teletext decoder connected to a display, showing a teletext page

Next: Data recovery, processing and page extraction

Saturday, April 18, 2020

VHDL Teletext Decoder Part 2: Page Display including Text and Graphics

In Part 1, I designed a very simple VGA controller, but without any content to display, it's not very useful. The next part that's required is the display controller, which is responsible for drawing the text, graphics and colours on the display.

The Display Controller shall take its input from the read side of a dual port RAM, which shall be written to by the Teletext Data Processor in this project, but the Teletext Data Processor could easily be replaced with something else for non-Teletext applications. A Display Controller based on the Teletext specification makes an excellent FPGA display generator, even for non-Teletext applications. This is because the specification was designed in the 1970s to be relatively easy to design an ASIC around, which means that the compiled logic size is very small compared to modern FPGAs, and makes it very "cheap" in terms of resources to add the display generator to any project.

This is generated entirely by an FPGA

Level 1 Teletext supports text and graphics via a character generator ROM, mosaics of 2x3 pixels, and selectable background colour.

Key display features

Text

The font used was taken from the datasheet for the Texas Instruments SN74S262N CGROM, originally used in the 1970s "Tifax" decoder design, and entered into a VHDL file.

The CGROM has a row output format, which is better than column output for horizontal-scanning display systems because the input address only has to change once for each character along the line currently being drawn rather than for every pixel.

Graphics

Basic graphics capability is provided by 2x3 mosaic tiles:

Careful examination of the mosaics set will reveal that each of the six tiles in the mosaic correspond to particular bits of the address. No additional CGROM is required, and the logic to draw the mosaics ends up being very simple. The addresses marked (1) are displayed from the CGROM; this allows CAPITAL LETTERS to be mixed in with mosaic graphics without having to switch between text and mosaic modes.

Mosaics can be contiguous or separated; this is set using spacing attributes.

Double height text

Double height display can be enabled at any point on a line, and applies to any characters or mosaics drawn after the Double Height command up until the end of the line or a Normal Height command. If a Double Height command is used anywhere on a line, the entire line below is ignored (even below characters that aren't double height), which seems strange, but this simplifies the logic design. The CGROM glyphs are easily "doubled in height" by bit-shifting the row selector right by one bit when in double height mode, with just a little bit of extra logic required to correctly draw the second line.

Colours

The available colours are: White, Yellow, Cyan, Green, Magenta, Red, Blue, Black. All of these can be output using only one bit per channel (R, G, B). Black cannot be used as the foreground colour in standards-compliant Level 1.0 and Level 1.5 Teletext decoders, but most modern decoders will interpret it anyway. Not interpreting it does not make the logic simpler, but my decoder will be designed to not interpret it in order to stick as closely to the specification as possible.

Spacing attributes

All attributes are set using these commands. They are called "spacing attributes" because each one takes up a space on the display. Level 2.5 and 3.5 Teletext also supports non-spacing attributes which do not take up space on the display, but that is outside of the current scope for this project.

All attributes are described in detail in Section 12.2 of the ETSI EN 300 706 V1.2.1 Enhanced Teletext Specification.

Attributes can be "set-at", meaning they apply immediately, or "set-after", meaning they apply from the next character position onwards. Correct implementation is required to ensure the correct display of "held" mosaics. Hold mosaics is a feature which displays spacing attributes as a repeat of the last mosaic to enable seamless colour changes; without hold mosaics, spacing attributes would always be displayed as spaces and graphics like the famous Ceefax weather map would not be possible.

Mix and Reveal

The Conceal attribute hides text and mosaics. Setting the Reveal input high will display the hidden content.

The Mix feature on a real TV displays the foreground content on top of the TV picture. All background colours effectively become transparent. Currently, the design has no capability of drawing the text on top of an existing video signal, so all this function does at the moment is show all background colours as black. The Start Box and End Box attributes are unimplemented for the same reasons.

Firmware

The main parts of the logic are:

  • Combinational logic for mosaic generation
  • CGROM
  • Active area controller: Determines whether we're currently in the area of the screen where the content will be drawn using a set of constants which determine character height, display height and width, characters per line, and number of VGA pixels per Teletext pixel
  • Line and character counters: each pixel and each row within each character needs to be counted
  • Display generator: reads the dual port RAM data and interprets the spacing attributes
  • Logic to control setting the output signals to the foreground/background colours, and flashing

Teletext uses 40 characters and 25 lines, but the display controller is in no way constrained to that, and this can easily be changed using constants in the code for other applications.

Development

As the Display Controller operates independently of the Teletext decoding, development can be done without the decoder by loading a Teletext page into the dual port RAM by specifing it as the default data in Quartus. It is not possible to use .tti files directly because the spacing attributes are converted into ASCII sequences, and unnecessary header stuff is added, but it's simple enough to convert it to raw data by hand.

Here's the display from an early version of the display decoder:

A largely-completed display generator combined with working teletext decoder:

The firmware is available on GitHub

Despite making little attempt to "optimise" the code, the whole system including teletext decoding takes up only 6% of the small (to modern standards) 22k logic element Cyclone 10 LP FPGA.

Still to come...

Next: Data recovery, processing and page extraction

See all posts with the label Teletext to see my future progress on this project.

An additional challenge would be to design firmware to synchronise to an external VGA signal and draw text on top of the incoming VGA signal.

Wednesday, April 15, 2020

Driving a VGA monitor from an FPGA made simple (VHDL Teletext Decoder Part 1)

When your FPGA project needs to display a large amount of data, there is one solution which is simple but may not be obvious: the VGA monitor. An analogue monitor from a digital FPGA, really? Yes, it's actually really straight forward to generate VGA signals, and no external chips are required to do so. This topic has been covered many times before by others, and in this blog entry I will try to simplify it as much as possible whilst including some important details that are sometimes missed. HDMI/DVI are covered later in the series...

Project objective

If you haven't read it already, please read the introduction to the VHDL Teletext project over on my dedicated VHDL Teletext Overview blog entry, which covers the project objectives, a brief history of Teletext and its current uses, and the FPGA Teletext hardware.

The basics

The hardware

VGA has five key signals - R, G, B, Horizontal Sync, and Vertical Sync. There are additional signals which can be used to get details of the monitor via I2C, but those are outside of the scope of this entry.

Horizontal Sync and Vertical Sync are TTL-level digital signals, but it is sufficient to use 3.3V FPGA pins directly. These are not terminated in the monitor, so you can use low value series resistors to protect the FPGA pins in case of a short circuit without affecting functionality.

R, G and B are the analogue signals, which are terminated with 75 ohms inside the monitor. 0V corresponds to black and 0.7V is full brightness. A simple resistor ladder DAC will suit most FPGA applications; the design is very simple: decide how many "bits" of colour depth are required, then calculate the resistor values so that they follow these rules

  1. The voltage at the output, when terminated with 75 ohms, is approximately 0.7V when all FPGA outputs are on
  2. Each bit in order of decreasing significance should have a resistor value of double that used for the last bit

For many text-based applications, all that is required is one bit per channel, in which case a 270 ohm resistor is ideal when using an FPGA IO voltage of 3.3V.

The firmware

The firmware is simple too; there's just a few terms which need to be understood, and the best way to explain these is with a diagram:

The dotted arrows indicate the retrace of the electron beam on CRTs; this happens during the blanking interval. LCDs don't have a retracing anything, but the timings remain the same for compatibility. The scanning is done from left to right, top to bottom. The HSync waveform happens once per line, whereas the VSync waveform happens once per frame, so the HSync signal will pulse hundreds of times for each VSync pulse.

Translating this to HDL, all that's needed is:

  1. A "Horizontal" counter which is normally incremented once per clock cycle
  2. A "Vertical" counter which is incremented every time the Horizontal counter completes a full cycle
  3. Some logic to generate the sync signals and ensure the video is blanked during the blanking intervals

But wait...

VGA was originally designed for CRT monitors, but nowadays it is frequently used on LCD monitors. How does it know what the horizontal time period of each pixel is when it doesn't know what the pixel clock is?

Well, it doesn't. The "Auto Adjust" function attempts to work this out, but it only works if the active area is full of content. If you're using a black background and the content doesn't reach the edges, then the Auto Adjust function may not work correctly. To ensure correct Auto Adjustment when using a black background, a border around the edge of the active area should be drawn. This will, of course, be visible on the monitor, but that's only a minor price to pay to guarantee correct adjustment. This is easy to implement with a line of combinational logic.

Resolution selection

The resolution is limited by the speed of the FPGA, the quality of your hardware, and the length of your cables. The resolution is normally dictated by the requirements for what needs to be displayed.

The most common resolution is 640x480; with a pixel clock of 25.15 MHz, this can be closely matched using a common 25MHz clock. 1280x960 can be achieved with a 100MHz clock. High resolutions can be achieved with lower clocks if you are happy to use non-square pixels (wider than they are tall).

It's a good idea to specify the parameters using constants or generics to allow them to be changed easily. Any experimentation should be done with an LCD monitor, since some CRTs can be blown up by improper timings. Here are some typical timings for 640x480:

  • H Size: 640 clock cycles
  • H Front Porch: 16 clock cycles
  • H Sync Pulse: 96 clock cycles
  • H Back Porch: 48 clock cycles
  • V Size: 480 lines
  • V Front Porch: 11 lines
  • V Sync Pulse: 2 lines
  • V Back Porch: 31 lines

The calculation for the required clock frequency is: F (Hz) = Frames per second * (H Size + H Front Porch + H Sync Pulse + H Back Porch) * (V Size + V Front Porch + V Sync Pulse + V Back Porch)

For most monitors, the frames per second should be around 60Hz. The lower limit is usually around 50Hz, and the upper limit 65Hz, or 80Hz for most monitors with a resolution of 1280x1024.

Example code

Link to my example code (read the points below first)

  • My code just outputs internal sync signals, but you may desire to output the pixel counter values instead; this would be simple to implement. (hint: convert integer to std_logic_vector)
  • This code does not generate any display content on its own, not even the test card. Generating this is a challenge for the reader.
  • The timings have been modified for 720x576 resolution, therefore the clock frequency required is higher (see equation above). The border generator has also been written to draw the vertical borders offset from the edges. LCD monitors which can't sync to this resolution should detect it as 800x600.

Next: let's generate our content.

Large dot matrix clock, timer and scrolling message display

First completed: August 2014. Additional work carried out late 2025/early 2026. Introduction Previously, the author had made a very small ...