Q2 Computer

A 12-bit single-board discrete transistor computer.

Similar projects worth following
The Q2 is a discrete transistor computer implemented on a single PCB using surface mount components. It is a 12-bit design with a bit-serial ALU.


Making a computer out of 7400-series logic, FPGAs, or PLDs is fun, but a lot of the complexity hides in those integrated circuits and their structure significantly affects any design that uses them. To avoid this influence, I like to use transistors. This led me to design and build a computer on perf-board out of NPN transistors between 2008 and 2011 (the Q1). In the end, I was left with a functioning transistor computer, but with a somewhat less than inspired design that would be extremely difficult to improve or replicate.

Today (2021), the situation has changed quite a bit from when the Q1 was built. While designing a custom integrated circuit is probably still beyond the reach of most, it is now fairly easy to get a PCB both fabricated and assembled. This means that designing and building a transistor computer is no longer such a labor-intensive endeavor. In fact, I now have the opposite problem where I end up with 5 clones every time I make a new revision. In some respects, I feel like putting this many transistors on a PCB is like designing an integrated circuit with a really big feature size.

The Q2 is my attempt at a single-board transistor computer. It is a 12-bit design with a bit-serial ALU. It is no coincidence that the architecture is similar to the PDP-8. In designing a transistor computer like this, the design decisions of old computers start to make a lot more sense.


The Q2 is implemented in NMOS using n-channel MOSFETs with resistor pull-ups.  The current design uses 1094 2N7002 transistors.  To keep the transistor count low and the power usage low, the Q2 runs at 80 kHz. This allows it to draw well under 500 mA at 5V, so I use a USB-B connector for power.

User Interface

Programming is accomplished via a front-panel interface on the lower left side of the board. The front-panel has 12 LEDs to show the current address and 12 LEDs to show the data bus. There are also 12 switches to serve as inputs and the following buttons:

  • Reset - Set P (the program counter/current address) to the value in the switches and reset the Q2.
  • Halt - Stop the clock.
  • Run - Start the clock.
  • Deposit - Store the value in the switches to the current address.
  • Next - Increment P.

The front-panel switches are exposed to a 40-pin header allowing the use of a Raspberry Pi for programming, which is much more convenient.

For interaction with running programs, the lower right side of the board has a 16x2 LCD that can be written at address 0xFFF (bit 8 determines if the write is a command or data). There are also 12 buttons under the LCD, whose state can be read from 0xFFF.

Here is a snake game:


The Q2 uses two 6264 SRAMs for main memory leaving 4 bits unused. A CR2032 battery is used for backup. The use of an SRAM IC might be controversial, but there aren't a whole lot of viable options. Making a DRAM out of transistors would be possible, and with large capacitors it might even be essentially non-volatile, but to have 12-bits of address space would use more transistors than the Q2 has. A core memory would also be neat, but I don't know an easy way of sourcing one.

All 12 bits of the address space are mapped to SRAM except for the last address (0xFFF), which is for I/O.  This means there are 4095 words of SRAM total. This seemly small amount actually provides enough to do quite a bit. The snake game shown above only uses a small fraction of the available memory.

Instruction Set

   \  \ \    \____ Operand
    \  \ \________ Zero-Page
     \  \_________ Dereference
      \___________ Opcode

  Opcode  | Name  | F | Description
  ------- | ----- | - | ----------------
  000     | LDA   | Z | A = [X]
  001     | NOR   | Z | A = A NOR [X]
  010     | ADD   | C | A = A + [X]
  011     | SHR   | C | A = [X] >> 1
  100     | LEA   | - | A = X
  101     | STA   | - | [X] = A
  110     | JMP   | - | P = X
  111     | JFC   | - | if !F, P = X

The instruction...

