The Good, the Bad and the Ugly

The next thing I got obsessed with was the keyboard. At first I had planned to get an old keyboard off eBay and reverse engineer it. So I bought an old VT-260 keyboard and removed its circuit board to expose the raw key matrix connections and I spent a fair bit of time with a multimeter working out how that matrix was organized. I got as far as having my own microcontroller reading the keyboard and displaying simple scan codes. But by the time I had done that, I had decided that the keyboard itself was not really what I wanted LEO-1 to have. I wanted something special.

You may recall my admiration of the ICL 2900 series mainframes. Well, the keyboards those things had were absolutely gorgeous and nothing like anything I’ve seen before or since. I wanted LEO-1 to have a keyboard like that, but sadly, not everything is available on eBay. The closest I could come to that would be to make my own keyboard with a similar layout and similar keycaps. So I started reading about custom keyboards and found that there is a whole subculture of people who are really into keyboards. I found a great YouTube channel by this funny bloke who calls himself Chyrosran22 and watched all his videos. After a month or two of this I think I knew more about keyboards than I know about my own mother so it was time to take a step back and regroup.

ICL 2900 keyboard

ICL 2900 keyboard

While looking for keyboards on eBay, I had discovered a cool device called a Rubidium Frequency Standard which piqued my clock-obsession — and so I spent a few months building my own atomic clock! I already have enough clocks in this room and didn’t need another one, but I couldn’t resist. I had lost my way. It seemed that I was just finding excuses to postpone finishing LEO-1 and looking back I can now see that I was terrified that I wouldn’t be able to make it work. If it didn’t work, it would be the biggest waste of time and money and I would look like such an idiot for even attempting something as absurd as trying to build a contraption as complicated as this.

Even so, I had been working on the PCB design for the Memory Board and the Control Board during the autumn and they were pretty much finished. Eventually after obsessively checking it over and over again, I had the Memory Board manufactured and I populated it. My initial tests with switches were promising, but then something terrible happened. In 2015 I had seen a very weird problem when testing the ROMs where the data bus would go into continual oscillation for no apparent reason. I thought I’d solved it with bus termination resistors, but now this problem came back to bite me in the arse once again, this time on my nicely made PCB. I couldn’t understand why all these people out there could build entire CPUs on breadboards with loads of untidy wires and have it all work perfectly, but I couldn’t even get a ROM to work on a neat PCB. At one point I just considered picking the whole thing up and placing it into the trash, but of course that was only a fleeting thought. I tried all kinds of hacks and guesses but really had no idea what was going on. This problem only happened on ROM not on RAM or any of the other devices. I blamed the ROM chips; they must be buggy. I managed to crowbar an old-school EPROM into the socket and would you believe it, the problem happened on those too. I checked the data sheet over and over again and couldn’t see anything that could explain it. Then I realized what I was doing. I was using switches to change the address bus for testing, and I was doing it while the ROM was chip-enabled and also output-enabled. My VDU does that and doesn’t have any trouble, but I wondered what would happen if I followed the process that the real CPU would. The address bus would only change when the ROM was not enabled, and the output-enable would only be on for a moment. Well, when I started testing it like that, the problem went away. I visualized the problem as this: when you change the address, the internal circuitry of the ROM will ‘rattle’ as the device finds the correct cell to output. If the device is output-enabled, this rattling will appear on the data bus as a randomly changing mess lasting some tens of nanoseconds. This is high-frequency business; you don’t want that noise getting onto the data bus in the first place. Possibly, the length and layout of the PCB traces could cause frequencies like that to start an oscillation, and if the changes are ‘evil’ enough, it could create power supply glitches, ground bounce or such, which would upset the ROM, cause it to rattle some more… and around you go, ad infinitum. This is all speculation. The real cause is still somewhat of a mystery, but the important thing was that I found that as long as the rattling doesn’t get onto the data bus, you’re safe. Unfortunately, I also found that this happens if the ROM’s chip-enable and output-enable are asserted at the same moment, and my CPU design actually did that. The only way it would work properly would be if the chip-enable came first, then the output-enable, on a different tick. I was going to have to change the design of the CPU’s sequencer. It was an easy fix, and a lucky catch at the eleventh hour, especially since I had not ordered the PCB yet.

