Introduction
In this short article I will talk about how I intend to implement registers for my Disintegrated Machine project. As you may know, the project aims at creating a usable 16-bit computer form discrete transistors. My goal is to get to a 2MHz clock-rate and a (core) memory of 32kByte.
The processor at the heart of the machine needs somewhere north of 125 bits of state for its operation (architectural registers, bus-driver registers, some extra stuff here and there). This means that there is a large multiplier on the complexity it takes to store a single bit. I’ve talked about this before, so I’m going to be brief here: edge-triggered D-flops, the go-to solution for modern digital design for state storage are very expensive in terms of transistor count. I decided to go with a much cheaper, but less capable bit-store: the transparent D-latch. There are a few problems with this approach, one being that it’s sensitive to logic glitches and the other that it, well, is transparent. I’ve shown you some consequences of that choice in the micro-architecture discussion. What I haven’t mentioned there is that you simply can’t use transparent latches for certain building-blocks, such as counters or shift-registers.
Variations on a theme
The Wikipedia article on the subject of registers is pretty comprehensive. I’ve linked it before, I will keep doing it.
The simplest way to build a D-latch is to start from an R/S flop and add some front-end logic to it:
There’s of course a variant that uses NAND gates instead of NORs. For it to work though, one has to replace the AND gates with NAND ones as well. If you do that, there’s a really simple optimization that gets rid of the inverter at the front of the logic:
There is a third circuit variant, that’s much harder to wrap your head around, but worth mentioning:
This logic needs an inverter to get the enable and it’s inverse going, but that can be shared among many latches. (Imagine a 16-bit register, where all bits would get loaded at the same time. Yes, you need an inverter, but only one for all 16 bits. The per-bit amortized cost is next to zero).
In CMOS designs, there’s a new circuit element (a pass-gate) that is not available in other logic technologies. Using this, one can implement a latch in a somewhat different way:
This CMOS approach looked compelling enough, so I tried using this design. I discovered however a few problems with:
For pass-gates to work in discrete designs, one needs two transistors in series for each leg of the gate. That is because companies don’t sell you 4-pin MOS transistors (where the 4th pin is the substrate). So we need 4 transistors per pass-gate, instead of 2. You can get around that by running the gates (wow, this is an overloaded word here. Here, I mean the gate pins of the MOS transistors that build up the pass-gates. The pins driven by the CLK line and its inverse) from a higher voltage supply. This would drop the transistor count per pass-gate down back to two, but now you need level-shifters to get the clock to this higher voltage level. Overall, this circuit needs somewhere between 10 and 16 transistors per bit, depending on how much of the clock-inversion and level-shifting logic can be shared among many bits.
The other problem I found was that CMOS logic is slow. Very slow.
The path taken
Overall, just as for my regular logic gates, I decided to not use CMOS and concentrate on bipolar solutions.
With the logic design I’m using, one gets a benefit from cascading AND and OR logic gates without inverters between them. After adding up the number of transistors required, it turns out that the very first approach is the cheapest due to this quirk, at “only” 12 transistors per bit. Quick calculation shows that the state stored in the processor would still require more than 1,500 transistors. Cheap is relative…
At any rate, here’s what a single-bit latch looks like in my logic implementation:

You can see the front-end inverter very clearly. The 5-transistor blocks on the right side implement the AND-OR-NOT gates of the flop. The resulting design is rather snappy, works well even with a 10MHz clock:

