Von Neumann, Harvard, and Modified Harvard Architecture
Processor architecture determines how a CPU fetches instructions, reads data, writes results, and reaches memory. Before writing embedded firmware, you should understand whether instructions and data share one memory path or use separate paths.
Learning Objectives
By the end of this lesson, you should be able to:
- Explain Von Neumann, Harvard, Princeton, and modified Harvard architectures.
- Describe the fetch-decode-execute cycle.
- Explain the Von Neumann bottleneck.
- Connect architecture choices to microcontroller Flash, SRAM, stack, constants, and peripheral registers.
- Avoid common firmware mistakes caused by memory-space assumptions.
The Problem
A processor needs access to two kinds of information:
- Instructions: the program bytes that tell the CPU what to do.
- Data: variables, stack values, buffers, peripheral registers, and constants.
The architecture question is simple: do instructions and data use one shared memory path, or do they use separate paths?
Von Neumann Architecture
Von Neumann architecture stores instructions and data in the same memory space and accesses them through a shared bus. Princeton architecture is another name commonly used for the same idea.
The strength is simplicity: one address space and one memory system. The weakness is contention: instruction fetches and data accesses compete for the same path.
Fetch-Decode-Execute
Most processors repeatedly run this cycle:
In a simple Von Neumann machine, a load instruction may require one memory transaction to fetch the instruction and another memory transaction to read the data.
This shared-path limit is called the Von Neumann bottleneck. Modern CPUs reduce it with caches, pipelines, prefetching, burst transfers, and out-of-order execution, but the bottleneck still explains why memory bandwidth is often a system limit.
Harvard Architecture
Harvard architecture separates instruction memory and data memory. Each has its own bus.
The CPU can fetch an instruction while reading or writing data.
This is a natural fit for microcontrollers because program code normally lives in non-volatile Flash, while variables live in volatile SRAM.
Microcontroller Example
Important embedded consequences:
- Program code survives reset because it is stored in Flash.
- RAM variables disappear after reset or power loss.
- Stack and heap share limited SRAM.
- Peripheral registers look like addresses, but reads and writes control hardware.
- Some architectures require special instructions or compiler attributes to read constants from program memory.
AVR is a classic example where program memory and data memory are distinct enough that reading constants from Flash may require special handling such as PROGMEM and pgm_read_byte(). ARM Cortex-M devices generally present a more unified programmer's memory map, but internally still use separate buses for instruction and data paths.
Modified Harvard Architecture
Many high-performance processors use a hybrid. They may have separate instruction and data caches close to the CPU, but a unified main memory below the cache hierarchy.
This gives parallel instruction and data access near the core while keeping the larger system simpler. It also introduces cache coherency and memory ordering issues that bare-metal microcontroller developers may not see on small parts.
Side-by-Side Comparison
Von Neumann or Princeton:
- Instructions and data use unified memory.
- The bus is shared.
- The programming model is simple.
- The main weakness is shared-bus contention.
- Examples include simple CPUs and the desktop main-memory model.
Harvard:
- Instruction and data memory are separate.
- Instruction and data buses are separate.
- Parallel fetch and data access are possible.
- The memory model is more complex.
- Examples include AVR, PIC, and many DSPs.
Modified Harvard:
- Paths are separate near the CPU but unified lower in the hierarchy.
- Split caches or buses feed shared memory.
- Performance is high with practical memory cost.
- Cache coherency and ordering require care.
- Examples include ARM Cortex-A, modern x86, and many high-end MCUs.
Worked Example: Why Stack Size Matters
Assume a microcontroller has:
128 KiBFlash.16 KiBSRAM.- A
2 KiBcommunication receive buffer. - A
4 KiBdisplay frame buffer. - Global variables using
3 KiB.
Remaining SRAM before stack and heap:
$$
16 - 2 - 4 - 3 = 7 \text{ KiB}
$$
If an interrupt handler and a nested function chain require more than this, the stack can collide with heap or global data. On many bare-metal systems there is no operating system to stop the damage. The firmware may simply behave unpredictably.
Memory Map Awareness
Embedded C often hides architecture details, but the compiled program still has sections:
| Section | Typical Location | Contains |
|---|---|---|
.text |
Flash | Instructions |
.rodata |
Flash | Constants and lookup tables |
.data |
Flash image copied to SRAM at startup | Initialized writable globals |
.bss |
SRAM, zeroed at startup | Uninitialized globals |
| Stack | SRAM | Function calls and local variables |
| Heap | SRAM | Dynamic allocation, if used |
The startup code initializes these regions before main() runs. That is why embedded startup files, linker scripts, and memory maps matter.
Common Mistakes
- Assuming
constalways costs no RAM on every compiler and architecture. - Allocating large local arrays on a tiny SRAM stack.
- Forgetting that DMA may need buffers in a memory region visible to the peripheral.
- Treating peripheral registers like normal variables; some bits clear on read or trigger actions on write.
- Ignoring instruction/data cache maintenance when using DMA on cached processors.
- Calling a system "Harvard" or "Von Neumann" without checking the actual programmer-visible memory model.
Practice
- Explain why a shared bus can slow a load-heavy program.
- Identify where firmware, stack, globals, and peripheral registers usually live in a microcontroller.
- A system has separate L1 instruction and data caches but shared DRAM. Classify it.
- Why can a power cycle erase a variable but not the firmware image?
- List two bugs that can happen when stack usage is not measured.
Summary
Von Neumann architecture uses one memory path for instructions and data. Harvard architecture separates instruction and data paths, which improves parallel access and maps naturally to microcontrollers with Flash and SRAM. Modified Harvard systems combine separate near-core paths with unified lower memory. For embedded developers, these ideas explain program memory, RAM limits, startup code, stack risk, constants, peripherals, and cache-related bugs.
Further Reading
- John L. Hennessy and David A. Patterson, Computer Architecture: A Quantitative Approach.
- ARM Cortex-M architecture reference manuals for memory map, bus, and exception behavior.
- Atmel/Microchip AVR documentation for program memory and data memory examples.
- Joseph Yiu, The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors.
- Linker script and startup-code chapters in embedded toolchain manuals for GCC and LLVM.