Simulated Ben Eater's programmable 8-bit computer from scratch using only simple logic gates and basic ICs
Clock
Bus
A Register
B Register
ALU
Flags Register
Memory Address Register
Memory
Manual Memory Input
Program Counter
Output Display
Instruction Register
Control Unit
Reset
Serial Monitor
Clock can operate in two modes:
- Astable Mode
- Monostable Mode
In Astable Mode clock speed can be changed by adjusting the potentiometer
In Monostable Mode single clock pulse can be generated by pressing the Generate Clock Pulse Button
- 2 X 555 ( Timer/Oscillator )
Mode Selector Switch | Halt Signal | Clock Output |
---|---|---|
Low | Low | Astable Mode |
High | Low | Monostable Mode |
X | High | Low |
All modules are connected to the Bus using a tristate to connect multiple outputs together
The Bus is connected to pull down resistors to pull the Bus to ground by default if there is no input
A-Register is an 8-bit register combined from two 4-bit registers
The register is connected to the Bus through a Tristate
The register also has a direct connection to ALU's inputs
- 2 X 74LS173 ( Quad D-Type Filp-Flops With Tristate Outputs )
- 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
Signal | Functionality |
---|---|
A Register In Signal | Read the Bus content into A Register |
A Register Out Signal | Output A Register content to the Bus |
Active High Reset | Reset A Register content |
B-Register is an 8-bit register combined from two 4-bit registers
The register is connected to the Bus through a Tristate
The register also has a connection to ALU's inputs through XOR Gates that decide whether the register's content should be negated or not based on the Subtraction Signal for subtraction operations
- 2 X 74LS173 ( Quad D-Type Filp-Flops With Tristate Outputs )
- 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
- 8 X 74LS86 ( 2-Input Exclusive-OR Gate )
Signal | Functionality |
---|---|
B Register In Signal | Read the Bus content into B Register |
B Register Out Signal | Output B Register content to the Bus |
Subtract Signal | Negate B Register content ( Get one's complement ) |
Active High Reset | Reset B Register content |
Note: The Control Unit currently has no Microinstruction that controls the B Register Out Signal, and the signal is manually fixed to be inactive (aka HIGH)
ALU is an 8-bit Full Adder combined from two 4-bit Full Adders
Both A-Register and B-Register are connected to the ALU's input, and the ALU is constantly operating on them
ALU supports two Arithmetic Operations:
- Addition
- Subtraction
Subtraction is achieved when the Subtraction Signal is enabled (Active HIGH), resulting in the B-Register's content to be negated, hence, getting One's Complement. And the Subtraction Signal is also fed to the carry In of the first Full Adder of the ALU (C0) (Note the Subtraction Signal is active HIGH), hence, getting Two's Complement
ALU generates two Flags:
- Carry Flag
- Zero Flag
- 2 X 74LS283 ( 4-Bit Binary Full Adders With Fast Carry )
- 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
- 74HC4078 ( 8-Input NOR Gate )
Signal | Functionality |
---|---|
ALU Out Signal | Output ALU content to the Bus |
Subtract Signal | Fed to the Carry In for subtraction operation ( Get two's complement ) |
Flags Register is a 4-bit register, which stores the ALU's two generated flags:
- Carry Flag
- Zero Flag
when the Flags Register In Signal is enabled
The register's output flags are connected to the Control Unit to support the Jump Carry and Jump Zero Instructions
- 74LS173 ( Quad D-Type Filp-Flops With Tristate Outputs )
Signal | Functionality |
---|---|
Flags Register In Signal | Read ALU's Flags into Flags Register |
Active High Reset | Reset Flags Register content |
Memory Address Register is a 4-bit register, which stores the 4-bit memory address where the memory is going to read and output this address content (If it is Run Mode, otherwise in Programming Mode, the memory is going to use and output the content of the Manual Address Input)
The register's output is connected to the Memory Address Input Selector
- 74LS173 ( Quad D-Type Filp-Flops With Tristate Outputs )
Signal | Functionality |
---|---|
Memory Address In Signal | Read the Bus content into Memory Address Register |
Active High Reset | Reset Memory Address Register content |
Memory is a 128-bit (16 X 8) memory combined from two 64-bit (16 X 4) Random Access Read/Write Memory
The Memory is connected to the Bus through a Tristate
The inputs (Memory Data Input, Memory Address, and Memory Write Enable) to the Memory is the outputs of the Memory Data Input Selector/Multiplexer, Memory Address Input Selector/Multiplexer, and Memory Write Enable Selector/Multiplexer where the:
-
Memory Data Input Selector/Multiplexer: Decides whether the manual memory data input or the data on the Bus will be used as an input to the Memory
-
Memory Address Input Selector/Multiplexer: Decides whether the manual memory address input or the address in the Memory Address Register will be used as an input to the Memory
-
Memory Write Enable Selector/Multiplexer: Decides whether the manual memory write enable or the Memory In Signal with the positive edge of the clock will be used as an input to the Memory
The Manual Inputs referenced above are generated from the Manual Memory Input Module
All Selectors/Mutliplexers selects the input to the Memory based on the Programming Mode Enable Signal generated also in the Manual Memory Input Module
- 2 X 74LS189 ( 64-Bit Random Access Read/Write Memory )
- 4 X 74LS157 ( Quadruple 1-of-2 Data Selectors/Multiplexers )
- 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
Signal | Functionality |
---|---|
Memory Out Signal | Output Memory content to the Bus |
Memory In Signal | Generates the Memory Write Enable Pulse with the Clock's positive edge |
Memory Write Enable | Write the Data to the Memory at the specified Address |
Programming Mode Enable Signal | Selects between the inputs of the two Selectors/Multiplexers |
Manual Memory Input consists of:
-
Memory Address Manual Input: Where the 4-bit Memory Address is entered manually through the Dip Switches
-
Memory Data Manual Input: Where the 8-bit Memory Data is entered manually through the Dip Switches
-
Memory Manual Write Enable Input: Where the Memory's Write Enable Signal is generated manually by pressing the Button
-
Memory Programming/Run Mode Selector: Generates the Programming Mode Enable Signal that selects between the Programming Mode where all the Memory inputs are supplied from the Manual Memory Input Module, and the Run Mode where all the Memory inputs are supplied from the corresponding source for each input as stated in the Memory Module. (Note the LEDs that indicate which mode you are operating in based on the state of the Switch)
- DIPSW_4 ( Interactive DIP Switch 4 Independent Elements )
- DIPSW_8 ( Interactive DIP Switch 8 Independent Elements )
- Button ( SPST Push Button )
- Switch ( Interactive SPST Switch (Latched Action) )
Signal | Functionality |
---|---|
Programming Mode Enable Signal | Selects between the Programming Mode and Run Mode |
Program Counter is a 4-bit binary counter which gets incremented with each Clock pulse if the Program Counter Enable Signal is enabled
The Program Counter also supports loading a 4-bit data from the Bus when the Program Counter In Signal is enabled (Note that, this is used to implement the Jump Instruction)
The Program Counter's output is connected to the Bus through a Tristate, however, its input is connected to the Bus directly
- 74LS161 ( Synchronous 4-Bit Binary Counters )
- 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
Signal | Functionality |
---|---|
Program Counter In Signal | Load the lowest 4-bits from the Bus into Program Counter |
Program Counter Out Signal | Output Program Counter content to the Bus |
Program Counter Enable Signal | Enable Program Counter counting which is synchronized with the Clock |
Active Low Reset | Reset Program Counter content |
Output Display Module can be divided into sections and sub-modules:
-
Output Display: Four 7-Segments, three of which display digits while the fourth display the sign ( Sign is supported but currently not utilized )
-
7-Segments Multiplexing Circuit: Mutliplexes through the Four 7-Segments (After the Arduino finishes programming the EEPORM), using a 2-Bit Counter (Counting from 0 to 3) and a 2 to 4 Decoder that takes the 2-Bit Counter output and outputs the four Seven Segment Enable Signal, where they select which 7-Segment should be active at a time. The Multiplexing Speed can be adjusted using the 555 Timer
-
Output Display Register: 8-bit register which stores in binary the current Output Display value being displayed on the 7-Segments. The Register's output is connected to the Display EEPORM address input through a Tristate which enables the Register's output to the Display EEPORM (After the Arduino finishes programming the EEPORM). The Register's input is connected directly to the Bus
-
Display EEPROM: Programmed using the Arduino such as, it would take the binary output display value from the Output Display Register as an Address to the Display EEPROM, and it outputs the 7-Segments representation for each digit. The EEPORM has four sections for each the Positive Numbers and the Negative Numbers (Two's Complement), three sections for each digit placement and the fourth is for the sign. These sections are:
- Ones Place
- Tens Place
- Hundreds Place
- Sign Place
As that, the 8-bit Output Display Register value can be represented by up to three digits and the sign ( 0 to 255 for Positive representation ) and ( -127 to 128 for Negative representation )
Address 7-Segments Digit 0 : 255 Ones Place of the numbers ( 0 : 255 ) 256 : 511 Tens Place of the numbers ( 0 : 255 ) 512 : 767 Hundreds Place of the numbers ( 0 : 255 ) 768 : 1023 Blank 7-Segments to indicate it is a Positive Value 1024 : 1279 Ones Place of the numbers ( -128 : 127 ) 1280 : 1535 Tens Place of the numbers ( -128 : 127 ) 1536 : 1791 Hundreds Place of the numbers ( -128 : 127 ) 1792 : 2047 Output "-" to indicate it is a Negative Value From the table:
-
By controlling the EEPROM's Address bit 8 and Address bit 9, we can control which digit place and sign place we output for that 8-bit binary number (AKA the address of the EEPROM, which is fed from the 8-bit Output Display Register)
-
By controlling the EEPROM's Address bit 10, we can select if that 8-bit binary number (AKA the address of the EEPROM, which is fed from the 8-bit Output Display Register) should be represented as a Positive Number or as a Negative Number (Two's Complement)
-
Display EEPROM Programmer: Programs the Display EEPROM as discussed. It outputs the EEPROM Addresses serially to shift registers (to make them parallel as the number of the Arduino pins are limited), while it outputs the EEPORM Data parallelly. It is also connected to a Serial Monitor for outputting the state of the EEPORM programming
- 4 X 7-Segments ( 7-Segments Common Cathode )
- 555 ( Timer/Oscillator )
- 2 X 74LS76 ( Dual JK Flip-Flops With Set And Reset )
- 74LS139 ( Dual 2-Line to 4-Line Decoders/Demultiplexers )
- 74LS273 ( Octal D-Type Positive-Edge-Triggered Flip-Flops With Clear )
- 62256 ( CMOS RAM (32k X 8-bit) )
- Arduino Nano ( ATMEGA328P )
- 2 X 74HC595 ( 8-Bit Shift Registers With Tristate Output Registers )
- 2 X 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
Signal | Functionality |
---|---|
Output Register In | Read the Bus content into Output Display Register |
Display EEPROM Output Enable Bit | Indication that the Arduino finished its programming |
Display EEPROM Write Enable | Write the data to the Display EEPROM with the provided address |
Display EEPROM Address8/9 | Select between the three Digit Places and the Sign Place |
Display EEPROM Address10 | Select between the representation of the Output Display Register as a Positive Number or as a Negative Number (Two's Complement) |
Display Seven Segments Switch Clock | Clock that controls the speed of the four 7-Segments multiplexing |
Active Low Reset | Reset Output Display Register content |
Instruction Register is an 8-bit register combined from two 4-bit registers
Each 4-bit stores a different information where:
-
Lower 4-bit: Contains the Operand (Data)
-
Higher 4-bit: Contains the Operation[OP] Code (Instruction)
The Instruction Register's input is connected directly to the Bus, however, only its Lower 4-bit output (the Operand) is connected to the Bus through a Tristate
- 2 X 74LS173 ( Quad D-Type Filp-Flops With Tristate Outputs )
- 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
Signal | Functionality |
---|---|
Instruction Register In Signal | Read the Bus content into Instruction Register |
Instruction Register Out Signal | Output Instruction Register lower 4-bit (Operand) content to the Bus |
Active High Reset | Reset Instruction Register content |
Outputs the 16-bit Control Word using Control Unit EEPROMs as a lookup table
Control Unit Module can be divided into sections and sub-modules:
-
Microcode Counter:
-
Counts from 0 to 4 (Five Steps/Cycles), as the Microcode Counter Reset Signal resets the Mircocode Counter instantly to zero when it reaches the fifth count/tick.
-
As Each instruction requires Five Steps/Cycles to execute the Fetch, decode, and excute cycle
-
Counter is incremented with the inverse of each clock cycle. As to set the approprite control signals before the next rising edge of the clock
-
Microcode counter 3-bit output is fed to the Control Unit EEPROMs's addresses 0 to 2 (After the Arduino finishes programming the EEPORM)
-
-
Control Unit EEPROMs Input Selector: Selects the address input for the Control Unit EEPROMs between:
-
The Arduino's address output. (When programming the EEPROM)
-
The Mircocode Counter output with the Instruction Register higher byte output (The Operation[OP] Code (Instruction)) with the Flags Register output. (After the Arduino finishes programming the EEPORM)
-
-
Control Unit EEPROMs: Consists of two EEPROMS programmed identically where the first EEPROM is used to output the higher 8-bit of the Control Word and the second EEPROM is used to output the lower 8-bit of the Control Word.
And selection between the first EEPROM output and the second EEPROM output (After being programmed) can be made using the Address Line 7 of the EEPROMs where the first EERPOM's Address Line 7 is fixed to Low and the second EERPOM's Address Line 7 is fixed to High.
Control EEPROM Output Enable Bit First EEPROM Address 7 Second EEPROM Address 7 Low (Arduino finished programming) Low High High (Arduino is programming) Control EEPROM Address7 (Byte Selector) (Arduino's Output) Control EEPROM Address7 (Byte Selector) (Arduino's Output) The EEPROMs are programmed using the Arduino such as, it would take the:
-
Microcode Counter 3-bit output as the first 3-bit address 0 to 2 to the EEPROM
-
Instruction Register higher byte (Operation Code) output as address 3 to 6 to the EEPROM
-
Address 7 is used as a selector between the output intended for each EEPROM, as they are programmed identically. Where the intended output of the first EEPROM is stored where Address 7 is set Low and for second EEPROM is stored where Address 7 is set High
-
Flags Register 2-bit output as address 8 to 9 to the EEPROM
Microinstructions/Microcodes can be formed by combining different Control Signals to form a 16-bit Control Word:
Control Signal Control Signal Functionality Control Signal Bit Position In Control Word HLT Halt the Clock 1000000000000000 MI Memory Address Register In 0100000000000000 RI Memory In 0010000000000000 RO Memory Out 0001000000000000 IO Instruction Register Out 0000100000000000 II Instruction Register In 0000010000000000 AI A Register In 0000001000000000 AO A Register Out 0000000100000000 EO ALU Register Out 0000000010000000 SU B Register Subtract Signal Enabled (Subtract) 0000000001000000 BI B Register In 0000000000100000 OI Output Display Register In 0000000000010000 CE Program Counter Enable (Count) 0000000000001000 CO Program Counter Out 0000000000000100 J Program Counter In (Jump) 0000000000000010 FI Flags Register In 0000000000000001 Control Unit supports 11 Instructions (OP Code):
Instruction (OP Code) Op Code Binary Functionality NOP (NO OPERATION) 0000, 1001, 1010, 1011, 1100, 1101 Doing just the Fetch and Decode cycle LDA (LOAD A) 0001 Load data from Memory at the specified address into the A register ADD 0010 Load data from Memory at the specified address into the B register, and load the sum into the A register. And load the flags status into the Flags Register before the Output Display's register is changed SUB (SUBRACT) 0011 Load data from Memory at the specified address into the B register, set the subtract bit and load the sum into the A register. And load the flags status into the Flags Register before the Output Display's register is changed STA (STORA A) 0100 Load data from the A register into the Memory at the specified address LDI (LOAD IMMEDIATE) 0101 Load the Operand (4-bits) into the A register JMP (JUMP) 0110 Load the Operand (4-bits address we want to jump to) into the Program Counter to be executed the next clock cycle JC (JUMP CARRY) 0111 If the Carry Flag is set. Load the Operand (4-bits address we want to jump to) into the Program Counter to be executed the next clock cycle JZ (JUMP ZERO) 1000 If the Zero Flag is set. Load the Operand (4-bits address we want to jump to) into the Program Counter to be executed the next clock cycle OUT 1110 load data from A register to the Output Display's register HLT (HALT) 1111 Halt/Stop the computer Clock EEPROM Address 0 : 2
Microcode Counter (Step)EEPROM Address 3 : 6
Instruction (OP Code)EEPROM Address 8
Carry Flag (CF)EEPROM Address 9
Zero Flag (ZF)Control Word 000 (Fetch) X X X X X X MI | CO 001 (Fetch and Decode) X X X X X X RO | II | CE 010 (Execute) 0000 (NOP) X X 0000000000000000 011 (Execute) 0000 (NOP) X X 0000000000000000 100 (Execute) 0000 (NOP) X X 0000000000000000 010 (Execute) 0001 (LDA) X X IO | MI 011 (Execute) 0001 (LDA) X X RO | AI 100 (Execute) 0001 (LDA) X X 0000000000000000 010 (Execute) 0010 (ADD) X X IO | MI 011 (Execute) 0010 (ADD) X X RO | BI 100 (Execute) 0010 (ADD) X X EO | AI | FI 010 (Execute) 0011 (SUB) X X IO | MI 011 (Execute) 0011 (SUB) X X RO | BI 100 (Execute) 0011 (SUB) X X EO | AI | SU | FI 010 (Execute) 0100 (STA) X X IO | MI 011 (Execute) 0100 (STA) X X AO | RI 100 (Execute) 0100 (STA) X X 0000000000000000 010 (Execute) 0101 (LDI) X X IO | AI 011 (Execute) 0101 (LDI) X X 0000000000000000 100 (Execute) 0101 (LDI) X X 0000000000000000 010 (Execute) 0110 (JMP) X X IO | J 011 (Execute) 0110 (JMP) X X 0000000000000000 100 (Execute) 0110 (JMP) X X 0000000000000000 010 (Execute) 0111 (JC) 0 X 0000000000000000 010 (Execute) 0111 (JC) 1 X IO | J 011 (Execute) 0111 (JC) X X 0000000000000000 100 (Execute) 0111 (JC) X X 0000000000000000 010 (Execute) 1000 (JZ) X 0 0000000000000000 010 (Execute) 1000 (JZ) X 1 IO | J 011 (Execute) 1000 (JZ) X X 0000000000000000 100 (Execute) 1000 (JZ) X X 0000000000000000 010 (Execute) 1001 (NOP) X X 0000000000000000 011 (Execute) 1001 (NOP) X X 0000000000000000 100 (Execute) 1001 (NOP) X X 0000000000000000 010 (Execute) 1010 (NOP) X X 0000000000000000 011 (Execute) 1010 (NOP) X X 0000000000000000 100 (Execute) 1010 (NOP) X X 0000000000000000 010 (Execute) 1011 (NOP) X X 0000000000000000 011 (Execute) 1011 (NOP) X X 0000000000000000 100 (Execute) 1011 (NOP) X X 0000000000000000 010 (Execute) 1100 (NOP) X X 0000000000000000 011 (Execute) 1100 (NOP) X X 0000000000000000 100 (Execute) 1100 (NOP) X X 0000000000000000 010 (Execute) 1101 (NOP) X X 0000000000000000 011 (Execute) 1101 (NOP) X X 0000000000000000 100 (Execute) 1101 (NOP) X X 0000000000000000 010 (Execute) 1110 (OUT) X X AO | OI 011 (Execute) 1110 (OUT) X X 0000000000000000 100 (Execute) 1110 (OUT) X X 0000000000000000 010 (Execute) 1111 (HLT) X X HLT 011 (Execute) 1111 (HLT) X X 0000000000000000 100 (Execute) 1111 (HLT) X X 0000000000000000 101 X X X X X X 0000000000000000 110 X X X X X X 0000000000000000 111 X X X X X X 0000000000000000 Note that EEPROM Address 7 is not mentioned in the table. As discussed earlier EEPROM Address 7 is used for selecting the Control Word Byte for each of the two EEPROMS
-
-
Control Unit EEPROMs Programmer: Programs the Control Unit EEPROMs as discussed. It outputs the EEPROMs Addresses serially to shift registers (to make them parallel as the number of the Arduino pins are limited), while it outputs the EEPORMs Data parallelly. It is also connected to a Serial Monitor for outputting the state of the EEPORMs programming
- 74LS161 ( Synchronous 4-Bit Binary Counters )
- 74LS138 ( 3-Line to 8-Line Decoders/Demultiplexers )
- 3 X 74LS157 ( Quadruple 1-of-2 Data Selectors/Multiplexers )
- 2 X 62256 ( CMOS RAM (32k X 8-bit) )
- Arduino Nano ( ATMEGA328P )
- 2 X 74HC595 ( 8-Bit Shift Registers With Tristate Output Registers )
- 3 X 74LS245 ( Octal Bus Transceivers With Tristate Outputs )
Signal | Functionality |
---|---|
Microcode Counter Reset Signal | Reset Microcode Counter to 0 |
Control EEPROM Output Enable Bit | Indication that the Arduino finished its programming |
(Inverse) Control EEPROM Output Enable Bit | The inverse of the Control EEPROM Output Enable Bit |
Control EEPROM Write Enable | Write the data to the Control Unit EEPROMs with the provided address |
Control EEPROM Address7 (Byte Selector) | Select between the lower and higher byte of the 16-bit Control Word |
Active Low Reset | Reset Microcode Counter to 0 |
Reset generates two reset signals when the Button is pressed:
- Active High Reset
- Active Low Reset
that are used to reset the different computer's modules
- Button ( SPST Push Button )
Signal | Functionality |
---|---|
Active High Reset | Reset components that reset with an Active High Signal |
Active Low Reset | Reset components that reset with an Active Low Signal |
Serial Monitor displays the serial output of the Arduino of each module:
- 2 X Virtual Terminal