Chapter 14

A Simple VGA Interface

Abstract

The VGA interface is common to most modern computer displays and is based on a pixel map, color planes, and horizontal and vertical sync signals. A VGA monitor has three color signals (Red, Green, and Blue) that set one of these colors on or off on the screen. The intensity of each of those colors sets the final color seen on the display.

Keywords

VGA interface

Display

14.1 Introduction

The VGA interface is common to most modern computer displays and is based on a pixel map, color planes, and horizontal and vertical sync signals. A VGA monitor has three color signals (Red, Green, and Blue) that set one of these colors on or off on the screen. The intensity of each of those colors sets the final color seen on the display. For example, if the Red was fully on, but the Blue and Green off, then the color would be seen as a strong red. Each analog intensity is defined by a 2-bit digital word for each color (e.g., red0 and red1) that are connected to a simple digital-to-analog converter to obtain the correct output signal.

The resolution of the screen can vary from 480 × 320 up to much larger screens, but a standard default size is 640 × 480 pixels. This is 480 lines of 640 pixels in each line, so the aspect ratio is 640/480, leading to the classic landscape layout of a conventional monitor screen.

The VGA image is controlled by two signals—horizontal sync and vertical sync. The horizontal sync marks the start and finish of a line of pixels with a negative pulse in each case. The actual image data is sent in a 25.17 μs window in a 31.77 μs space between the sync pulses. (The time that image data is not sent is where the image is defined as a blank space and the image is dark.) The vertical sync is similar to the horizontal sync except that in this case the negative pulse marks the start and finish of each frame as a whole and the time for the frame (image as a whole) takes place in a 15.25 ms window in the space between pulses, which is 16.784 ms.

There are some constraints about the spacing of the data between pulses which will be considered later in this chapter, but it is clear that the key to a correct VGA output is the accurate definition of timing and data by the VHDL.

14.2 Basic Pixel Timing

If there is a space of 25.17 s to handle all of the required pixels, then some basic calculations need to be carried out to make sure that the FPGA can display the correct data in the time available. For example, if we have a 640 × 480 VGA system, then that means that 640 pixels must be sent to the monitor in 25.17 μs. Doing the simple calculation shows that for each pixel we need 25.17 μs/640 = 39.328 ns per pixel. If our clock frequency is 100 MHz on the FPGA, then that gives a minimum clock period of 10 ns, so this can be achieved with a relatively standard FPGA.

14.3 Image Handling

Clearly it is not sensible to use an integrated image system on the FPGA, but rather it makes much more sense to store the image in memory (RAM) and retrieve it frame by frame. Therefore, as well as the basic VGA interface, it makes a lot of sense for the images to be moved around in memory and using the same basic RAM interface as defined previously is sensible. Therefore, as well as the VGA interface pins, our VGA handler should include a RAM interface.

14.4 A VGA Interface in VHDL

14.4.1 VHDL Top Level Entity for VGA Handling

The first stage in defining the VHDL for the VGA driver is to create a VHDL entity that has the global clock and reset, the VGA output pins, and a memory interface. The outline VHDL entity is therefore given as follows:

1 library  ieee;

2 use  ieee . std_logic_1164 . all;

3 entity  vga  is