Read more »

  • I2C

    Joe Wingbermuehle04/14/2021 at 02:21 0 comments

    I should have my test board for the 2SK3018 transistors back next week, but in the meantime I've been thinking about other changes.

    In the interest of adding more I/O capabilities, I think I've settled on adding an I2C interface to the Q2.  I2C is pretty easy to support, requiring 2 open-drain outputs (SDA for data and SCL for a clock), and an input (only SDA assuming there isn't a need for clock stretching).

    To implement the output for I2C is the most complicated, requiring a latch for SDA and SCL. Input is easy, requiring only a single NAND gate. Here's the current proposal:

    The idea is that bit 11 of address 0xFFF will select between the LCD (0) and I2C (1), allowing easy access to the LCD just as before.  When bit 11 is set, bit 10 sets SCL and bit 9 sets SDA. The software for I2C is fairly simple. For start/stop, I think something like this should work:

    .def I2C_EN   0x800
    .def I2C_SCL  0x400
    .def I2C_SDA  0x200
    ; Note I2C signals are inverted.
      .dw   I2C_EN | I2C_SDA | I2C_SCL
      .dw   I2C_EN | I2C_SDA
      .dw   I2C_EN | I2C_SCL
      .dw   I2C_EN
      .dw   ~I2C_SDA
    ; Send I2C start
    ; Take SDA low while SCL stays high.
      sta   =x1
      lda   i2c_one_clk   ; SDA=1, CLK=1
      sta   @=neg1
      lda   i2c_zero_clk  ; SDA=0, CLK=1
      sta   @=neg1
      jmp   @=x1
    ; Send I2C stop
    ; Take SDA high while SCL stays high.
      sta   =x1
      lda   i2c_zero_clk    ; SDA=0, CLK=1
      sta   @=neg1
      lda   i2c_one_clk     ; SDA=1, CLK=1
      sta   @=neg1
      jmp   @=x1

     For writing, we just loop over each bit.  Being a 12-bit architecture, we have to shift off 4 bits first. So, something like:

    ; Write byte in x0.
    ; Destroys x0-x2
      sta   =x1
      ; Shift out high 4 bits
      lda   =x0
      add   =x0
      sta   =x0   ; x2
      add   =x0
      sta   =x0   ; x4
      add   =x0
      sta   =x0   ; x8
      add   =x0
      sta   =x0   ; x16
      lea   =8
      add   =neg1
      sta   =x2
      lda   =x0
      add   =x0
      sta   =x0
      jfc   i2c_write_zero
      ; Write 1
      lda   i2c_one
      sta   @=neg1
      lda   i2c_one_clk
      sta   @=neg1
      lda   i2c_one
      jmp   i2c_write_cont
      ; Write 0
      lda   i2c_zero
      sta   @=neg1
      lda   i2c_zero_clk
      sta   @=neg1
      lda   i2c_zero
      sta   @=neg1
      lda   =x2
      jfc   i2c_write_loop
      ; Acknowledge
      lda   i2c_one
      sta   @=neg1
      lda   i2c_one_clk
      sta   @=neg1
      lda   i2c_one
      sta   @=neg1
      jmp   @=x1

    Reading is similar. From simulation, this would make reading 256 bytes from an EEPROM take somewhere in the neighborhood of 26 seconds at a 80kHz clock.  It would be nice to get this faster, but that's plenty fast to use some I2C sensors or a real-time clock, etc.

  • Faster Transistors

    Joe Wingbermuehle04/04/2021 at 22:04 0 comments

    The high gate capacitance of the 2N7002 transistors that the Q2 uses prevents it from running much faster than 80 kHz without becoming unstable.  This is because, with resistor pull-ups, the charge stored in the gates is pulled high through the resistor, causing slow rise times when a lot of gates are connected together.

    Consider the A register in the Q2, which is 12 bits. To clock the A register, 2 transistor gates per bit need to be pulled high. This is a fanout of 24, so a capacitance of 50pF * 24 = 1200pF. The threshold voltage of a 2N7002 is 2.5V worst-case. If we pull this high through a 10k resistor, we get the following expression for the rise time:

    Solving for t we get 8.3us for a single gate (if there were only one level of logic, the frequency would be limited to 120 kHz, but there are more levels involved). Substituting a 1k resistor solves the problem, but introduces another: instead of using 0.5mA, we use 5mA. This power draw quickly adds up.

    The control lines of the Q2 are carefully designed to use 10k resistors where possible, and fall back to 1k in just enough places to allow stable 80 kHz operation. Unfortunately, going faster becomes increasingly difficult and wastes more power. This raises the question of whether another transistor would be more suitable.

    We want the following characteristics:

    • Low gate capacitance (lower than 50pF)
    • Low threshold voltage. Not only does a high threshold cause problems with the supply voltage, it also makes the computer slower since the gate output needs to reach a higher voltage, which takes longer. 
    • Low price. When using 1000s of transistors, we can't ignore the price.
    • ESD protection. Not strictly necessary, but certainly nice to have. With lower gate capacitance, this is probably more important.

    One transistor that seems to be a good contender is the 2SK3018 (Shikues brand available through LCSC). It has a gate capacitance of 13pF and threshold of 1.5V. This means that in our example, the delay would be 1.1us with a 10k resistor and 0.11us with a 1k resistor. This should allow running the Q2 at nearly 8x the clock speed and/or save some power.

    To investigate this further, I put together a simple test circuit. to see how the transistors compare:

    The circuit is a simple relaxation oscillator (identical to the oscillator used in the Q2, but implemented using 2SK3018s instead of 2N7002s).  The output is run through two circuits with a fanout of 4 using both types of transistors so I can compare the rise times. This circuit will also allow me to try out an SMD switch so I don't have to worry about soldering switches in future revisions.

