PCIe Series — PCIe-20: Base Address Registers (BARs) — VLSI Trainers
PCIe Series · PCIe-20

Base Address Registers (BARs)

How PCIe devices request address space from software — the full BAR bit encoding, Memory vs I/O BARs, 32-bit vs 64-bit addressing, prefetchable vs non-prefetchable, the three-step sizing algorithm, the Expansion ROM BAR, 64-bit BAR pairing, unimplemented BARs, and Gen 6 implications.

📋 Why BARs Exist

A PCIe device has internal registers and storage — control registers, status registers, device memory, DMA ring buffers. Software needs to be able to read and write these locations over the PCIe fabric. To do that, each location must have an address in one of the three PCIe address spaces: Memory, I/O, or Configuration.

Configuration space is used for standardised hardware identification and capability registers. That leaves Memory and I/O space for device-specific registers. But the device itself cannot choose which addresses to use — that would create conflicts when multiple devices compete for overlapping address ranges. Address assignment is exclusively the job of system software (BIOS during POST, then the OS during enumeration).

The Base Address Registers solve this negotiation problem. The device designer hardcodes the lower bits of each BAR to communicate what kind and how much address space the device needs. Software reads these hardcoded bits, allocates a suitable range from available address space, and programs the upper bits with the base address of that allocation. From then on, any TLP whose address falls within a programmed BAR range is claimed and processed by the device.

📋 Where BARs Live

BAR Locations in Type 0 and Type 1 Headers Type 0 Header (Endpoint) 6 BARs at offsets 10h, 14h, 18h, 1Ch, 20h, 24h Each 32 bits wide · 64-bit BAR uses two consecutive slots Max 6 independent 32-bit regions, OR 3 independent 64-bit regions, OR any mix of the two Type 1 Header (Bridge / Switch Port) 2 BARs at offsets 10h and 14h only Same encoding rules as Type 0 BARs Used for the bridge’s own management registers NOT used for downstream device address windows
Figure 1 — BAR locations. Type 0 (endpoint) has six 32-bit BAR slots. Type 1 (bridge) has only two. A 64-bit BAR consumes two consecutive slots — so a device using three 64-bit BARs uses all six Type 0 slots and has no room for additional 32-bit BARs.

Each BAR slot is always 32 bits wide in the hardware register. A 64-bit address requirement uses two adjacent slots: the lower BAR holds bits [31:0] of the address and the upper BAR holds bits [63:32]. Software reads both, writes both, and treats them as a single logical 64-bit register.

📋 Bit 0 — Memory vs I/O

The single most important bit in any BAR is bit 0 — it determines whether the BAR requests memory address space or I/O address space. This bit is hardcoded by the device designer and cannot be changed by software.

BAR Bit 0 — The Single Bit That Determines Everything 0 Memory BAR Requests MMIO (memory-mapped I/O) address space Bits [2:1] and bit 3 carry additional meaning All modern PCIe-native devices use Memory BARs 1 I/O BAR Requests legacy I/O port address space Bits [2:1] reserved. Bit 3 not used Discouraged in PCIe — legacy compatibility only
Figure 2 — BAR bit 0 determines the address space type. When bit 0 = 0, the BAR requests MMIO space and bits [2:1] and bit 3 carry further encoding. When bit 0 = 1, the BAR requests I/O port space and the remaining lower bits have no meaning for the memory type.

📋 Memory BAR Bit Fields

A Memory BAR (bit 0 = 0) uses bits [3:0] to fully encode its type. The upper bits [31:4] or higher are the writable base address field.

Memory BAR — Full 32-bit Layout Base Address [31:N] Writable by software · programmed with the allocated base address N = lowest writable bit = determines size (2^N bytes) Bit 3 Prefetch 0=NP·1=P Bits [2:1] Type field 00 or 10 0 Mem bits [31:4] or higher (depends on size) bit 3 bits [2:1] bit 0 Type Field [2:1] Encodings 00b = 32-bit decode address must be below 4 GB 01b = Reserved (legacy) do not use 10b = 64-bit decode uses this BAR + next BAR as pair
Figure 3 — Memory BAR full layout. Bit 0 = 0 identifies a memory BAR. Bits [2:1] are the Type field selecting 32-bit or 64-bit decoding. Bit 3 is the Prefetchable flag. All remaining upper bits are the writable Base Address field — their count is determined by how many lower bits the device designer hardwires to 0 to specify the required size alignment.