Oscillation

Ugly things on the data bus

 

You may recall that I have a terrible habit of going off at a tangent and ordering things that I hope to be able to use in the future, even when I’m not sure that any of it will work. Well, I had ordered a small thermal receipt printer a month before, so with the memory board working, I decided to try it out. This also enabled me to test the device expansion ports. I hooked up a UART to the memory board and the printer, and manipulated it as a device using the test switches. I was able to manually make the printer print a single character. So this means LEO-1 will almost certainly be able to use that printer too. Finally happy with these results, I decided to press on and finish the control board.

“You’re all clear kid, now let’s blow this thing and go home!” — Han Solo

Advertisements

Slow motion

I can’t believe it’s almost the end of May and the memory board is still not finished. LEO-1 has taken a bit of  a back-burner position, it seems. This started in January when my wife and I moved to a new apartment. For several weeks before and after the move, I had precious little time for soldering, with packing and organizing taking priority. During the move I managed to hurt my back very badly by lifting my AKAI X-355 tape recorder incorrectly. The thing is built like a tank and weighs a ton. I was in terrible pain for about 3 weeks and couldn’t face any hobby stuff at all.

Somehow, I managed to not do any LEO-1 work during March either, having got out of the habit of even thinking about it. Then, towards the end of March I bought an old Conn organ from a charity shop and spent several weeks fixing it up. During April, I decided I needed a proper amplifier and speakers in the living room, and took it upon myself to build the amplifier myself. It came out beautifully and sounds great, but this project once again kept me away from the LEO-1.

I got back to it a couple of weeks ago, doing something that I should have done right at the start. I went through the schematics and labelled all the ICs with their numbers. The software I’m using, ExpressSCH, doesn’t do this in a very useful way when there are gates involved, because it numbers all the gates as separate ICs, which they are not. So I had to do it manually. When I finally finished, I found that I had 282 ICs in total, including the RAM, ROM and ZIF sockets. At the start, I had estimated there would be about 200. Believe it or not, I had actually gone ahead and ordered all the chips I thought I was going to need back last autumn, without actually doing a proper count. Obviously I had counted certain chips like the bus drivers and registers, but for AND, OR, NOT gates, etc., I had not bothered; I just bought a load of them cheap on eBay. This means that I now have a large quantity of surplus chips, in particular 74HC14. I had bought 50 of them for about 6 bucks and it turns out I only need three. I laughed out loud at that mistake and I still can’t really work out how it happened.

I also found I needed to change an important design decision, the decision to not use IC sockets for the numerous small chips. I had initially decided to not use sockets because they are so expensive and I need a couple of hundred of them. But while working on the memory board, I saw the glaring truth. If one of those 74HC32s for example were to fail, either due to an error on my part or for some other reason, I would simply not be able to replace it without desoldering all the wires connected to it first, and soldering them back afterwards. What is the likelihood that a chip would fail? Well, probably not very high, but accidents do happen; it’s easy to drop a screwdriver and short something out. I decided it just wasn’t worth the risk, so I bit the bullet and ordered sockets for all the chips on the Control, ALU and Register boards. I don’t trust the really cheap sockets that use blades so I had to get machine tooled ones that have nice round holes with grips inside. I also decided to get the best quality I could afford so I got ones with gold-plated contacts. By buying Jameco ValuePro parts I managed to cut the cost in half over what Mouser wanted for the real brand-name parts. It sucks that in some cases a single socket can cost over a dollar when I was able to buy nine ICs for the same money, so I wanted to try and cut the cost without sacrificing quality too much.

Toil and trouble

