Introduction

In the previous write-ups about my disintegrated machine project, I talked about the overall machine design, the CPU architecture and micro-architecture. I threw in a bunch of hints about how to think about complexity when it comes to transistors and kept saying that I will come back to the topic of transistor-based digital logic eventually.

This is the time to do that. Obviously, the subject is well-explored, after all all modern logic design eventually boils down to transistors. Almost all to CMOS these days.

In this write-up, I will introduce you to the way I intend to go about implementing my logic and explain some of the decisions that led me down this particular path. There will be a lot of Spice simulations along the way. I intended to use KiCAD and ngspice for this purpose, which almost worked out but not quite. The problem I ran into was that neither of these tools support (one nicely, the other at all) temperature-sweeps. This is important as I want to showcase how the performance of the circuit changes with temperature and how to think about worst-case performance. So, for now, I will stick with LTSpice for the simulations.

The family-tree

There are several types of transistors to choose from and several topologies to use with each. I will spare you the exhaustive search for the right pairing, I'll just give you a quick recap. Suffice to say that it took me a while to settle on the one that I'm using.

The natural choice these days is complementary MOS, or CMOS technology. A basic inverter in that corner of our universe looks like this:

When building with individual transistors, the choices are fairly limited, especially if one wants to stay cheap. The BSS84 for the P-channel part and the 2N7002 for the N-channel one.

There are several problems with this design, at least within these parameters.

One is shoot-through: anything higher than ~3.3V supply is going to generate significant currents through the transistors in every transition.

The other is capacitive loads: MOSFETs have relatively high gate- and output-capacticances. They add up to more than 100pF per gate. This, combined with the relatively high channel resistances of these FETs result in slow operation. There is also a problem that once you want to build anything more complicated than an inverter, you need transistors both in parallel and in series and that breaks the drive-symmetry of the gate: one of the edges get significantly slower than the other. Not to mention that the edge-rate starts depending on how many of the inputs change in sync, so the propagation delay is not only large, but input-pattern dependent.

Finally, the total number of transistors needed for an N-input gate is 2N.

There are other MOS variants, such as NMOS or PMOS logic, but they suffer from some of the same problems.

On the bipolar side of the fence, there are numerous choices: TTL, DTL etc. not to mention all the sub-variants, (S, LS, H, F etc.). I promised not to give you the whole low-down, so just one sentence on these techniques: they are nice for integrated circuits but not for discrete solutions. Finally, there's ECL logic. This is extremely fast, but it is difficult to interface to and somewhat convoluted to build anything but NOR gates out of. Noise immunity is also a low, which, coupled with it's high edge-rates would be problematic on a large-scale project.

My inverter

The basic inverter design I've settled on is the following:

I chose the 2N3904/06 pair of transistors because they are ubiquitous and cheap: one can get a 3,000 component reel for less than thirty bucks (fine, it's the MMBT3904, same difference). Also, these transistors have been available since at least 1966. So they are period-accurate too.

Let's look at it in detail!

The input stage (Q1, R1 and R2) are in a common collector configuration. This stage doesn't provide any voltage amplification, but boost current drive capability. The emiiter voltage of Q1 is essentially (one Vbe lower) the input voltage. This gets divided by 2 in the output voltage divider.

The output stage is a common-emitter setup. D1 provides a Baker clamp, in essence creates a Schottky transistor on the cheap. This is the type of transistor that gives the name to the 'S' and 'LS' family of logic gates. The function of D1 is to prevent Q2 from going into saturation, thus preventing it from collecting extra charge that would need to be removed when the transistor is turned off. The end result is much improved speed. The other side of the bargain is significantly higher power consumption. Much of this extra current flows through the diode and consequently comes from whatever drives the base of Q2. This is the main reason for having Q1 in the first place: isolate this high current from the driving logic.

Q2, being in the common-emitter setup also provides voltage amplification and negative gain (i.e. it is the thing that's doing the inverting in the inverter).

I intend to operate this circuit from a 3.3V power supply. This is to make it easy to interface it to modern electronics. (Yes, I've said that I'm quite religious about only using transistors, but that doesn't mean that during build and debug I don't need access to FPGAs for instance for signal generation and capture purposes.)

Let's look at the transfer function of this gate:

Each curve corresponds to a different temperature in the range of -25 to 125C. Fine, that's excessive, this is a table-top design after all, but I want it to be robust.

At any rate, you see a couple of things here: the first one is that the curves move quite a bit with temperature. This carves out the 1.5 to about 2.5V region as a no-fly zone: the output is not well defined for these input voltages. That's about the same width as that of the 3.3V logic standard, but shifted by about 0.5V:

The reason for the shift is going to become apparent soon, but before we get there, there's a second important thing to note on the plot: logic low output is actually around 0.5V. That is borderline acceptable for a 3.3V logic family; I won't have terribly clean logic low levels. I don't think it will cause terrible interoperability issues though except under really extreme circumstances (very high temperatures coupled to a very marginal receiver). It certainly won't be problematic when interfacing this logic with itself.

So, this is the static behavior of this inverter, but what about dynamics? Here's the no-load transient response:

In this example, I'm driving the gate with a 10MHz square wave. The output shows rise and fall-times in the range of 15ns. Datasheets for something, like the 74AHC04 show similar propagation delays, however for a 50pF load. So, let's add that and see what we get!

The falling edges are not effected, but the rising edge (which is driven by the 100ohm pull-up) degraded to a rise-time of about 20ns. In other words, this gate is about 33% worse than a modern AHC logic gate. At least in terms of performance. To get to this speed, the current consumption is not gentle on the grid:

I've added a sense resistor to VCC (R7) and plotted the power consumption on it. This is 120-130mW when the output is low. This is coming from the fact that both the R1-R2 divider and R3 are connected between VCC and GND in this case. When the gate is driving high of course the power consumption is 0. One can roughly assume in a large digital logic that 50% of the gates are in the high output state, so the average per-gate power consumption should be around 60-65mW. This is high by any standards. If I end up with about 1000 gates in the design (don't know, just a guess at this point), I should expect a power consumption of 65W. Or 20A on my 3.3V rail. Luckily the design is also going to be large, so dissipating this much heat will not be big trouble.

Before we move on, let me just briefly mention that a complementer design is also possible:

It's performance is almost identical, except for of course the flipped rising- and falling-edge behavior:

Also notice how this variant has clean logic '0', but not so clean logic '1' levels. This is well within the range of acceptable logic high values for a 3.3V CMOS design though: the minimum is 2.4V, we see 2.8V on the output.

Fast inverter

What happens if we try to combine the PNP and NPN variants of the inverter? We get a push-pull output inverter:

Here, we can employ another trick, that would not make too much sense in the previous designs: a capacitor across R21 and R28 to make the edges even quicker. There are several benefits to this circuit, but let me show you the behavior first:

You see very clean edges and almost no temperature dependence in the output waveform. The edges are now about 4ns long, so is the propagation delay. At the same time, the power consumption went down to a constant 25mW, except for the transition cases, where we need to charge and discharge a bunch of capacitors. Here's how it's doing with a 50MHz input:

Still very clean outputs, but the power consumption is now dominated by the transient effects and is much higher. This of course is typical for any logic family, so no surprises there.

Before we get to the down-sides of this design, let's look at it's transfer function:

You see no temperature variability, though the threshold level is biased a bit higher than the 1.5V typical for TTL and TTL-compatible devices: it's essentially VCC/2, or 1.65V in this case.

The power consumption curves (the red ones) show no shoot-through, except at very high temperatures and even then it's effect is very minimal.

Enough praise though, let's look at the problems!

First off, this design is - obviously - twice as expensive. That may not sound like a big deal, but it adds up, if not in component cost, in assembly time and cost.

Second, this variant doesn't have either clean 0-s or 1-s: both output levels are about 0.5V off of the power rails.

The third problem may not be immediately obvious. Look at the power consumption curves on the transfer function plot: for most temperatures it reaches a very low values in the middle, roughly between 0.9 and 2.4V inputs. This means that in this region neither the top, nor the bottom side driver is open, the output for all practical purposes floating. If the input signal is marginal, say only 0.8V, and the device is very cold, the output impedance is high and the drive capability is very poor.

Next is input capacitance and input current: since there are now two transistors connected to the input, both of these are double then the simple variants. This means more loading on the previous stage in the logic. For this reason, I actually simulated the response of this gate with a 100pF load, instead of the normally used 50pF value.

There is some more bad news about the push-pull inverter that I will come to in the next section, but rub is this: I don't think the benefits outweigh the problems and for most applications, I'm going to stick with the simple two-transistor designs. In certain cases where symmetrical behavior is desired or speed is of absolute importance, I will resort to the four-transistor version.

From inverters to gates

OK, I have a basic inverter, but how can I build something more complex out of it? Let's try to build a NOR gate! This is actually easier than it may sound, at least with the two-transistor design: we simply need to add a second emitter follower on the input:

Now, either input can pull the base of Q2 high, thus driving the output low. This is confirmed by a transient simulation:

A NAND gate similarly can be implemented using the complementer inverter:

Notice how none of these simple tricks would work with the four-transistor inverter: as it is an amalgamation of both types of designs, duplicating the emitter follower stage simultaneously would attempt to implement a NOR and a NAND gate, which of course is nonsense. Building more complex gates based on the four-transistor inverter would be more complicated.

NOR and NAND gates are something, but not all that interesting. In digital logic, we quite often end up with expressions of the kind: output = (in1 AND in2) OR (in3 AND in4). In fact, one can prove that any logic function can be decomposed into a statement such as that, provided all inputs and their inverse is available. This kind of logic is so important that even the venerable 74xx series had gates of this ilk: the 74LS54. The variant they have also has an inverter at the end, so it's really an AND-OR-INVERTER (AOI) gate.

Here's how one could build such a gate within my logic 'family':

This variant has two two-input AND gates followed by a two-input NOR gate.

The impulse response is as follows:

I admit, this is getting hard to see, but believe me, it works. Here is the interesting thing: since the rise- and fall-times are dominated by the output stage and the load capacitance (the emitter-followers are wicked fast), this complex logic gate is almost as fast as our original inverter: worst case about 23ns rise-time.

There's one more trick we can do with these gates: notice how the output stage is made of a transistor and a pull-up (or -down) resistor. This is essentially an open-collector output, only I kept showing the pull-up in the design. Open-collector outputs can be used to make a wired-AND (or OR for the complementer version) logic: the output of several gates can be simply connected together as long as we remove all but one of the pull-up resistors. This functionality is not always useful as gates quite often drive more than one other gate (that is, they have a fan-out of more than one), but there is an important exception: multiplexers:

Notice, how each AND gate in this design produces one input to the final OR gate. This OR gate can be implemented in a wired-OR configuration, saving some transistors.

Complexity estimation

Before we part ways, I want to come back to one more question: given a logic, how to estimate the required transistor count? These AOI gates have one transistor for every input, one transistor for every OR term, then one transistor to drive the output. When dealing with simple NAND or NOR gates, the logic is even easier: the number of transistors needed is the number of inputs plus one. This is the math I've used previously to estimate the cost of various circuits.

CMOS inverter

I'm writing this section quite a bit later then the previous parts of this chapter. I wanted to take a second look at CMOS gates, after going through the rest of the circuits needed for the Disintegrated Machine and thought (and learned) a lot more about transistors and their behavior.

I have disparaged this design at the beginning of this chapter, but let's look at it a bit closer as well:

I've already shown the basic diagram, here I cascaded three of them and put a pretty hefty load on the final one. I also put current measurement resistors (R9, R11, and R13) in so I can monitor power consumption. Here's how the first stage (M3, M4) behaves:

This is actually pretty nice, I don't know why I was so negative about it before! The rising edge is around 13ns (up to 2.9V which is not exactly 90%, but close enough and in the middle of the flat region before the final push to the top). Fall times are similar, being a symmetrical circuit, in fact a bit faster as the N-channel device is somewhat stronger. The humps on the transitions BTW are the result of the loading from the subsequent stage: this is the place where the gates need to be charged/discharged which halts the ramp in voltage for a bit. The gate delay is on the order of 16ns.

Being a push-pull circuit, it's threshold voltage is constant (albeit supply dependent) and is not dependent on temperature at all:

The shoot-through that mares these gates a great deal is not at all bad for a 3.3V supply (it gets bad very quickly though as supply voltage increases):

So, why not CMOS gates? I don't know. Maybe I should use them. I still don't think that building complex gates CMOS style is a good idea: the balance between the high-side and low-side breaks down very quickly and the performance along with the threshold level would deteriorate quickly.

However, let's look back at the complex gate from the last section again:

You can divide this circuit into two parts: a bunch of serial-parallel emitter followers in the form of Q1, Q5, Q7, Q8, Q9 and Q10 and a final, common-emitter stage, Q2.

The actual logic is built out of the emitter followers, the last stage serves two functions: it adds gain to the circuit (so that signal levels don't deteriorate along long logic paths) and inverts the signal. There's no reason why I can't replace this last function with a CMOS inverter while leaving the logic function in the emitter-follower setup. This is what something like that would look like:

Notice the very low value resistor R8. This is because we are driving a capacitive load (the gates of M1 and M2) with essentially a resistor. If we want the logic to perform well, we need a small resistance: the rising edges are more or less determined by the RC constant of this circuit. Talking about rising edges, here's how it's response looks like:

I overlaid the output of the CMOS output stage (red) with my previous design (green) and also plotted their current draw in light and dark blue respectively.

The timing performance is roughly comparable, not surprising as I set the value of R8 to make sure of that. However the downside of that is pretty obvious on the current draw chart: the circuit is even more power hungry than the BJT-based design. Whenever the output is high, we're drawing 50-55mA of current compared to the ~40mA of the older approach. In return, we get clean logic levels for both low and high values and a slightly simplified output stage: two components instead of four if I count the removal of the divider at the output of Q4.

So, after all this, what do I think? I like the simplicity of the CMOS output, but I don't like it's drive requirements (i.e. the small resistor value and the corresponding power). It's also less period-accurate then the BJT-based design. Since the performance seems to be the same, I think I'm going to stay with the BJT-based gates.

Parting words

This article is getting long, so I will stop here. We've gone through a lot of material and arrived at a decent AOI gate design. My intention is to build a single gate of this kind with a few modifications: I will add a second inverter on the output, so it can implement not just AOI, but AO functionality. I will also increase the number of OR and AND gate inputs to match what my CPU design requires. I don't know what that number is yet, but probably similar to what the 74LS54 offers. I will then use this gate as the building block in many many places. The nice thing about a discrete design is this: I don't have to populate the unused inputs. If all I need is a NOR gate, I can just forgo all the 'AND' transistors and hook the inputs directly to the NOR gate. I will probably build a complementer variant (that would be an OAI gate) as well, but I will have to see what the need is. It's quite likely that a stand-alone inverter will make sense as well. This is two or three kinds of circuit that I can use to build almost anything. Anything combinational that is. I will still need registers and latches and the like to build sequential logic, but that's for a different time.

Till then I bid you farewell!

Leave a comment

Comments are reviewed before they are published, so yours will not show up right away.

You can use **strong** and *italic* text, `code`, and lists (lines starting with - or 1.). Everything else, HTML included, is shown as it was typed.