📋 Type Field — 32-bit vs 64-bit

The Type field at bits [2:1] of a Memory BAR tells software whether the device can be placed anywhere in the 32-bit address space or whether it requires (or supports) placement anywhere in the full 64-bit address space.

32-bit vs 64-bit BAR — Address Space Placement 32-bit BAR (Type = 00b) Bits [2:1] = 00b hardcoded by designer Allocated base address must fit in 32 bits (below 4 GB) Software uses one BAR slot for this register Device cannot DMA to addresses above 4 GB unless it uses a separate 64-bit DMA address in its driver Typical use: control registers on older devices Always 3DW TLP header when accessed 64-bit BAR (Type = 10b) Bits [2:1] = 10b hardcoded by designer Base address may be placed anywhere in 64-bit space Consumes two consecutive BAR slots (e.g. BAR0 + BAR1) Lower BAR: bits [31:0] of address Upper BAR: bits [63:32] of address Use: GPU framebuffer, AI accelerator memory, NVMe queues 4DW TLP header required if address is above 4 GB
Figure 4 — 32-bit vs 64-bit BAR placement. A 32-bit BAR must be placed below 4 GB — software may not assign a base address with bits [63:32] set. A 64-bit BAR can be placed anywhere — software may assign any 64-bit address. The 64-bit BAR uses two consecutive BAR slots, so a device with one 64-bit BAR consumes two of the six available Type 0 slots.
Why 64-bit BARs matter for modern devices. Systems with large DRAM (256 GB+ servers) have memory maps that place many MMIO regions above the 4 GB boundary. An AI accelerator with 80 GB of HBM must declare a 64-bit BAR — a 32-bit BAR cannot hold an address above 4 GB. At Gen 6 bandwidth, devices also tend to have large MMIO windows (tens of gigabytes) that require 64-bit placement regardless of system RAM size.

📋 Prefetchable Bit — What It Really Means

Bit 3 of a Memory BAR declares whether the region is prefetchable. This is a promise from the device designer to software about how the device behaves when its memory region is read:

Prefetchable vs Non-Prefetchable — Two Promises to Software Non-Prefetchable (Bit 3 = 0) Reads may have side effects at the device Example: status register that clears on read Example: hardware FIFO — reading removes an entry CPU/bridge must NOT speculatively prefetch this region Write merging is NOT permitted Typical: control registers, status registers, command queues Prefetchable (Bit 3 = 1) Reads have NO side effects — data stays unchanged CPU/bridge MAY speculatively read ahead (cache line prefetch) Write merging IS permitted (combine small writes into one) If prefetched data is discarded, reading again gets same result Can be placed in WC (Write Combining) CPU memory type Typical: GPU framebuffer, device memory, DMA ring buffers
Figure 5 — Prefetchable vs non-prefetchable. The bit 3 declaration controls how CPU caches and PCIe bridges may handle the memory region. Non-prefetchable (0) means every read is an actual device access — no caching, no speculation. Prefetchable (1) means the CPU and bridges may cache, prefetch, and merge writes freely without unexpected side effects.

Practical impact on CPU MTRR / PAT settings

On x86 systems, the CPU’s MTRR (Memory Type Range Registers) or PAT (Page Attribute Table) must be configured to match the BAR’s prefetchable declaration:

Setting a non-prefetchable BAR region as WC is a software bug — speculative reads to a status register would change device state silently, causing unpredictable behaviour. The BAR’s bit 3 is the hardware contract that drives this CPU configuration decision.

📋 I/O BAR Bit Fields

When BAR bit 0 = 1, the BAR requests I/O port address space. The format is simpler than a Memory BAR — there is no type field and no prefetchable bit.