My worst fears have come to pass and for a while I thought the LEO-1 was not going to make it. I had made a test board to act as a fake control board so that I could test the memory board. I had tested it with the Real Time Clock chip and everything was working great. So I carried on building the memory board and connected up the two ZIF sockets. I burned some test data to one of the EEPROMs and put it in one of the sockets, connected up a couple of digits to one half the data bus and used the test board to address the ROM. Lo and behold, the digits showed the correct data coming out of the ROM. It was working as expected. Here’s a picture of the set-up. The digits on the protoboard are showing 3 4 which is the first byte in the ROM.

Memory Board working

Memory board displaying data

I switched various addresses and watched the data come out and everything seemed fine, when suddenly the digits went all weird, like a square 8 which is an impossible display. It was pretty clear that the display was oscillating between two values at such a high speed that it looked like all the dots were on at the same time.

And so began several weeks of pure hell trying to figure out what was going on and what caused it. I was able to reliably repeat the problem by setting a certain address and then changing to the next address so that the data changed from 0 0 to  0 1. Often, but not always, the thing would start oscillating. I put my scope on it and found an 8Mhz oscillation on the data bus. I suspected the ROM was faulty but all the ROMs had the same behaviour. I suspected the test board because I had forgotten to buffer the address bus lines. That was a dumb mistake which I decided to fix, so I spent a week adding 74244 buffers to the test board. That didn’t fix the problem. Then I realised I had forgotten to buffer the control signals too, so I used a few spare gates on the board’s 7400 to do that. Didn’t fix it. Weeks had gone by and this random oscillation was still happening. I remembered that you can get this kind of problem if you forget to connect an unused input on a gate, so I checked the schematics and scoured the board for disconnected pins. No luck. Everything was as it should be. One night in early December as I sat there with my head in my hands, I realised that if I couldn’t solve this, there would be no chance of the LEO-1 working at all. My fairly trivial memory board wasn’t even stable at manual switching speeds! I thought about giving up — all that time, effort and money down the drain. By the time I went to bed, I was pretty depressed.

Diagnostics

Trying to figure out the oscillation problem

The next day I took one last crack at solving the problem. I had been studying all the data signals on my scope’s logic analyzer and had not had any ideas beyond unconnected pins and faulty chips.

You can see in the picture above that the test board’s ribbon cable was sticking up in the air. It was like that because I was using a short cable for the address bus and the long data bus cable had to be bent in order to be plugged in. Well, for some reason, while the oscillation problem was happening, I just happened to push the cable a bit flatter — and the oscillation stopped. That didn’t seem like a coincidence, so I tried changing the address with the cable flat — no oscillation. I bent the cable again and then next time I switched addresses, the oscillation started again. I could control whether the oscillation would happen or not by bending and straightening the cable. What’s more, the scope showed that the oscillation frequency was changing between about 7 and 8 MHz depending on how the cable was folded.

Then it hit me. Was the ribbon cable suffering from transmission line problems? Those problems that I had been obsessing over right back at the start? Things like reflections and ringing and all that annoying stuff? And did folding the cable do something random like perhaps change the impedance or cause cross-talk? And could that make the bus transceiver chip go into some kind of oscillation? Well, I was not sure and I’m still not sure now, but I have a theory. When reading data out of the ROM, the test-board end of the 12″ cable was not connected to anything. That meant that when the data bus switched from 00 to 01, that 1 bit went down to the other end of the 12″ and reflected back. Ordinarily I would have expected the reflection to die out pretty quickly and just cause a bit of ringing. But something about the folded cable caused it to keep bouncing, perhaps amplified by the bus driver itself. After reading up a bit about line termination, I did an experiment. I added a bunch of 82 ohm resistors at the memory board end of the cable, just before the 74245 transceiver. After that, I couldn’t repeat the problem any more. No amount of switching addresses or folding the cable in any way caused the oscillation and I haven’t seen the problem since. I don’t really understand why it works except that possibly the resistors absorb the reflection and damp it so that it doesn’t turn into an oscillation, but I still don’t understand why I got full permanent oscillation in the first place. I’d love to hear from any experts out there who can explain what the heck was going on.