View all 2 project logs

Enjoy this project?



Karl-Wilhelm Wacker wrote 04/10/2021 at 21:59 point

Have you looked at how the original PDP-8 and other DEC computers sped up their logic?

The pull-up resistors pulled to a higher voltage and then were diode clamped to a lower voltage.  If you pulled the 10K's to +12V and clamped them to +5V , this would put you in the earlier and faster part of the R-C charge  pull-up curve.

  Are you sure? yes | no

Joe Wingbermuehle wrote 04/11/2021 at 12:53 point

They do some clever things. I've been particularly interested in how the PDP-8 implements flip-flops using a capacitor for edge detection since flip-flops take up so much room.

I like the simplicity of NMOS, but I think bipolar transistors would probably be a better choice for the most part since they're cheaper and faster. I could always try to bring some of those tricks over to NMOS (just using a higher voltage would probably help a lot, though would require a different power supply and level shifting... but hey, I already have the MOSFETs for that!). 

  Are you sure? yes | no

Karl-Wilhelm Wacker wrote 04/11/2021 at 13:43 point

I would keep your NMOS logic, just pull the 10K's to the higher level, and clamp to your current logic level with the diodes.  Remember that the clamp level power supply [VCC} has to sink the voltage from the diodes, not source it. a zener [multi-watt?] to ground with a pull up resistor to 12V or whatever to bias it should do the trick,and let you keep VCC as your logic high level

  Are you sure? yes | no

Joe Wingbermuehle wrote 04/11/2021 at 14:26 point

It is a neat idea for only an extra diode per gate.  If my understanding is correct, it would raise the power draw a bit. A gate would draw either 1.2mA (sinking 12V through the transistor) or 0.7mA (sinking 7V through the diode). On the other hand, with just the pull-up it draws 0.5mA when the transistor is conducting, and basically nothing otherwise.  I think it would be roughly 3x faster though.  Using a 22k resistor pull up instead would get it into the same range power-wise, but then it would only be about 30% faster if my calculations are correct.

So I think it gets 3x the performance for 2x the power whereas a lower-valued pull-up would get 3x the performance at 3x the power.  I might be thinking about this all wrong though.

Another issue to solve would be getting the 12V supply (currently I'm just powering the thing directly off of USB).

  Are you sure? yes | no

Karl-Wilhelm Wacker wrote 04/11/2021 at 15:58 point

what is your power draw for the whole unit [worst case]? A way to calculate this would be to ask how many 10K resistors in your design.

I have used a dc-dc converter to get 24v [at about 50mA] from the USB 5V for a modbus/4-20mA calibrator I made that talks on modbus to a 4-20mA output smart pressure sensor I also designed, 4-20 for operation, modbus to calibrate the sensor.

  Are you sure? yes | no

Joe Wingbermuehle wrote 04/12/2021 at 12:17 point

Total current draw is around 380mA. 188mA from 10k pull-ups, 85mA from 1k pull-ups, and the rest for LEDs, SRAM, etc.  I've gone through the design and calculated the capacitive load on all of the gates to try to adjust down the pull-ups for certain gates that have the greatest impact on the performance, but at some point I just need all the gates to go faster. I think the 2N7002 is probably the biggest problem with it's 50pF input capacitance though, so I'm hopeful that using a different transistor (supposedly only 13pF) will help a lot with basically no extra effort or components.

  Are you sure? yes | no

marazm wrote 04/08/2021 at 17:52 point

please add network. meybe put it to web?

  Are you sure? yes | no

Joe Wingbermuehle wrote 04/09/2021 at 11:52 point

Not sure about putting it on the web, but I am planning on adding an I2C interface to the next revision.

  Are you sure? yes | no

vincent stuchly wrote 04/08/2021 at 16:00 point

Woah, this is awesome! To be honest I would be interested also in the perfboard version. The pcb looks stunning. Great work

  Are you sure? yes | no

Joe Wingbermuehle wrote 04/09/2021 at 11:50 point

Thanks! There is some info and pictures of the perfboard one here:

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates