Using an FPGA to
replicate no longer available components on the
Sound
and CPU Boards in a WPC-DCS Pinball Machine.
These include the two ASICs and PAL.
Introduction
With the introduction of Indiana
Jones in 1993, Williams Pinball
started to base their audio system on the Digital Compression
System. The main CPU for this system is an ADSP-2105 DSP
chip. There were two main generations of these boards, the
first
(used in the IJ) used a PAL to contain the decoding logic.
This
is known as the WPC-DCS audio
board. In the second generation, Williams combined the dot
matrix
controller onto the same board, and moved all the logic into an
ASIC. This latter generation is known as WPC-95.
Due to the unavailability of the ASIC, the WPC-95 audio boards have
soared in price (RGP
discussion).
They are commonly several hundred dollars each. In the summer
of
2007, I was looking for a pinball/FPGA related project, and this seemed
like a good one to dive into. After some discussion with
Martin
Reynolds, I decided to give it a try.
The first step was to reproduce the WPC-DCS circuit, since it was
simpler (only uses a PAL). My hope was that once it was
running
using IJ ROMs, I could make some small changes, load in MM ROMs, and
show
that WPC-95 was simply a repackaging of WPC-DCS. To get
started,
I needed to know the equations for U16, the PAL on the WPC-DCS
board. Help came from Brent Butler, who was the one who
reverse
engineered the WPC Security PIC. He read the contents of his
PAL,
and sent me the equations. This, along with the WPC-DCS
schematics, and the part datasheets were all the ingredients I needed
to start coding.
The
prototype/breadboard
The platform I chose to use was the Spartan3A
development board. It includes
a Spartan3A FPGA, and lots of peripherals such as 4 MBytes of Flash,
buttons, LEDs, a power supply, a USB
programmer, and an expansion connector. This latter connector
allowed the connection to wire
wrap boards, where the DSP and D/A chips would
reside. The
self-contained Flash memory was nice because it would allow me to
eliminate the large PROM chips, and put their contents in a compact
modern part. IJ needs 3.5 MBytes, so it should fit quite
nicely.
The first step was to load the ROMs for the IJ into the Flash of the
Spartan3A board. The image of the seven sound ROMs were
merged
into one large binary file, and I then used Ken Chapman's excellent
Flash
loader (see link below) to transfer this into the Spartan 3A.
Once that was complete, I could use his bootloader program to read back
sections, and I could see that the 7 chip
image was correctly loaded into the Flash. The next step was
to
develop the VHDL code. A simulation of the result is shown
below.
Simulation of the VHDL
code shows no bus contention and the proper data
being fetched for the DSP. In general, the signal names are
the
same as
on the WPC-DCS schematic.
In the above waveform diagram, the signals from 'cmd_read' to
'ram_read' indicate what the decoding logic thinks the DSP is
doing. We can see only one of these become asserted at any
one
time. Also, an inspection of the databus shows the proper
data
being fetched for the DSP. This shows that the PAL equations
and
my reading of the schematic appear to be correct.
Once the code was complete, it came time to assemble the
prototype. As mentioned before, the DSP and D/A were to be
installed onto the breadboard that plugs into the Spartan3A.
I
decided to mount them pin side up to allow access to all the
connections. A photograph of the completed prototype is shown
below.
The WPC-DCS prototype board. The board on the right holds the
DSP
and the D/A.
All other components are on the other board.
Close-up of the DSP and D/A area. The two components are on
the
reverse side.
The DSP is mounted into
a socket, and
then plugged into some wire wrap pins. This is the square
grid of
pins in the above image. To the right of that is the 16-pin
D/A
chip, also mounted pin side up. The D/A needs a -5V supply,
and
this is supplied by a AA battery pack. As you can see in the
image above, the DSP connects to the Spartan via series
resistors. These are included to protect the Spartan 3 device
because its inputs are not 5V tolerant.
Testing
I did some tests to characterize what the DSP is supposed to do on
startup. When an original WPC-DCS board is properly running,
the
clock (DacClk) and strobe (DacStb) lines from the DSP to the D/A are
expected to be clocking, but the data (DacDat) line will be
static. The data line will have transitions only when a sound
is
being played. Also, the power-up BONG is expected to be
played by
the DSP a few seconds after bootup. In contrast, a WPC-DCS
board
with no ROMs installed, will have these lines as hi-Z, which is
presumably the power-on state for this port. Clearly, there
will
be no sound issued by the DSP if it does not boot. This information
will allow me to gauge if the DSP on the prototype is running or
not.
Up to this point, a lot of effort had been invested, and if the DSP did
not boot, I would have very few options to incrementally debug the
problem. A great deal was at stake here, and it would all
hinge
on the initial power-up. I flipped the power switch, and to
my
relief, the DSP appeared to be working. I could see the D/A
lines
clocking, and a single burst would appear on the data line a few
seconds after power-up. Also, I programmed the switches on
the
Spartan 3A for command input (to play sounds), and when a command was
sent to the DSP, I could see it fetch the command, and clear the
interrupt flag. This was very promising.
I then plugged in the D/A chip next. In addition to the usual
5V,
this chip requires -5V as well. Presumably for the internal
audio
op amp. This latter supply is provided by a battery
pack. I
connected the audio output of this chip to a signal tracer (basically a
small audio amplifier with speaker). On the subsequent power
up,
I get a cool single "BONG" from the prototype. This indicates
the
DSP boots and that the checksum of all the Flash was good.
Success!
The DCS audio board is controlled by a bus consisting of 8 data bits, 5
address bits,
and 2 control lines. I needed to know what
commands the DSP code would recognize so
that I can stimulate it on my test bench. Help came
once
again from
Martin, who knows how to run PinMAMED.
Using this emulator/debugger, we were able to figure out that command
packets are two bytes long. For example "0001" causes the
Indy
march to play on PinMAMED. A check with an oscilloscope on my
IJ
shows that the two
bytes are normally separated by about 1 millisecond. Based on
my
initial testing with sending single bytes, the DSP times out after a
certain period, and the two bytes in the packet need to follow each
other by 1
millisecond or less. I then changed my user interface to send
two
bytes in rapid succession, with the bottom nibble controlled by the
four slide switches on the Spartan3A board. This proved
successful, and I was able to get my prototype to play all the sounds
identified by 0001 through 000F on PinMAMED.
A short video of
the board playing the Indy march.
WPC-95
The next step was to emulate/replicate the WPC-95 A/V board.
When
this project was started, I had hoped to be able to simply port the
WPC-95 ROMs into the testbed above. This would eliminate my
having to reverse engineer the ASIC. However, examination of
the
schematic shows why this would not work.
WPC-95 ROMs are larger than WPC-DCS. Per the
schematic, the
address bus for the WPC-DCS ROMs is 23 bits wide (8 Mbytes address
space). In contrast, the address bus on WPC-95 is 24 bits (16
Mbytes address space).
Of this latter 24 bits, the 10 LSB bits come directly from
the
DSP address bus, while the remaining 14 come from a paging register
inside the ASIC. This is much different than WPC-DCS, where
the
bottom 12 bits are from the DSP, and the top 11 from a paging register.
Only 8 bits of the DSP's data bus is connected to the ASIC,
thus
the 14 bits of the paging register must be stored as two separate
writes. This makes the two platforms incompatible.
Loading
the paging register is completely
different in one configuration compared to the other.
There are two RAM enable signals on WPC-95 (both from ASIC)
vs.
one on the
WPC-DCS.
Cracking
the code
An important tool to understand what the ASIC and DSP are doing is a
logic analyzer. I connected three pods of my unit to the
various
points on the WPC-95 board that was loaned from Jim Knight.
This
unit had Scared Stiff ROMs on it. I was able to slowly gain
an
understanding of what the DSP does using this setup. It felt
like
the process of cracking a cipher code or opening a combination lock.
The logic analyzer connected to a WPC-95 board.
The order of operations of the DSP is as follows:
When the POR timer expires, the DSP reads the inital boot
code
from ROM using the BMS signal. This takes about 610 useconds.
The internal boot functions take about 300 useconds, and
the
first of the RAM checks then start.
A pattern is written to the entire range of RAM using the
DMS
line.
The pattern is read back using the DMS line.
Another pattern is written to RAM with the PMS line.
The pattern is read back using the PMS line.
If any of the previous four steps fail, the DSP issues ten
bongs. This checkout takes about 200 microseconds.
RAM is then erased with the PMS line.
Another erasure of RAM with the DMS line.
The DSP then proceeds to the checksum calculation of the
ROMs.
This takes about 2 seconds on Scared Stiff ROMs.
If all is well, at t=2.5 seconds, the BONG
starts. A single
sound indicates all is well with the ROMs. This takes about
0.6
seconds.
At t=3.4 seconds, the DSP reads from ROMs again with the
BMS
line. At this point, the DSP starts to operate.
While this is going on, every second or so, the DSP writes
to a
register in the ASIC to flash the red LED on the board on and off.
Another important tool
is Xilinx's
Chipscope.
This allows one to see live data from an FPGA using only its JTAG
port. Although I extensively tested my code with the
simulator, I
do not have a simulation of the DSP, and this tool allows me to see the
interaction of the DSP on the prototype board, and my FPGA code.
View of Xilinx's
Chipscope Pro tool. It allows you to display
live data from the
FPGA design using only the JTAG interface. For example, at
the
blue "X"
cursor, the DSP is writing "F20B00" to the
address "2FFF". This maps to RAM.
After implementing the
logic
relationships that were read with the logic analyzer, my prototype
initially bonged ten times. This indicated a communication
problem with the embedded FPGA RAM. After debugging with
Xilinx's
Chipscope Pro
(see above), I found the problem, and the prototype
bonged twice on startup, indicating a remaining problem with the way
the paging register is being handled. After about one month
of
work,
I finally fixed all the bugs, and the prototype bonged once on startup,
and flashed one of the LEDs on the Spartan board, matching the
operation of the loaned WPC-95 board. This represented a huge
milestone in the project, and I was very pleased to get to this point.
It
works!
To understand how the CPU commands the WPC-95 A/V board, I
inserted the loaned board into a WPC-95 machine, to see how it
interacts
with the CPU. The kids were tickled when they heard our TOTAN machine
producing Scared Stiff sounds. By activating the sound test
menu,
I could see how the ASIC interrupts the DSP, and how the command is
delivered to the DSP.
The loaner board with all the probes hooked up installed
into my Tales of the Arabian Nights machine.
With the information
gathered, I
created a command interface with a pushbutton and the four slide
switches on the Spartan board similar to the interface to the WPC-DCS
board above. On the first try, I was able to get the
prototype to
play Scared Stiff sounds! I had successfully reproduced the
audio
portion of the ASIC.
Youtube video of the
WPC-95 prototype
in action.
Note the flashing LED on the prototype, matching the original board.
The
DMD
interface
The second half of the A/V board is the DMD interface. After
the
above results, I took a few months off from this project, and returned
to it during the 2007 Christmas season. After a week or two
of
work, I was able to display a test pattern on my IJ's DMD on the first
test.
The test image shown on the DMD is produced by the Spartan development
board.
The image is sharp with no ghosting showing that the timing
is
correct.
In the above image, the DMD data cable is plugged into a solderless
breadboard that is in turn plugged into the Spartan board. I
initially started to implement the circuit by simply converting the
schematic from the WPC-89 schematics into VHDL. I soon
realized
that the discrete design was not very suitable for implementation in an
FPGA. This is because good FPGA coding practices recommend a
fully synchronous design. The original WPC-89 implementation
is
far from it. I rewrote the VHDL code as a fully synchronous
design, and using only one clock (the CPU E-clock) rather than two as
the original DMD interface board. By using a single clock, it
makes it easier to one day integrate everything into one FPGA.
The DMD display shown working on a WPC-89 machine.
I develop the code on my laptop and load it into the FPGA via JTAG.
After another week's work, I found all the bugs and was able to drive
the DMD by plugging into the CPUs IO expansion ribbon cables!
It
took more effort than I thought it would, and was mainly stymied by the
design of the original controller. The way it switches the
address bus into the RAM violated the FPGA RAM's hold time, and it took
me a while to figure it out.
The photo above shows my development system. The solderless
breadboard has the three connectors to the IJ (two to the CPU board,
and one for the DMD). The Spartan board interfaces to these
connectors, and is controlled by the CPU board. The laptop is
simply set on the playfield glass, and connected via JTAG to the
Spartan. I can then develop the code, and run it with
Chipscope
(on the laptop). This allows me access to the signals without
a
logic analyzer connected.
A day after I was able to successfully use the programmed Spartan on my
Indiana Jones (WPC-89), I was able to use it on my Medieval Madness
(WPC-95). This shows that the DMD interface on both
generations
are the same.
The
CPU
Board
In September 2007, after the successful
completion of
the WPC-95 A/V VHDL code, I started working on an FPGA
version of
the CPU board. Since so much of this board is digital in
nature,
it would lend itself well to recasting into an FPGA. After
some
searching, I found a 6809
core. If I could also combine the RAM and the
ASIC's
functionality, we would have the essential parts of the CPU board on a
single FPGA. I also previously had the good fortune of
purchasing
a CPU board (with
the
valuable ASIC) on Ebay for $65. The board did not initially
boot,
but after I replaced the RAM, it booted fine. I was thus set
to
start the replication process.
I first loaded the game ROM code into the Flash memory that is on the
Spartan board, and started by tackling the ROM paging that is done
inside the ASIC. Information on how this is done is very
sketchy,
and even looking at the logic analyzer plots showed very little
information. I then found this
memory map by Matthias Bucher, and the
PinMAME source code. Among the files in this latter resource, the file
WPC.H contains the memory map locations of the ASIC
registers, and
a description of the "shifted bit hardware". I implemented my
best guess of the paging
algorithm, and it appeared to successfully step through the ROM check
process (by using Chipscope).
With the logic analyzer hooked up to read the CPU's activity, I could
roughly see what the WPC CPU does during the boot process:
After the reset is deasserted, the reset vector is fetched
at
FFFE and FFFF. These are the last two bytes of the program
ROM.
The ROM is checked first starting with the highest
32kbytes. This is the non-paged section. Then, the
lower
ones are checked in turn by using the paging register at 3FFC.
If that passes, the CPU moves onto the RAM check.
It does
this by writing and then reading the data byte 0x55 to the RAM, and
then repeating the test with 0xAA as data.
If that is ok, it continues with inits to the I/O such as
the
flippers, sound board, DMD, etc.
Then comes a lot of initialization which purpose is not
clear to
me.
After about one second of initialization, the main code
enters an
infinite loop and waits for an interrupt.
It took me one month to
get my VHDL code to this point.
However,
I did not at the time suspect the role of the interrupt.
Therefore, I could not figure out why the code was getting stuck in the
infinite loop. This prevented the LED on the Spartan board
from
flashing, which is how the game code indicates that all is well with
the tests. It was very puzzling and somewhat
frustrating. I
then decided to set aside this project for a few months and take a
fresh look later.
In January 2008, I completed the DMD
interface
above, and decided to tackle this again. One tool that I had
set
up was a separate Spartan board consisting of a simple 24 bit counter
that would basically be my time keeper for the WPC CPU board.
With this addition, I could tell how many clock cycles had elapsed
since the reset was released. This data was available on the
logic analyzer, and I could trigger on events (such as writing to a
register or memory location), and see the accompanying count of the
elapsed clock cycles. The use of this latter counter allows
me to
know where in the boot process the CPU board was. I had
always
noticed that the first flash of the LED always occurs with some spread
in the values for the elapsed time count, and I could never figure out
why. Then it hit me that the LED process may be controlled by
another separate asynchronous process...... such as an
interrupt!
A quick check of the IRQ line on the WPC CPU showed that it pulsed at
1kHz. Thus the WPC ASIC interrupts the processor at that
rate,
and this is probably why the main code on my Spartan board was stuck in
an infinite loop!
I added the interrupt feature in my VHDL ASIC in the Spartan, and
bingo! the LED on the Spartan board flashes rapidly and continuously
after a second from power
up, just like the WPC CPU board. Then, intentionally
corrupting
the ROM paging results in one flash of the LED, and corrupting the RAM
mapping results in two flashes. These results agree with the
diagnostic information from my IJ manual. All this means I am
able to
successfully get the game code to boot up. The RAM, CPU, ASIC
and
other minor glue logic from the CPU board were all inside a single FPGA.
Combining
the CPU board and the DMD interface
To truly verify that the game code is running properly on the Spartan
board, I decided to integrate the previously developed DMD interface in
the same FPGA. This would allow the board to drive a DMD
using
the game code that is loaded into the Flash memory. It should
have not taken me much time, but a simple mistake cost me a few days to
track down. Once that was corrected, success! As
can be
seen in the animated GIF below, the game code boots up and drives the
DMD display cleanly. If I hit the reset button after initial
bootup, the display shows the familiar "Testing..." message, and then a
"Check Fuses...." message. This is because the switch matrix
has
not been hooked up, and the game code thinks a fuse is blown.
An animated GIF showing the Spartan board running WPC code.
In
this case
"Slug Fest". The only connections to the IJ machine is power
for
the DMD.
The DMD data is driven by
the Spartan board.
A VGA Interface
Up to this point, if I
wanted to test
the DMD interface, I had to walk down two floors from my office to my
basement where my machines are located. This started to get
old,
and I wanted to find another way to display the DMD
information.
I hit upon the idea of using a VGA interface so that the TV/monitor in
my office could be used. This idea has several advantages:
As mentioned, I would be able to test the WPC prototype
anywhere
with a VGA monitor.
This is consistent with the underlying theme of this
project,
which is prevention of hardware obsolesence. Although
unlikely,
monitors could be substituted if DMD displays become unavailable one
day.
Since only one third of the display is used, the rest could
be
used for display of animation. For example, with a machine
like
IJ, the video scenes from where the quotes are taken could be displayed
along with the sound. The index of which sound is being
played
can be obtained by intercepting the command to the DCS sound board.
The Spartan board has a
VGA interface connector, and the associated
hardware is very simple. It consists only of a dozen
resistors
and the connector. I found a document that describes the VGA
interface timing, and started work on the
interface. The WPC
DMD is refreshed at about 122 Hz. The normal VGA frequency is
60Hz. I knew from my previous work that the WPC CPU switches
rapidly between two screens to form pixels of half intensity.
I
wanted to make sure that this feature was preserved with the VGA
display. However, since it only displays about one
every
other DMD screen, some special accomodations needed to be
made.
Another aspect is that the VGA display is 640 pixels wide, which is
five times more than a DMD display. It means that each DMD
pixel
can be mapped to one five by five grid of VGA pixels.
The VGA interface hardware on the Spartan board is very simple, and
consists of
just some resistors. It is possible to get by with just six
resistors for
this application
(fewer bits and no blue resistors).
The VGA interface on the
WPC
prototype works by writing the DMD data into a separate buffer, and
'smooths' the value across two screens. This analog value is
then
sent to the monitor for display. This simulates a persistence that
lasts across multiple VGA screenfuls. I coded this idea up,
and
found it to work quite well. After initial testing, I refined
the
code to provide a black border around each pixel, and rounded the
pixels' corners. This makes it much more like a real
DMD.
Referring to the image below, one can see half-intensity round pixels
in the simulated DMD.
The VGA interface at work. I can now debug the WPC Spartan
prototype from
anywhere such as my home office. Note the monitor cable
plugged
into
the top connector of the Spartan board. The
code being run here is "Slug Fest",
a baseball themed game. Note that I no longer get the "Check
Fuses" message
because I faked out
the switch matrix and simulated some closed switches.
Close-up of the VGA screen shows that the displayed dots are round with
gaps in between. This is meant to match the appearance of a
real
DMD. This hardware could in principle be added to any
machine
that uses the 128x32 dot DMD display.
Today
it may be difficult for a Dayton criminal
defense lawyer to believe that pinball machines were at one
time considered
illegal in some U.S. cities. Early machines were considered games of
chance,
much like gambling, rather than a game of skill.
Project
Log
6/20/07 - Start of Project.
6/21/07 - Equations for U16 PAL from Brent Butler
(pinball.at.ptpbroadband.com).
8/13/07 - WPC-95 prototype bongs ten times on
initialization. This indicates that the critical boot ROM
access
is good, but
the DSP RAM interface is faulty.
8/16/07 - Fixed DSP RAM problem, and WPC-95 prototype bongs
twice
on startup. Now I need to fix the page register.
9/1/07 - A/V board prototype finally issues a single BONG
on
startup! One of the LEDs on the Spartan board serves as the
red
LED on the WPC-95 board, and flashes just like the original
board. Another huge milestone passed.