As if that wasn’t enough trouble, the next thing that went wrong was that I discovered that I had not left nearly enough room between the address bus header and the first bunch of chips. There was no room left to solder any more wires and I still had a couple of busses to connect there. I found I had no option but to move the address bus header to the other side of the board. I had to disconnect all the wires, desolder the header, re-solder it and redo all the work of connecting the 24 address lines to the ZIF sockets. While I was at it, I bit the bullet and removed the majority of the thick annoying wires and replaced them all with magnet wire. I learned a valuable lesson there. You have to leave plenty of room between headers and the chips they connect to so that there’s room to solder multiple busses to the same header pins. From now on, I’m only going to use regular wire for power connections. Everything else will be done with thin magnet wire.

Address Bus

Address Bus connections

So, the LEO-1 lives on after all and right now I’m in the process of wiring up the main RAM and ROM sockets. After that it’s the I/O chips and then board number 1 of 4 is complete.

Memory Board

Memory board as of New Year 2016

Emitting Light

Even though I really had no idea if I was going to be able pull this off, I couldn’t resist thinking about what the completed LEO-1 would look like. I had all kinds of grand visions of switches and flashing lights that all the best CPUs have had over the decades since they were invented and I wanted LEO-1 to look like my favourite, the ICL 2900 series. Well, that wasn’t going to happen, was it? A nice dream, but that’s all.

I had originally considered making the enclosure out of wood and mounting individual LEDs on a hardboard front panel. I also investigated the idea of obtaining some kind of heavy-duty metal enclosure but decided it would be too difficult to drill all the holes. Then I discovered a company called Tap Plastics which lets you order various kinds of plastic sheet cut to the sizes you need. I got a few samples from them and decided that this was the way to go. I should be able to make the whole enclosure out of 1/8″ thick acrylic sheets.

A while after I had begun construction of the memory board, I had a brainwave which invalidated the whole front panel design and sent a ton of prematurely-ordered components into the spare parts bin. Instead of mounting the LEDs on the front panel with clips and rings, why not mount them along the edges of the circuit boards? Then I could make the front panel out of a piece of red transparent plastic and mask off the unwanted areas with black paint on the back of the sheet. The LEDs would shine through from the stack of boards behind. This was such a great idea in so many ways I’m surprised I didn’t think of it earlier. There would be no need for any wires flying from the boards to the LEDs, no need to drill loads of holes, and no need to mount loads of LEDs with clips and rings. The best part of this was the ease with which I would be able to make sure all the LEDs were exactly the same distance apart and in precisely horizontal rows. I was already using circuit board indicators for showing that boards have power and I knew they would be really easy to line up straight.

There was just one problem: I could only fit 32 such indicators across a board. That would allow 128 LEDs for the whole CPU which is exactly how many I was originally thinking of using, but the trouble was that some of them would be on the wrong boards. To display the address and data buses on the memory board I would need 40 right there. 32 just wasn’t going to be enough on any particular board. The solution I found was bi-level LEDs (or ‘double-deckers’ as I like to call them). When you run out of room in a city, you build upwards. With these you can do the same on a circuit board. As soon as I had figured this out I had to figure out how to drive that many LEDs without needing a private power station. Standard LEDs run bright at about 10mA so, say, 200 of them, would consume two amps. What’s more, they need resistors and drivers. It was getting a bit complicated. Then I found these (and evidently ordered a bucket-load of them):

23448600892_64a150b22d_z

Double-decker LEDs

They are super-bright bi-level LEDs. That means that at the ‘normal’ current of 10mA, they would be too bright to look at. As it turns out, each LED can run brightly from as little as 1mA which reduces the total power consumption by an order of magnitude to about 200mA. The best part of this is that special drivers are not needed; I can just use 74HC240 or 74HC244 bus drivers.

If I stick to my current layout idea, I should end up with front panel lights that look a bit like this:

LEO-1 LED Layout

Which, surprisingly, looks a little tiny bit like this:

Part of an ICL 2970

Prior planning and preparation…

I haven’t updated this for ages and anyone reading it would be forgiven for thinking I had given up on the LEO-1. Nothing could be further from the truth. I finally got some answers about the worrying stuff I mentioned earlier, partly from some friendly guys on the Electronics Point forum. You can read the thread here.