I/O BAR Layout — 32-bit Register I/O Base Address [31:2] Writable by software — programmed with allocated I/O port base address Lowest set writable bit determines size: 4-byte minimum (bit 2) Bit 1 Reserved = 0 1 I/O PCIe discourages I/O space — only supported for PCI backward compatibility. All modern native PCIe devices use Memory BARs exclusively.
Figure 6 — I/O BAR layout. Bit 0 = 1 identifies an I/O BAR. Bit 1 is reserved (must be 0). Bits [31:2] are the writable I/O base address. The minimum I/O allocation is 4 bytes (bit 2 is the lowest possible writable address bit). In practice, I/O allocations are always 4 KB aligned due to I/O window granularity in bridges.

📋 The Sizing Algorithm

Software cannot read the size of a BAR directly. The size is encoded by how many lower address bits the device designer has hardcoded to 0. To discover the size, software uses a three-step write-and-read procedure:

BAR Sizing Algorithm — Three Steps ① Save then Write All 1s 1. Save current BAR value 2. Write 0xFFFF_FFFF to BAR 3. Hardcoded lower bits unchanged 4. Writable upper bits all become 1 1111 1111 … 0000 0000 0 Where to write: BAR offset in config space (10h, 14h, etc.) ② Read Back and Interpret 1. Read the BAR value back 2. Mask off lower attribute bits 3. Find lowest bit that is 1 4. Size = 2^(position of that bit) 1111 1111 1111 0000 0 Lowest 1-bit at position 12 → Size = 2¹² = 4 KB ③ Write Base Address 1. OS allocates 4 KB region e.g. at F900_0000h 2. Write base address to BAR 3. Device now claims TLPs to F900_0000–F900_0FFF F900_0000h programmed 4. Enable Memory Space bit in Command register
Figure 7 — BAR sizing algorithm. Step ①: save current value, then write 0xFFFFFFFF. Step ②: read back, mask type bits, find lowest 1-bit → that position is log₂(size). Step ③: allocate a region of that size, write the base address, enable the BAR. Between steps ① and ③ software must also clear Memory Space Enable in the Command register to prevent accidental device claims during sizing.

How software computes size from the read-back value

After writing all-1s and reading back the BAR, software:

  1. Saves the raw value.
  2. Clears the type bits: for a Memory BAR, mask out bits [3:0] (set them to 0). For an I/O BAR, mask out bits [1:0].
  3. Bitwise-negate the result: flip all bits. The result is (size − 1) in the lower bits.
  4. Add 1 to get the size.

Equivalently: size = ~(readback & ~0xF) + 1 for Memory BARs. This formula works for any BAR size from 16 bytes upward.

Example: 32-bit Non-Prefetchable Memory BAR

A device needs 4 KB of non-prefetchable MMIO for its control registers. The device designer hardcodes BAR0 as follows:

StateBAR0 valueMeaning
Uninitialized XXXX_X000h Upper bits unknown (X). Lower 12 bits hardwired to 0 (size marker). Bit 3=0 (NP). Bits [2:1]=00b (32-bit). Bit 0=0 (memory).
After write 0xFFFFFFFF FFFF_F000h Upper 20 bits all 1s (writable). Lower 12 bits still 0 (hardcoded). Lowest 1-bit at position 12 → size = 2¹² = 4 KB.
After programming base F900_0000h Software allocated 4 KB at F900_0000h. Device claims addresses F900_0000h through F900_0FFFh. TLPs to this range are accepted.

Example: 64-bit Prefetchable BAR Pair

A GPU needs 64 MB of prefetchable MMIO for its framebuffer. The device designer uses BAR1+BAR2 as a 64-bit pair:

64-bit BAR Pair — BAR1 (lower) + BAR2 (upper) After write all-1s to both BARs and read back: BAR1 (lower): 1111 11[00 0000 … 0000] 1 10 0 Lowest 1-bit: pos 26 Size = 2²⁶ = 64 MB bits [31:26] writable · bits [25:4] = 0 bit 3=1 (pref) · [2:1]=10b (64-bit) · bit 0=0 BAR2 (upper): 1111 1111 1111 1111 1111 1111 1111 1111 All 32 bits writable · holds address bits [63:32] After programming base address 0x0000_0002_4000_0000h (above 4 GB): BAR1 = 4000_000Ch (low 32 bits · pref · 64-bit type · memory) BAR2 = 0000_0002h (high 32 bits = 2 → full address = 0x2_4000_0000h)
Figure 8 — 64-bit BAR pair. BAR1 holds the lower 32 bits and the type encoding. BAR2 holds the upper 32 bits with no special encoding. After sizing, software programs both: BAR1 gets the lower 32 bits of the base address (preserving type bits), BAR2 gets the upper 32 bits. This places the 64 MB region at address 0x0000_0002_4000_0000h — above the 4 GB boundary.

Example: I/O BAR

A legacy NIC requests 256 bytes of I/O port space on BAR3. The device designer hardcodes:

StateBAR3 valueMeaning
After write 0xFFFFFFFF FFFF_FF01h Lowest 1-bit in address field is at position 8. Size = 2⁸ = 256 bytes of I/O space. Bit 0 = 1 confirms I/O BAR.
After programming base 4000h 0000_4001h I/O base at 4000h. Device claims I/O transactions to ports 4000h–40FFh. Software must set I/O Space Enable (Command register bit 0).

📋 64-bit BAR Pairing Rules

When a device declares a 64-bit BAR (Type field bits [2:1] = 10b), software must follow specific rules to read it correctly:

  1. The next sequential BAR slot is automatically the upper half. If BAR0 is a 64-bit BAR, then BAR1 is the upper 32 bits. Software must not evaluate BAR1’s type bits — they have no type meaning when BAR1 is the upper half of a 64-bit BAR.
  2. Software skips the upper BAR in its iteration. After reading and evaluating BAR0 as a 64-bit BAR, software jumps its index to BAR2 for the next independent BAR check.
  3. The upper BAR is written with the high 32 bits of the base address. If the allocation is below 4 GB, BAR+1 is written as 0x00000000.
  4. A 64-bit BAR must always start on an even BAR index. BAR0, BAR2, BAR4 may be 64-bit lower halves. BAR1, BAR3, BAR5 may be the upper halves but never the start of a new 64-bit pair.
  5. If BAR5 is a 64-bit lower half, there is no BAR6 in a Type 0 header. This is an illegal configuration — the device designer must not declare BAR5 as 64-bit in a Type 0 header because the required upper half BAR6 does not exist.
Valid 64-bit BAR Pair Positions in a Type 0 Header BAR0 64-bit lower BAR1 64-bit upper → BAR2 64-bit lower BAR3 64-bit upper → BAR4 64-bit lower BAR5 64-bit upper Rule: A 64-bit BAR can start at BAR0, BAR2, or BAR4 only. Starting at BAR1, BAR3, or BAR5 is illegal. All three pairs shown: BAR0+BAR1, BAR2+BAR3, BAR4+BAR5. A device cannot have more than three 64-bit BARs in a Type 0 header.
Figure 9 — Valid 64-bit BAR pair positions. Three possible pairs exist: BAR0+BAR1, BAR2+BAR3, BAR4+BAR5. A 64-bit BAR must always start on an even slot. The upper half (odd slot) must not be declared as a 64-bit lower half — it has no type meaning of its own.

📋 Unimplemented BARs

If a device does not need all six BAR slots, the unused ones are hardwired to all zeros. When software writes 0xFFFFFFFF to an unimplemented BAR and reads it back, the result is 0x00000000 — all bits return 0 regardless of what was written. Software uses this to detect unimplemented BARs and skip them during allocation.

ScenarioWrite 0xFFFF_FFFF, read back resultSoftware action
Memory BAR requesting 4 KBFFFF_F000hAllocate 4 KB, write base address, skip to next BAR
64-bit BAR lower (upper half follows)FFC0_000ChAllocate per size, write lower base, then write upper base to BAR+1, skip BAR+1 in loop
Unimplemented BAR (hardwired 0)0000_0000hNo allocation. Move to next BAR index.
I/O BAR requesting 256 bytesFFFF_FF01hAllocate 256 bytes of I/O space, write base address, skip to next BAR
Always save and restore the BAR during sizing. A real-world complication: the sizing procedure writes 0xFFFFFFFF to the BAR temporarily. If an interrupt fires or another access occurs between the write and the read-back, the device might claim unrelated addresses. The correct procedure is to save the original BAR value before writing 0xFFFFFFFF, complete the read-back, then restore the saved value (or program the new base address) before re-enabling address decoding.

📋 Expansion ROM BAR

The Expansion ROM BAR at offset 30h in Type 0 headers and offset 38h in Type 1 headers is a special BAR used exclusively for the device’s option ROM — firmware executed at POST time to initialise the hardware before the OS loads. Its format is similar to a 32-bit memory BAR but with a different lower-bit encoding.

Expansion ROM BAR — 32-bit Layout ROM Base Address [31:11] Writable by software — programmed with the ROM base address Bits [10:1] hardwired 0 → minimum ROM size = 2 KB (2¹¹ bytes) Bits [10:1] Hardwired 0 Size marker En bit 0 Bit 0 = Enable: must be set to 1 before the ROM is decoded. Writing 0 disables address decoding even when a base address is programmed. To detect ROM presence: write 0xFFFF_FFFF, read back, check bits [31:11]. All zero → no ROM.
Figure 10 — Expansion ROM BAR. Bit 0 is the Enable flag — software must set this to 1 after programming the base address for the ROM to be decoded. Bits [10:1] are hardwired to 0, making the minimum ROM alignment 2 KB. The sizing procedure is identical to a regular BAR: write 0xFFFFFFFF, read back, find lowest 1-bit in bits [31:11] for size.

ROM BAR sizing and programming

The sizing procedure is the same as for regular BARs, but the mask applied is 0xFFFFF800 (instead of 0xFFFFFFF0) to isolate the writable address bits. After sizing:

  1. Software allocates a ROM-sized region at an appropriate 32-bit address.
  2. Programs the base address into bits [31:11] with bit 0 = 0 (disabled).
  3. Sets bit 0 = 1 to enable decoding — ROM is now accessible for reading.
  4. BIOS reads and executes the ROM at POST. After POST, bit 0 may be cleared to free the address space if the ROM is no longer needed.
ROM BAR is always 32-bit. There is no 64-bit variant of the Expansion ROM BAR. The ROM must be placed below the 4 GB boundary. This is acceptable because option ROMs execute at POST time in a 16-bit x86 real-mode environment that cannot access addresses above 4 GB.

📋 Enabling and Disabling BARs

BARs do not activate just by having a base address programmed into them. The device only decodes (claims) TLPs targeting a BAR’s address range when the appropriate enable bit in the Command register is set. This separation of programming from activation is intentional — it allows software to safely size and program all BARs before enabling any of them.

BAR typeEnable bit in Command registerTypical software sequence
Memory BAR (bit 0 = 0) Bit 1 — Memory Space Enable Clear bit 1. Size all memory BARs. Allocate ranges. Program base addresses. Set bit 1.
I/O BAR (bit 0 = 1) Bit 0 — I/O Space Enable Clear bit 0. Size all I/O BARs. Allocate I/O ranges. Program base addresses. Set bit 0.
Expansion ROM BAR Bit 1 — Memory Space Enable AND ROM BAR bit 0 Both Memory Space Enable and ROM BAR bit 0 must be set for the ROM to respond.

During BAR sizing, software must temporarily clear Memory Space Enable (and I/O Space Enable for I/O BARs) in the Command register. This prevents the device from accidentally claiming TLPs that happen to hit the all-1s value written to the BAR during sizing. After programming all base addresses, software re-enables the relevant bits.

📋 Real-Device BAR Layouts

Different device classes have characteristic BAR layouts that reflect their MMIO needs:

Device classTypical BAR layoutReason
NVMe SSD BAR0+BAR1: one 64-bit prefetchable BAR, 16 KB–256 KB Single MMIO window for NVMe controller registers, submission/completion queue doorbells
Intel Ethernet NIC (i210) BAR0+BAR1: 64-bit NP MMIO (512 KB) · BAR3: 32-bit I/O (32 bytes) Main register space in 64-bit BAR. Legacy I/O BAR for old driver compatibility.
NVIDIA GPU (modern) BAR0: 32-bit NP MMIO (16 MB) for MMIO regs · BAR1+BAR2: 64-bit prefetchable (GB range for framebuffer) · BAR3+BAR4: 64-bit prefetchable (smaller) Control registers in 32-bit NP BAR. Large framebuffer and device memory require 64-bit prefetchable placement.
USB XHCI controller BAR0+BAR1: one 64-bit non-prefetchable BAR, typically 64 KB Host controller registers accessed with strong ordering (non-prefetchable). Single 64-bit BAR for placement flexibility.
Intel WiFi (AX210) BAR0+BAR1: one 64-bit NP MMIO (16 KB) Minimal register footprint. Non-prefetchable because device registers have side effects.
AI Accelerator (A100/H100) BAR0: 32-bit NP MMIO (32 MB) · BAR1+BAR2: 64-bit prefetchable (80 GB) · BAR3+BAR4: 64-bit prefetchable (small) Massive prefetchable BAR for HBM device memory. Requires 64-bit placement and PCIe 4.0/5.0 Resizable BAR.
Resizable BAR (ReBAR). Modern GPUs and AI accelerators need BARs in the tens of gigabytes, but the default BAR size (before ReBAR negotiation) may be only 256 MB for compatibility. The PCIe Resizable BAR Extended Capability (Extended Cap ID 0015h) allows a device to advertise multiple possible BAR sizes and lets software select the largest that fits in available address space. This is how a GPU with 80 GB of device memory exposes all of it through a single BAR in modern systems.

BARs in Gen 6

The BAR format — bit encoding, sizing algorithm, 64-bit pairing rules, ROM BAR structure, Command register enable bits — is completely unchanged in Gen 6. BARs are a Configuration Space feature; Gen 6 changes only the Physical Layer.

What changes with Gen 6 in practice:

📋 Quick Reference

ItemValue / Rule
BAR count per deviceType 0 (endpoint): up to 6 × 32-bit slots. Type 1 (bridge): up to 2 × 32-bit slots. A 64-bit BAR uses 2 slots.
Bit 0 = 0Memory BAR — bits [2:1] and bit 3 carry further meaning
Bit 0 = 1I/O BAR — bit 1 reserved, no type or prefetchable fields
Bits [2:1] = 00b32-bit memory decode — base address must be below 4 GB. One BAR slot.
Bits [2:1] = 10b64-bit memory decode — base address may be anywhere in 64-bit space. Two consecutive BAR slots (lower + upper).
Bit 3 = 0Non-prefetchable — reads may have side effects. CPU must map as UC/UCMINUS. No speculative reads, no write merging.
Bit 3 = 1Prefetchable — reads have no side effects. CPU may map as WC. Speculative prefetch and write merging permitted.
Sizing step ①Clear Memory/IO Space Enable. Save current BAR value. Write 0xFFFFFFFF to BAR.
Sizing step ②Read BAR back. Mask attribute bits [3:0] (or [1:0] for I/O). Find lowest 1-bit position N. Size = 2^N bytes.
Sizing step ③Allocate 2^N bytes at an aligned address. Write base address to BAR. Re-enable Space Enable bits.
Unimplemented BARReads all-zeros after write 0xFFFFFFFF. Software skips allocation.
64-bit pair ruleLower BAR always at even index (BAR0, BAR2, BAR4). Upper BAR is the next slot. Software must not evaluate upper BAR’s type bits.
ROM BAR — offsetType 0: offset 30h. Type 1: offset 38h. Always 32-bit. Minimum 2 KB alignment.
ROM BAR bit 0Enable: must be 1 for ROM to be decoded. Memory Space Enable must also be set. Bit 0 = 0 disables ROM even when base is programmed.
Memory Space EnableCommand register bit 1. Must be set before device responds to Memory BAR address range TLPs.
I/O Space EnableCommand register bit 0. Must be set before device responds to I/O BAR address range TLPs.
Prefetchable and MTRRNP BAR → UC memory type. P BAR → WC or WB memory type (CPU-configurable). Mismatch causes bugs.
Resizable BAR (ReBAR)Extended Cap 0015h. Device advertises multiple possible BAR sizes. Software selects largest that fits. Essential for Gen 6 AI accelerators.
Gen 6 impactBAR format, sizing algorithm, pairing rules — all unchanged. BAR sizes grow. ReBAR essential. 64-bit prefetchable BARs required for large device memories.
Scroll to Top