Emulating Logic Gates with an ATmega328P MCU - Part 1
I’m discovering the world of electronics with my son. We encountered to some nice videos that are about recreating a whole CPU with only logic gates. The most well known one being the one from Ben Eater and the one from the NAND2Tetris project.
That said, as anyone soon notices, all the combinations of the logic gates become enormous very soon. Therefore, Ben showed a very nice trick : replacing combined logic gates with a simple EEPROM.
The idea itself is brilliant, and is the famous space versus time optimization. Effectively precalculation of all the possible inputs, and sorting them. Note that using an EEPROM has several drawbacks. Regular 74HCxx logic gates operate in the 5ns range, which is very fast. Beware that if you have multiple stages of logic, which is the case here, you have to multiply by at least the number of stages. The EEPROM reads in about 150ns, so we can afford 30 stages. Not so bad.
But that assumes a parallel EEPROM, and the one that Ben uses is the AT28C64B that is easy to source, but not really cheap.
I just happen to have a whole bunch of unused ATmega328P in a DIP packaging. This MCU is very well known, as it is the basis of the Arduino Uno. So it is ubiquitous and rather cheap. The fact that it is more versatile than an EEPROM is a nice side-effect.
So, lets see if can emulate that EEPROM1 with the MCU. I know it is a little cheating, but we are also emulating old game consoles for convenience. So I guess it is similar here.
Problems
The first problem is very simple : the EEPROM is a DIP-28 and the MCU is a DIP-28 also. Which means that no matter how you will look at it, there won’t be enough pins.
Reduce Pin Count
We therefore have to see how much pins we have in the MCU that we can actually use. There’s 3 GND, 4 VCC, 1 RESET & 2 for the external quartz. We could leverage those 2 if needed, but let’s try not to at first.
The EEPROM can be reduced to 4 buses at runtime, since we don’t write to it. We actually use it as a ROM.
- Power, is ignored as every IC is powered anyway ;)
- Chip Enable, otherwise the outputs are all high impedance.
- Address
- Data
The Chip Enable & the address are IN, and Data is OUT. Also, the Data needs to be 8-bit wide, otherwise it will be too complex.
We want to keep the control pins, such as the RESET & TOSC1/TOSC2.
Control
Pin | Function |
---|---|
PC6 | RESET (original) |
PB6 | TOSC1 (original) |
PB7 | TOSC2 (original) |
PD0 | TX (original) |
PD1 | RX (original) |
PD2 | INT0 (original) |
Chip Enable
Pin | Function |
---|---|
PD5 | Chip Enable |
Data
Pin | Function |
---|---|
PB0 | D0 |
PB1 | D1 |
PB2 | D2 |
PB3 | D3 |
PB4 | D4 |
PB5 | D5 |
PD6 | D6 |
PD7 | D7 |
Address
Pin | Function |
---|---|
PC0 | A0 |
PC1 | A1 |
PC2 | A2 |
PC3 | A3 |
PC4 | A4 |
PC5 | A5 |
PD3 | A6 |
PD4 | A7 |
Which fits perfectly, as we have 8 bits in (A) and 8 bits out (D).
In case we’d need a pin for carry, we could reuse either the TX/RX or the TOSCx ones. I’d prefer to leverage the TX/RX ones as having an external oscillator is easier to program, since the UART is much more precise, and the boot-loader can still leverage TX/RX while programming.
Carry (Optional)
Pin | Function | Carry |
---|---|---|
PD0 | TX | OUT |
PD1 | RX | IN |
Programming the MCU
this will be in part 2.
-
Actually not an EEPROM, but a ROM, as we won’t emulate the writing part. The writing part is done by uploading a new firmware on the MCU. So it is definitely not at runtime. ↩