The original 8-bit computer design used the 74LS561 counter for the program counter. The 561 is definitely capable, but it is also a bit complex with synchronous and asynchronous operation in a 20-pin package. I tried using a few counters, but the ones I settled on are the 74LS193. This little 16-pin IC allows for loading, resetting to zeros, increment, and decrement. Most of the counter I tried are increment only, including the 561, so I ordered twently 193s and started prototyping when they arrived.
As you can see from the video (or the thumbnail, I won't judge), the 16-bit 193-based counter is working. I tested with load, clear, increment, and decrement successfully. This was with a 10Hz clock provided by the development board (middle, right, with all of the unconnected red wires), and I used one of the 4-digit hex displays for output.
The breadboard wiring looks complicated, but it worked almost perfectly from the start. A problem I did have was with the first 193 not counting. I eventually replaced it to the same effect and finally removed it. This necessitated adding a new forth IC to the left and moving the display wires, but the result was a successful prototype.
This IC is pretty simple, with a decent pin out. My preference would be for the data pins to be together and in order, but that is seldom the case with TTL components. The main things to look at with this IC are the count up/down pins, and the borrow/carry pins, as these are used to chain the ICs together to create a bigger counter... four of them, for a 16-bit version.
The increment and decrement signals are connected to the count up/down pins of the first (lowest 4-bits) counter IC. The carry from that counter is connected to the count up pin of the next IC, while the borrow is connected to the count down of the next IC. This is the same for the next two ICs, as they carry pin of one is connected to the count up of the next, and so on.
When incrementing, the counter will count to 15 before rolling back to a zero. When it does this, it sends a signal on the carry pin, which acts as the count up for the next IC. So, the first IC increments on every clock (10 times per second, in my prototype), but the second IC increments sixteen times slower.
I have discovered some issues with the PCB designs I have had manufactured, and part of the issue is a lack of consistency. Take bus access, for example. The register card has placement for soldered wires or DIP switches to connect the register to the data or address bus. This is a bit limiting, but works and greatly reduces complexity. The memory card is even less complex, with the data pins linked to the data bus and the address pins linked to the address bus. When it comes to the counters, I want more flexibility to use them on either bus and as transfer registers between the buses. If the ALU can write to either bus, it can act as a partial transfer register... but it will need to be able to read from either bus for this to be useful.
There are various flags that need to be set for the computer to work correctly, such as a carry flag when an arithmetic operation results in a value larger than 16-bits.
Halt / Run
This flag is used to determine in the clock run or not. When the clock is not running, the user can single-step through a program manually. Useful at the end of a program to preserve output, or during execution for breakpoints. Can be set by the user, by an instruction, or perhaps by an error (hopefully),
Normal / Input
In normal mode, the PC increments via the clock (or the single step button, if the clock is halted). When in input mode, the user is able to program the computer via front-panel controls.
This mode determines how an instruction is processed by the control logic. If the first digit of an instruction in RAM is a zero, the instruction is decoded from the instruction register. If the first digit of an instruction in RAM is a one, the remaining 15-bits are treated as an address, loaded into the firmware counter, and this flag is set. This flag will keep the firmware counter in control until a return instruction causes the PC to become active and continue executing instructions in RAM.
This 4-bit register indicates which counter is in charge of the address bus (effectively, which counter is the current program counter).
Counters are critically important in any computer, as the Program Counter, loop counters, various other address registers, stack pointers, etc. In my design, pointers can load from either bus, output to either bus, increment, and decrement. To accommodate this, I plan on using 74LS193s, which are two state 4-bit counter ICs. Each bus will require transceivers to control access to and from the counter, and an additional transceiver is required to control the output from the counters, as they are always outputting their value and this would conflict with data or address information being read from a bus.
Technically speaking, this will require ten ICs. I will need two 74LS245 transceiver ICs for the data bus, and two more for the address bus. An additional two 74LS245s are required to to enable and disable the counter output (again, to prevent conflicts when reading a bus). The counters themselves will comprise four 74LS193s. These ICs are 4-bit up/down counters with load and reset. I still need to prototype a counter on breadboard, but it is possible I will need an additional inverter IC (perhaps the 74LS06 hex inverter) to simplify the control signals.
What are the benefits of this design?
- Counters can store values from either bus.
- Counters can output values to either bus.
- Counters can reset to zero, increment, and decrement.
- Counters can act as transfer registers between the data and address bus.
- Counters can act as loop counters, with the carry and borrow pins acting as flags for > FFFF and < 0000.
- The ALU can write directly to any counter.
It is going to be pretty complex to design, unfortunately. More on that in a later post.
Bad breadboards have been the bane of this project, even when the circuits I am testing are on PCB. The last few register transfer tests failed because the data would corrupt or not actually save, and a few times the stored data in a register would change. I added 1k pull-downs to the data bus today, and it seemed to help, but constant glitches continued.
With the help of a new breadboard, I was able to successfully load values into Register-1, copy those values to Register-2, load a different value into Register-1 (clearing it, basically), then copy the value from Register-2 back to Register-1. I tested with the oscilloscope, and used the 16-bit display boards to see the values being moved around.
The next step is to build additional registers, then design the counters that will be used for memory addressing. Once we are at that stage, looping tests (driven by an arduino or raspberry pi) will be useful again.
I remember learning about SCSI controllers, and hearing about termination. At the time I did not know what it meant, just that SCSI cables needed to be terminated for "some reason". The same is true for the computer I am building, the backplane (particularly the two two 16-lane buses) needs termination as well.
What is termination? Why do I need it?
In my case, termination really means pull-down and pull-up circuitry to eliminate glitches caused by floating voltages.
In theory, the computer is digital, passing values of "0" and "1" around... but in reality, it is an analog device where a certain voltage range indicates a "0" and another voltage range indicates a "1". Between these two ranges, there are voltages that are effectively invalid.
When voltages float between 0.8 and 2 volts, the computer will react unpredictably. Voltages are, at least in a computer like I am building, difficult to keep at exact levels.. transistors switching on and off, leaking voltages from ICs, even noise from outside the computer can add up to an "invalid" voltage reaching the low or high range and triggering an unwanted "0" or "1" response.
To prevent floating voltages from leading to problems, we will use pull-down and pull up resistors. A pull-down resistor is just a resistor between the floating pin or lane and ground. This effectively increase the voltage range that will be treated as a digital "0", by lowering the voltage by an amount. For example, if a -1 volt pull-down is implemented, a digital "0" will be seen for any voltage applied to that pin or late between 0 and 1.8 volts.
There is a lot of interesting math one can use to calculate the appropriate values for pull-down and pull-up resistors, but I am a prototyper so you will not see that math here. Instead, I try various resistor values until I find ones that work.
Using a connection/development board, I have added1k resistors between the 16-lanes of the backplane and ground. The purpose of this is to pull the bus low as a default, and 1k is a compromise for the first attempt.
I also added 1k pull-down resistors to the Output Enable and Write Enable signals on this card, for testing purposes. These pins are not shared , so they will not act as pull-downs for any other card... but I have some tests in mind that they will be useful for.
The next step is to set the computer back up and test moving values between resistors again.
The first connection board has been partially assembled and was ready for the first round of tests to make sure my timer circuit works. For this first board, I setup a 10Hz astable 555 timer, with the the output going to a test pin. The output also goes to a 74LS04 hex inverter to give me a second, inverted, clock signal.
I tested by hooking channel 1 (yellow trace) of the oscilloscope to the 555 output, and channel 2 (purple) to the inverted 555 output.
I adjusted the two channels to make it easy to see the two square waves, and it looks pretty good. The only possible concern is what appears to be a fair bit of noise when the signals are low. The long, unshielded test leads are probably responsible, but I plan on further testing.
All that remains is to solder on the 16-pin headers and this first connection/development board will be complete.
One of the issues when moving from breadboard to PCB is coming up with an interface between the two. To that end, I came up with a simple connection/development board that breaks all of the backplane lanes out to headers. To make development and testing easier, I added a 555 circuit, a hex inverter, and an extra set of grounded headers. The purpose of the grounded headers is to allow one of these boards to act as terminating resistors, pulling the buses low as a default. The 555 timer circuit allows us to have development clocks that can optionally be tied to the clock lanes of the backplane (again, via pin headers).
The main purpose of the connector board is to make it easy to connect to breadboards, so all of the headers are clearly labeled. Where labels are missing, the lane has not yet been assigned.
The most interesting part of this card is the labeled control header, as it sets a lot of things in stone. The rightmost connector lane is a common clock signal (on both sides of the card), but the next pads are not shared lanes, they are linked to the selector headers on the backplane. The rest of the signals and controls are lanes, including the ALU signals and the various flags. When I designed the board I was planning a minimum of three flags and was not sure what the third was going to be (A = B, A > B, A < B, Negative, etc), so I labeled it as the "Magnitude Flag", but it turns out that combining it with the carry flag can give us all three. There is room for more shared flags and controls.
I have wanted a decent display system since I started this hobby in 2015, and finally had the opportunity to create something. The front-panel that I have planned will display binary output for the (control, signal, data, and address) buses, but will have four-digit hex displays for registers. The reason for this is it is much easier to quickly see values when they are displayed in hexadecimal, but binary is obviously better for showing which bits are set.
This display board was designed in EasyEDA, directly as a PCB... so no schematic. I probably should have created a schematic first, but I am a prototyper at heart and it feels better to me to lay out PCBs the way I lay out breadboards. I never draw circuits before breadboarding, so I am trying to do the same with these PCBs.
This is not the final version, but it is close and I wanted to compare it to the final version I had made. Because I do not do schematics, I end up laying out the PCB several times as I refine the components and positioning. This board is relatively simple, as it just needs four MC14995s to drive four 7-segment common cathode displays. I added a 16-pin header to connect the board, a clock pin, and power. Because I plan to use these with a front panel, there are pins to connect to controls... but for stand-alone use the switch pins can be shorted or jumpered. The decoupling capacitors are optional.
So, what was wrong with this design? Several things. First, I had planned to mount the displays on the reverse side, but I became confused during the layout and did not lay the traces 100% correctly. A second issue was the 16-pin header... really, I needed two so that the display could act as a pass-through.
The final version is very similar to the 1.0 version, with the most notable exceptions being the displays and an extra 16-pin header. Front-panel related pin headers have been adjusted as well. The R1 resistor on the bottom of the board is a pull-up for the "on flag" pin.
Fully populated, the hex display board was tested with a breadboard, 8-bits at a time (I only had one dip switch unit handy).
I have enough PCBs to build ten display boards, but I probably only need five or six, maybe a couple more for other projects.
I had a lot of different plans for the 16-bit register, but all of them had two basic requirements:
- The PCB needs to work for both data-bus and address-bus registers.
- The PCB needs to have an "always on" header (for the front-panel or ALU)
I would prefer to design registers for specific uses, but when having PCBs manufactured it was more economical to try to combine designs as much as possible. Because of that, the PCB links the data and address-buses to the 74LS574 ICs, with breaks that I could jumper when populating the boards. To accomplish 3-state bus access but have the register value always available to a 16-pin header, I went with a second set of 74LS574s that store with the register ICs, but always outputs to the header. This adds about $2 to the cost of populating a register board, so in instances where the header might never be used, those extra ICs can be left out.
This design was created by hand as a PCB, without a schematic. I explain my reasoning in the Hex Display post, but basically I design PCBs the same way that I lay out breadboards... I grab the datasheets and start connecting pins. This is inefficient, and requires multiple iterations before I am happy with a design, but it feels better to me. In this case, this was not the final design but it makes an interesting contrast to what was produced. If you compare them, the main difference is how the signal test pins are located. The "clock" was dropped from this board, as the actual clock signal will be gated to this card and transmitted on the write enable line.
I also dropped the second 16-pin header on the top, as the display board was updated to act as a pass-through when necessary.
Fully populated, the register board was tested with my first display board. The oscilloscope probes are connected to the output of the 555 timer (on the foreground breadboard) and the clock test pin on the display board... I wanted to see how if the clock waveform deforms or picks up any noise. Unfortunately, when I took this photo I had the oscilloscope in "roll mode" so the waveform looks a bit dirty.
I have enough PCBs to build ten register boards, but I will start with three... A-Register and B-Register for the ALU, and one general purpose register. I might also populate an address register or two once I start programming branching and jumps.
One of the nice things about designing PCBs to replace breadboards is we are forced to make decisions. Once a PCB is manufactured, it is not easy to make changes, where a breadboard is almost by definition always changing. This means that I am forced to finalize designs, so that all of the PCBs will work correctly.
In the case of the backplane, and the modified JAMMA connectors, there is some flexibility. I do not need to assign a purpose to every lane, but the shared lanes that are assigned must be respected by every card or device attached to the backplane. This has resulted in a series of pinout schematics, with the most recent displayed below.
The front of the connector, and any PCBs inserted into the connector, is dominated by +5V, the 16-bit data bus, and the ALU signal lanes. The reverse is side has Ground, the 16-but address bus, and the flags. Each side also has three isolated breakout pins that are not shared between connectors. Note that the OE and WE pins are on opposite sites... this was caused by a layout error on the register card that was most easily fixed by reassigning the location of OE... even if it seems a little random when viewing the pinout. Whoops.
The "Reset" line is planned to be both user triggered (when the computer is turned on, or when a reset button is pressed) as well as software triggered. The purpose of reset would be to clear registers and set counters back to zero, so that a program can start "fresh". The software triggered reset is planned for future use... perhaps after a program is transferred from storage to ram, we clear the registers and start the program counter at the start of the program. In the case of "Reset", I plan to have a special purpose register that is not cleared... so that a program can react differently based on the contents of that register.
The ALU signals will be discussed at length elsewhere, but basically we have a mode flag that switches the 74LS181 ALU ICs between logical and mathematical functions, plus four command signals that select one of sixteen functions.
The six breakout pins, in orange, are unique to each card and can be assigned to anything, but the Write Enable and Output Enable are common for most devices so those are labelled. The DIR pin, and the three CMD pins are effectively unassigned at this time.
I ordered five backplane PCBs, and received them on November 27th. They turned out really well, except I did not leave space for the screw mounts on the ends of each JAMMA edge connector. The solution involved cutting the screw months off with a hacksaw, but the end result was the first two backplanes soldered and 100% tested.
My soldering skills are pretty low, but the results turned out okay. The zoomed in shot shows the 6-lane breakout point for two of the connectors.