The edges of the enable and data changes are deliberately skewed form one another so we can see the transparent nature of the latch in action.
The input-to-output delay is around 35ns in the low-to-high and 23ns in the high-to-low case.
Edge-triggered flops
As I explained before, latches can’t be used to implement counters or shift registers. I currently have no need for the latter, but I will need the former. Luckily, not that many. At the moment I only have a single 3-bit counter. So I do need some edge-triggered flops for this purpose. There are some money-saving tricks one can use, but the most trivial way to get to an edge-triggered flop from a latch is to just cascade a pair of latches together with inverted clock signals:
This is what I intend to do instead of being terribly clever about it. The main reason is this: I will have a single design (schematic, layout etc.) for a latch. That’s a building block that I’m going to use and re-use. Yes, the transistor count will be slightly higher than the minimum, but I get to save on volume: cheaper PCBs as I have fewer variants to order and – if I go for professional assembly – big savings on assembly cost.
Latch timing
Let’s get back to our latch and try to analyze it a bit further. In the test below, I’ve set up the enable and the data signal to be at slightly different frequencies. This means that they slowly shift in and out of phase with one another, generating all sorts of setup and hold relationships between the two. The interesting area to consider is the falling edge of enable: if the data line changes around that edge, the latch may or may not have time to react and capture the new value. I zoomed in below to this transition:
In this section, the input line starts low in the enabled period and switches high closer and closer to the falling edge of enable. Eventually, the latch can’t respond. This happens around the 4.2us mark. The setup time for the last pulse that worked was 20ns. The first that failed had a setup time of 16ns. I’m only showing one temperature setting, the one that has the worst behavior, at -25C. From this, we can claim that the latch has a setup time requirement of 20ns (maybe a bit better, I stepped the difference by 4ns each time).
How about hold time? Now, we’re looking at cases where the data input changes right after the falling edge of enable. Here’s one such occurrence:

The output is stable. This measurement is done on the fastest temperature setting, 125C, still, it appears that if the data changes simultaneously with the enable, the old value gets captured: the hold time is 0ns.
Finally, let’s look at how long does it take for data to get stable after the falling edge of the enable:

Here you see the worst-case setup time (and temperature) configuration. The output of the latch becomes stable about 14ns after the falling edge of the clock. This is going to be our propagation delay.
Timing analysis
In digital logic, it’s important to analyze how long signals take to propagate through the design and whether they meet the requirements of the storage elements. These days, normally D-flops are used, but my latches will have to make do as a replacement. The picture below shows the very generic understanding of how digital logic is constructed: there are some storage elements (latches for me) connected by some logic. The storage elements are controlled by a common clock. For latches, the picture is a bit more complicated because the enable signal is a bit more complicated than a clock, but let’s ignore that.

So what happens on the falling edge of the enable (or clock) signal? In my circuit, we would have to wait 14ns to make sure the output of the latch is stable. This is the starting point for the logic to propagate any changes through. At some point the output of the logic becomes stable as well, maybe for the sake of example it takes 66ns for that to happen (worst case). This should still happen in time for the next latch to capture the change, that is, at least 20ns before the next falling edge of the clock. So, the time between the two falling edges (in this example) must be at least 14+66+20 = 100ns. In other words, this particular example can run at a clock rate of 10MHz, but not faster. Of course, one would need to analyze all possible paths between any two latches and – again – take into account the complexity of the latch-enable generation. There are tools that do this for you in FPGA and ASIC design flows, but here, I’m out of luck: I’ll have to do this manually.
It is a big relief though that the hold-time requirement is zero. Otherwise a similar analysis would need to be performed for every path for hold time violations as well. However, since signal propagation takes positive time, it’s not possible to come up with negative hold times.
The fact that no hold violations are possible is good news for another reason: setup violations can always be fixed by running the clock slower. This should be obvious from the previous example: setup timing puts an upper limit on operating frequency, not a lower one. Hold violations cannot be cleared by changing the clock frequency; those need design changes. So, given this state of affairs, I don’t have to do timing analysis more than to convince myself that things are ‘fast enough’. If I’m wrong, all I’ll need to do is to clock the system a bit lower. Mistakes and misses are forgiving.
Outro
This write-up indeed turned out to be rather short. Hopefully it made sense and I didn’t skip over things that are crucial to understanding what’s going on in the design.
Next time around, I plan on talking about the ALU and its implementation.
Till that time, thanks for reading all the way to the end!