To cut a long story short, it seems I have been overthinking this issue a little bit too much. I shouldn’t run into any trouble at the speeds my circuit will be running at. Some of the spiky stuff I was worried about even seems to be generated by the act of measuring it, due to reflections inside the scope’s probes. Some of it is also caused by doing tests on a breadboard with long ratty wires all over the place. When I build the real thing on real boards with short wires, it should be fine. I’m going to go on that assumption for now.

During this time I’ve also been figuring out what other parts I’ll be needing and getting them together. You may recall that I was worried that HCT parts were not the best parts to use and I actually did decide to back up on that and switch over to HC parts. It just wasn’t worth the risk of buying hundreds of chips only to find they don’t work the way I expected. So I counted my losses and reordered the original prototyping parts in HC. I now have a bunch of HCT chips that I won’t use but I’ll hold on to them for a rainy day. Once I had figured out what I was going to need, I ordered a ton of chips. There’s a company on eBay that sells unused surplus parts amazingly cheaply. For example, I was able to get about fifty 74HC32s for about $7. The rest of the stuff I’ve been getting from Mouser and some (like the circuit boards) from DigiKey. The EEPROM I’ve chosen is the Greenliant GLS29EE010 which is a 1Mbit device organised as 128 x 8 bits. They only cost $2 each; two of those in parallel and I’ve got a 16-bit ROM for the monitor program. At this point I decided I was going to have to get a reliable EEPROM programmer. I’d seen cheap Chinese device programmers on eBay but I’d also read appalling things about their reliability and usability. It sounded like a false economy that I couldn’t risk. Perhaps when I was a poor teenager but not now. So I bought a Phyton ChipProg 40, mainly because it has the GLS29EE010 on its supported device list, but also because it was available, and I could afford it. I’m happy to report that it works perfectly and I was able to burn some test garbage into my ROM chips. I was also able to use it to have a look at the old PICs that I’d programmed in 2008 on a PIC development board. The code was all still there. An amazing thing is Flash memory.

In other news, I wanted to have some red LED digits on the front panel for debugging and discovered some really nice smart hex display chips (HP 5082 7340) — but they turned out to be obsolete, very expensive and difficult to get. They look so nice that I don’t know why they would be obsolete. I’ve never seen these kind of things on any equipment before and wonder where they were used. Everything has the ubiquitous seven-segment displays, but not these. The last time I saw anything like them was on my first digital watch in 1978, but they were much tinier. Anyway, I found something similar, TIL311, on eBay and acquired four of them. That’s enough to display a 16-bit value. Here’s a couple of pictures:

TIL311 smart display

TIL311 smart display

TIL311 in action

TIL311 in action

When I haven’t been experimenting with the actual parts, I’ve been drawing the schematics. So far I have drawn two of the four boards and I’ve been finding design flaws while doing it. As soon as I started drawing with real components I noticed I had missed a line driver here and there. I also found a potential race hazard that meant I had to revise the simulation. I hadn’t realised that the memory address decoding will take a finite amount of time to settle and that during that time, it will be possible to select multiple devices onto the data bus. If that happens even for 10 nanoseconds, it won’t be good for the devices or the power consumption, not to mention the stability of the machine. The solution is to wait an extra tick for the decoding to finish and only then actually assert the chip select signal. When I spotted this I found another similar issue and realised my instruction cycle of only 4 states was too simple. I had to increase it to 8 states for memory operations and 5 states for non-memory operations. Very disappointing, but makes sense since I haven’t seen any other designs out there with only 4 states for an instruction. This means that all instructions no longer take the same amount of time to execute which seems a bit weird. Still, I think it will work just fine.

I also figured it would be nice to have a means to switch off the main clock and be able to single step instructions with a button. I spent some time experimenting with ways to achieve that and added it to the schematic for the clock section. During this time I revisited the 555 timer, a familiar friend from my early digital learning days. I still have my old ‘Babani’  book IC 555 Projects by the very drole Mr. E.A. Parr B.Sc, C.Eng, M.I.E.E — that’s a lot of letters 🙂 In the end, I used a 555 for the ‘slow’ clock (a crystal oscillator will be used for the ‘fast’ or normal clock) and didn’t need one for the single step circuit.

