In short: The ATmega328P is an 8-bit Harvard-architecture AVR microcontroller with 32 KB flash, 2 KB SRAM and 1 KB EEPROM, three timers and a 10-bit ADC, all running RISC instructions that mostly finish in one clock at 16 MHz.
Why the chip matters more than the board
People say "I programmed an Arduino", but the Uno is just a carrier. The part that executes your code is Microchip's (formerly Atmel) ATmega328P, a 28-pin chip with a CPU, memories and peripherals on one die. The board adds a 16 MHz crystal, a 5 V regulator and a USB-serial bridge. If you want the board-level view, read our Arduino Uno block diagram walkthrough; this article stays inside the silicon.
Interviewers love this chip because it is small enough to hold completely in your head: every register, every memory and every peripheral. That is exactly why it is a favourite for screening questions on memory architecture and interrupts.
Harvard architecture and the three memories
The AVR core is Harvard: program and data live in separate address spaces with separate buses. The CPU can fetch the next instruction while the current one reads or writes data, which is how most instructions complete in a single cycle. A von Neumann core sharing one bus would stall on every load.
| Memory | Size | Volatile? | Typical use |
|---|---|---|---|
| Flash | 32 KB (about 0.5 KB taken by the Optiboot bootloader on an Uno) | No | Program code, constants in PROGMEM |
| SRAM | 2 KB | Yes | Globals, stack, heap |
| EEPROM | 1 KB | No | Calibration, counters, user settings |
Key fact: Flash holds 16K words because every AVR instruction is 16 or 32 bits wide, so the program counter addresses words, not bytes.
The practical trap is SRAM. Two kilobytes disappears fast: a single char buf[512] is a quarter of it, and every Serial.println("long text") string literal is copied into SRAM at boot unless you wrap it in F() to keep it in flash. When stack and heap collide, the chip does not raise an exception. It just misbehaves, which is why "random resets" is a classic debugging question.
EEPROM is slow and wears out. A byte write takes about 3.3 ms and the datasheet guarantees roughly 100,000 cycles per cell, so logging a counter every second would exhaust one cell in about a day. Spread writes across addresses (wear levelling) or write only on change.
The register file and AVR instruction basics
There are 32 general-purpose 8-bit registers, R0 to R31, mapped at the bottom of the data space. R26-R27, R28-R29 and R30-R31 pair up as the 16-bit X, Y and Z pointers; Z also reads flash through LPM. The ISA has about 131 instructions, grouped into arithmetic, logic, branch, data transfer and bit/status operations.
; blink PB5 (Arduino pin 13) in AVR assembly
sbi DDRB, 5 ; PB5 as output (2 cycles)
loop:
sbi PINB, 5 ; writing 1 to PIN toggles the pin
ldi r24, 0xFF
delay:
dec r24 ; 1 cycle
brne delay ; 2 cycles if taken, 1 if not
rjmp loop ; 2 cycles
Here ldi loads an immediate (only R16-R31 accept it), dec and brne form a counted loop and sbi flips a single I/O bit atomically. Compare this to digitalWrite(), which takes about 50 cycles because it looks up the pin, port and timer tables. Direct port writes (PORTB |= (1<<5)) compile to a single sbi, roughly 60 ns at 16 MHz, which matters when bit-banging protocols.
Key fact: At 16 MHz one clock is 62.5 ns, so a one-cycle instruction executes in 62.5 ns and the core delivers up to 16 MIPS.
Timers: Timer0, Timer1 and Timer2
Three hardware timers count clock ticks without CPU involvement. Timer0 and Timer2 are 8-bit (0-255); Timer1 is 16-bit (0-65535). Each has a prescaler (1, 8, 64, 256, 1024), compare-match registers and PWM output modes.
Arduino's core configures Timer0 with a /64 prescaler: 16 MHz / 64 / 256 = 976.56 overflows per second, roughly 1.024 ms each. That interrupt drives millis(), so if you reconfigure Timer0 you break timekeeping and delay(). The default analogWrite() PWM is about 490 Hz on most pins (Timer1/Timer2) and 976 Hz on pins 5 and 6.
Example: for a 1 Hz tick with prescaler 256, OCR1A = 16,000,000 / (256 x 1) - 1 = 62,499. That fits in 16 bits, which is why Timer1 is the right choice here; an 8-bit timer could never count that high.
The 10-bit ADC
A successive-approximation ADC with a multiplexer reads six analog pins (A0-A5 on the Uno, eight on TQFP packages), plus an internal temperature sensor and the 1.1 V bandgap. Resolution is 10 bits, so with a 5 V reference one LSB is 5 / 1024 = 4.88 mV.
The ADC clock must sit between 50 kHz and 200 kHz for full accuracy. Arduino uses a /128 prescaler: 16 MHz / 128 = 125 kHz. A conversion takes 13 ADC clocks, about 104 microseconds, so analogRead() tops out near 9.6 kSPS. You can drop the prescaler to /16 for faster reads at lower effective resolution.
- Source impedance should be under about 10 kohm, otherwise the sample-and-hold capacitor does not charge in time.
- Measuring VCC against the 1.1 V bandgap is a known trick for battery monitoring without a divider.
- Noise: add a 100 nF capacitor on AVCC and consider ADC sleep mode for quieter readings.
Interrupts and a quick sizing check
The chip has 26 interrupt vectors in a table at the start of flash. An ISR costs at least 4 cycles to enter plus 4 to return via reti, and the compiler saves any registers it touches. Keep ISRs short and mark shared variables volatile, otherwise the optimiser may cache them in a register.
Silicon vendors use this class of simple core as a teaching baseline before moving to ARM Cortex-M, where tools such as Synopsys Design Compiler or Cadence Genus synthesize far larger designs. If you want to turn this foundation into a career, ChipXpert's VLSI and embedded training builds on exactly these fundamentals. For the next step, see AVR timers and PWM explained.
Key fact: The ATmega328P is 8-bit with 32 KB / 2 KB / 1 KB of flash / SRAM / EEPROM; compare that to even a modest Cortex-M4 with 1 MB flash and 256 KB RAM to see why memory discipline is the core embedded skill.
Ready to go beyond blinking LEDs? Explore the hands-on embedded track in our CourseTron courses and write AVR code with real registers, timers and ADCs.
FAQ
How much memory does the Arduino Uno really have for my sketch?
About 31.5 KB of flash after the bootloader, 2 KB of SRAM and 1 KB of EEPROM. SRAM is the tight resource, so keep strings in flash with F() and avoid large local arrays.
Why is analogRead() slower than digitalRead()?
The SAR ADC needs 13 ADC clocks at 125 kHz, about 104 microseconds per sample, while a digital read is a single port-register read taking a few CPU cycles.
Can I change Timer0 safely?
Not without consequences. Arduino uses Timer0 for millis(), micros() and delay(). Use Timer1 or Timer2 for your own timing and PWM instead.
Is AVR assembly still worth learning?
Yes. Reading compiler output and counting cycles teaches you how registers, the stack and interrupts really behave, which transfers to ARM and RISC-V.