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 RangeSizeDescription
0x00 - 0x3F64 bytesPCI-Compatible Header
0x40 - 0xFF192 bytesPCI Capabilities
0x100 - 0xFFF3840 bytesPCIe 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 TypeValueDevice Type
Type 00x00Endpoints, Legacy Endpoints
Type 10x01PCI-to-PCI Bridges, Switches
Multi-functionBit 7 setDevice 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:

OffsetType 0 (Endpoint)Type 1 (Bridge)
0x10-0x17BAR0, BAR1BAR0, BAR1
0x18BAR2Primary Bus Number
0x19BAR2Secondary Bus Number
0x1ABAR2Subordinate Bus Number
0x1BBAR2Secondary Latency Timer
0x1C-0x1DBAR3I/O Base, I/O Limit
0x1E-0x1FBAR3Secondary Status
0x20-0x23BAR4Memory Base, Memory Limit
0x24-0x27BAR5Prefetch 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

OffsetRegisterSizeAccessDescription
0x00Vendor ID16-bitROManufacturer (0xFFFF = no device)
0x02Device ID16-bitRODevice model number
0x08Revision ID8-bitROSilicon revision
0x09Class Code24-bitRODevice type classification
0x2CSubsystem Vendor ID16-bitROCard/board manufacturer
0x2ESubsystem ID16-bitROCard/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)

BitNameDescription
0I/O Space EnableRespond to I/O transactions
1Memory Space EnableRespond to Memory transactions
2Bus Master EnableCan generate Memory/IO requests
6Parity Error ResponseEnable parity error reporting
8SERR# EnableEnable system error reporting
10Interrupt DisableDisable INTx interrupts

Status Register (0x06)

BitNameAccessDescription
3Interrupt StatusROINTx asserted
4Capabilities ListROCapabilities present (always 1 for PCIe)
566MHz CapableROLegacy PCI field
8Master Data Parity ErrorRW1CParity error as bus master
11Signaled Target AbortRW1CDevice signaled Target Abort
12Received Target AbortRW1CReceived Target Abort
13Received Master AbortRW1CReceived Unsupported Request
14Signaled System ErrorRW1CSERR# asserted
15Detected Parity ErrorRW1CParity 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 0TypeDescription
0Memory BARMemory-mapped I/O region
1I/O BARI/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]TypeAddress Range
0032-bitAnywhere in 32-bit space
01ReservedDo not use
1064-bitAnywhere in 64-bit space
11ReservedDo 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

  1. Save original BAR value
  2. Write all 1s (0xFFFFFFFF) to BAR
  3. Read back BAR value
  4. Mask off type bits, invert, add 1 = size
  5. 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

IDCapabilityDescription
0x01Power ManagementD-states, PME support
0x03VPDVital Product Data
0x04Slot IDSlot identification
0x05MSIMessage Signaled Interrupts
0x10PCIePCI Express Capability
0x11MSI-XEnhanced MSI
0x12SATASerial ATA capability
0x13AFAdvanced 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

OffsetRegisterDescription
+0x00Cap ID + Next Ptr0x10 + next capability
+0x02PCIe CapabilitiesVersion, device type, slot, IRQ msg num
+0x04Device CapabilitiesMPS, phantom funcs, extended tag, etc.
+0x08Device ControlEnable bits, MPS/MRRS settings
+0x0ADevice StatusCorrectable/Uncorrectable errors, etc.
+0x0CLink CapabilitiesMax speed/width, ASPM, L0s/L1 latency
+0x10Link ControlASPM control, RCB, link disable
+0x12Link StatusCurrent speed/width, training status
+0x14Slot CapabilitiesSlot present, power limits
+0x18Slot ControlSlot power, indicators
+0x1ASlot StatusAttention, power fault, presence
+0x1CRoot ControlRoot Complex event enables
+0x1ERoot CapabilitiesCRS software visibility
+0x20Root StatusPME requester ID, status
+0x24Device Capabilities 2Completion timeout, ARI, etc.
+0x28Device Control 2Completion timeout value/disable
+0x2ADevice Status 2Reserved
+0x2CLink Capabilities 2Supported speeds vector
+0x30Link Control 2Target link speed, compliance
+0x32Link Status 2De-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

IDCapabilityDescription
0x0001AERAdvanced Error Reporting
0x0002VCVirtual Channel (when >VC0)
0x0003Serial NumberDevice Serial Number
0x0004Power BudgetingPower consumption information
0x000DACSAccess Control Services
0x000EARIAlternative Routing-ID Interpretation
0x0010SR-IOVSingle Root I/O Virtualization
0x0017TPHTLP Processing Hints
0x0018LTRLatency Tolerance Reporting
0x001EL1 PM SubstatesL1 Power Management
0x0019Secondary PCIeSecondary PCIe Capability
0x0025Data Link FeatureDL Feature exchange
0x0026Physical Layer 1616GT/s physical layer
0x0027Physical Layer 3232GT/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 CategoryScenarioVerification Goal
Basic AccessRead all RO fieldsCorrect default values
Basic AccessWrite/readback RW fieldsFields are writable
Basic AccessWrite to RO fieldsNo effect on values
Basic AccessRW1C fieldsWrite-1-to-clear behavior
BAR TestingSize each BARCorrect size returned
BAR TestingProgram BAR addressesDevice responds to programmed range
BAR Testing64-bit BAR pairingUpper BAR linked correctly
BAR TestingOverlapping BAR addressesError handling
CapabilityWalk capability listValid linked list structure
CapabilityWalk extended capabilitiesValid extended list
CapabilityRequired caps presentAll mandatory caps exist
Command/StatusEnable/disable spacesDevice response changes
Command/StatusBus master enableDMA capability control
Error HandlingInvalid offset accessUR completion returned
Error HandlingMisaligned accessCorrect 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.

Author
Mayur Kubavat
VLSI Design and Verification Engineer sharing knowledge about SystemVerilog, UVM, and hardware verification methodologies.

Comments (0)

Leave a Comment