ECEn 320

VGA Text Generator

Purpose: The purpose of this laboratory is to create a character generator circuit for displaying ASCII text characters on the VGA display. You will also create a simple circuit to emulate a simple terminal that accepts characters from the UART and displays these characters on the VGA display.

Preparation

Displaying text is an important function of a video controller. Dedicated circuits are often used to facilitate the display of text characters on a screen. To display text on our VGA display, we will organize the 640x480 display area into larger "tiles" where each tile represents a character location. In this lab, the size of each character in the font we will use is 8 pixels wide and 16 pixels high. Mapped onto a 640x480 display, this font will display 80 text characters in each line (i.e., 640 pixels divided by 8 columns per character) and 30 lines (480 / 16). Each of the 640x480 pixels in the display are associated with one of the 80x30 character locations.

Font ROM: The appearance of the characters on the screen is determined by a "font ROM". The font ROM contains the pattern of pixels that should be displayed on the screen when a particular character needs to be displayed. The bits within the font ROM indicate which pixels of a 8 x 16 bit tile should be displayed in the 'foreground' and which pixels on the display should be displayed in the background. A '1' in the font ROM indicates the corresponding pixel should be displayed in the foreground (i.e., white in our case) while a '0' in the font ROM indicates that the corresponding pixel should be blanked or put in the background (black in our case). The text below demonstrates the contents of the font ROM for the upper-case character 'A':
   "00000000", -- 0
"00000000", -- 1
"00010000", -- 2 *
"00111000", -- 3 ***
"01101100", -- 4 ** **
"11000110", -- 5 ** **
"11000110", -- 6 ** **
"11111110", -- 7 *******
"11000110", -- 8 ** **
"11000110", -- 9 ** **
"11000110", -- a ** **
"11000110", -- b ** **
"00000000", -- c
"00000000", -- d
"00000000", -- e
"00000000", -- f

A font ROM has been created for you in VHDL. Open this file and review its contents (you will need to include it within your project). The font ROM is organized as an array of std_logic_vector values. The ROM contents are defined by the constant named "ROM" and will be preloaded into the memory. Download this ROM VHDL file and review the contents of this font ROM to answer the following questions in blackboard:
Character Memory: In addition to the font ROM, you will need to use a "character memory" that stores the character value at each of the 80x30 character locations on the display. The minimum size of this memory is 80x30x8 bits to provide enough room to store characters (one byte each) for each of the 80 columns and 30 rows. This size, however, is not an even power of two. To simplify our circuit, we will create a character memory that is sized with even powers of two. Specifically, the memory size will be 128x32x8 bits. Although this will result in wasted memory, it will significantly simplify the circuit we are creating.

A character memory has been provided for you in VHDL for this lab. This memory has a default initial value  for displaying the initial message. You may edit this message to create your own custom default message. This memory has two memory ports: a character read port and a character write port. The read port is needed by the character generator to read the character value of the current character location. The write port is used to update the contest of the character display.

The char_read_addr port is a 12 bit signal used to determine which location in the character memory to read. The result of the read is available on the char_read_value. Like the font ROM, this character ROM will provide the character result one clock cycle after the address is provided.

The second memory port is the character write port. This write port is used to update the contents of the character memory so the display can be changed. This port operates simultaneously with the read port so the character display can access characters while the character memory is being updated. Three signals are used for this port. The char_write_addr signal is used to indicate the address within the character memory that will be updated. The char_write_value is the value that will be written at this address. The char_we signal is the control signal to enable a write into the character memory. When the char_we signal is asserted, the value on the char_write_value signal will be written at address char_write_addr at the next clock edge. After reviewing the contents of this VHDL memory file, answer the following questions:

Exercise #1: Character Generator

Once you understand the function of the character memory and the font rom, you are ready to build the character generater. The character memory and font ROM are combined together as shown below to determine the pixel value for the current pixel value being displayed. There are three steps in the process of determining the current pixel value.
  1. The first step in this process is to determine the current character based on the current pixel address. The pixel_x and pixel_y signals from the VGA timing controller are decoded to determine the value of the character within the current character tile. 
  2. The second step is to use this character value to access the font ROM so the current character pixels can be drawn. The address for the font ROM is based on the current character value (which was read from the character memory) and the current pixel row. The current pixel row is used to determine which line of the character needs to be displayed. 
  3. The final step is to determine which of the 8 pixels from the current character row should be displayed. The pixel_x address is used to drive an 8 to 1 mux to select the current pixel.

c

Both the character memory and the font ROM are synchronous meaning that the output values from the ROM or RAM are provided one clock cycle after the inputs change. Since there are two memories in series, your pixel output will arrive two clock cycles after the input. By the time the pixel value is determined, the result does not correspond with the current value of pixel_x and pixel_y (it should be two clock cycles late). The table below describes the sequence required to determine the appropriate pixel value:

Cycle Pixel Action
n x,y Start character memory read
n+1 x,y Character memory result is available.
Start font ROM memory read
n+2 x+1,y Font ROM result is available.
Choose the appropriate bit from the font ROM.

A problem occurs during the last step of the process when selecting which of the 8 bits in the font ROM row to display. Since the pixel_x value changes every other clock cycle, the incorrect pixel_x value is used for the input to the 8:1 pixel multiplexer. To resolve this problem, you need to provide a pipelining register for the pixel_x input for use by the 8:1 multiplexer.

Create a new VHDL file named charGen.vhd for the character generator. Design your character generator based on the structure described in the figure above and using the provided font ROM and character RAM. Your efforts will be focused on the proper connection of these two modules and the 8:1 MUX. As a minimum, include the following inputs and outputs to your entity:


Inputs
Port Bits Purpose
clk 1 50Mhz clock input
char_we 1 Character write enable. When this signal is asserted, a new character is written into the character memory.
char_value 8 The 8 bit value to write into the character memory.
char_addr 12 The write address of the character memory
pixel_x 10 The column address of the current pixel
pixel_y 10 The row address of the current pixel

Outputs
Port Bits Purpose
pixel_out 1 The value of the character output pixel. A logic '1' indicates that the current pixel should be displayed in the foreground and a logic '0' indicates that the piel should be blanked or put in the background.

Once you have created your design, make sure it compiles and simulate it with a few simple tests.

Exercise #2: Testbench

A simple testbench has been provided for you to test your character generator. Unlike previous labs, this testbench will not check to see if your circuit is operating correctly. Rather, it provides a few input cases for you. You will need to manually check the output of your character generator and decide whether it is operating correctly. The testbench writes a few characters into the character memory and then applies the pixel_x and pixel_y intputs to the location where the character should reside on the screen. Unlike previous testbenches, this testbench will not self-validate your design. You will need to manually verify that the pixel_out signal is correct for the given character and at the appropriate pixel_x and pixel_y locations.

tb_chargen.vhd

Once you are happy with the performance of your character generator, modify your my320pkg.vhd package to include a component declaration for your component. Submit your modified package and your .tcl file on blackboard.

Exercise #3: Top-Level Design

The final design exercise is to integrate your VGA character controller, VGA timing controller, and the UART receiver to create a rudimentary terminal. Your terminal will accept characters from the serial port and display them on the screen. When a character is received, it should be written into the character memory in the next character position. You will need to create two registers to keep track of the position of the next character to be written (i.e., a register storing the current x location and a register storing the current y location).
  1. Begin by instancing your VGA timing synchronization circuit from the previous lab.
  2. Instance your character generator from the previous design exercise and hook it up to the VGA timing controller. Note that due to the delays associated with the memories in the character generator, the pixel output will be delayed by two clock cycles in comparison with the HS and VS signals. You should add two flip flops to the HS and VS signals to line them up with the pixel out signal.
  3. You will need to generate the RGB signals from the pixel_out signal. The character ROM only indicates foreground vs. background (i.e., the result is only a single bit and does not store color). You will want to choose a foreground color and background color and convert the value of the pixel into your chosen foreground and background colors (i.e., if you want the foreground to be white, you will assign RGB="111" when the pixel_out = '1' and if you want the background to be black, you will assign RGB="000" when the pixel_out = '0'). Make sure to blank the RBG signals when the VGA timing controller asserts the blank output. 
  4. Instance your UART receiver so you can receive keystrokes from the keyboard. Create two registers that keep track of the current "Write" position of characters: one register for the character column and one register for the character row. When a new character is received from the UART receiver, write the value of the character into the character memory. Also, increment the column register so the next character is written in the next column position. If the character is written into the last column (79), set the column address to 0 and increment the row register (i.e., have the characters wrap around once you have finished a complete line).
  5. Provide a reset button to the board so you can reset your position counters manually.
Simulate your design carefully by simulating a complete frame.

Exercise #4: Synthesis and Download

Once your circuit has simulated properly, create a UCF file that incorporates the I/O signals used in your project. Synthesize your circuit and test it on the board. Plug in a VGA monitor and see if your circuit works.

Personal Exploration

For your personal exploration, modify the top-level design to add some additional  feature. Ideas include:

Pass Off

Demonstrate the following to a TA to passoff your lab:

  1. Show your VGA character controller simulation in the testbench. 
  2. Demonstrate a working terminal

Lab Write-Up

Complete your laboratory write-up on blackboard.