Prototyping single step

Prototyping single step

Design decisions: Electronics

By the time I had finished the first draft of the simulation, I really felt like it would be possible to implement LEO-1 with real electronics. I had tested the simulator by spending a few days writing an assembler so that it would be easier to test by copying and pasting the assembler output into Logisim instead of having to work it out on paper and type the 16-bit instruction codes in. Now I had a simple assembler which I could use to program the real thing. I had to try and build it for real.

As I’ve mentioned before, I’m not totally new to digital electronics. I designed and built a digital clock in 1978 or so and I designed and built a decoder and display for the (now defunct) ‘Maplin Rugby Clock receiver’. I felt confident that I could get back into this fairly easily. I already had a soldering iron, multimeter, wire, basic tools and a cabinet full of spare electronic parts that I’d collected over the years. How hard could it be? I decided to find out by seeing if I could come up with a design for a single one of the 16-bit registers LEO-1 needs.

I had already decided that the 74 series chips was the way to go, just because I’ve always liked them (after I got over hating them), I’ve never really felt comfortable with the 4000 series CMOS parts, and many mini and mainframe computers like the ones I worked with in the 80s were made out of them. I read through this page to refamiliarise myself with some of the parts I was going to need and started to realise that in the last 20 years, things have changed a bit. Originally, in the 70s, I used plain old 74xx parts. These were the real TTLs. They got warm during use and were rather robust. Some were even made of ceramic instead of plastic.

Pictures I took of the clock project I did in 1993 show that I used 74HCxx parts. I’m not sure that at the time I knew that these parts are actually implemented with CMOS and are not really TTL compatible. The project worked because I used all the same kind (HC) and 5 volts (the standard TTL supply)  works for CMOS as well. What’s changed for the better is that there now also exist 74HCTxx parts which are implemented as CMOS but have a completely TTL-compatible interface. By using these, you get compatibility with old TTL parts like 74LSxx but they use less power. I needed to choose between HC and HCT. My decision was influenced by something (that I should have, but didn’t) expect: many of the 74 series are now obsolete and either very difficult or impossible to get. If I went with HC parts (which are ‘preferred’ for new designs), I would be locked into them and if a part was not available, I would be royally screwed. If however, I went with HCT parts, I would have the option of falling back on LS for difficult-to-get items. I decided to go with HCT for this reason. The only downside I could see was HCT’s ‘lower immunity to noise’. I’m not sure if that will affect what I’m doing; I hope not. In retrospect I think I may have made a mistake as I probably won’t need to interface to any LS chips and HC would have been a better choice, but I’m not going back now.

Anyway, after choosing 74HCT chips I ordered a few parts for prototyping the register board. While I was at it I looked into the kind of memory (RAM, ROM) I might want to try. I found a static RAM chip which provides 512k x 8 bits and figured I could use two of those to make a 16-bit memory. As for ROM, I’d like to try EEPROM (i.e., Flash memory in a chip) but I’m still doing research on that because of the need for a programming device to get the code into the chip. There are two options for this: build my own, or buy one. I think I’m going to have to go for option 2 on that. Still thinking about it though. Once I know which device I want to use, I can try and find an affordable programmer that supports it.

So, what do I need for a single register? It turns out that a pair of 74273 octal registers will do the trick. They latch the data in on a positive-going clock edge which is what my design needs, and they have two-state outputs. LEO-1 has four internal register buses which I called RIN, ABUS, BBUS and CBUS. The RIN bus is the register input bus and it will be constantly connected to all register inputs. The other three are the register output buses. ABUS and BBUS go to the inputs of the ALU and CBUS is used for writing a register to memory. This means that the output of every register has to be connected to three bus drivers which will enable any register value to be output to any of the A, B or C buses. The instruction decoder will ensure that only one register at a time can get onto a bus by selecting only one of the eight registers for each case of A, B and C. I chose the 74244 octal bus driver chip for this purpose. The 273 and 244 being octal (8-bit) chips means I have to ‘bit-slice’ to get 16-bits from pairs of 8-bit chips. So, one half (low 8 bits) of a register will need a 74273 register and three 74244 bus drivers, and the other half (high 8 bits) will need the same. This gives a total of eight chips per register for a total of 64 chips across all eight registers. While I was designing LEO-1 in the simulator, I didn’t give this kind of thing much thought. I’m now glad I didn’t try to design a 32-bit machine! Here’s a picture of the chips attached to a bit of static-proof foam:

Register chips

Register chips

Design decisions: Architecture

One of the first design decisions I made for LEO-1 was that it would be a pure 16-bit machine. I wanted to make something that would be unlike anything I had prior experience of and yet would still be useful, while at the same time not being too terribly expensive to build for real if I decided to do that. It was pretty clear to me that it would be more expensive to build, say, a 32-bit machine than it would be to build an 8-bit machine, because the cost pretty much scales with the number of bits you want. For example, if you want to make a 4-bit CPU, you could use a 4-bit adder chip in the ALU. But if you wanted 32-bits, you would need eight of those chips in a cascaded arrangement. Similarly, registers, bus drivers and all that kind of stuff scales in cost according to the number of bits. More bits needs more chips. Chips are cheap, but the more chips you need, the more circuit board space you need. Circuit boards are not cheap, especially if you want proper ones made by a PCB house (which is something I’m considering as I’m very tired of messy veroboard circuits). Also, doubling the number of bits about doubles the power requirements and the amount of heat generated… and so on. So I made a compromise. I would have loved to build a 32-bit monster but it would have been slightly over-the-top for a first CPU. However, 8 bits just wasn’t enough and I’m a bit bored by 8-bit computers now. 16 bits is in the middle (and it also makes me think of the early minis like the PDP-11), so I decided on 16 bits. That also gave me a good chance of making a clean instruction set with one 16-bit word for every instruction.

The second decision followed quickly after realising that 16 bits was good for packing a lot of codes and switches into an instruction word. That was the decision to not use microcode. The main reason for this is that I didn’t want to have to deal with writing microcode, fixing bugs in it and having to store it in a ROM somewhere. Microcode was invented to simplify the design of complex-instruction CPUs, but in order to simplify my design, I decided not to use it. This meant my instruction set was going to have to be pretty simple. The simplest instruction set to build hardware for would have instructions that were all the same length and which were directly ‘wired’ to the registers and ALU and other circuitry. In other words, no microcode. The simplest hardware would also have only one ALU and things like registers would be generalized. Having a bunch of general purpose registers which can all do the same things instead of having an accumulator here and a stack pointer there and an index register somewhere else would just make it easier to design. With general purpose registers, you can design one and then clone it as many times as you like. Of course, the number of registers you can have depends on how many you can address from within an instruction. I decided on eight, which requires three instruction bits to provide a register identifier from 0 to 7. I called the registers R0 to R7 and they are all exactly the same.

I also gave a bit of thought to performance. Using TTL chips I’m not likely to be able to get into the megahertz clock range, I shouldn’t think. I’ll be happy if it runs at 1MHz. In fact, I’ll be happy if it runs at any speed. But I decided early on to make a conscious effort to minimise the number of clock cycles needed to run one instruction. The fewer cycles per instruction, the faster it can go. That was when things got really tricky in the simulator. I found you have to be really careful to make sure that ‘stuff has time to happen’ after each clock tick. For example, you can’t put a memory address on the address bus and expect the memory to be ready at the same moment. There is a real delay, in the order of tens of nanoseconds, before the requested data will be stable on the data bus. So you have to wait at least one clock cycle after addressing the memory before trying to read the data out. Also, the clock ticks have to be far enough apart which is why you can’t just crank up the clock speed indefinitely and expect it to just ‘go faster’. There are also other hazards to think about like race conditions and bus contention. I started to get freaked out by how much you have to consider and how the problem gets worse the faster the circuit is clocked. A few times I almost decided I couldn’t do this and it might be wise to stop. It really made me appreciate the ingenuity of the engineers who designed the computer I’m writing this on. The CPU in my computer is (at least) thousands of times faster than LEO-1 will ever be and probably thousands of times more efficient with its caches and pipelines and branch prediction and whatever else miracles they managed to squeeze into it. I couldn’t even figure out how to implement a simple pipeline and I gave up thinking about it pretty quickly.

Anyway, as I designed the instructions and played around in Logisim I realised that the instructions were reminding me of something I’d seen before, namely RISC instructions. In particular, when I came to design the memory access instructions I found that since I only had one ALU, I could calculate a memory address to load from, but I couldn’t use the loaded value to do more maths in the same instruction. This meant it was only possible to load or store to memory at some calculated address; in other words, LEO-1 has a load-store architecture. Although at first I was just experimenting without much planning ahead, I kind of gravitated towards a RISC design because I was trying to keep things simple. I let the limitations of the architecture guide the design of the instruction set. After a while I started getting inspiration from real RISC machines like the MIPS and I started letting the MIPS design guide me somewhat. I started to see why MIPS has no stack (and therefore no built-in nestable subroutine calls). All that kind of stuff requires either the use of microcode or else insanely complex electronics. Since this is complex enough already, I decided to go the same way. No stack pointer. No automatic subroutine calls. And no interrupts. That’s a decision that is still troubling me as I’m scared it might make the thing less usable in the end. Interrupts are needed for efficient interfacing to external devices and peripherals. Without them, LEO-1 will have to poll devices in a wait-loop and preemptive multitasking is entirely out of the question. Well, I’m not trying to design a real mini-computer with disc drives and tapes and I don’t plan on trying to port Minix to it either. I’m planning on having a simple hex keypad and an LCD display for debugging. I also have a fantasy of making a simple video card like the one on my MK14 if I can wrap my brain around doing that in TTL. Assuming the thing even works, if I ever do need to interface it to a UART or something, I’m sure there’s a way of doing it by polling instead of relying on getting an interrupt.

Another design decision was to eliminate the concept of Condition Codes. At first, my simulation had the ‘traditional’ condition codes (Zero, Negative, Carry and Overflow) but they started to cause trouble. Since I was trying to limit the number of clock cycles needed to execute one instruction, I didn’t like the fact that there didn’t seem to be any reliable way to update the condition code register without burning a clock cycle to do it. I also didn’t like the way I had to ‘wire up’ the carry flag to check it, and the fact that if it was provided as a programmer-visible flag, I would need instructions to set and clear it.  As for the overflow flag, the lack of that flag is not of much concern to me. In 35 years of programming I have never typed a single instruction that checked for overflow, but then again, I have never written a compiler or a maths library (or built a space rocket). Since I’m not planning to use LEO-1 for anything mission critical, I decided I could live without overflow handling as well. Other CPUs that don’t have condition codes such as MIPS handle overflow by using a trap which is like a software interrupt. Since I’m not doing interrupts, I can’t very well do traps either. So, checking for overflow will be completely out of the question without some heavy duty programming jiggery pokery around every operation that cares about overflow. Hopefully it won’t be a problem. As for zero and negative, that’s easy. You can check for zero with a wide AND gate and you can check for negative by just looking at bit 15 of a register. I was able to add instructions for branching if a particular register is ‘not zero’, ‘positive’ or ‘negative’. (It turns out you need a wide OR gate for checking ‘not zero’, but hey).

Things were working out very well when I hit a pretty obvious problem that I would have to work around somehow. A nice clean pure 16-bit CPU has an annoying limitation: 16 bits can only address 64K (words) of memory. That’s all memory space; RAM, ROM and memory-mapped devices. I hummed and hahhed about it for a few hours, wondering if I should just take the easy way out and limit LEO-1 to 64K, but I couldn’t do it. Solving the problem turned out to be quite tricky and will increase the cost and difficulty of building the machine, but it should be worth it. At least I’m not alone: the PDP-11 had exactly the same problem 🙂