The 1-bit Industrial Control System (pronounced as "one-bicks") is a single-bit processor designed for use in systems requiring decisions based on sequence of single-bit information. The design is directly based on the MC14500B CMOS microprocessor from Motorola.
It has a remarkably low transistor count, with my current iteration of the design requiring only ~250-300 discrete transistors for the entire chip, which I have physically built and documented the results of in this post. In comparison, even the first microprocessor the 4-bit Intel 4004 (1971) contained about 2250 transistors.
The image of the board above is the complete Logic Unit, using Manhattan circuit construction technique.
The chip has an unusual architecture, hosting only a 1-bit data bus and a 4-bit instruction set. It does not include any support for features that a typical microprocessor has. The program counter, instruction fetches, memory, jumps and subroutine calls will need to be implemented with external circuitry.
| Instruction Code | Mnemonic | Action |
| 0 0000 | NOPO | RR → RR, Flag O → HIGH |
| 1 0001 | LD | Data → RR |
| 2 0010 | LDC | !Data → RR |
| 3 0011 | AND | RR & Data → RR |
| 4 0100 | ANDC | RR & !Data → RR |
| 5 0101 | OR | RR | Data → RR |
| 6 0110 | ORC | RR | !Data → RR |
| 7 0111 | XNOR | !(RR ^ Data) → RR |
| 8 1000 | STO | RR → Data, Write → HIGH |
| 9 1001 | STOC | !RR → Data, Write → HIGH |
| A 1010 | IEN | Data → IEN |
| B 1011 | OEN | Data → OEN |
| C 1100 | JMP | JMP Flag → HIGH |
| D 1101 | RTN | RTN Flag → HIGH, and skip next instruction |
| E 1110 | SKZ | Skip next instruction if RR = 0 |
| F 1111 | NOPF | RR → RR, Flag F → HIGH |
With the use of a rudimentary assembler and access to sufficient memory, the processor proves itself to be more than capable of performing complex computational tasks.
The example below shows a basic arithmetic subroutine performing a 4-bit addition.
; enable data input/output
0x00: IEN HIGH
0x02: OEN HIGH
0x04: JMP 0x20 ; goto main code
; FULL-ADDER SUBROUTINE
0x06: LD A
0x08: XNOR B
0x0A: STOC A_XOR_B ; store A ^ B
0x0C: LD A
0x0E: AND B
0x10: STO A_AND_B ; store A & B
0x12: LD A_XOR_B
0x14: XNOR C
0x16: STOC B ; S = (A ^ B) ^ C
0x18: LD A_XOR_B
0x1A: AND C
0x1C: OR A_AND_B
0x1E: STO C ; C = ((A ^ B) & C) | (A & B)
0x20: RTN 0 ; exit
; z0 = x0 + y0
0x22: LD X0 ; load x0 as 1st argument
0x24: STO A
0x26: LD Y0 ; load y0 as 2nd argument
0x28: STO B
0x2A: JMP 0x04 ; call to subroutine
0x2C: LD B
0x2E: STO Z0 ; store result to z0
; z1 = x1 + y1 + c
0x30: LD X1
0x32: STO A
0x34: LD Y1
0x36: STO B ; carry is stored from
0x38: JMP 0x04 ; previous subroutine call
0x3A: LD B
0x3C: STO Z1
; z2 = x2 + y2 + c
0x3E: LD X2
0x40: STO A
0x42: LD Y2
0x44: STO B
0x46: JMP 0x04
0x48: LD B
0x4A: STO Z2
; z3 = x3 + y3 + c
0x4C: LD X3
0x4E: STO A
0x50: LD Y3
0x52: STO B
0x54: JMP 0x04
0x56: LD B
0x58: STO Z3
0x00: 0100000000000000
0x10: 0100000000000000
0x20: 0000000000000000
0x30: 1110000000000000
0x40: 0100000000000000
0x50: 1001000000000000
0x60: 0000000000000000
0x70: 0000000000000000
0x80: 0000000000000000
0x90: 0000000000000000
0xA0: 0000000000000000
0xB0: 0000000000000000
0xC0: 0000000000000000
0xD0: 0000000000000000
0xE0: 0000000000000000
0xF0: 0000000000000000
Memory address 0x30 holds the first value
(0b0111), and 0x40 for the second value
(0b0010). The result of this addition is stored at
0x50 with the output 0b1001, which agrees
with the fact that 0b0111 + 0b0010 = 0b1001 and assures that
the program is running as expected.
For a more detailed description of how such a microprocessor works, visit Ken Shirriff's excellent blog post: "A one-bit processor explained: reverse-engineering the vintage MC14500B"
I have first started out this project with the goal of fully building a computer from discrete electronic components (no integrated circuits). Being terribly unmotivated to follow any courses or guides, I began working with digital logic circuitry (NAND, NOR, NOT, etc.) one unit at a time. From registers to counters to arithmetic units, it became increasingly apparent to me the time and financial cost required to build a homebrew computer was much too great for the resources I had available to me.
Thus, I begin the process of extreme pruning, learning about the history of computing and the creative solutions people came up with. Even with a complex ALU, the biggest offender to the cost of computing was usually always memory.
Let's say, if one bit (not byte!) of memory requires 5 transistors, and I desire a full 256 bytes of RAM, and each 2N2222 transistor costs around $0.08... I would require a whopping 10,240 transistors. That's $819.20 in transistors alone! That is not including all the resistors required to make it work, nor the decoder that's required to make it addressable! Don't forget all the labour, time, and soldering too!
If we insist on using only discrete electronic components, we would have to be very selective and reserved with our use of transistors.
Discrete transistorised version of the 4-bit instruction register
To make the project more approachable, I had to reduce the scope down to the CPU. Even then, a basic 8-bit ALU would still require hundreds of transistors. This has forced me to continue searching until I came across 1-bit microprocessor designs, where the ALU (or the Logic Unit in this case) only required 35-40 or so transistors!
With a small 4-bit instruction set and a couple of 1-bit registers, this discovery made the project a lot more feasible within the time span and finances I had to work with.
A fully working simulation of 1bICS I made on Digital Logic Sim (Sebastian Lague)
Once I got the logical aspects of the chip working, I began to learn more about digital electronics and schematic design. Prototying units using electronic simulations and then breadboards, incrementally piecing parts together such that they work in predictable and synchronous manner.
Clicking on some of the pictures above will lead you to the videos of the circuits working in action. I certainly could have made some optimisations in the design, by utilising electronic "trickery" to save a transistor here or there, but at some point the concept of "good enough" applies.
This project has been an overall success, and it has achieved the goals I have set out to do. However, not all of the circuits were fully built as it is now apparent to me that manual prototyping methods can be incredibly time consuming. So far, every circuit has behaved exactly as I expected, thus I will make the move to create PCB designs and get them printed in the future.