Software, technology, sysadmin war stories, and more. Feed
Saturday, September 17, 2011

Supposed hardware bugs with a TTL logic lab

I took some EE classes once upon a time and had to build a bunch of small circuits in various labs. One two-person lab went really badly and it turned into a hardware debugging situation. I had never really done troubleshooting on individual chips and other components before. At the end, I wondered if it had been planted by my prof as some kind of test.

Our task was to create a traffic light using just the things in our parts kit and our breadboards. We had a bunch of TTL logic: there was a 4 bit counter, a bunch of inverters, AND gates, OR gates, LEDs, a 555 timer chip, some buttons, a few resistors, and hookup wires of all lengths.

For simplicity's sake, we only had to render the 3 lights for a theoretical side street which connected to a bigger main road. By default, that side street would stay red. If a car rolled up (simulated by pushing a button, or sliding a DIP switch), it should go green for 10 seconds. Then it should go yellow for no more than 2 seconds, then go back to red.

There was also another requirement: it had to stay red for another 10 seconds even if a car was present the whole time. This way, the side street wouldn't totally starve the main street if the sensor was broken or if there was just heavy traffic at the time.

I looked at this and realized this would fundamentally be four states. There's the first red which can be interrupted by the car sensor. Then there's green, which lasts 10 seconds, yellow, which would last 1 or 2 seconds, and then the second red which must last 10 seconds as well. It would always go 0-1-2-3, and then go back to 0. No other state changes were possible.

Given all of that, I said, oh cool, this is just 00, 01, 10, 11, and we can just steal the bottom two bits of our four bit counter! It'll really be counting from 0000 to 1111, but by ignoring the top two, we'll get the sequence we want.

So counter #1 was pressed into service as my state tracker. The "enable" line which allowed it to progress upon receiving a pulse was wired up in a big mess of TTL logic, which did something like this:

Enable = (red #1 + car) OR (green + 10 sec) OR (yellow + 1 sec) OR (red #2 + 10 sec).

We had rigged our 555 to make pulses at 1 Hz. When the "enable" condition was met, those pulses would make it through to the first counter, and it would advance to the next step.

Counter #1 had outputs, naturally, and we sent them through a series of logic gates to trigger the lights.

Red = (not-bit 1 AND not-bit 0) [00] OR (bit 1 AND bit 0) [11]

Yellow = (bit 1 AND not-bit 0) [10]

Green = (not-bit 1 AND bit 0) [01]

The only other thing we needed was the "10 sec" trigger. For this, we just routed that timer pulse around to another counter, and hacked up some logic which would go true when it reached 10 (decimal) or 1010 (binary). It was something like this:

10sec = (bit 3 AND not-bit 2 AND bit 1 AND not-bit 0)

That signal was then used in a couple of places. First, it was used in the "progression" logic shown above any time we needed to act when 10 seconds had elapsed. Next, since this was a counter which went from 0 to 15 decimal, we made this 10 second signal actually loop back to counter #2's RESET so it would actually go from 0 and 9 and back to 0. That turned it into a 0-9 counter for us. Pretty cool.

So that was the design. I got it wired up, and it worked! It would start up at red, then went to green when I pushed the button, then yellow, then red again! Yay! We tested it some more. It worked!

But then ... it failed. It went red-green-red -- no yellow. Huh? We tested it again. It worked. What? Again. It worked! Huh? Again. It failed. WHAT?

Finally I realized that I needed to know what was going on in the brain of this thing, so I crammed a 7-segment display encoder onto the board and wired counter #1 (state) up to it, then had it drive an actual 7-segment display (one left over from my decimal display lab) to show 0 through F.

I debug with printf, or its local equivalent, and this was exactly that. I powered it back up. Red. 0. Push the button. Green. 1. 10 seconds pass. Yellow. 2. One second passes. Red. 3. 10 seconds pass. Red. 4. Okay so far.

I start another round with a button press. Green at 5. Yellow at 6. Red at 7. Red at 8. Okay again.

I start it one more time with a button press. Green at 9. Red at 0? What happened to A, B, C, D, E and F? Why is this thing only counting 10 numbers instead of 16?

Upon closer inspection, the actual counter chips, though they were supposed to be identical, were not. One of our counters had been dead on arrival, and the prof grabbed a replacement from the back for us. We had been using it and never realized it was any different. Now we knew we had to look it up in the big yellow TI book.

There it was: this particular part number, which was one digit off from the part we were supposed to have, was a decade counter. Yep, it would never get to A-F. Since it jumped from 9 (1001) to 0 (0000), it broke one out of every three tests in a consistent fashion. We just had to play with it long enough to figure it out.

At this point, I didn't want to mess with this any more. Things were really tight on the board and swapping the counters was not a viable option. I needed a fix which would involve the minimum amount of screwing around just to get this done and move on.

My solution was a horrible, ugly hack. I took bit 2 of our screwy little decade counter and routed it to the RESET line on itself. That way, it would count 0000, 0001, 0010, 0011, and then get to 0100 briefly before being smacked back to 0000. The bits I was using didn't change during this transition, so there was no danger from a race.

So now we had a hex counter turned decade, and a decade counter turned quad, but it worked. The lab did exactly what it was supposed to, even though it had serious evil under the hood. We got our sign-off from the prof, I dismantled the parts, and that was that.

I may never know if my professor did that on purpose just to see how we'd handle it. I like to think that he did, just to make it more interesting and useful, since otherwise the lab itself would have been over in a jiffy. By raising the bar, I actually learned a few things.