5. PCIe for DV Engineers - Configuration Space & BARs
Part 5 of the PCIe for DV Engineers series | Part 4: Transaction Layer
Configuration Space is the foundation of PCIe device management. Every PCIe function exposes a standardized set of registers that software uses for discovery, enumeration, and resource allocation. For DV engineers, understanding Configuration Space is essential because BAR programming, capability enumeration, and register access patterns are among the most verification-intensive areas of any PCIe design.
Where Configuration Fits
Configuration Space is accessed through the Transaction Layer:
Configuration Read/Write TLPs (Type 0 and Type 1) provide access to this standardized register space.
1. Configuration Space Overview
Every PCIe function has its own Configuration Space containing:
- Device identification: Vendor ID, Device ID, Class Code
- Resource allocation: Base Address Registers (BARs)
- Control/Status: Command, Status, and control registers
- Capabilities: Extended functionality (MSI, Power Management, PCIe, etc.)
Configuration Space Layout
| Offset Range | Size | Description |
|---|---|---|
| 0x00 - 0x3F | 64 bytes | PCI-Compatible Header |
| 0x40 - 0xFF | 192 bytes | PCI Capabilities |
| 0x100 - 0xFFF | 3840 bytes | PCIe Extended Configuration Space |
Access Methods
Configuration Space can be accessed through:
- Type 0 Configuration TLPs: Direct access to target function
- Type 1 Configuration TLPs: Routed through bridges to downstream devices
- ECAM (Enhanced Configuration Access Mechanism): Memory-mapped access to full 4KB space
flowchart LR
subgraph CPU["Software"]
SW[Driver/BIOS]
end
subgraph ACCESS["Access Methods"]
IO["I/O Ports
CF8/CFC"]
ECAM["ECAM
Memory Mapped"]
end
subgraph RC["Root Complex"]
CFG_GEN[Config TLP Generator]
end
subgraph EP["Endpoint"]
CFG_SPACE[Configuration Space]
end
SW --> IO --> CFG_GEN
SW --> ECAM --> CFG_GEN
CFG_GEN -->|"CfgRd0/CfgWr0"| CFG_SPACE
DV Insight: ECAM maps Configuration Space to a memory region where each function gets 4KB. Address = ECAM_BASE + (Bus << 20) + (Device << 15) + (Function << 12) + Offset.
2. Configuration Header Types
The Header Type field (offset 0x0E) determines the layout:
| Header Type | Value | Device Type |
|---|---|---|
| Type 0 | 0x00 | Endpoints, Legacy Endpoints |
| Type 1 | 0x01 | PCI-to-PCI Bridges, Switches |
| Multi-function | Bit 7 set | Device has multiple functions |
Type 0 Header Layout (Endpoints)
block-beta
columns 4
block:row0:4
columns 4
A0["Vendor ID
0x00-0x01"] B0["Device ID
0x02-0x03"]
end
block:row1:4
columns 4
A1["Command
0x04-0x05"] B1["Status
0x06-0x07"]
end
block:row2:4
columns 4
A2["Revision ID
0x08"] B2["Class Code
0x09-0x0B"]
end
block:row3:4
columns 4
A3["Cache Line
0x0C"] B3["Latency
0x0D"] C3["Header Type
0x0E"] D3["BIST
0x0F"]
end
block:row4:4
columns 1
BAR0["BAR0 - 0x10-0x13"]
end
block:row5:4
columns 1
BAR1["BAR1 - 0x14-0x17"]
end
block:row6:4
columns 1
BAR2["BAR2 - 0x18-0x1B"]
end
block:row7:4
columns 1
BAR3["BAR3 - 0x1C-0x1F"]
end
block:row8:4
columns 1
BAR4["BAR4 - 0x20-0x23"]
end
block:row9:4
columns 1
BAR5["BAR5 - 0x24-0x27"]
end
block:row10:4
columns 1
CIS["CardBus CIS Pointer - 0x28-0x2B"]
end
block:row11:4
columns 4
A11["Subsystem Vendor ID
0x2C-0x2D"] B11["Subsystem ID
0x2E-0x2F"]
end
block:row12:4
columns 1
ROM["Expansion ROM Base - 0x30-0x33"]
end
block:row13:4
columns 4
A13["Cap Ptr
0x34"] B13["Reserved
0x35-0x37"]
end
block:row14:4
columns 4
A14["Reserved - 0x38-0x3B"]
end
block:row15:4
columns 4
A15["Int Line
0x3C"] B15["Int Pin
0x3D"] C15["Min Gnt
0x3E"] D15["Max Lat
0x3F"]
end
Type 1 Header Layout (Bridges)
Type 1 headers have the same first 16 bytes but replace BARs 2-5 with bridge-specific registers:
| Offset | Type 0 (Endpoint) | Type 1 (Bridge) |
|---|---|---|
| 0x10-0x17 | BAR0, BAR1 | BAR0, BAR1 |
| 0x18 | BAR2 | Primary Bus Number |
| 0x19 | BAR2 | Secondary Bus Number |
| 0x1A | BAR2 | Subordinate Bus Number |
| 0x1B | BAR2 | Secondary Latency Timer |
| 0x1C-0x1D | BAR3 | I/O Base, I/O Limit |
| 0x1E-0x1F | BAR3 | Secondary Status |
| 0x20-0x23 | BAR4 | Memory Base, Memory Limit |
| 0x24-0x27 | BAR5 | Prefetch Memory Base/Limit |
DV Insight: Bridges use the Bus Number registers for routing. Verify that Type 1 config accesses are correctly converted to Type 0 when the target bus matches the secondary bus number.
3. Standard Header Fields
Identification Registers
| Offset | Register | Size | Access | Description |
|---|---|---|---|---|
| 0x00 | Vendor ID | 16-bit | RO | Manufacturer (0xFFFF = no device) |
| 0x02 | Device ID | 16-bit | RO | Device model number |
| 0x08 | Revision ID | 8-bit | RO | Silicon revision |
| 0x09 | Class Code | 24-bit | RO | Device type classification |
| 0x2C | Subsystem Vendor ID | 16-bit | RO | Card/board manufacturer |
| 0x2E | Subsystem ID | 16-bit | RO | Card/board model |
Class Code Structure
Class Code (24 bits):
[23:16] Base Class - Major category
[15:8] Sub Class - Sub-category
[7:0] Prog IF - Programming interface
Examples:
0x020000 - Ethernet Controller
0x010802 - NVMe Controller
0x030000 - VGA Controller
0x060400 - PCI-to-PCI Bridge
0x0C0330 - xHCI USB Controller
Command Register (0x04)
| Bit | Name | Description |
|---|---|---|
| 0 | I/O Space Enable | Respond to I/O transactions |
| 1 | Memory Space Enable | Respond to Memory transactions |
| 2 | Bus Master Enable | Can generate Memory/IO requests |
| 6 | Parity Error Response | Enable parity error reporting |
| 8 | SERR# Enable | Enable system error reporting |
| 10 | Interrupt Disable | Disable INTx interrupts |
Status Register (0x06)
| Bit | Name | Access | Description |
|---|---|---|---|
| 3 | Interrupt Status | RO | INTx asserted |
| 4 | Capabilities List | RO | Capabilities present (always 1 for PCIe) |
| 5 | 66MHz Capable | RO | Legacy PCI field |
| 8 | Master Data Parity Error | RW1C | Parity error as bus master |
| 11 | Signaled Target Abort | RW1C | Device signaled Target Abort |
| 12 | Received Target Abort | RW1C | Received Target Abort |
| 13 | Received Master Abort | RW1C | Received Unsupported Request |
| 14 | Signaled System Error | RW1C | SERR# asserted |
| 15 | Detected Parity Error | RW1C | Parity error detected |
DV Insight: Status register bits [15:8] are RW1C (Read, Write-1-to-Clear). Verify that writing 1 clears these bits, while writing 0 has no effect.
4. Base Address Registers (BARs)
BARs define the address ranges a device responds to. This is a critical verification area.
BAR Types
| Bit 0 | Type | Description |
|---|---|---|
| 0 | Memory BAR | Memory-mapped I/O region |
| 1 | I/O BAR | I/O port region (legacy) |
Memory BAR Format
Memory BAR (Bit 0 = 0):
[0] 0 = Memory Space
[2:1] Type:
00 = 32-bit BAR
10 = 64-bit BAR (consumes next BAR too)
[3] Prefetchable:
0 = Non-prefetchable (has side effects)
1 = Prefetchable (no side effects)
[31:4] Base Address (32-bit) or lower 32 bits (64-bit)
64-bit BAR:
BAR[n] [31:4] = Address[31:4], [3:0] = type bits
BAR[n+1] [31:0] = Address[63:32]
I/O BAR Format
I/O BAR (Bit 0 = 1):
[0] 1 = I/O Space
[1] Reserved
[31:2] Base Address
Memory BAR Type Encoding
| Bits [2:1] | Type | Address Range |
|---|---|---|
| 00 | 32-bit | Anywhere in 32-bit space |
| 01 | Reserved | Do not use |
| 10 | 64-bit | Anywhere in 64-bit space |
| 11 | Reserved | Do not use |
flowchart TB
subgraph BAR_CHECK["BAR Decode Logic"]
READ[Read BAR] --> CHK_B0{Bit 0?}
CHK_B0 -->|0| MEM[Memory BAR]
CHK_B0 -->|1| IO[I/O BAR]
MEM --> CHK_TYPE{Bits 2:1?}
CHK_TYPE -->|00| B32[32-bit Memory]
CHK_TYPE -->|10| B64[64-bit Memory]
B64 --> NEXT[Uses next BAR for upper 32 bits]
end
DV Insight: When a device claims a 64-bit BAR, the next BAR register is consumed. Software must not program BAR[n+1] independently. Verify your DUT correctly implements this pairing.
5. BAR Sizing Algorithm
The BAR sizing algorithm determines how much address space a device requires:
Sizing Steps
- Save original BAR value
- Write all 1s (0xFFFFFFFF) to BAR
- Read back BAR value
- Mask off type bits, invert, add 1 = size
- Restore original value or program new base address
Sizing Examples
Example 1: 4KB Memory BAR (32-bit, non-prefetchable)
Write: 0xFFFFFFFF
Read: 0xFFFFF000 (bits [11:0] = type + hardwired 0s)
Mask: 0xFFFFF000 & 0xFFFFFFF0 = 0xFFFFF000
Size: ~0xFFFFF000 + 1 = 0x1000 = 4KB
Example 2: 1MB Memory BAR (64-bit, prefetchable)
BAR[n]: Write 0xFFFFFFFF, Read 0xFFF0000C
BAR[n+1]: Write 0xFFFFFFFF, Read 0xFFFFFFFF
Combined: 0xFFFFFFFF_FFF00000
Size: 0x00000000_00100000 = 1MB
Example 3: 256-byte I/O BAR
Write: 0xFFFFFFFF
Read: 0xFFFFFF01 (bit 0 = 1 for I/O)
Mask: 0xFFFFFF00 & 0xFFFFFFFC = 0xFFFFFF00
Size: ~0xFFFFFF00 + 1 = 0x100 = 256 bytes
BAR Sizing Verification
// BAR sizing verification task
task automatic verify_bar_sizing(
input int bar_num,
input bit [63:0] expected_size,
input bit is_64bit,
input bit is_prefetch,
input bit is_io
);
bit [31:0] bar_lo_orig, bar_lo_read;
bit [31:0] bar_hi_orig, bar_hi_read;
bit [63:0] mask, size;
int bar_offset = 'h10 + (bar_num * 4);
// Save original values
cfg_read(bar_offset, bar_lo_orig);
if (is_64bit)
cfg_read(bar_offset + 4, bar_hi_orig);
// Write all 1s
cfg_write(bar_offset, 32'hFFFFFFFF);
if (is_64bit)
cfg_write(bar_offset + 4, 32'hFFFFFFFF);
// Read back
cfg_read(bar_offset, bar_lo_read);
if (is_64bit)
cfg_read(bar_offset + 4, bar_hi_read);
else
bar_hi_read = 32'hFFFFFFFF;
// Verify type bits
if (is_io) begin
`ASSERT_EQ(bar_lo_read[0], 1'b1, "BAR should indicate I/O space")
mask = {bar_hi_read, bar_lo_read} & 64'hFFFFFFFF_FFFFFFFC;
end else begin
`ASSERT_EQ(bar_lo_read[0], 1'b0, "BAR should indicate Memory space")
if (is_64bit)
`ASSERT_EQ(bar_lo_read[2:1], 2'b10, "BAR should indicate 64-bit")
else
`ASSERT_EQ(bar_lo_read[2:1], 2'b00, "BAR should indicate 32-bit")
`ASSERT_EQ(bar_lo_read[3], is_prefetch, "Prefetchable bit mismatch")
mask = {bar_hi_read, bar_lo_read} & 64'hFFFFFFFF_FFFFFFF0;
end
// Calculate and verify size
size = (~mask) + 1;
`ASSERT_EQ(size, expected_size,
$sformatf("BAR%0d size mismatch: expected 0x%0h, got 0x%0h",
bar_num, expected_size, size))
// Restore original values
cfg_write(bar_offset, bar_lo_orig);
if (is_64bit)
cfg_write(bar_offset + 4, bar_hi_orig);
`uvm_info("BAR", $sformatf("BAR%0d sizing verified: %0d bytes %s %s",
bar_num, size, is_64bit ? "64-bit" : "32-bit",
is_prefetch ? "prefetchable" : "non-prefetchable"), UVM_MEDIUM)
endtask
6. Capabilities List
Capabilities extend the Configuration Space with optional features. They form a linked list starting at offset 0x34.
Capability Structure
Capability Header (each capability starts with):
Byte 0: Capability ID
Byte 1: Next Capability Pointer (0x00 = end of list)
Byte 2+: Capability-specific registers
flowchart LR
CAP_PTR["Cap Ptr
0x34"] --> PM["Power Mgmt
0x40"]
PM --> MSI["MSI
0x50"]
MSI --> PCIE["PCIe
0x70"]
PCIE --> MSIX["MSI-X
0x90"]
MSIX --> END["0x00
End"]
Common Capability IDs
| ID | Capability | Description |
|---|---|---|
| 0x01 | Power Management | D-states, PME support |
| 0x03 | VPD | Vital Product Data |
| 0x04 | Slot ID | Slot identification |
| 0x05 | MSI | Message Signaled Interrupts |
| 0x10 | PCIe | PCI Express Capability |
| 0x11 | MSI-X | Enhanced MSI |
| 0x12 | SATA | Serial ATA capability |
| 0x13 | AF | Advanced Features (FLR) |
Capability Enumeration Checker
// Walk capability list and verify structure
class capability_checker extends uvm_component;
`uvm_component_utils(capability_checker)
// Expected capabilities for this device
bit [7:0] required_caps[$];
bit [7:0] found_caps[$];
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
task walk_capabilities();
bit [31:0] status;
bit [7:0] cap_ptr;
bit [7:0] cap_id;
bit [7:0] next_ptr;
int iterations = 0;
const int MAX_CAPS = 48; // Prevent infinite loops
// Check if capabilities exist
cfg_read('h06, status);
if (!status[4]) begin
`uvm_info("CAP", "No capabilities present", UVM_MEDIUM)
return;
end
// Get first capability pointer
cfg_read('h34, cap_ptr);
cap_ptr = cap_ptr & 8'hFC; // Must be DWORD aligned
while (cap_ptr != 0 && iterations < MAX_CAPS) begin
bit [31:0] cap_header;
// Verify pointer is valid (0x40-0xFF, DWORD aligned)
if (cap_ptr < 'h40 || cap_ptr > 'hFC) begin
`uvm_error("CAP", $sformatf(
"Invalid capability pointer: 0x%02h", cap_ptr))
break;
end
cfg_read(cap_ptr, cap_header);
cap_id = cap_header[7:0];
next_ptr = cap_header[15:8] & 8'hFC;
`uvm_info("CAP", $sformatf(
"Found capability 0x%02h at offset 0x%02h, next=0x%02h",
cap_id, cap_ptr, next_ptr), UVM_MEDIUM)
found_caps.push_back(cap_id);
verify_capability(cap_id, cap_ptr);
cap_ptr = next_ptr;
iterations++;
end
// Check for infinite loop
if (iterations >= MAX_CAPS)
`uvm_error("CAP", "Capability list loop detected!")
// Verify all required capabilities present
foreach (required_caps[i]) begin
if (!(required_caps[i] inside {found_caps}))
`uvm_error("CAP", $sformatf(
"Required capability 0x%02h not found", required_caps[i]))
end
endtask
virtual task verify_capability(bit [7:0] cap_id, bit [7:0] offset);
case (cap_id)
'h01: verify_power_management(offset);
'h05: verify_msi(offset);
'h10: verify_pcie_capability(offset);
'h11: verify_msix(offset);
default: `uvm_info("CAP", $sformatf(
"Unknown capability 0x%02h - skipping verification", cap_id), UVM_LOW)
endcase
endtask
endclass
7. PCIe Capability Structure
The PCIe Capability (ID 0x10) is mandatory for all PCIe devices and contains essential information:
PCIe Capability Layout
| Offset | Register | Description |
|---|---|---|
| +0x00 | Cap ID + Next Ptr | 0x10 + next capability |
| +0x02 | PCIe Capabilities | Version, device type, slot, IRQ msg num |
| +0x04 | Device Capabilities | MPS, phantom funcs, extended tag, etc. |
| +0x08 | Device Control | Enable bits, MPS/MRRS settings |
| +0x0A | Device Status | Correctable/Uncorrectable errors, etc. |
| +0x0C | Link Capabilities | Max speed/width, ASPM, L0s/L1 latency |
| +0x10 | Link Control | ASPM control, RCB, link disable |
| +0x12 | Link Status | Current speed/width, training status |
| +0x14 | Slot Capabilities | Slot present, power limits |
| +0x18 | Slot Control | Slot power, indicators |
| +0x1A | Slot Status | Attention, power fault, presence |
| +0x1C | Root Control | Root Complex event enables |
| +0x1E | Root Capabilities | CRS software visibility |
| +0x20 | Root Status | PME requester ID, status |
| +0x24 | Device Capabilities 2 | Completion timeout, ARI, etc. |
| +0x28 | Device Control 2 | Completion timeout value/disable |
| +0x2A | Device Status 2 | Reserved |
| +0x2C | Link Capabilities 2 | Supported speeds vector |
| +0x30 | Link Control 2 | Target link speed, compliance |
| +0x32 | Link Status 2 | De-emphasis, equalization status |
Device Capabilities Register
Device Capabilities (Offset +0x04):
[2:0] Max Payload Size Supported (128/256/512/1024/2048/4096)
[4:3] Phantom Functions Supported
[5] Extended Tag Field Supported (8-bit vs 5-bit tags)
[8:6] Endpoint L0s Acceptable Latency
[11:9] Endpoint L1 Acceptable Latency
[15] Role-Based Error Reporting
[25:18] Captured Slot Power Limit Value
[27:26] Captured Slot Power Limit Scale
[28] Function Level Reset Capability
Link Capabilities Register
Link Capabilities (Offset +0x0C):
[3:0] Max Link Speed (1=2.5GT/s, 2=5GT/s, 3=8GT/s, 4=16GT/s, 5=32GT/s)
[9:4] Max Link Width (1/2/4/8/12/16/32)
[11:10] ASPM Support (L0s, L1)
[14:12] L0s Exit Latency
[17:15] L1 Exit Latency
[18] Clock Power Management
[19] Surprise Down Error Reporting
[20] Data Link Layer Link Active Reporting
[21] Link Bandwidth Notification
[22] ASPM Optionality Compliance
[31:24] Port Number
PCIe Capability Verification
task verify_pcie_capability(bit [7:0] cap_offset);
bit [31:0] pcie_caps, dev_caps, link_caps;
bit [15:0] dev_ctrl, dev_status, link_ctrl, link_status;
// Read registers
cfg_read(cap_offset + 'h02, pcie_caps);
cfg_read(cap_offset + 'h04, dev_caps);
cfg_read(cap_offset + 'h0C, link_caps);
cfg_read(cap_offset + 'h08, {dev_status, dev_ctrl});
cfg_read(cap_offset + 'h10, {link_status, link_ctrl});
// Verify PCIe capability version
`ASSERT_EQ(pcie_caps[3:0], 4'h2,
"PCIe Capability version should be 2")
// Verify device type
case (pcie_caps[7:4])
4'h0: `uvm_info("PCIE", "Device Type: Endpoint", UVM_MEDIUM)
4'h1: `uvm_info("PCIE", "Device Type: Legacy Endpoint", UVM_MEDIUM)
4'h4: `uvm_info("PCIE", "Device Type: Root Port", UVM_MEDIUM)
4'h5: `uvm_info("PCIE", "Device Type: Upstream Switch Port", UVM_MEDIUM)
4'h6: `uvm_info("PCIE", "Device Type: Downstream Switch Port", UVM_MEDIUM)
4'h9: `uvm_info("PCIE", "Device Type: Root Complex Integrated EP", UVM_MEDIUM)
default: `uvm_error("PCIE", $sformatf(
"Unknown device type: 0x%0h", pcie_caps[7:4]))
endcase
// Verify MPS is within capability
`ASSERT_LE(dev_ctrl[7:5], dev_caps[2:0],
"Configured MPS exceeds device capability")
// Verify current link speed/width within capability
`ASSERT_LE(link_status[3:0], link_caps[3:0],
"Current link speed exceeds max capability")
`ASSERT_LE(link_status[9:4], link_caps[9:4],
"Current link width exceeds max capability")
`uvm_info("PCIE", $sformatf(
"Link: %s x%0d, MPS=%0d bytes",
get_speed_string(link_status[3:0]),
link_status[9:4],
128 << dev_ctrl[7:5]), UVM_MEDIUM)
endtask
function string get_speed_string(bit [3:0] speed);
case (speed)
4'h1: return "2.5GT/s";
4'h2: return "5GT/s";
4'h3: return "8GT/s";
4'h4: return "16GT/s";
4'h5: return "32GT/s";
4'h6: return "64GT/s";
default: return "Unknown";
endcase
endfunction
8. Extended Configuration Space
Extended Configuration Space (0x100-0xFFF) provides additional capabilities for PCIe devices:
Extended Capability Structure
Extended Capability Header (4 bytes):
[15:0] Extended Capability ID
[19:16] Capability Version
[31:20] Next Capability Offset (0 = end)
Common Extended Capabilities
| ID | Capability | Description |
|---|---|---|
| 0x0001 | AER | Advanced Error Reporting |
| 0x0002 | VC | Virtual Channel (when >VC0) |
| 0x0003 | Serial Number | Device Serial Number |
| 0x0004 | Power Budgeting | Power consumption information |
| 0x000D | ACS | Access Control Services |
| 0x000E | ARI | Alternative Routing-ID Interpretation |
| 0x0010 | SR-IOV | Single Root I/O Virtualization |
| 0x0017 | TPH | TLP Processing Hints |
| 0x0018 | LTR | Latency Tolerance Reporting |
| 0x001E | L1 PM Substates | L1 Power Management |
| 0x0019 | Secondary PCIe | Secondary PCIe Capability |
| 0x0025 | Data Link Feature | DL Feature exchange |
| 0x0026 | Physical Layer 16 | 16GT/s physical layer |
| 0x0027 | Physical Layer 32 | 32GT/s physical layer |
Extended Capability Walker
task walk_extended_capabilities();
bit [31:0] ext_cap_header;
bit [15:0] cap_id;
bit [11:0] next_offset;
bit [3:0] cap_version;
int offset = 'h100; // Extended caps start at 0x100
int iterations = 0;
const int MAX_EXT_CAPS = 64;
while (offset != 0 && iterations < MAX_EXT_CAPS) begin
// Verify offset is valid and DWORD aligned
if (offset < 'h100 || offset > 'hFFC || (offset & 3)) begin
`uvm_error("EXTCAP", $sformatf(
"Invalid extended capability offset: 0x%03h", offset))
break;
end
cfg_read(offset, ext_cap_header);
// Check for end of list (all zeros)
if (ext_cap_header == 0) begin
`uvm_info("EXTCAP", "End of extended capability list", UVM_MEDIUM)
break;
end
cap_id = ext_cap_header[15:0];
cap_version = ext_cap_header[19:16];
next_offset = ext_cap_header[31:20];
`uvm_info("EXTCAP", $sformatf(
"Extended Cap 0x%04h v%0d at 0x%03h, next=0x%03h",
cap_id, cap_version, offset, next_offset), UVM_MEDIUM)
verify_extended_capability(cap_id, cap_version, offset);
offset = next_offset;
iterations++;
end
if (iterations >= MAX_EXT_CAPS)
`uvm_error("EXTCAP", "Extended capability list loop detected!")
endtask
9. DV Verification Scenarios
Configuration Space Test Matrix
| Test Category | Scenario | Verification Goal |
|---|---|---|
| Basic Access | Read all RO fields | Correct default values |
| Basic Access | Write/readback RW fields | Fields are writable |
| Basic Access | Write to RO fields | No effect on values |
| Basic Access | RW1C fields | Write-1-to-clear behavior |
| BAR Testing | Size each BAR | Correct size returned |
| BAR Testing | Program BAR addresses | Device responds to programmed range |
| BAR Testing | 64-bit BAR pairing | Upper BAR linked correctly |
| BAR Testing | Overlapping BAR addresses | Error handling |
| Capability | Walk capability list | Valid linked list structure |
| Capability | Walk extended capabilities | Valid extended list |
| Capability | Required caps present | All mandatory caps exist |
| Command/Status | Enable/disable spaces | Device response changes |
| Command/Status | Bus master enable | DMA capability control |
| Error Handling | Invalid offset access | UR completion returned |
| Error Handling | Misaligned access | Correct error response |
Configuration Space Coverage Model
covergroup config_access_cg with function sample(pcie_cfg_txn t);
// Transaction type
txn_type: coverpoint t.txn_type {
bins cfg_rd_type0 = {CFG_RD0};
bins cfg_wr_type0 = {CFG_WR0};
bins cfg_rd_type1 = {CFG_RD1};
bins cfg_wr_type1 = {CFG_WR1};
}
// Offset coverage (in DWORD granularity)
offset: coverpoint t.offset[11:2] {
// Standard header
bins vendor_device = {0}; // 0x00
bins cmd_status = {1}; // 0x04
bins class_rev = {2}; // 0x08
bins header_bist = {3}; // 0x0C
bins bar0 = {4}; // 0x10
bins bar1 = {5}; // 0x14
bins bar2 = {6}; // 0x18
bins bar3 = {7}; // 0x1C
bins bar4 = {8}; // 0x20
bins bar5 = {9}; // 0x24
bins subsystem = {11}; // 0x2C
bins rom_base = {12}; // 0x30
bins cap_ptr = {13}; // 0x34
bins int_line_pin = {15}; // 0x3C
// Capability space
bins cap_space = {[16:63]}; // 0x40-0xFF
// Extended space
bins ext_space = {[64:1023]}; // 0x100-0xFFF
}
// Byte enables
first_be: coverpoint t.first_be {
bins all_bytes = {4'hF};
bins word_lo = {4'h3};
bins word_hi = {4'hC};
bins byte_0 = {4'h1};
bins byte_1 = {4'h2};
bins byte_2 = {4'h4};
bins byte_3 = {4'h8};
}
// Completion status
cpl_status: coverpoint t.cpl_status {
bins success = {SC};
bins ur = {UR};
bins crs = {CRS};
bins ca = {CA};
}
// Cross coverage
type_x_offset: cross txn_type, offset;
type_x_be: cross txn_type, first_be;
endgroup
BAR Programming Sequence Test
task test_bar_programming_sequence();
bit [31:0] bar_val;
bit [63:0] bar_sizes[6];
bit [63:0] bar_bases[6];
bit [63:0] next_base = 64'h8000_0000; // Start of memory region
// Phase 1: Size all BARs
`uvm_info("BAR", "Phase 1: Sizing BARs", UVM_LOW)
for (int i = 0; i < 6; i++) begin
bar_sizes[i] = size_bar(i);
`uvm_info("BAR", $sformatf("BAR%0d size: 0x%0h", i, bar_sizes[i]), UVM_MEDIUM)
end
// Phase 2: Allocate addresses (simple first-fit)
`uvm_info("BAR", "Phase 2: Allocating addresses", UVM_LOW)
for (int i = 0; i < 6; i++) begin
if (bar_sizes[i] == 0) continue;
// Align to size
if (next_base % bar_sizes[i] != 0)
next_base = ((next_base / bar_sizes[i]) + 1) * bar_sizes[i];
bar_bases[i] = next_base;
next_base += bar_sizes[i];
`uvm_info("BAR", $sformatf("BAR%0d allocated at 0x%0h", i, bar_bases[i]), UVM_MEDIUM)
end
// Phase 3: Program BARs
`uvm_info("BAR", "Phase 3: Programming BARs", UVM_LOW)
for (int i = 0; i < 6; i++) begin
if (bar_sizes[i] == 0) continue;
program_bar(i, bar_bases[i]);
end
// Phase 4: Enable memory space
cfg_read('h04, bar_val);
bar_val[1] = 1; // Memory Space Enable
cfg_write('h04, bar_val);
// Phase 5: Verify device responds
`uvm_info("BAR", "Phase 5: Verifying BAR access", UVM_LOW)
for (int i = 0; i < 6; i++) begin
if (bar_sizes[i] == 0) continue;
verify_bar_access(bar_bases[i], bar_sizes[i]);
end
`uvm_info("BAR", "BAR programming sequence complete", UVM_LOW)
endtask
10. Common Configuration Space Bugs
Watch for these issues during verification:
BAR-Related Bugs
- Incorrect BAR size: Wrong bits hardwired to 0 in sizing
- 64-bit BAR pairing: Upper 32 bits not linked properly
- Prefetchable bit: Wrong value for memory with side effects
- BAR overlap: Device responds to wrong BAR's range
- Memory Space disable: Device responds even when disabled
Capability List Bugs
- Pointer corruption: Invalid next pointer causes loop or crash
- Missing capabilities: Required caps not present
- Wrong capability ID: ID doesn't match actual capability
- Misaligned pointer: Pointer not DWORD-aligned
Register Access Bugs
- RO field writable: Read-only field changes on write
- RW1C not clearing: Write-1-to-clear doesn't work
- Reserved bits: Reserved fields return non-zero
- Byte enable: Wrong bytes affected in partial writes
Type 0/1 Confusion
- Bridge routing: Type 1 not converted to Type 0 at target
- Bus number: Wrong bus number in secondary registers
- Type 1 to endpoint: Endpoint receives Type 1 transaction
// Common bug detection assertions
// BAR must not respond when Memory Space disabled
assert property (@(posedge clk)
!command_reg.memory_space_enable |-> !bar_hit)
else `uvm_error("CFG", "BAR responded with Memory Space disabled")
// RO fields must not change
assert property (@(posedge clk)
$rose(cfg_write && offset == 'h00) |=>
(vendor_id == $past(vendor_id)) && (device_id == $past(device_id)))
else `uvm_error("CFG", "Read-only Vendor/Device ID changed on write")
// Capability pointer must be DWORD aligned
assert property (@(posedge clk)
cfg_read && offset == 'h34 |-> (read_data[1:0] == 2'b00))
else `uvm_error("CFG", "Capability pointer not DWORD aligned")
Key Takeaways
- Configuration Space provides device identification and resource management
- Type 0 headers are for endpoints; Type 1 for bridges/switches
- BARs define memory/IO regions - sizing uses write-all-1s algorithm
- 64-bit BARs consume two consecutive BAR registers
- Capabilities form a linked list starting at offset 0x34
- PCIe Capability (ID 0x10) is mandatory and contains link/device info
- Extended Configuration Space (0x100-0xFFF) requires ECAM access
- Verify all register access types: RO, RW, RW1C, and byte enables
Next Up
In Part 6, we'll explore Interrupts (INTx, MSI, MSI-X), covering legacy interrupt routing, message signaled interrupts, and their verification challenges.
Comments (0)
Leave a Comment