Or, examining the reset cycle.
In traditional fashion for myself, I bit off a bit more than I can easily chew. I added some nice routines to a vintage computer’s system debugger in an attempt to quickly port it to the board and hopefully just get rolling. These software modifications will be covered later. Today however we’ll be covering a second mental exercise. Off to the oscilloscope.
For a bit of early reference, I’ll be doing this with a Tektronix 2246 scope, using four channels as described. They’re listed in the order they appear in the ‘scope pictures, from top to bottom.
- Channel 1: /RESET line on the cpu
- Channel 3: Phase2 system clock, to which all CPU transactions are synchronized
- Channel 2: Floating probe to examine data lines during the reset loop
- Channel 4: Floating probe with hook to attach to a visual reference signal
The challenge in figuring out the reset sequence is that there’s no really good way to trigger it besides repeatedly resetting the CPU. Even then, you’d have to trigger on the rising edge of the /RESET line and then change your trigger to the CPU’s PHASE2 output to get good sync with your data transfers or you’ll just see intermittent pulses of unaligned digital noise.
A challenge I faced as well is that I run everything on a two by four foot desk that has a PC keyboard as well as my GPIO enhanced Commodore. The scope fortunately doesn’t mind running on the floor in a pinch, so it sits to my left. This excludes any space for a signal generator to provide the repeating reset pulse. What to do?
Finding a slow clock
As I needed to repeatedly reset the CPU at a low frequency and didn’t have a clock generator handy, I thought of options:
- Program the 6522 VIA on my Commodore to generate a repeating pulse
- Use a serial TTY to generate a repeating pulse by sending nulls out its port continuously
The second option was chosen since the serial port was already connected. I modified the reset pin on the CPU so that it hangs out of its socket instead of being attached, and tied it to the RXD pin on the 6551 ACIA. Note carefully that I didn’t connect it directly to the serial line, which would have permanently destroyed the CPU. RS232 lines can run as much as +/-12v.
That fixed, we’re back to the trigger challenge.
Advanced triggering on the Tektronix 2246
The Tektronix 2246 oscilloscope has a pretty remarkable trigger section on it. I had not considered the use of an A/B trigger setup since college, so it was a bit of self re-education. The needs are simple: First, wait for /RESET to go high (trigger A, rising slope, channel 1). Next, trigger on phase2 (Trigger B, rising slope, channel 3).
After a bit of trial and error as well as some remembering how to read the double ghosted image on the Tek’s display, I remembered what was I was seeing and settled on a configuration. Here’s the steps I take.
- Attach lines in the configuration listed earlier in this article
- Set up A trigger (the most commonly used scope mode)
- Get my PC transmitting nulls:
jbevren@epicfail:~/projects.local/iieasy$ sudo stty ispeed 9600 </dev/ttyS4
jbevren@epicfail:~/projects.local/iieasy$ cat /dev/zero >/dev/ttyS4
- Set up a stable signal for channel 1:
- Trigger source: channel 1, DC coupling, auto level
- Place horizontal section into ‘alt’ mode
- Reduce the ‘A’ intensity and increase the ‘B’ intensity
- Set up a stable trigger for channel 3:
- Trigger source: channel 3, DC coupling, auto level
At this point, you should see something similar to the picture here.
The bright section at the left illustrates the section visible in trigger B. It may help to understand the configuration if you monitor the width of the highlighted section while changing the horizontal sweep time while in the A/B alt setup: The horizontal setting no longer affects the initial A trigger setup, allowing me to magnify as needed.
At this point, I don’t need to see the ‘A’ section, as I’ll only be checking in on the first few transactions to ensure the NVRAM’s getting read in correctly. In this case, I set the horizontal section’s mode to ‘B’ only. The ‘scope still processes the A trigger but no longer displays it. This saves me some brain time as I won’t have to mentally separate the two overlaid images.
Now, I can see what’s going on through a slight bit of visual jitter on the B trigger. I’ll attach channel 4 to the CPU’s SYNC output, which asserts at a logic high level each time a new instruction is fetched.
As you can see there’s a bit of ghosting going on the display. I can see through it, so it’s not a huge issue for me. However, to try and clean it up a bit, I’ll try triggering on channel 4 (now on SYNC) to see if it remains stable after the A trigger’s processed.
This setup is perfect. The initial SYNC pulse caused by the reset isn’t visible any longer, and a single SYNC pulse is visible on the right third of the display’s bottom line.
Finally, examining the reset sequence
Our first target is the address on the first SYNC pulse after RESET completes. The address will let us know if the reset vector gets read correctly from the firmware NVRAM and will also tell us where the CPU’s actually starting up. The data’s not yet important, so we’ll check address bits one by one.
I chose to provide more entertaining drivel over image processing, so the montage above only contains a few of the address lines I checked, recklessly copy/pasted into a modified image. They’re address bits 3-0 from top to bottom. If you look at them aligned with the clock at its low state you’ll see the first fetch is %1100, or a $C- the last digit in $FF5C.
I prefer to work from the high address bits down, as it makes it easier to think in a numeric fashion: We write our digits in such a way that the highest valued digit is entered first. In “$1,000,002” the 1 certainly has a higher value than the 2. Examining the bits in this order allows me to simply enter each binary digit as I walk my way down the chip’s address lines.
To help find the appropriate pins, I’ve marked my CPU out with a bit of pencil to separate the address bits into groups of four. The lead shines in the overhead light enabling me to quickly see where the labels are.
For anyone following along on a datasheet, remember this isn’t a standard CMOS 65c02. It’s a g65sc102 CPU, which is software compatible but has a slightly modified pin layout.
In the end, the 16 address bits at the SYNC mark show me this first opcode fetch at address %1111 1111 0101 1100. Note that the SYNC pulse is two cycles long, and the second address is incremented by one. This comes to $FF5C, which is correct. For reasons I’ll share later, a few lightbulbs may be appearing in some readers’ heads. Don’t feel alarmed if the address looks familiar.
As the reset vector’s getting read correctly but the serial chip’s not getting polled as I expected, I’ll also read the opcodes it should be reading. In a working system I’d only have one SYNC cycle, as the initial opcode is a JSR to a subroutine.
Here’s what the oscilloscope told me:
- [sync] 0011 0011 = $33, invalid opcode [nop]
- [sync] 0010 0000 = $20, JSR
- [norm] 0010 0111 = $27
- [norm] 1111 1111 = $FF
If the scope tells the truth, we start with an invalid $33 opcode, followed by jsr $FF27. This isn’t what should be happening. Considering I’m prone to errors as a part of being human, it’s entirely possible that my NVRAM programmer wiring or code may be at fault, so it’s time to re-verify that part of the project. Perhaps it’s even a good opportunity to wire up a board that would plug into the GPIO card rather than running a haystack of wires to a dip socket. 😉
Today I learned a lot from things not working as expected. A few people who do projects have told their audience that the best chance to learn is when there’s a failure. I can agree wholeheartedly after today’s experience, as I re-learned a skill we got in our second year of college: debugging a looping program using a simple 4-channel oscilloscope. I also learned the new skill of advanced triggering on my own ‘scope, and will be able to use it in future projects without a doubt!
More will come later, as I take some time to re-verify the code for the programmer as well as the GPIO wiring to the socket used to set the NVRAM up with its code.