4   port  (

5   clk  :  in  std_logic;

6   nrst  :  in  std_logic;

7   hsync  :  out  std_logic;

8   vsync  :  out  std_logic;

9   red  :  out  std_logic_vector  (  1  downto  0);

10   green  :  out  std_logic_vector  (  1   downto  0);

11   blue  :  out  std_logic_vector  (  1  downto  0);

12   address  :  out  ( std_logic_vector  (  15  downto  0);

13   data  :  in  ( std_logic_vector  (  7  downto  0);

14   ram_en  :  out  std_logic;

15   ram_oe  :  out  std_logic;

16   ram_wr  :  out  std_logic

17  );

18 end  entity  vga;

19

20 architecture  core  of  vga  is

21

22  −−  vga internal signals go here

23

24 begin

25

26 −− vga interface core goes here

27

28 end architecture core;

The architecture contains a number of processes, with internal signals that manage the transfer of pixel data from memory to the screen. As can be seen from the entity, the data comes back from the memory in 8-bit blocks and we require 3 × 2 bits for each pixel and so when the data is returned, each memory byte will contain the data for a single pixel. In this example, as we are using a 640 × 480 pixel image, this will therefore require a memory that is 307,200 bytes in size as a minimum. To put this in perspective, this means that using a raw memory approach we can put three frames per megabyte. In practice, of course, we would use a form of image compression (such as JPEG for photographic images), but this is beyond the scope of this book.

We can therefore use a simple process to obtain the current pixel of data from memory as follows:

1 mem_read  :  process  (  pclk ,   nrst   )   is

2  signal  current_address  :  unsigned  (16  downto  0);

3 begin

4  if  nrst  =    0 then

5 pixelcount <= 0;

6 current_address <= 0;

7  else

8 if rising_edge(pclk) then

9 current_address <= current_address + 1;

10 address <= std_logic_vector(current_address);

11 pixel_data <= data;

12 end if;

13  end if;

14 end process;

This process returns the current value of the pixel data into a signal called pixel_data, which is declared at the architecture level:

1  signal  pixel_data  :  std_logic_vector   (   7   downto   0   );

This has the red, green, and blue data defined in the lowest 6 bits of the 8-bit data word with the indexes, respectively, of 0-1, 2-3, and 4-5.

14.4.2 Horizontal Sync

The next key process is the timing of the horizontal and vertical sync pulses, and the blanking intervals. The line timing for VGA is 31,770 ns per line with a window for displaying the data of 25,170 ns. If the FPGA is running at 100 MHz (period of 10 ns) then this means that each line requires 3177 clock cycles with 2517 for each line of pixel data, with 660 pulses in total for blanking (330 at either side). This also means that for a 640 pixel wide line, 39.3 ns are required for each pixel. We could round this up to 4 clock cycles per pixel. As you may have noticed, for the pixel retrieval we have a new internal clock signal called pclk, and we can create a process that generates the appropriate pixel clock (pclk) with this timing in place.

With this slightly elongated window, the blanking pulses must therefore reduce to 617 clock cycles and this means 308 before and 309 after the display window.

The horizontal sync pulse, on the other hand, takes place between 26,110 ns and 29,880 ns of the overall interval. This is 189 clock pulses less than the overall line time, and so the horizontal sync pulse goes low after 94 clock cycles and then at the end must return high 95 clock cycles prior to the end of the line. The difference between the outside and inside timings for the horizontal sync pulse is 377 clock cycles and so the sync pulse must return high 94 + 188 clock cycles and then return low 95 + 189 prior to the end of the window.

Thus, the horizontal sync has the following basic behavior:

ClockCycle Value
01
940
2821
28930
30821

and this can be implemented using a process with a simple counter:

1 hsync_counter  :  process  ( clk ,  nrst  )  is

2  hcount  :  unsigned  (  11  downto  0  );

3 begin

4   if   nrst   =     0 then

5 hcount <= 0;

6 hsync <= 1;

7 else

8 if hcount > 2611 and hcount<2988 then

9 hsync <= 0;

10 else

11 hsync <= 1;

12 end if;

13 if hcount < 3177 then

14 hcount <= hcount + 1;

15 else

16 hcount <= 0;

17 end if;

18  end if;

19 end process;

14.4.3 Vertical Sync

The horizontal sync process manages the individual pixels in a line, and the vertical sync does the same for the lines as a whole to create the image. The period of a frame (containing all the lines) is defined as 16,784,000 ns. Within this timescale, the lines of the image are displayed (within 15,250,000 ns), then the vertical blanking interval is defined (up to the whole frame period of 16,784,000 ns) and finally the vertical sync pulse is defined as 1 until 15,700,000 ns at which time it goes to zero, returning to 1 at 15,764,000 ns.

Clearly it would not be sensible to define a clock of 10 ns for these calculations, so the largest common divisor is a clock of 2 s, so we can divide down the system clock by 2000 to get a vertical sync clock of 2 s to simplify and make the design as compact as possible.

1 clk_div  :  process  ( clk ,  nrst  )  is

2 begin

3   if   nrst  =     0 then

4 count <= 0;

5 vclk <= 0;

6 else

7 if count = 1999 then

8 count <= 0;

9 vclk <= not vclk;

10 else

11 count <= count + 1;

12 end if;

13  end if;

14 end process;

where the vertical sync clock (vclk) is defined as a std_logic signal in the architecture. This can then be used to control the vsync pulses in a second process that now waits for the vertical sync derived clock:

1 vsync_timing   :   process   ( vclk )   is

2 begin

3   if   nrst   =     0 then

4 vcount <= 0;

5 else

6 if vcount>15700 and vcount <15764 then

7 vsync <= 0;

8 else

9 vsync <= 1;

10 end if;

11 if vcount > 16784 then

12 vcount <= 0;

13 else

14 vcount <= vcount + 1;

15 end if;

16 end if;

17 end process;

Using this process, the vertical sync (frame synchronization) pulses are generated.

14.4.4 Horizontal and Vertical Blanking Pulses

In addition to the basic horizontal and vertical sync pulse counters, we have to define a horizontal blanking pulse which sets the line data low after 25,170 ns (2517 clock cycles). This can be implemented as a simple counter in exactly the same way as the horizontal sync pulse and similarly for a vertical blanking pulse. The two processes to implement these are given in the following VHDL.

1 hblank_counter   :   process   ( clk ,   nrst   )   is

2   hcount   :   unsigned   (   11   downto   0   );

3 begin

4   if   nrst   =     0 then

5 hcount <= 0;

6 hblank <= 1;

7 else

8 if hcount > 2517 and hcount<3177 then

9 hblank <= 0;

10 else

11 hblank <= 1;

12 end if;

13 if hcount < 3177 then

14 hcount <= hcount + 1;

15 else

16 hcount <= 0;

17 end if;

18 end if;

19 end process;

20 vblank_timing : process (vclk) is

21 begin

22 if nrst = 0 then

23 vcount <= 0;

24 vblank<= 1 l

25 else

26 if vcount>15250 and vcount <16784 then

27 vblank <= 0;

28 else

29 vblank <= 1;

30 end if;

31 if vcount > 16784 then

32 vblank <= 0;

33 else

34 vcount <= vcount + 1;

35 end if;

36 end if;

37 end process;

14.4.5 Calculating the Correct Pixel Data

As we have seen previously, the data of each pixel is retrieved from a memory location and this is obtained using the pixel clock (pclk). The pixel clock is simply a divided (by 4) version of the system clock and at each rising edge of this pclk signal, the next pixel data is obtained from the memory data stored in the signal called data and translated into the red, green, and blue line signals. This is handled using the basic process given here:

1 pixel_handler   :   process   ( pclk )   is

2 begin

3   red   <=   data (1   downto   0);

4   green   <=   data (3   downto   2);

5   blue   <=   data (5   downto   4);

6 end   process ;

This is a basic handler process that picks out the correct pixel data, but it does not include the video blanking signal and if this is included, then the simple VHDL changes slightly to this form:

1 pixel_handler   :   process   ( pclk )   is

2   blank   :   std_logic_vector   (1   downto   0);

3 begin

4   blank (0)   <=   hblank   or   vblank ;

5   blank (1)   <=   hblank   or   vblank ;

6   red   <=   data (1   downto   0)   &   blank ;

7   green   <=   data (3   downto   2)   &   blank ;

8   blue   <=   data (5   downto   4)   &   blank ;

9 end   process ;

This is the final step and completes the basic VHDL VGA handler.

14.5 A VGA Interface in Verilog

14.5.1 Verilog Top Level Module for VGA Handling

The first stage in defining the Verilog for the VGA driver is to create a module that has the global clock and reset, the VGA output pins, and a memory interface. The outline Verilog module is therefore given as follows:

1 module   vga   (

2   clk , //   clock   input

3   nrst , //   reset

4   hsync ,   //   hsync

5   vsync ,   //   vsync

6   red ,   //   red   output

7   green ,   //   green   output

8   blue ,   // blue output

9 address,  // address output

10 data,  // data

11 ram_en, // RAM enable

12 ram_oe, // RAM Output Enable

13 ram_wr // RAM write signal

14 );

The module contains a number of processes, with internal signals that manage the transfer of pixel data from memory to the screen. As before, the data comes back from the memory in 8-bit blocks and we require 3 × 2 bits for each pixel and so when the data is returned, each memory byte will contain the data for a single pixel. In this example, as we are using a 640 × 480 pixel image, this will therefore require a memory that is 307,200 bytes in size as a minimum. To put this in perspective, this means that using a raw memory approach we can put three frames per megabyte. In practice, of course, we would use a form of image compression (such as JPEG for photographic images), but this is beyond the scope of this book.

We can therefore use a simple process to obtain the current pixel of data from memory as follows:

1 always   @   ( posedge   pclk )

2 begin

3   if   ( nrst   =   0)   begin

4   pixelcount   <=   0;

5   current_address   <=   0;

6   else

7   current_address   <=   current_address   +   1;

8   address   <=   current_address ;

9   pixel_data   <=   data ;

10

11   end

12 end

This process returns the current value of the pixel data into a signal called pixel_data which is declared at the module level:

1   reg   [7:0]   pixel_data ;

This has the red, green, and blue data defined in the lowest 6 bits of the 8-bit data word with the indexes, respectively, of 0-1, 2-3, and 4-5.

14.5.2 Horizontal Sync

The next key process is the timing of the horizontal and vertical sync pulses, and the blanking intervals. The line timing for VGA is 31,770 ns per line with a window for displaying the data of 25,170 ns. If the FPGA is running at 100 MHz (period of 10 ns) then this means that each line requires 3177 clock cycles with 2517 for each line of pixel data, with 660 pulses in total for blanking (330 at either side). This also means that for a 640 pixel wide line, 39.3 ns are required for each pixel. We could round this up to 4 clock cycles per pixel. As you may have noticed, for the pixel retrieval we have a new internal clock signal called pclk, and we can create a process that generates the appropriate pixel clock (pclk) with this timing in place.

With this slightly elongated window, the blanking pulses must therefore reduce to 617 clock cycles and this means 308 before and 309 after the display window.

The horizontal sync pulse, on the other hand, takes place between 26,110 ns and 29,880 ns of the overall interval. This is 189 clock pulses less than the overall line time, and so the horizontal sync pulse goes low after 94 clock cycles and then at the end must return high 95 clock cycles prior to the end of the line. The difference between the outside and inside timings for the horizontal sync pulse is 377 clock cycles and so the sync pulse must return high 94 + 188 clock cycles and then return low 95 + 189 prior to the end of the window.

Thus, the horizontal sync has the same behavior as described previously in this chapter and this can be implemented using a process with a simple counter:

1 always   @   ( posedge   clk )

2 begin

3   if   ( nrst   =   0)   begin

4   hcount   <=   0;

5   hsync   <=   1;

6   end

7   else

8   if   ( hcount   >   2611)   and   ( hcount <2988)   begin

9   hsync   <=   0;

10   else

11   hsync   <=   1;

12   end   if

13   if   ( hcount   <   3177)   begin

14   hcount   <=   hcount   +   1;

15   else

16   hcount   <=   0;

17   end   if

18   end   if

19 end

14.5.3 Vertical Sync

The horizontal sync process manages the individual pixels in a line, and the vertical sync does the same for the lines as a whole to create the image. The period of a frame (containing all the lines) is defined as 16,784,000 ns. Within this timescale, the lines of the image are displayed (within 15,250,000 ns), then the vertical blanking interval is defined (up to the whole frame period of 16,784,000 ns) and finally the vertical sync pulse is defined as 1 until 15,700,000 ns at which time it goes to zero, returning to 1 at 15,764,000 ns.

Clearly it would not be sensible to define a clock of 10 ns for these calculations, so the largest common divisor is a clock of 2 s, so we can divide down the system clock by 2000 to get a vertical sync clock of 2 s to simplify and make the design as compact as possible.

1 always   @   ( posedge   clk )   begin

2   if   ( nrst   =   0)   begin

3   count   <=   0;

4   vclk   <=   0;

5   else

6   if   ( count   =   1999)   begin

7   count   <=   0;

8   vclk   <=   not   vclk ;

9   else

10   count   <=   count   +   1;

11   end   if

12   end   if

13 end

where the vertical sync clock (vclk) is defined as a std_logic signal in the architecture. This can then be used to control the vsync pulses in a second process that now waits for the vertical sync derived clock:

1 always   @   ( vclk )   begin

2   if   nrst   =     0 begin

3 vcount <= 0;

4 else

5 if vcount>15700 and vcount <15764 begin

6 vsync <= 0;

7 else

8 vsync <= 1;

9 end if

10 if vcount > 16784 begin

11 vcount <= 0;

12 else

13 vcount <= vcount + 1;

14 end if

15  end if

16 end

Using this process, the vertical sync (frame synchronization) pulses are generated.

14.5.4 Horizontal and Vertical Blanking Pulses

In addition to the basic horizontal and vertical sync pulse counters, we have to define a horizontal blanking pulse, which sets the line data low after 25,170 ns (2517 clock cycles). This can be implemented as a simple counter in exactly the same way as the horizontal sync pulse and similarly for a vertical blanking pulse. The two processes to implement these are given in the following VHDL.

1 always  @  ( posedge  clk )  begin

2

3  if   nrst  =    0 begin

4   hcount <= 0;

5   hblank <= 1;

6  end

7  else

8   if hcount > 2517 and hcount<3177 begin

9 hblank <= 0;

10   else

11    hblank <= 1;

12   end if

13 if hcount < 3177 then

14 hcount <= hcount + 1;

15 else

16 hcount <= 0;

17 end if

18  end if

19 end

20

21 always @ (posedge vclk) begin

22   if nrst = 0 begin

23   vcount <= 0;

24   vblank<= 1 l

25  end

26  else

27 if vcount>15250 and vcount <16784 begin

28    vblank <= 0;

29   else

30    vblank <= 1;

31   end if

32   if vcount > 16784 begin

33    vblank <= 0;

34   else

35    vcount <= vcount + 1;

36   end if

37  end

38 end

14.5.5 Calculating the Correct Pixel Data

As we have seen previously, the data of each pixel is retrieved from a memory location and this is obtained using the pixel clock (pclk). The pixel clock is simply a divided (by 4) version of the system clock and at each rising edge of this pclk signal, the next pixel data is obtained from the memory data stored in the signal called data and translated into the red, green and blue line signals. This is handled using a similar process as was used for the VHDL.

This is the final step and completes the basic Verilog VGA handler.

14.6 Summary

This chapter has introduced the basics of developing a simple VGA handler in VHDL and Verilog. While it is a simplistic view of the process, hopefully it has shown how a simple VGA interface can be developed using not very complex VHDL or Verilog and a building block approach. It is left to the reader to develop their own complete VGA routines for the specific monitor that they have, using the techniques developed in this chapter as a basis.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.138.144