While looking into some custom built CPUs I've noticed the frequencies at which they operate are relatively low compared to modern CPUs (in the order of several MHz). Is there an electronics engineering reason for that limitation, e.g. for breadboards? If yes, how to determine the max frequency that is possible to achieve with your design?
It mostly has to do with the lengths of interconnections and propagation delays through the gates. If we reduce a CPU to its essence, it is a feedback machine. A bunch of combinatorial logic circuits compute some boolean functions over the current state of the machine, and those functions determine the new state, which is latched in by sequential circuitry when a new clock edge arrives. The combinatorial circuits all have delays. The clock period cannot be shorter than the time it takes for the slowest path through these gates to produce a stable result because a single incorrect bit stops the show.
Furthermore, the sequential logic has timing requirements. Before the clock edge arrives, there is some minimum setup time that the inputs have to be stable and then afterward they have to be stable for some hold time. If these are violated, the state becomes garbage.
The propagation delays are caused by things like how fast parasitic capacitances can charge, how fast current can build in the face of an inductance and how fast silicon devices can switch. For example, a bipolar transistor with a smaller base can switch faster than one with a larger base, so a tiny transistor on a chip will be faster than a discrete one.
In an earlier answer that I deleted, I wrote about transmission line effects. But I didn't consider that these effects don't even come into the picture at the speeds we are talking about because, say, at 10 Mhz, the wavelength is still about 30 meters. So on the scale of an ordinary sized circuit board, pulses on the time scale of a few megahertz still reach all parts of a copper network simultaneously.
So, if you make a CPU out of discrete components, you're simply not achieving the small components with fast switching times, and the same proximity which minimizes stray capacitances and inductances.
Nevertheless, ancient discrete-component machines in the 1960's did run quite a bit faster than these homebrew machines. It took some time and cunning to get there. For instance the IBM 360 Model 44 (1964) ran at 4 Mhz. That may still be "homebrew speed", but the CDC 7600 released just a few years later in 1969 surpassed 36 Mhz. The Wikipedia article http://en.wikipedia.org/wiki/CDC_7600 gives a hint at some of the tricks that were pulled, for instance:
"As always, Cray's design also focussed on packaging to reduce size, shorten signal paths, and thereby increase operating frequency. ... [E]ach circuit module actually consisted of up to six PC boards, each one stuffed with subminiature resistors, diodes, and transistors. The six boards were stacked up and then interconnected along their edges, making for a very compact, but basically unrepairable module."
So the homebrew CPU's are not necessarily built to their true potential due to some confounding effects having to do with the build quality and layout. Still, anyone who builds a CPU out of individual integrated circuits and discrete components which runs at several megahertz should be applauded.
As a former high schooler who built a special-purpose computer with 7400 series TTL, which won some kinda award at the science fair, I observed these things that kept it from running as fast as possible:
Stray capacitance in the breadboard. A few pF between every adjacent pair of connectors. That limited pulse edge rise/fall times and in places added crosstalk. This was probably the biggest factor.
Variations of grab-bag chips. (Anyone remember Poly-Paks?) 74LSxx, 74Hxx, 74xx with different propagation delays and other characteristics, made it impossible to have signals stay in sync at higher clock speeds than a few MHz.
Cheapo static memory chips, again from a grab-bag or other no-quality source. They just couldn't read or write reliably beyond a certain rate.
My test instrumentation was limited to homebrew signal generators, a 5MHz bandwidth oscilloscope, and temporary jury-rigged digital circuitry. Hard to check signal integrity, timing, amplitudes of digital signals that have been low-pass filtered into wobbly mush.
Today, it would be hard to find a 5MHz scope unless one is an antique buyer. Better chips of all kinds are just as easy to get, even in 0.1" spaced DIP packages, except I haven't seen much in the way of grab-bags in a long time. Socket breadboards, however, haven't changed much. Stray capacitance is still a speed killer for any edgy creative digital projects.
Avoiding breadboards by using a homebrew PCB is the best way to avoid stray capacitance, but of course requires more effort and time.
I'd think the primary reason is that as you increase frequency, the impedance of your breadboard's connections will increase and limit the final speed of your circuit.
Every connection in your breadboard has a low, but non-zero inductance. As your frequency gets higher and higher, you need to take these effects into account. The impedance of the wires can be found by:
where L is the inductance of the wire. Eventually, Z will become high enough that not current will flow and your circuit will stop working. Finding the exact numerical value for this number will be very complicated, especially because breadboards have traces next to each other and that will change each wire's impedance a bit from this formula. If you really want an (imprecise) number, you can try here to calculate the inductance (and therefore impedance) of your wires. If you know the lowest current that a part can operate from, you can determine the maximum frequency before you hit that limit.
Others have answered the "why". Here's how to determine the maximum speed.
- For each flip-flop, look up it's clock to Q.
- Total the wire length of all the wires from the flip-flop to the next flip flop. Turn this length to time. Wire is ~2/3 speed of light
- Total any gate delays, including through asynchronous RAM.
- Take the setup time at the next flip-flop.
- Add 1-4. This is you minimum clock period. Invert to get frequency.
- Consider clock skew. If the clock gets to the second ff before the first, add skew in with 1-4.
- If the clock gets to the second ff before the first, calculate the minimum of 1-3. Make sure that they are less than the hold time required by the second ff plus the clock skew.
For homebrew machines, it comes down to two factors. The propagation delay for the chips you are using and the number of chips you need to use on the longest path through your CPU design.
For example, a 74HC574 (8-bit register) has a maximum propagation delay of about 41ns (taken from its datasheet). Now let's say the longest path through your CPU design requires it to pass through 8 different chips. Add up the propagation delays for each of the 8 and let's imagine it comes to 333ns. With 1000ns being the same as 1Mhz that would give you a maximum speed of 3Mhz.
In practice, you might want to restrict yourself to something slower like 2Mhz, in order to ensure a stable design. Even if you think you will only miss the timing once per billion cycles, then you're still in trouble. 10 billion divided by 3 million means you miss-execute once every 3,333 seconds, which is about once per hour. Crashing your machine every hour is not good!
To make it go faster you can use faster chips and/or change the design to reduce the number of chips in the slowest path. About the fastest homebrew speed you see is around 4Mhz which gives you 250ns to complete each cycle.
Apart from all the electrical reasons that limit the speed, there is also one on the logical level:
You cannot throw as many resources at making things run faster, such as pipelined operation with branch prediction, faster arithmetic and whatnot. Caches don't make much sense either if they aren't faster than your main memory.