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:
- How wide, in bits, is each data value of this ROM?
- How many data values are there in this ROM?
- What is the total size, in bits, of this ROM?
- What is the binary value of address 0x32a in this ROM?
- How many characters can be displayed on the 640 x 480 display
using this font?
- What character location is associated with the pixel location of
(237,103)? Provide your response as a (x,y) character location where
the character (0,0) is the top, left-most character.
- Assume the upper-case character 'B' is located at the character
location indicated in the previous location. What is the value of the
font ROM at this location (1 or 0)?
- Describe the timing of the ROM access. In particular, describe
when the ROM data values are given once an address value is provided.
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:
- What is the default message that will be displayed on line 0? You
will need to interpret the default ASCII values for line 0 to determine
the text output.
- How many bits are stored in this memory?
- The character that is read from the character memory will depend
on the current pixel_x and pixel_y values that are generated by the VGA
timing circuit from the previous lab. Determine how you are going to
generate the 12-bit address signal that will drive the char_read_addr input of the
character memory. Specifically, respond by creating a VHDL statement
that will drive this char_read_addr input (i.e., char_read_addr <=
<fill in the blank>).
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.
- 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.
- 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.
- 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.
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).
- Begin by instancing your VGA timing synchronization circuit from
the previous lab.
- 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.
- 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.
- 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).
- 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:
- Add some objects on top of the character display (i.e., like the
pong lab)
- Support different color characters
- Modify the font ROM to create a different font or a set of
symbols (i.e., rather than characters, create pictures)
Pass Off
Demonstrate the following to a
TA to passoff your lab:
- Show your VGA character controller simulation in the
testbench.
- Demonstrate a working terminal
Lab Write-Up
Complete your laboratory write-up on blackboard.