Simon
6.195 ViewLogic VHDL project
William Moyne
12/11/1997
OVERVIEW:
Simon is a VHDL version of the popular toy where the user attempts to mimic
the sequence of lights displayed by the toy. If the use succeeds, the sequence
is increased by one and the game continues. The game ends when the
player presses the wrong button (i.e. doesn't correctly replay the sequence).
The goal of the game is to get as long a sequence as possible.
Figure 1. Simon Game
(Note: Colors (4) are Buttons)
DESIGN:
To manage the complexity of the design, Simon was divided into a number
of sub-components each performing its own simple task. The module interactions
are shown below with descriptions of each following.
Figure 2: Simon Hierarchy
Simon:
The Simon module controls the overall flow of the hardware. It acts as
a connector for information between the various modules. In addition to
connections, Simon also handles system-wide reset and initialization as
well as keeping track of sequence length and the player's current position
within the sequence (when they are trying to play it back).
The goal of the Simon module is to keep track of all sequence-related
information, enabling the other modules to be written for one-event tasks
(such as one button press, one button check, playing one light in the sequence).
All other modules in the system are designed to be asynchronous with
an enable line and a done line. This simplifies the design
of the Simon module by making all operations a simple invoke/wait procedure.
Timing issues are essentially eliminated.
Random
Random uses a feedback shift register to generate a pseudo-random number
sequence. This sequence is essential for the functionality of the Simon
system. The reason is that the goal of the game is to play a sequence of
some length then check a user-provided sequence against this one. Pseudo-random
numbers are ideal for this because they require storage of only one item,
the seed, all other members of the sequence can be computed serially given
the seed.
For the purpose of debugging the system, a simple 8 bit seed was used
with feedback from bits 0,4,7. Clearly this sequence has at most
256 random starting locations and has a maximum unique length of 256.
The length of the seed does not change the fundamental interfaces to this
module, so more complex sequences can be generated as needed. Matlab was
used to ensure that the initial seed and feedback locations shown did not
lead to a degenerate sequence of 0, 1, or some small period repetition.
Random2:
Random2 is a simple wrapper for the random class that generates 2-bit random
numbers asynchronously to the system. When the newnum line
of Random2 is driven high, Random2 queries Random for 2 bits which are
concatenated and sent out the rndbits line. Once the computation
is done, the done line is asserted. The reason for the asynchronous
behavior was to enable different implementations of the random2 interface
to use the same entity. The extension of Random2 to more than 2 bits
is trivial since timing of the computation is not critical.
Play:
The play module is responsible for driving the four colored lights (and
optionally buzzers) to indicate to the user which "note" is next in the
random sequence. Play's main function is to hold the light/note for a human-readable
length of time. This is accomplished by running a large counter to slow
down the system clock to a more reasonable level (1 Hz or so).
When the playnote line of the Play module is driven high, Play
queries Random2 for the next random number (2 bits) and then drives the
corresponding light. When done, Play asserts the done line.
Button:
The Button module receives the input from the user (in the form of four
buttons underneath the four colors). In order to eliminate possible
switch bouncing, Button latches in the button information and holds it
until it is reset. This behavior makes it insensitive to bounce.
The Button module also encodes the one-hot button sequence (0100,1000,
...) into 2 bits (10,11,...). In addition, an additional signal is
provided which notifies the system when any button is pressed.
The Button module also ignores buttons that are pressed prior to reset
and held. This prevents double sampling of user input do to the speed
of the system clock (resets). Illegal button combinations are also ignored
(1100,1001, ...).
Check:
The Check module checks one user input against the next digit in the random
sequence. When the check enable line of Check is driven high, Check
queries Random2 for the next random number and compares it against the
number provided by the user. If they are not the same, the checkbad
line is driven high, otherwise it is driven low. Once the process is complete,
the checkdone line is asserted.
Note: Check and Play use separate instanciations of the Random2 class.
The reason for this was the control lines for Random2 were being driven
by both modules simultaneously. This gave logic levels of 'X' rather than
'1' or '0'. Another way to remedy this problem would be to put Random2
on a bus and selectively drive signals from Play and Check. The former
approach was taken due to ease of programming and debugging.
Other Modules (Not implemented):
In addition to the core modules implemented, there are a number of modules
that would be necessary to move Simon from the debug phase into the playable
phase. A few of these are listed below:
-
Reset-to-random: Currently simon resets to the same state every
time the game is played. This would lead to a fairly boring experience
once the player learned the sequence. To fix this, a module that randomly
selects a seed on startup is needed.
-
Clock Generator: The timing of Simon is not an essential element,
so to keep the parts count at a minimum it would be advantageous to implement
a 555-like timer circuit using the CPLD/FPGA. This circuit would take few
cells, but would eliminate the need for an external crystal. All
that would be needed external to the chip would be a capacitor and a resistor
to provide the RC delay (for timing).
-
Tone Generator: The original Simon played a distinct tone in addition
to flashing a light to denote a sequence. This gave the user two ways to
remember the sequence. A tone generation module could be designed that
would generate four distinct tones given the current output light.
One novel way to generate the tone would be to modulate the "light" signal.
For example the light could be pulsed at 4000Hz which would be imperceptable
to the user, but if the same line was tied to a speaker, a tone would be
produced.
SIMULATION:
The modules where each designed and simulated in isolation to make sure
that they worked before the final integration was performed. The Viewlogic
tools were used to first analyze (compile) and then test (simulate) the
design. Project 1 shows a simple example of how to use the analyzer and
simulation tool.
In order to simplify the testing of modules, command (.cmd) files were
written that provide interesting stimulus to each module to ensure that
it exhibits correct behavior. The source-files along with their associated
test files are shown below:
These tests can be performed by first analyzing the VHDL code
with the viewlogic analyzer, then loading the entity into the fusion/speedwave
tool. At the speedwave prompt, type: execute
testfilename.cmd and the test file with bring up the viewtrace window
with the results of the simulation.
In addition to the file itself, Random2 and Simon require use
of the user library because they include other modules. In order
to simulate Simon a user library called "simon" must be created with the
library manager (see
proj 1) and all other source files must be analyzed. To compile Random2
follow the same procedure, but only Random needs to be compiled into the
"simon" library.
NOTE: It was discovered (after much work) that naming an entity with
the same name as the library causes strange errors. For this reason,
the main module is actually called asimon so that it does not conflict
with the "simon" library.
A typical trace of the main simon module is shown (here).
The important signals on this line are:
User Inputs:
-
Buttons: Buttons are the four buttons that the user can push. One
is under each colored light. Valid buttons are "0000" (nothing pushed),
"1000", "0100", "0010","0001". All other combinations are considered
invalid and are ignored.
-
RST: Reset. This would usually occur on power-up
Output
-
Lights: Lights have a one-to-one connection with the buttons. There is
a light under each button to tell the user in what order to replay the
sequence. The valid light combinations are the same as the valid button
combinations.
All other signals are shown for debugging only. The length of time
that the lights are held to non-zero values is much shorter than would
happen in a real game. The reason for the short time is that simulating
events that occur over large cycle times are difficult.
The game shown in the traces goes for three rounds. Using the
following color key, a play-by-play account is given:
KEY: BLUE=1000
RED=0100 GREEN=0010
YELLOW=0001
-
RESET/IDLE
-
User presses any key to start game: RED
-
Game Plays sequence length one: BLUE
-
User Pushes BLUE
-
Game increases sequence by one and plays both: BLUE
BLUE
-
User repeats sequence: BLUE
BLUE
-
Game increments sequence to three: BLUE
BLUE GREEN
-
User messes up: GREEN
-
User Loses and game starts over
SYNTHESIS:
Unfortunately, the synthesis of the VHDL was not as successful as the simulation.
Ideally one code base can be used to both simulate and synthesize a design.
Unfortunately, the simulator accepts syntax that may or may not lead to
correctly synthesized code. The use of Variables can also lead to
unexpected results.
Starting from the bottom, random was compiled using the WARP synthesis
tool to see whether modification were needed. 1 error was encountered which
was caused by a " 'event" operator. Apparently, the only " 'event"
reliably supported in synthesis is "clk".
After completely removing the " 'event" and rewriting the procedures
accordingly, it was found that the component compiled but oscillated during
simulation. There was no clear reason for this, but there was a variable
in use so this was marked as the culprit.
Once again random
was completely rewritten. This time eliminating the variable completely
and changing most of the other code to compensate. This synthesized and
simulated fine with the following utilization
using a 371 CPLD:
Results were not as promising when Random2 was compiled with random1.
Although in simulation random2 works fine and random works fine even in
sythesis/simulation. When they are put together, an unexplained oscillation
occurs. The utilization (for what it's worth) is shown here.
The check module showed similar utilization. The Play module has an
interesting property. Because it uses an integer to count for the delay,
WARP extends that variable out to 32 bits (and all the product terms that
go with them) even though currently the variable only counts to 10. The
utilization of play could easily be brought down to similar levels of random
and check if this integer was changed to a bit vector.
From the utilization numbers obtained from these modules it seems clear
that Simon could easily fit in a small FPGA and maybe a CPLD. One of the
reasons it may fit in a CPLD even though each module takes over 30% of
the 371 is that they share many of the same signals, this would lead to
a savings of macrocells within the chip.
If the design could not fit on a single device or if only small PLD's
are available, the boundaries used in the simon simulation are fairly good
to chop up the design in that they have few signals between them and each
do specific tasks.
CONCLUSION:
VHDL is a very powerful language to design digital systems without actually
having to hook them up. Ideally a designer could write some VHDL
code, simulate it using a fast behavioral simulator, then synthesize the
same code into actual hardware.
The reality of the current tools is not quite this easy. First,
the libraries (such as ieee) used seem to differ between manufacturers
so there are incompatibilities moving code between tools. Also, the
simulators seem to support syntax that cannot be synthesized. It
would be nice if the simulator still simulated non-synthesizable code but
warned the user.
Given the current state of the tools, the best path may be to design
each module with VHDL in a behavioral model to get the fast simulation
times, but then synthesize each module before moving on. This will ensure
that if drastic changes are needed within a module (possibly changing its
timing profile) then the user will not have to propagate these changes
all the way up the design hierarchy.