System/1 Build Log
An ongoing chronology of System/1's construction, and any other related musings.
28th November, 2017 - Driving the serial interface prototype from software
As I was pondering the easiest way to hook a breadboard full of parts up to the memory bus for further testing of the UART design, the extender card PCBs I'd ordered arrived. They're nothing at all fancy — just 96 traces connecting two DIN41612 footprints — but having fitted appropriate connectors to each end of one of the boards I could hang the clock board a few inches further out from the backplane than the other boards. That's just far enough to allow me to connect the logic analyser up to the debug header (and various other signals) to try and track down some of the odd behaviour I'd noticed previously.
Of course, as soon as I installed the extender card between the clock board and the backplane, I found I couldn't reproduce the couple of minor issues I'd jotted down during my previous testing. I'm not sure whether this was because my notes were insufficient to remind me of the exact details, or whether introducing the extender changed some aspect of the system enough to 'fix' whatever was happening (possibly a signal integrity issue?), but unfortunately I didn't have enough time to dig further before real life dragged me away from testing with various work commitments, a holiday, a family wedding and a monumental tidying-up session in case any relatives attending the last of those wanted to drop by when they were in the country. Consequently it ended up being the middle of November before I had a chance to sit back down and resume tinkering.
In the meantime, however, I'd ordered some PCBs to go into a wedding present I was building, and as I did so realised I might as well dust off the design for some breakout boards I'd been considering as a way to hook the logic analyser up to the memory bus in case I needed to do any fancy debugging. Not only would this make better use of the expedited postage cost I was incurring to get the important boards here quicker, but it would make it a lot easier to connect up the serial board prototype to the system for testing. As the clock board seemed to be behaving itself with the extender card installed I decided to give this a shot next and hopefully get to a point where I could dismantle the serial board prototype and reclaim some desk space!
The first step was to cobble together some connections using the breakout boards, some ribbon cable and a handful of transition headers (which are IDC connectors for the ribbon cable that solder directly to a PCB, rather than requiring a mating connector) to get eight data bits, a few high-order address lines and the various bus control lines over to the breadboard. At the breadboard end, using 0.3" DIP transition headers would theoretically allow the ribbon cables to be conveniently plugged into the breadboard; alas, in practical terms one of the breadboards refused to make good contact with the header, so I ended up having to apply pressure before data would flow reliably. Eventually, though, I had the transmit and receive FIFOs wired up to the memory bus and could send and retrieve bytes via the front panel memory access functions.
This first step had neglected any address decoding, though, so any attempt to read or write memory would end up reading or writing the relevant FIFOs — enough to prove the basic idea was sound but not much use when the machine wants to do anything else, such as run code! In order to allow RAM and the UART prototype to co-exist on the bus, I figured I'd hook a 74HC139 dual 1-of-4 decoder up to the top two address lines; this would give me a bottom 1GB of address space where the RAM board could live, another 1GB of address space which would map to the FIFOs as before, and a couple of spare mappings with which to experiment with ideas for control registers etc. With this quick-and-dirty address decoding in place, I was able to write a short loop to output bytes under software control and was somewhat surprised to discover that the machine could actually outpace the serial link — I hadn't done the maths on my loop timings and had convinced myself that my pile of parts was going to be painfully slow, but of course 9600bps serial lines are also painfully slow!
Obviously this meant I needed to prioritise adding a status register so that software could check whether there is room in the send FIFO before trying to write to it, and similarly so that it wouldn't try to read from the receive FIFO if there was no data available. A 74HC125 tri-state buffer between the relevant FIFO status lines and the data bus would suffice, hooked up to one of the spare outputs on the address decoder; with this in place my test loop could now send 0 - 255 reliably without dropping bytes. Another test program successfully echoed my typing back to me with the case flipped, just to reassure myself it wasn't accidentally echoing bytes without software assistance, and finally I cobbled together a quick test script on the laptop to send random blocks of data and ensure they were correctly echoed back. This ran happily for a good twenty minutes or so until my finger got tired of holding that loose connector into the breadboard, at which point I called it a day.
That just left a few bits and pieces left to prototype, the most obvious one being handshaking support. Although the receive FIFO provides some buffer space in case the software is too busy to service incoming data, that can only stretch so far — 16 bytes, in fact. It makes sense to expose the FIFO's DIR signal (Data Input Ready) to the serial port as one of the handshaking lines; later versions of RS232 suggest repurposing the obsolete RTS (Request To Send) line for this, indicating actually that the terminal is Ready To Receive. Since at least some of the hardware I might be hooking up to this serial port treats dropping that signal as a notification to immediately stop sending data, instead of possibly sending a few more bytes before stopping as it would with DTR (Data Terminal Ready) handshaking, this seemed like a good option. Testing this out by halting the machine and sending blocks of data from the laptop showed that indeed, once the FIFO was full, the USB adapter immediately stopped sending bytes; restarting the echo loop resulted in the FIFO draining and transmission resuming without any bytes being lost.
Testing handshaking in the other direction proved a bit trickier; although it was easy enough to add some logic to stop the serial transmitter from sending the next byte if the remote device dropped either the DSR (Data Set Ready) or CTS (Clear To Send) lines, I couldn't convince my laptop and USB adapter to actually do so. I'll have to dig out the serial-to-parallel adapter I built for the logic analyser, since I know that can signal this condition if the printer is offline.
Finally, although I was running out of room on the breadboard, I wanted to try generating an interrupt when data was available for reading, in case I ever get fancy with the software side of things. Generating the interrupt itself was easy enough, by running the receive FIFO's DOR (Data Output Ready) line to a transistor that pulls the IRQ bus line low when active, but I also added a flip-flop connected to the spare line from the address decoder to act as a makeshift configuration register. This allowed the interrupt to be enabled or disabled from software, but more importantly served as a proof-of-concept for other software-configurable options once I've decided what the finished board will support in that respect. Rewriting the echo test program to do the echoing from the interrupt handler whilst counting iterations of an idle loop seemed to work well enough...
... but by this point I was well and truly fed up with that loose connection to the memory bus requiring constant attention during testing — it's time to get a proper schematic drawn up and build a more durable serial board!