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.
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 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.
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.
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.
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.
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
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.
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
Note the flashing LED on the prototype, matching the original board.
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
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
"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
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.
Go to Page 2 of the WPC FPGA Project
6/20/07 - Start of Project.
6/21/07 - Equations for U16 PAL from Brent Butler
7/24/07 - BONG! Powered up the complete prototype, and it gives one sound indicating DSP booted properly. A huge milestone.
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.
9/27/07 - Initial development of the CPU board code
completed. At this point, this project was shelved for a few
1/22/08 - CPU board on an FPGA boots! I get a fast flashing from the LED that is mapped to the equivalent LED on the WPC CPU board.
(c) 2012 Edward Cheung, all rights reserved.