A memory controller is the hardware block between the processor and the DRAM modules that manages the protocol for reading and writing memory. It hides DRAM’s complexity (refresh, row/column timing, bank scheduling, multiplexed addressing) behind a simple “read this address, write this address” interface to the rest of the chip.

In modern processors, the memory controller is integrated into the CPU package. In older designs (pre-2008 Intel, for example), it was a separate chip on the motherboard’s “northbridge.”

What it does

For every memory access from the processor:

  1. Decode the high-order address bits to determine which memory module to talk to (asserts the Chip Select signal for that module).
  2. Split the remaining address into row and column halves for DRAM’s multiplexed addressing.
  3. Send the row address with RAS (Row Address Strobe).
  4. Send the column address with CAS (Column Address Strobe).
  5. Wait for the data, transfer it to/from the processor.

For batches of accesses, more cleverness:

  • Bank parallelism — modern DRAM has multiple internal banks; the controller can have transactions in flight on multiple banks simultaneously.
  • Burst transfers — read a sequence of consecutive addresses with a single command, the data streams out on consecutive clock edges.
  • Refresh scheduling — interleave refresh cycles with normal reads/writes to maximize bandwidth.
  • Reordering — group related accesses together to maximize row-buffer hits (consecutive accesses to the same DRAM row are much faster than accesses spanning rows).

DRAM access protocol details

DRAM access has several distinct phases, each with timing constraints. The memory controller manages all of them:

Activate (ACT) phase

Before reading or writing a row, the controller must “open” it — copying the entire row from the DRAM cell array into the row buffer (a row of sense amplifiers). This takes time, called (RAS-to-CAS delay), typically 10-20 ns.

Once a row is open, accesses to columns within it are fast.

Read/write phase

With the row open, the controller can issue read or write commands to specific columns. Each takes the column-access time (CAS latency), typically 10-15 ns.

Precharge (PRE) phase

Before opening a different row in the same bank, the current row must be closed and the bit lines precharged. This takes (precharge time), typically 10-15 ns.

So accessing a new row in the same bank costs — much more than accessing within an already-open row.

Refresh

DRAM cells lose charge over time. Every row must be refreshed (read and rewritten) every ms. The controller schedules refresh commands to interleave with normal accesses, typically refreshing one row every μs.

Multi-module systems

When a system has multiple DRAM modules (multiple DIMMs on the motherboard), the memory controller decodes high-order address bits to assert the right Chip Select line:

[ chip select bits | local address ]

Each module sees only its share of the address space. The controller routes each access to the appropriate module, transparently to the processor.

For maximum bandwidth, modern memory controllers can interleave addresses across modules — successive cache lines go to different DIMMs, allowing parallel access. This requires careful BIOS configuration but doubles bandwidth in best case.

Multi-channel architecture

Modern systems have multiple memory channels — independent paths from the controller to DRAM, each with its own command/address/data wires. Common configurations:

  • Single channel: 64-bit data path, simplest, lowest bandwidth.
  • Dual channel: two 64-bit paths in parallel, doubling theoretical bandwidth.
  • Quad channel: four 64-bit paths, used in workstations and servers.
  • Eight-channel: server-class chips for memory-bandwidth-hungry workloads.

To use multi-channel, you need DIMMs installed in matched pairs (or sets) in the right slots — the BIOS and motherboard manual specify which.

Scheduling policies

The controller chooses which pending request to service next. Common policies:

  • First-come-first-served (FCFS): simple, fair, but doesn’t exploit row-buffer locality.
  • First-ready-first-come-first-served (FR-FCFS): prioritize requests that hit the open row, fall back to FCFS otherwise. Good for typical workloads.
  • Bank-aware scheduling: track which banks are busy, prefer requests to idle banks.
  • Quality-of-service (QoS) scheduling: ensure low-latency for high-priority requesters (e.g., GPU vs. background tasks).

The right policy depends on workload mix. Modern controllers often use adaptive heuristics that try several policies.

Why it lives near the CPU

Latency is the killer. Every nanosecond of round-trip time costs the processor cycles. Putting the memory controller on the CPU die:

  • Eliminates the chip-to-chip hop that an external northbridge required.
  • Lets the controller share clock and protocols with the CPU.
  • Makes integrated GPUs and other on-chip subsystems easier to feed.

Intel introduced an on-die memory controller with Nehalem (2008); AMD did so earlier with the Athlon 64 (2003). Today every consumer CPU has its memory controller on-chip.

In the broader picture

The memory controller is invisible to software but critical to performance. Workloads that fit poorly in cache thrash the memory controller, which becomes the bottleneck. Tweaks like enabling dual-channel mode, matching DIMM speeds, populating slots in the right order — all aim to maximize memory controller throughput.

The controller is also the integration point for memory protection — ensuring different processes can’t read each other’s RAM. Modern controllers support ECC (Error-Correcting Code) memory: extra bits per word allow detection and correction of single-bit errors, critical for server reliability.

For the actual cells the controller talks to, see Dynamic RAM and Memory cell. For where data goes after coming out of memory, see Cache memory. For the address translation that happens before the controller sees the request, see Translation lookaside buffer.