So here I go again - here is another memory cell which I'd be pleased if the community could criticise.
For background, I want to build a discrete component CPU with enough LEDs so you can see how it all works and for it to be simple enough that others could build the design (e.g. for it to be a school 6th form project). It has quite a few registers, about 8, so that you can see how a processor works (smaller would be possible but tortuous to program). It's got a 16 bit instruction, data and address space to keep everything clean. So, just for the sake of keeping things clean there are a lot of memory cells to build, something like 16 x 8 = 128 and this could dominate the component count and so be the major blocker to other people building this processor.
So I want the smallest number of components to build a memory cell. The accepted smallest number is two, the standard DRAM design, in which a capacitor stores charge and is isolated by a MOSFET. Whilst I applaud the efforts to recreate these in discrete components (look at https://hackaday.io/project/8449-hackaday-ttlers/log/44217-dynamic-ram-with-single-mosfet-per-bit) I really can't get my head around the precharge and the sharing of the bit line between read and write. My CPU is meant as an introduction to everything from resistors through to compilers, so if it's not simple for me to understand then it's too complex for my processor.
As a result I've done a lot of thinking and here is a 2 transistor memory cell using a capacitor to store the charge:
Or, showing all the shared components, here it is as part of a register file:
DataInAll is the data line for one bit position across all registers/words. It drives a NPN/PNP pair so that one conducts and the other doesn't. So DataIn is connected to ground or +ve. Provided WriteEnable is set low, then the capacitor keeps its charge, but if WriteEnable is set high then the capacitor will charge or discharge according to DataIn/DataInAll. Reading is done with the MOSFET, so it doesn't suck current from the capacitor. If ReadEnable is high then the state of DataOutAll is set by the state of the MOSFET, and the charge on the capacitor. Only one register/word has ReadEnable set high, so only one can pull down DataOutAll.
From playing about, it seems that the capacitor is not needed, I can use the gate capacitance of the MOSFET. So that's just two components for the memory cell. I can't get lower than two so hopefully I can now move on to fixing my diode ripple carry, the rest of the ALU, etc.
With no capacitor, things run fast. Using a RPi I can put data on DataInAll, assert write enable, wait, ground WriteEnable, assert ReadEnable, wait, read the data, ground ReadEnable all at 200kHz. That's as fast as I've got any of my memory cells to run, and is pushing the limits of WiringPi to set the GPIO (ultimately I'll abandon WiringPi).
Note that whilst I could use just the BJT and MOSFET (as above), the requirement that every state is visible means that I'm using the MOSFET to power a LED and running the read circuitry from that. I want two read lines, so I have the basic BJT and MOSFET as above (no capacitor) but then I'm using an LED two more BJT, one for each read line. So that's 5 components in total, not 2 as above, but a big improvement on the 21 components of my last written up memory cell.