You are here: HomeProjectsSeries 1 - Display Driver BoardsSeries 1: Part 8 - Writing A Skeleton TFT-LCD Display Device Driver

Series 1: Part 8 - Writing A Skeleton TFT-LCD Display Device Driver


Abstract

0000445In this part of the series a method of using symbolic logic to represent HDL, when designing the architecture of a hardware algorithm, is introduced. The method is used to develop a modular, skeleton, device driver for TFT-LCD displays in general and the LQ043T3DX02 display (used in the Sony PSP) in particular. The design method introduced is used to generate a display’s horizontal and vertical synchronisation timing signals, by using its (the display's) timing characteristic parameters provided in its datasheet. Finally, by using a color look-up table a method of displaying images on the display is demonstrated.

1.8.1 Introduction

In the last part of the series the prototype display and driver board has been discussed. However, since then a lot has been going on in the background here and a need has arisen to pursue a slightly different approach. Hence, the series will continue with the development of display specific driver boards and display independent graphic processor boards.

The newly derived system design, which should allow us to support more display types, should work like this. Typically, each display type requires specific driver board functionality, but similar display graphics. Hence, the FPGA used to provide the display's graphics, including drawing routines, could be developed independently. Therefore, the new design philosophy is to develop display driver boards and a separate FPGA graphics processor board. Also, the newly developed system will allow the user to configure the FPGA using their own microcontroller. The conceptual diagram can be seen, in Figure 1:8-1, below.

0000388b

Figure 1:8-1: The newly designed system will consist of display specific driver boards (1) and a display independent graphics processor board (2). Also, configuration of the FPGA will be be provided by a user provided micro-controller (3). 

More information, on this revised concept, will be provided from the next part of the series onwards with the introduction of a new display! For now the aim of this part of the series is to tackle one of the most fundamental parts of any display system, developing the display's device drivers, used to display images on the display. This article  has been republished from Ben's former blog and appropriately slotted in here, as part 8 of series 1.

The aim of developing a skeleton device driver so early on in the series, before finalising the hardware design concept, has some of the many disadvantages stressed repeatedly in system design text. However, in this particular case there could be some advantages too:

  1. Having a skeleton display system could allow us to experiment with computer graphics algorithms in real-time as the series develops.
  2. It should provide some much needed experience for readers that are either rusty with their digital design (and need a practical project to encourage them to dust off that old FPGA development kit bought out of enthusiasm but rarely used) or for those that are just starting out and want to see how it is done.
  3. It also provides an opportunity for the more advanced user to smile knowingly when a recognisable design feature unravels during the design process.

If you fall into the second category then enjoy the trip! The system requirements of the project will be "matured''  anyway as other parts in the series are developed.

0000445c

Figure 1:8-2: To illustrate the pronciples of hardware design a skelton device driver is developed.

Whilst it is probably more efficient to implement digital logic by writing code in a text editor using a Hardware Description Language (HDL) like VHDL or Verilog, it is sometimes much easier to explain a concept or idea by using diagrams, after all a picture is regarded as being worth a thousand words (although maybe not a thousand lines of well thought out and well written code!).

Hence, in this series, or this part at least, an attempt should be made to show and explain the architecture of an algorithm or concept rather than simply "hand'' readers the code. Taking this approach is probably the one that would be favoured by most readers anyway, as they are likely to be  abhorred by the thought of not being allowed to think and work things out for themselves.

An added benefit of doing things this way is that the series should remain hardware language independent, which allows users of "higher-level of abstraction" type hardware description languages, like System-C, to follow along and contribute too, if they so wish. It also presents the opportunity, in the future, to consider OpenCL for FPGAs [1] too.

1.8.2 Notation

Standard schematic notation, if there is such a thing, is not used in this document. However, if a reader sees the symbol in Figure 2 below it would not be unreasonable to expect the reader to interpret it in the following way. 

The symbol in Figure 1.8-3 could be that of an entity (VHDL) or module (Verilog) known as Register32 (1). It has a 32-bit input signal known as DIN (2) and a 1-bit input signal known as Enable (3), which is asserted when set to a logic level of '1'. The value on the input, DIN, is transferred to the 32-bit output signal DOUT (5), on the rising edge of the Clock (4) signal.

0000445d

Figure 1.8:3 : A demonstration of a schematic symbol that has two 32-bit ports (2), (5). The name of the symbol is Register32 (1) and it has an Enable (3) signal. Register32 transfers its input data DIN(2) to its output DOUT (5) on the rising edge of the Clock (4) signal. 

If instead of transferring data on the rising edge of the clock it is transferred on the clock's falling edge, then the clock symbol would be changed from that labelled (4) to the one labelled (6). Likewise, if the Register32 transfers data from its input to its output when the Enabled signal is asserted low then symbol (7) would replace symbol (3).

Right, with all that housekeeping taken care of, lets not hold back and jump in at the deep end, as usual!

1.8.3 Horizontal Synchronisation Signal, Hsync.

To begin with lets assume that we have a counter that is driven by a clock, say with the same timing characteristics as a clock used to drive a TFT-LCD display, like the one shown in Figure 1.8-4.

0000445e

Figure 1.8-4 : Display timing characteristics of the clock parameter for the LQ043T3DX02 display, used in the Sony PSP displays.

When this counter is enabled, it increments its value every 1/(9 MHz) or 111.11 ns.

 0000445f

Figure  1:8-5 : Schematic symbol of a 10-bit counter with an asynchronous reset signal. When the Enable signal is asserted (logic level = '1') the counter outputs a sequence of numbers that increment by 1 on the rising edge of the Clock signal.

Now, a counter that can increment its value up to at least 524 must have a data width of at least 10-bits since 52410 = 10000011002. The symbolic representation of such a counter could be similar to the one shown in Figure 1:8-5.

If this counter is enabled permanently it would output a sequence of numbers repeatedly from 0 to 1023, in increments of 1, on the rising edge of the clock. If the signal, nAclr, is asserted (low) the counter could be set to its reset value asynchronously. If nAclr is never asserted the counter would return to 0 on the next clock cycle after 1023 and repeat the same sequence of numbers unfailingly. The counter can, of course, only increment its value when it is enabled.

Now, supposing that we require a counter that repeats itself after every 525 clock cycles. If the duration or time period of a clock cycle is 111.11 ns, then the period Th of the repeated sequence is   111.11 ns x 525  or 58.363 us and the frequency fh of this time period  can then be determined to be

 1/(58.363 x 10-9) = 17.13 KHz

0000445g

Figure  1:8-6 : The horizontal period of the LQ043T3DX02 display  is 525 clock cycles in duration and has a typical horizontal scanning frequency of 17.13 KHz.

Given our counter, defined in the schematic symbol in Figure 1:8-5, how can we use it to repeatedly count from 0 to 524 as shown in Figure 1:8-6? Well, one way could be to use an equality comparator to assert the counter's reset signal, asynchronously, each time its comparative value is true. [We could also create a coutner that we could clear  synchronously, if we wanted to. - B.P]

 0000445h

Figure 1:8-7 : Horizontal Synchronisation Timing Generator: When the counter's output is equal to 525, then it is comparatively equal to the constant, 525 and the inverted output of the comparator is asserted low.

So what have we just done? Well, If you refer to the timing characteristics diagram, in section 7-1 of the LQ043T3DX02 data manual and repeated in Figure 1:8-8, then you would have noticed that the number 525 corresponds to the length of the horizontal synchronisation period, in clock cycles.

0000445i

Figure  1:8-8 : The horizontal synchronisation parameter's timing characteristics.

Thus, we have created the first meaningful component of our skeleton device driver, a horizontal synchronisation timing generator, or counter, that could  be used to generate all of our horizontal synchronisation and timing data, shown in Figure 1:8-8.

Now supposing that we have constructed this horizontal synchronisation module using our favourite HDL or schematic entry package, how do we know if it will always work as expected? Well, one way of ensuring that it should always work as expected is to test it by writing a test infrastructure or testbench. It is generally considered to be  good practise to write a testbench and simulate every module after its completion and before moving on to designing the next module in the design.

If our testbench is rigorous and our horizontal synchronisation timing generator passes all of its tests, then it should mean, in principal at least, that we would never have to write this module again and we could file it safely away!

In practise however there could be a bug in our code, which would require us to modify it and hence make what is generally regarded as a revision or version change. To facilitate tracking all of our version changes we could make use of the numerous revision and version control tools that are available non-commercially, as well as commercially, on the various computing platforms.

We could also decide that we wish to keep a log of all of the bugs that we have come across in our design,  by using some form of bug tracking software. No doubt such software packages exist too and using one could enhance your chances of success considerably, on major as well as small projects.

Before we deposit this part of our code's  design into our archive, for use later on in this and other projects, we might also like to provide a header at the top  of the code to describe what this chunk of code does, when it was written and by whom. Recording the version number and any known issues could help too. A summary of some housekeeping tasks that could be done after each module is written is shown in Figure 1:8-9.

After all that has been done we can bank this module and move on to the next part of the design exercise.

 0000445j

Figure  1:8-9 : Writing a testbench, recording version changes and keeping documentation for all of the modules in a design should be habitual rather than occasional. Keeping track of bugs in the whole of the design should help too!

Now according to Figure 1:8-8, which is a snap shot of the horizontal synchronisation timing characteristics (taken from section 7-1 of the LQ043T3DX02 data manual), the horizontal synchronisation period is typically 525 clock cycles in duration. This horizontal synchronisation region consists of four horizontal sub-regions of varying  durations. They are the (1)  horizontal pulse width, (2) horizontal back porch, (3) horizontal period and (4) the horizontal front porch as can be seen in Figure 1:8-10. Each region has a column in the table that defines the typical duration of the region  in clock cycles.

 0000445k

Figure 1:8-10 : The horizontal synchronisation timing parameters are the pulse width, back porch, horizontal period and the front porch. The ordering of these parameters is typical of most displays and maintains backward compatibility with analog displays.

Consider the sub-regions of the horizontal synchronisation period described above. Then, each sub-region could be considered to have a start value that coincides with a horizontal timing counter value ai and ends at a horizontal timing counter value of bi. In other words the width, x, of each horizontal synchronisation parameter or sub-region, in clock cycles, is bounded by the region given by:

ai <=  x  >=  bi

where x, as noted previously, is the translated duration of the region in clock cycles. Thus, if we wish to determine which region the horizontal synchronisation timing counter is in at any particular time, we could design an algorithm that allows us to detect when the value of the counter's output, Q[9:0], is within the region between the boundary points ai and bi at any instance as can be seen in Figure 1:8-11.

 0000445l

Figure  1:8-11: The instantaneous value of the horizontal synchronisation  timing counter  determines the current region that the count value coincides with.

If we decide to use logic to implement a region detect algorithm, then one method that could be used would require two magnitude comparators and an AND gate as can be seen in Figure 1:8-12.

 0000445m

Figure  1:8-12 : Region Detect: A region detect algorithm could be used to determine the horizontal sub-region, that coincides with the output value of the horizontal synchronisation timing counter. The bounds of the region are defined by the inputs a and b. The input, c, is the output of the horizontal synchronisation timing counter and is compared to a,b to determine if c is within the bounds of a,b.

Lets demonstrate how this works with an example. The horizontal pulse width period is bounded by the horizontal synchronisation timing counter values of ai = 0 and bi = 40 as can be seen in Figure 1:8-13.

Thus, if logic for the region detect algorithm shown in Figure 1:8-12 has been written, tested and documented then, we could set the a input to 0 and the b input to 40. Then, if we connect the c input of the algorithm to the output of the horizontal synchronisation timing generator, seen inn Figure 1:8-7 above, you should see the detection of what is referred to as the horizontal synchronisation signal or Hsync as can be seen in Figure 1:8-13.

 0000445n

Figure  1:8-13 : The Horizontal Synchronisation Signal or Hsync can be detected between counter values 0 and 40. A display can require a Hsync signal with negative or positive polarity.

Likewise, you could detect the horizontal period  or horizontal active video width by setting the a input to 43 and the b input to 522. What you have done, now, is detect the active display area of a line or in other words you have detected a horizontal region on the TFT-LCD screen where pixels are visible. In fact each counter value in this region corresponds to a pixel value.

What about the front and back porch detection areas then? Well, as it turns out they have no real "immediate'' value in driving the LQ043T3DX02 display or digital displays in general for that matter. However, they are usually provided in data manuals to maintain backward compatibility with device drivers written for the previous generation of analog displays. We can safely ignore the porch regions in our skeleton driver.

To conclude this section we should put all the symbolic building blocks together and end up with an entity or  module symbol similar to the one shown in Figure 1:8-14. We could name this module our Horizontal Synchronisation Timing Module.

 0000445p

Figure  1:8-14 : A Horizontal Synchronisation Timing Module allows us to determine all of the sub-regions with the horizontal period. In the  figure shown the horizontal period and Hsync can be detected.

If you have got this far and have understood every printed word, then you are a third of the way into writing a skeleton display device driver for the LQ043T3DX02 display. On the other hand if you have really struggled up to this point then the good news is that when you do get it and you have developed your first working model, the more you will appreciate the product of your hard work.

The bad news is that we are still travelling uphill, thus it might be a while before there is some respite. However, like most things the more effort you put in the easier it should become, so stick with it. In the next section we shall look at another third of the design, the vertical synchronistion signals.

 

 1.8.4 Vertical Synchronisation Signal, Vsync.

For those of you that are clever, lazy or both even, you might be wondering if there could be some commonality of code between the horizontal and vertical synchronisation  timing modules. You could be wondering whether you could save some time, energy or both, by reusing the same code. Especially since the horizontal synchronisation timing module already developed should have been verified by simulation, documented and impressively archived  with a testbench by now!

0000445q

Figure  1:8-15 :The vertical synchronisation parameter's timing characteristics.

Indeed, if you compare both of the horizontal and vertical synchronisation timing parameters, in Figures 1:8-8 and 1:8-15, you should recognise that the names are identical except where in some cases the word horizontal has been replaced with vertical.

However, there is a distinctive and important difference between the two sets of parameters. Whilst the horizontal synchronisation parameters are typical values defined in units of  pixel clock cycles, the vertical synchronisation parameters are a typical value in units of the number of display lines.

 0000445r

Figure  1:8-16: The vertical synchronisation timing parameters are the pulse width, back porch, horizontal period and the front porch. The ordering of these parameters is typical of most displays and maintains backward compatibility with analog displays.

For those of you that have recognised the similarity and a "bell has tinkled'' then you have stumbled upon a very important concept in the digital design process, the concept of modularity and component reuse. Why write a separate module to generate the vertical "synchronisation'' timing data, when we have already written one to generate the horizontal "synchronisation'' timing data. The two should be almost identical, except for their incremental units.

If the synchronisation timing  module used to derive the Hsync and horizontal active region, described in the Horizontal Synchronisation Signal section above, has been carefully coded, then all that we should need to do is to create a  generic module that could be adapted to generate both of our vertical and horizontal synchronisation timing data.

Creating a generic synchronisation core could mean that you would only need to code a singular synchronisation module once, test and document it, but use it for both of the horizontal and vertical synchronisation instances. To do so however could require you to make some changes to the horizontal synchronisation timing  module that you have already developed.

If you have hard-coded parameter values, to detect regions for example (in the horizontal synchronisation timing module), then you could use generic constants in a paramerterised module. This should provide a path towards developing a single module with parameterised inputs. In this case the parameterised inputs could be used to detect the periods of interest.

However, there is one caveat as mentioned before. While the horizontal synchronisation counter increments in units of clock cycles the vertical equivalent counter should increment once for every new horizontal line [Hint: That is the vertical synchronisation counter increments by 1, every 525 clock cycles.] So what is missing?

 0000445s

Figure  1:8-17 : The vertical period of the LQ043T3DX02 display  is 285 lines in duration and has a typical vertical refresh frequency of 60 Hz.

Well, we need a way of detecting the end of a horizontal period, such that it can be used to update or increment the vertical period. Thus, if we can detect the end of this period, by creating a horizontal period end detect module, we could use it to assert the enable signal of our vertical synchronisation counter in a vertical synchronisation timing module.

A symbolic modification to the horizontal synchronisation function, developed previously, with an integrated horizontal period end detect algorithm could look like the one in Figure 1:8-17. Doing things this way incorporates our modular HDL approach.

 0000445t

Figure  1:8-18: A horizontal period end detect module is used to assert the enable flag of a synchronisation timing module. The synchronisation timing module can now be used to provide the timing of the vertical period parameters in line durations.

Thus, with modularity and code reuse, which the clever readers amongst you may have already spotted, we could save a considerable amount of time on coding, testbench writing, simulation, verification documentation and version control management as well as bug tracking to say the least! The lazy ones should be delighted too, as it means that only the minimum amount of extra work is required to generate our vertical synchronisation data to drive the display. To make the point again, this has been achieved all because of modularisation and code reuse.

If you have successfully navigated your way up to this point then you should end up with a modified synchronisation function  like the one shown in Figure 1:8-18. We should now be able to generate the horizontal synchronisation signal, hsync and the vertical synchronisation signals, vsync. We should also be able to detect the horizontal and vertical active display periods or regions in display coordinates.

In fact if we make a plot of our horizontal vs vertical synchronisation parameters on a piece of paper we should end-up with a diagram similar to the one shown in Figure 1:8-19 below.

0000445

Figure  1:8-19 : A plot of the horizontal timing counter as columns against the vertical timing counter as rows produces the familiar x,y matrix or grid of displays, in display coordinates.

Yes, although we are able to display "nothing" on the display's screen, we should be able to drive the display with our Hsync and Vsync signals! What is missing however is some colour, that is,  a way to synchronise our horizontal and vertical period parameters with R,G and B display values. This is exactly what we are going to do in the next section, find a way of relating the horizontal and vertical display parameters and the horizontal and vertical active regions to RGB data.

If you have made it up to here then you are practically swimming!

 

1.8.5 Displaying Images 

Well, by the end of this section we should be able to display images on TFT-LCD displays once we have bridged the gap between generating the display's control signals and synchronising them with R,G and B display data.

If we refer back to Figure 1:8-19, then it should be noticed that the active video display area appears between horizontal and vertical timing counter values of  (12,43) at the top left-hand corner and (283,522) at the bottom right-hand corner, in display coordinates. What is need now is to translate this active video area in display coordinates into the familiar screen coordinates defined in part 1 of the series.

 0000445u

Figure  1:8-20 : Within the active display region,  the display coordinates generated by the horizontal and vertical timing counters could be converted into screen coordinates by a translation algorithm.

In our screen coordinate system pixel values start in the top, left hand corner at (0,0) and end at the right bottom hand corner at (271, 479). It seems that what we need to do is create a module that would allow us to translate the active periods generated by our horizontal and vertical timing counters into our screen coordinate system. A display to screen coordinated module that could perform this translation for us, with the horizontal and vertical periods used as control signals, can be seen in Figure 1:8-20.

So what does this module do exactly? Well it translates the display's coordinate system into the screen's coordinate system. It is this translation  that now provides us with the means to display images.

QU: 1 : If you were unhappy with the screen coordinate system could you convert it to a Cartesian coordinate one, by transforming the screen coordinate system such that (0,0) is at the bottom left hand corner and (271, 471) is at the top, right hand corner? Could this transformation be performed in the same or similar module to the Display-to-Screen-Coordinates module?

You might be thinking at this point that this all sounds and looks good but we still have not displayed images on the screen. Well, the next and final thing we need to do, in the development of our skeleton display driver, is use the generated screen coordinate values to index our memory display area which could be either an internal portion of a FPGA's embedded memory or an external memory module.

0000445v

Figure  1:8-21 : A screen coordinate (x,y) can be used to index a pixel 's RGB color by using a color look-up table.

If our screen coordinate value at any instance, (x,y),  indexes the colour value in a look-up table, for instance, and writes it to the screen within the same clock cycle, then our skeleton device driver is complete. However, when indexing colour look-up tables problems associated with timing synchronisation and memory latency could arise. If we cannot access the look-up table in the required time period, that is within 1 pixel clock cycle, then we could require more capable and generally more expensive memory devices to achieve a zero turn around clock cycle period.

Instead, what is need is a method that allows us to read a region of memory and store the data from it, a few clock cycles earlier than it is required for display.  Hence, we wiill still be in synchronisation with the horizontal and vertical synchronisation timing signals, Hsync and Vsync respectively. That is, enough clock cycles earlier to allow cheaper memory devices to be used. How this is done will be the subject of another next part of the seriesthat could be titled: A Skeleton TFT-LCD Device Driver using Video Memory.

1.8.6 Application to the LQ043T3DX02 Display Driver Board

There is nothing like putting theory to practise! The DS0-2150 oscilloscope, reviwed previously,  has been attached to the LQ043T3DX02 prototype driver board, which is itself  attached to the spartan 3A Starter Kit. The sampling of the hsync and vsync signals can be seen in Figures 1:8-22 and 1:8-23 below. If your skeleton device driver can display something similar then you're swimming!

 0000445w

Figure  1:8-22 : The   Hsync periods of a skeleton display driver have been captured in this  oscilloscope screen shot. The amplitude of the Hsync signal is 3.19V and the horizontal scan frequency is measured to be approximately 17.12Hz.

 

0000445x

Figure  1:8-23 : The amplitude of the Vsync signal is 3.25V  and the measured frequency of the  vertical period is 60.68 Hz.  This period is known as the refresh rate of the display, is it what we were expecting?

As you have probably noticed the timing periods and hence the frequencies measured  are slightly different from our theoretical values. Where have these discrepancies come from?  

1.8.7 Conclusion 

Well, this has really been a long journey. This part of the series has shown how to interpret the timing characteristics in a display's data manual and use it to derive the essential synchronisation signals required to display images. It has shown that paying careful thought to the architecture of a design, in a modular fashion, should help develop blocks of code that can be reused in other projects. It has also shown that by developing an efficient coding style and making use of all of the utility coding management software tools, that are available, there is  a greater chance of developing a skeleton display driver, or any module, that works flawlessly!

Thanks for taking the time in reading another part in the series. Next time we will continue by developing our new architecture and presenting a new display! That's all for now, until next time.

1.8.8 References

  1. Higher Level Programming Abstractions for FPGAs using OpenCL, 2011, Altera Corp.


Go to comments start