本文基于 PCI Local Bus Specification Revision 3.0 材料整理而成,面向有一定计算机基础、正在学习 PCI/PCIe 的开发者。
目录
- [第 1 章 引言与信号定义](#第 1 章 引言与信号定义)
- [1.1 PCI 总线概述](#1.1 PCI 总线概述)
- [1.2 引脚功能分组](#1.2 引脚功能分组)
- [第 2 章 总线操作](#第 2 章 总线操作)
- [2.1 总线命令定义](#2.1 总线命令定义)
- [2.2 寻址](#2.2 寻址)
- [2.3 事务排序与 Posting](#2.3 事务排序与 Posting)
- [2.4 总线事务时序(读 / 写)](#2.4 总线事务时序(读 / 写))
- [2.5 事务终止](#2.5 事务终止)
- [2.6 延迟事务](#2.6 延迟事务)
- [2.7 仲裁](#2.7 仲裁)
- [2.8 延迟](#2.8 延迟)
- [2.9 其他总线操作](#2.9 其他总线操作)
- [2.10 错误功能](#2.10 错误功能)
- [2.11 64 位总线扩展与 64 位寻址](#2.11 64 位总线扩展与 64 位寻址)
- [第 3 章 配置空间](#第 3 章 配置空间)
- [3.1 配置空间组织](#3.1 配置空间组织)
- [3.2 配置寄存器详解](#3.2 配置寄存器详解)
- [3.3 PCI 扩展 ROM](#3.3 PCI 扩展 ROM)
- [3.4 系统复位](#3.4 系统复位)
- [3.5 Capabilities List](#3.5 Capabilities List)
- [3.6 MSI / MSI-X](#3.6 MSI / MSI-X)
第 1 章 引言与信号定义
1.1 PCI 总线概述
PCI(Peripheral Component Interconnect,外围部件互连)是一种连接 CPU、内存与各类外设的本地总线标准。在 PCI 体系里,CPU 并不直接挂在 PCI 总线上,而是通过桥接器件(Bridge)与 PCI 总线相连。这套桥接结构的意义在于:它为处理器提供了一条低延迟的访问路径,使 CPU 能够直接访问那些映射在内存或 I/O 地址空间任意位置上的 PCI 设备。
下面这张图展示了一个典型的 PCI 本地总线系统架构:

图 1-1 典型的 PCI 本地总线系统架构。
架构中有两类桥接器件需要区分:
- Host--PCI Bridge(主桥):负责连接 CPU/内存与 PCI 总线,承担地址转换的职责。它把 CPU 侧的访问转换成 PCI 总线上的事务。
- PCI-to-PCI Bridge(PCI 到 PCI 桥):负责连接上游 PCI 总线和下游 PCI 总线,从而扩展出更多的 PCI 总线(Bus #1、Bus #2 等),同时转发上下游之间的事务。
理解桥接器件是理解整个 PCI 体系的关键,后文大量关于 Posting、事务排序、配置空间访问的规则,本质上都是围绕桥接器的行为展开的。
flowchart TB CPU --- HostBridge"Host-PCI Bridge" Cache --- HostBridge Memory"Memory" --- HostBridge HostBridge --- Bus0"PCI Bus #0" Bus0 --- DevA"PCI 设备 A" Bus0 --- P2P"PCI-to-PCI Bridge" P2P --- Bus1"PCI Bus #1" Bus1 --- DevB"PCI 设备 B" P2P --- Bus2"PCI Bus #2" Bus2 --- DevC"PCI 设备 C"
图 1-2 上图的框图化表示:CPU/内存通过主桥接入 Bus #0,PCI-to-PCI Bridge 又向下扩展出 Bus #1、Bus #2。
1.2 引脚功能分组
PCI 总线上的信号引脚可以按功能分成几组。分组的目的不是死记引脚名,而是理解每类信号在事务中扮演的角色。下面这张引脚总图给出了全貌,随后逐组解释:

图 1-3 PCI 引脚功能分组。
1.2.1 地址与数据引脚(Address and Data Pins)
PCI 最核心的设计之一就是地址与数据复用(multiplexed)------同一组 AD 引脚,在事务的不同阶段分别承载地址或数据。
- AD31::00 :地址与数据信号复用。一个总线事务由一个地址周期(Address Phase) 以及一个或多个数据周期(Data Phase) 组成。
- C/BE3::0#:总线命令(Command)与字节使能(Byte Enable)信号复用。在地址阶段,C/BE# 承载总线命令;在数据阶段,C/BE# 用作字节使能位,标出当前 DWORD 中哪些字节有效。
- PAR :对 AD31:0 与 C/BE#3:0 做偶校验(Even Parity) 得到的校验位。计算者随相位而变:地址相位和写数据相位由 Master 计算 PAR,读数据相位由 Target 计算 PAR。PAR 比 AD 晚一个时钟(CLK)有效,即延迟一拍。
这里顺带解释几个贯穿全文的术语:
- Master(主设备):发起事务的一方。
- Target(目标设备):被事务寻址、做出响应的一方。
- DWORD:双字,即 32 位(4 字节),是 PCI 数据通路的基本单位。
1.2.2 接口控制引脚(Interface Control Pins)
这组引脚负责事务的握手与生命周期管理,是理解读写时序的基础:
| 引脚 | 驱动方 | 含义 |
|---|---|---|
| FRAME# | Master | 被拉低表示 Master 开始发起一次总线事务 |
| IRDY# | Master | Initiator Ready,Master 就绪,表明它已准备好传输数据 |
| TRDY# | Target | Target Ready,Target 就绪,表明它已准备好传输数据 |
| STOP# | Target | Target 请求终止当前事务 |
| DEVSEL# | Target | Device Select,地址解码完成后 Target 发出响应 |
| IDSEL | 系统 | 配置空间访问时的设备「片选」信号 |
| LOCK# | Master | 对桥接器执行原子操作(不建议使用,会影响总线效率) |
关于
#后缀:PCI 中大量信号名带#,表示该信号低电平有效(active low)。例如 FRAME# 拉低才表示「有效」。
LOCK# 值得单独说明:持有锁的 Master 进行事务时,不允许其他设备发起请求。这种锁会阻塞整条总线,因此在实践中很少使用,规范也不鼓励。
1.2.3 仲裁、错误报告与中断引脚
- 仲裁引脚 :
REQ#由 Master 发给仲裁器,表示该代理希望使用总线;GNT#由仲裁器发给 Master,表示已授予其访问总线的权限。 - 错误报告引脚 :
PERR#:报告数据传输过程中的奇偶校验错误。SERR#:报告地址奇偶校验错误、Special Cycle 命令中的数据奇偶校验错误,或任何可能导致灾难性后果的系统错误。
- 中断引脚 :
INTx#(即 INTA#~INTD#)用于请求中断,一个多 Function 设备最多可实现 4 条 INTx# 线路。
1.2.4 64 位扩展引脚与 SMBus 引脚
PCI 可选地支持 64 位数据通路,需要额外引脚:
- AD63::32:额外提供高 32 位地址/数据。
- C/BE7::4#:64 位下的高位命令/字节使能。
- REQ64#:Master 请求 64 位事务。
- ACK64#:Target 支持并响应 64 位事务。
- PAR64:对 AD63:32 与 C/BE7:4# 做奇偶校验。
此外还有 SMBus(System Management Bus)接口的两个引脚:
- SMBCLK:SMBus 时钟信号。
- SMBDAT:SMBus 数据信号。
第 2 章 总线操作
2.1 总线命令定义
在地址阶段,C/BE3:0# 引脚承载的是总线命令,它告诉所有设备这次事务「要做什么」。命令大致可以按寻址空间来分类:

图 2-1 总线命令编码表。
内存类命令
| 命令 | 含义 |
|---|---|
| Memory Read | 从内存地址空间中映射的 Agent 读取数据 |
| Memory Read Line | 一次性读取整个 CacheLine(缓存行)边界的数据,而非仅读单个内存周期 |
| Memory Read Multiple | 为批量顺序传输设计,允许内存系统在存在软件透明缓冲区时多预读几条缓存行以提升性能 |
| Memory Write | 将数据写入内存地址空间中映射的 Agent |
| Memory Write and Invalidate(MWI) | Master 通过单次事务把地址对应缓存行的所有字节写入,除非被目标设备中断;只能用 Linear Burst 模式 |
I/O 类命令
| 命令 | 含义 |
|---|---|
| I/O Read | 从映射在 I/O 地址空间中的 Agent 读取数据 |
| I/O Write | 将数据写入映射在 I/O 地址空间中的 Agent |
配置类命令与其他
| 命令 | 含义 |
|---|---|
| Configuration Read | 读取 Agent 的配置空间 |
| Configuration Write | 向 Agent 的配置空间写数据 |
| Special Cycle | 在 PCI 总线上提供一种简单的消息广播机制 |
| Interrupt Acknowledge | 隐式地址指向系统中断控制器的读取指令,返回中断向量 |
DAC(双地址周期)
DAC(Dual Address Cycle,双地址周期) 用于把 64 位地址传输给支持 64 位寻址的设备。前提是该地址不在低 4GB 地址空间范围内------换句话说,只有当目标地址确实超出 32 位表示范围时,才需要动用两个地址周期来传输完整的 64 位地址。
2.2 寻址
PCI 支持三类地址空间的解码:I/O 空间 、内存空间 和配置空间。三者的解码规则差异很大,尤其是配置空间,它根本不依赖地址线来选择设备,而是靠 IDSEL 信号。
2.2.1 I/O 空间解码
发起 I/O 事务的 Master 必须确保 AD1::0 指示该事务所涉及的最低有效字节位。一个 Function 可以限制自己在 I/O 空间支持的访问类型------设备可以要求驱动只用字节、字(16 位)或 DWORD 操作来访问,并对其他访问方式直接以 Target-Abort 终止。

图 2-2 I/O 空间解码。
2.2.2 内存空间解码
在内存地址空间中,AD31::02 提供 DWORD 对齐的地址,而 AD1::0 用于指示 Master 请求的数据传输顺序。内存空间支持两种突发(Burst)寻址模式:
- Linear Incrementing(线性递增):32 位事务每拍递增 1 个 DWORD(4 字节),64 位事务每拍递增 2 个 DWORD(8 字节),直至事务结束。使用 Memory Write and Invalidate 命令的事务仅支持 Linear Burst 模式。
- CacheLine Wrap(缓存行回绕):访问过程逐 DWORD 递增(64 位时每拍 2 个 DWORD),到达缓存行末尾后回绕到同一缓存行的起始位置,循环直至传完该行数据。这种模式配合缓存行边界访问,能避免跨行的低效传输。

图 2-3 内存空间解码与突发寻址。
2.2.3 配置空间解码
对于 PCI 设备,每个 Function 都会被分配一个独立的 256 字节地址空间,即配置空间(Configuration Space) 。配置空间的访问方式与内存/I/O 完全不同:它不是靠地址线来选择设备,而是靠 IDSEL 信号(作用等同于传统片选)来选中目标设备。
根据设备是否跨越 PCI-to-PCI Bridge,配置事务分为两种类型:
- Type 0 (AD1::0 = "00"):用于在当前执行该事务的总线上选择目标设备。
- Type 1 (AD1::0 = "01"):用于向其他总线段转发配置请求。
两种类型里,Register Number 字段与 Function Number 字段含义相同;Device Number 和 Bus Number 仅用于 Type 1 事务。
对于 Type 1 事务,PCI-to-PCI Bridge 会把它转换成 Type 0 配置事务:把 AD1::0 设为 "00"、AD10::02 保持不变,同时解码设备编号以从本地总线的 32 个设备中选出目标,确认正确的 IDSEL 地址后即启动 Type 0 配置事务。
flowchart LR CPU -->|"Type 1(总线号不同)"| Bridge"PCI-to-PCI Bridge" Bridge -->|"转为 Type 0,选中 IDSEL"| Dev"目标设备" CPU -->|"Type 0(总线号相同)"| Dev2"本总线上的设备"
图 2-4 配置事务的 Type 0 / Type 1 选择与转换。

图 2-5 配置空间解码。
2.2.4 软件如何生成配置事务(0xCF8 / 0xCFC)
在 PC-AT 兼容系统中,软件通过两个 DWORD 的 I/O 端口来生成配置事务:
- CONFIG_ADDRESS :端口
0xCF8 - CONFIG_DATA :端口
0xCFC
当 Host Bridge 检测到 I/O 访问落在 CONFIG_DATA 地址开始的 DWORD 范围内时,会检查 CONFIG_ADDRESS 寄存器中的使能位和总线编号:若使能位有效且总线编号与自身一致,则发起传输。
CONFIG_ADDRESS 的典型位域布局如下:
CONFIG_ADDRESS (0xCF8, 32 位):
31 30:24 23:16 15:11 10:8 7:2 1:0
Enable Bit Reserved Bus # Device # Function# Register# Type
(DWORD 偏移) 00=Type0 / 01=Type1
这套 0xCF8/0xCFC 是 CPU 软件层面的编程接口,最终还要由 Host Bridge 转换成总线层面的物理信号。转换规则(Type 0):
- 对 Device Number 解码以确定 IDSEL;
- AD1::0 = "00";
- Function Number / Register Number 原样复制过去。
Type 1 转换:除使能位(第 31 位)之外的内容复制到地址线上,AD1::0 = "01"。

图 2-6 CONFIG_ADDRESS / CONFIG_DATA 接口。
2.2.5 通过 0xCF8/0xCFC 生成 Special Cycle
当向 CONFIG_ADDRESS 写入特定值、满足「总线号与 Bridge 自身总线号匹配、设备号全为 1、功能号全为 1、寄存器号为 0」时,桥接器会进入预触发状态,下一次对 CONFIG_DATA 的写操作将生成一条 Special Cycle 命令事务------写操作时,桥接器在地址阶段把 C/BE3:0# 设为 Special Cycle 的命令编码。
2.2.6 设备配置空间的选中(IDSEL)
访问配置空间不依赖地址译码,而是依赖 IDSEL 信号。每台设备都有独立的 IDSEL 输入引脚。对于 Type 0 配置事务,目标设备在同时满足以下条件时拉低 DEVSEL# 响应:
- 成功译码出当前是配置命令;
- 自身的 IDSEL 处于有效状态;
- 地址阶段 AD1::0 为 "00"。
多 Function 设备还需额外满足:AD10::08 的功能号字段与该设备已实现的某个 Function 匹配。选中目标 Function 后,设备通过 AD7::2 寻址一个 DWORD,再结合字节使能确定该 DWORD 内要访问的具体字节。
IDSEL 信号由系统具体定义,规范给出的一个示例是:设备号 0 的 IDSEL 接 AD16、设备号 1 的 IDSEL 接 AD17......依此类推,设备号 15 的 IDSEL 接 AD31。

图 2-7 通过 IDSEL 选中设备。
2.3 事务排序与 Posting
这一节是 PCI 里最容易让人困惑、但也是桥接器行为核心的部分。PCI 的事务排序(Transaction Ordering)规则有三大作用:
- 满足生产者-消费者模型:系统中任意一个主设备(生产者)发出的写操作结果,只能按原始顺序被另一个主设备(消费者)观测到。
- 允许部分事务被 Posted,从而提升总线传输性能。
- 避免总线死锁:在需要满足第一条排序规则时,可通过 Flush Posting Buffer 来打破死锁条件。
先解释两个关键概念:
- Posted 事务:在抵达最终目标设备之前,就已经在发起端设备完成「传输」的事务。主设备无需等待它真正到达最终目标,就可以继续执行其他任务甚至发起其他事务。内存写(Memory Write、Memory Write and Invalidate)是允许 Posted 的。
- Non-Posted 事务:必须先抵达最终目标设备,才能在发起端标记为完成。内存读(Memory Read / Read Line / Read Multiple)、I/O 事务、配置事务都属于 Non-Posted 事务(主机桥接器有特殊场景除外)。
一个典型的 Posted 死锁规避例子:
A 向 B 写一段数据 → 数据被 Bridge 暂时 Posting → B 又要从 A 读数据 → 为满足排序规则一,必须把 Posting Buffer 中的数据 Flush 给 B → Flush 完成后,B 的读请求才完成。这样既不死锁,数据顺序也不会错乱。
2.3.1 简单设备的事务排序与 Posting
Simple Device(简单设备) 指它作为 Master 发起写操作时,不要求其写数据在总线接口逻辑处被 Posted。一般不与本地 CPU 直接相连的设备会被实现为简单设备。
简单设备的 Master 状态机与 Target 状态机必须相互独立,这是出于死锁考量。举例来说:A 向 B 发一笔写事务,同时 B 向 A 发一笔读事务。如果两个状态机不独立,A 要求 B 先接收数据才能继续、B 又要求先收到 A 的数据才能继续,双方互相等待就死锁了。反之,状态机独立、读写互不影响,死锁即可避免。
还有桥接器场景:A 和 B 之间有 Bridge,A 写给 B 的数据还留在 Bridge 的 Posting Buffer 中,此时 B 发起读事务,按规则需要先 Flush 这个 Buffer;若 B 的状态机不独立,同样会死锁。
2.3.2 桥接器的事务排序与 Posting
Bridge 指所有支持对向外发起的内存写事务做内部 Posting 的设备,通常用于连接两组总线,允许对双向流经自身的内存写事务做 Posting。
桥接器必须遵守两条规则,以保证存在 Posting 时写结果仍能被正确观测,同时避免排空缓冲时死锁:
- 流经桥接器、方向相同 的 Posted 内存写事务,在目标总线上的完成顺序必须与源总线上的完成顺序一致;方向相反的写事务之间无强制排序约束。
- 桥接器双向的所有 Posted 内存写缓冲,必须在任意方向的读事务完成前全部排空。
第二条规则的意义在于数据一致性:例如 A 向内存某区域写一笔数据,数据还留在 Posting Buffer 中,此时 B 向该区域发起读事务,若不先 Flush,B 就可能读到旧数据。
flowchart LR A"主设备 A" -->|"写数据(Posted)"| Buf"Bridge Posting Buffer" Buf -->|"Flush"| Mem"内存区域" B"主设备 B" -->|"读该区域"| Mem Buf -.->|"读之前必须先 Flush"| Mem
图 2-8 桥接器在读操作前必须先 Flush Posting Buffer。

图 2-9 桥接器 Posting 示意(与架构图复用同一张底图)。
2.4 总线事务时序(读 / 写)
2.4.1 读事务
一次读事务的典型时序分为两个阶段:
- Address Phase(地址阶段):FRAME# 被拉低后开启。AD 线上是有效地址,C/BE3:0# 上是总线命令。Target 需要在 3 个 CLK 内用 DEVSEL# 响应。
- Data Phase(数据阶段) :AD 线上是数据,C/BE# 线包含下一个数据相位的字节使能信息。读事务中,AD 线在地址与数据之间需要一个 Turnaround Cycle(周转周期)------因为读事务里地址阶段由 Master 驱动、数据阶段改由 Target 驱动,总线需要一拍来切换驱动者,避免两个设备同时驱动造成冲突。
数据传输的握手规则:IRDY# 和 TRDY# 同时置位时传输一拍数据。IRDY# 拉高表示 Master 插入等待周期(Wait Cycle),TRDY# 拉高表示 Target 插入等待周期。FRAME# 取消置位后的那个数据相位是最后一个周期。

图 2-10 读事务时序。
2.4.2 写事务
写事务的地址阶段与读事务相同,数据阶段的差异在于:写事务中地址和数据都由 Master 驱动,控制权没有切换,因此不需要 Turnaround Cycle。握手规则与读事务一致:IRDY#、TRDY# 同时置位时传输数据,FRAME# 取消置位后的数据相位是最后一个周期(若有 Wait Cycle,则等待结束后传完最后一个数据即结束)。

图 2-11 写事务时序。
sequenceDiagram participant M as Master participant T as Target M->>T: Address Phase:FRAME# 拉低,AD=地址,C/BE#=命令 Note over M,T: 读需 Turnaround,写无需 M->>T: Data Phase:IRDY# & TRDY# 同时置位 → 传一拍 M->>T: FRAME# 取消置位 → 最后一拍 T-->>M: 读数据由 Target 驱动
图 2-12 读写事务握手流程的简化表示。
2.5 事务终止
事务可以正常完成,也可能被 Master 或 Target 提前终止。终止方式分两大类:Master 发起的终止 和 Target 发起的终止。
2.5.1 Master 发起的终止
Master 终止的完成条件是:FRAME# 取消置位且 IRDY# 置位。当 IRDY# 与 TRDY# 同时置位时发生最终数据传输;FRAME# 与 IRDY# 都取消置位(回到空闲状态)时事务完成。
Master 终止的原因有两种:
- Completion(正常完成):数据传完。
- Timeout(超时):Master 的 GNT# 被取消置位且其内部 Latency Timer 已过期时终止。
注:Memory Write and Invalidate 不受 Latency Timer 约束,但当到达 CacheLine 边界后需要终止。

图 2-13 Master 发起的终止。
此外还有一个细节:如果 Master 未准备好传输数据(IRDY# 在地址阶段之后的某个时钟被取消置位),FRAME# 必须保持置位,但这种终止延迟不应超过两到三个时钟。
2.5.2 Master-Abort
Master-Abort 是 Master 在目标设备无响应时发起的一种异常终止(Configuration 或 Special Cycle 命令除外)。典型场景是 Master 在若干个时钟内没等到 DEVSEL# 响应,于是强行中止事务。

图 2-14 Master-Abort 时序。
一个关键约束:FRAME# 不能在 IRDY# 被置位之前取消置位;并且即使事务以 Master-Abort 终止,IRDY# 也必须在 FRAME# 取消置位后至少保持置位一个时钟周期。
2.5.3 Target 发起的终止
Target 有三种终止方式:Retry 、Disconnect(With Data) 和 Target-Abort。
Retry:Target 能响应(DEVSEL# 置位)但无法完成请求(TRDY# 不置位、STOP# 置位),该相位没有数据传输。FRAME# 取消置位,Master 取消传输,过后再重试该请求。
Disconnect With Data:完成一个数据相位后结束。Target 同时置位 TRDY# 和 STOP#,表示只想再完成一个数据相位就断开。检测到 STOP# 时,Master 应在 IRDY# 能置位时尽快取消置位 FRAME#。


图 2-15 Retry(上)与 Disconnect With Data(下)时序。
Disconnect 还有两个细分场景:
- 单数据相位 Disconnect:Master 在 FRAME# 置位后的下一个 CLK 即取消置位,表示本周期只传单个数据;Target 同时置位 TRDY#、STOP#、DEVSEL#,表示传一个数据即终止。
- 带 Wait Cycle 的 Disconnect:在单数据 Disconnect 基础上,IRDY# 插入一个 Wait Cycle;STOP# 置位后 FRAME# 取消置位,之后 IRDY# 必须再置位一个 CLK,保证之后至少还有一个数据相位。



图 2-16 几种 Disconnect 变体。
Target-Abort:表示 Target 要停止事务,并且不需要 Master 重复请求。Target 在拉低 STOP# 的同时取消置位 DEVSEL#;由于请求停止时不再传输数据,因此也取消置位 TRDY#。

图 2-17 Target-Abort 时序。
2.5.4 Target 终止对 Master 的要求
- 对于 Retry 和 Disconnect,作为「单一 Master 活动源」的设备必须在至少两个 CLK 周期内取消置位 REQ#。
- 对于多 Function 等设备,即使一个或多个源被目标终止(STOP# 置位),也不会取消置位 REQ#;但在重复任何被 Target 终止的事务之前,必须在两个连续时钟周期内取消置位 REQ#。
- 对于 Retry,必须无条件重发事务,但不是立刻重发------可以先去做别的事情,规范要求在若干时钟内重发。
- 对于实现了 Delayed Transaction 的 Target,如果 Master 长时间不重试,Target 可能超时并丢弃缓存的数据。
2.6 延迟事务
Delayed Transaction(延迟事务) 是为了解决「慢速设备长时间占用总线导致带宽浪费」的问题。适用于 I/O Controller(单事务)和 PCI-to-PCI Bridge(多事务)这类慢速目标。
核心思想是:当 Master 遇到慢速设备时,目标设备直接回应 Retry 并释放总线,让其他 Master 插队使用总线,而不是让总线空转等待。
2.6.1 基本流程
延迟事务分三步:
sequenceDiagram participant M as Master participant T as Target(慢速设备) M->>T: 1. 发起访问 T-->>M: 锁存地址/命令/字节使能,立刻 Retry 终止 Note over T: 2. 目标独立完成读写(Master 可去干别的) M->>T: 3. 仲裁后原样重发请求 T-->>M: 匹配到已完成请求,返回数据/状态,事务结束
- 主设备请求:Master 发起访问,Target 锁存必要信息(地址、命令、字节使能等),立刻以 Retry 终止。
- 目标设备完成请求:Target 利用锁存的信息,在目标总线上独立完成读写(此时 Master 不在总线上,可以干别的事),完成后生成「延迟完成」(若是读,包含读回来的数据)。
- 主设备完成事务:Master 再次仲裁并原样重发原请求,Target 匹配到已完成的请求后直接返回数据和状态,事务结束。
适用命令:中断应答、I/O 读写、配置读写、内存读等(内存写不属于此类)。
图 2-18 延迟事务流程。
2.6.2 完成延迟事务所需的信息
- 必须锁存:地址、命令、字节使能(BE)、数据奇偶校验;64 位传输还需锁存 REQ64#。
- 写请求中,数据仅当 IRDY# 有效时才有效,而字节使能在数据阶段始终有效,目标必须完整锁存数据。
- Master 必须原封不动重发原请求(地址、命令、数据完全一致),否则目标会视为新请求。
- 对比时,对于可预取(Prefetchable)的内存读,目标判断请求是否相同时可忽略字节使能。
- Master 重试时必须 100% 复制原始事务。
2.6.3 丢弃延迟事务与内存写
- 丢弃规则:成为「延迟完成」后,若 Master 超过 2¹⁵ 个时钟(Discard Timer 到期)仍未取走,设备必须丢弃该完成包。丢弃 Non-prefetchable 完成包会破坏数据完整性,建议向驱动报错;丢弃 Prefetchable 完成包则无伤大雅,可不报错。
- 内存写与延迟事务 :Bridge 在转发延迟读之前,必须先等内存写数据 Flush;若此时 Target 又用 Retry 拒绝接收内存写,就会死锁。因此使用延迟事务的 Target 必须无条件接受新的内存写数据,即使自己的延迟事务还没走完------这打破了死锁链。
2.7 仲裁
PCI 采用基于访问(Access-based)的仲裁,而非固定的时间片。每个主设备都有独立的 REQ# 和 GNT# 信号。它的几个要点:
- PCI 的仲裁过程是「隐藏」的------在前一次总线访问期间进行,不额外消耗总线周期,极大提升了利用率。
- 仲裁器必须实现公平性算法。公平 ≠ 平均:高优先级设备获得总线的概率相对更高。
图 2-19 仲裁示意。
2.7.1 仲裁信号协议
- Agent 请求时断言 REQ#;仲裁器决定时断言对应的 GNT#。
- GNT# 与 FRAME# 同时取消置位时,当前事务有效。
- 两次 GNT# 切换之间(除非在同一时钟)必须至少间隔 1 个时钟。
- 只做一次事务的话,在 FRAME# 断言的时钟就取消置位 REQ#;要继续访问,REQ# 就一直保持。
- 若某 Agent 获得了总线(GNT# 被断言)但总线空闲了 16 个时钟还没发起操作,仲裁器应视其为故障并切断其 REQ#。
- 无总线申请时,建议不要把 GNT# 从当前 Master 身上撤走,让它一直保持到空闲状态。

图 2-20 仲裁信号协议。
2.7.2 Fast Back-to-Back 事务
Fast Back-to-Back Transaction 指同一 Master 在上一个事务结束后,不插入空闲总线周期,直接发起下一个事务,以提升带宽利用率。有两种实现机制:
- Master-based:Master 自己保证后一个事务访问的目标与前一个相同。
- Target-based:允许访问不同目标,但所有目标设备均需支持 Fast Back-to-Back,保证 DEVSEL#、TRDY#、STOP#、PERR# 等控制信号不发生竞争。

图 2-21 Fast Back-to-Back 事务。
2.7.3 仲裁停放(Arbitration Parking)
当没有 Agent 使用或请求总线时,仲裁器将 GNT# 固定断言给一个 Default Master(默认主设备,可以是固定设备、上次使用的设备或仲裁器自己)。这样总线空闲时有一个明确的「停靠」对象,避免 GNT# 悬空。
| 停放状态 | 最小仲裁延迟 |
|---|---|
| Parked | 停放代理:0 CLK;其他代理:2 CLK |
| Not Parked | 所有代理:1 CLK |
2.8 延迟
延迟(Latency)是衡量总线性能的关键指标。PCI 从 Target、Master 和仲裁三个角度分别定义延迟,并用 Latency Timer(延迟定时器) 来限制单个 Master 占用总线的时间。
2.8.1 Target 延迟
- Target Initial Latency(初始延迟) :从 FRAME# 置位到 TRDY# 或 STOP# 置位的时钟周期数。规范要求目标在 16 个时钟 内完成初始数据阶段;主机总线桥在访问命中缓存中已修改行时,最多可达 32 个时钟。为满足初始延迟,目标有三种选择:
- 永远在 16 个时钟内直接传完数据;
- 大多数情况能 16 个时钟传完,偶尔内部忙碌时立刻发 Retry 释放总线;
- 经常超时,则必须用 Delayed Transaction 机制先记下请求、发 Retry,后台慢慢做,Master 以后再重试。
- Subsequent Latency(后续延迟) :一次突发传输中,上一数据阶段完成到下一数据阶段开始的间隔。规范要求目标在前一数据阶段完成后的 8 个时钟内完成后续数据阶段;无法满足时应用 STOP# 提前结束 Burst,而不是让总线一直等。
2.8.2 Master 数据延迟
Master Data Latency 指 Master 置位 IRDY# 所花费的时钟周期数。规范要求 Master 在初始数据阶段 FRAME# 置位后的 8 个时钟内置位 IRDY#,且在后续数据阶段也须在 8 个时钟内完成。
写操作的最大完成时间限制:Target 可能偶尔以 Retry 终止内存写事务(如写 Buffer 满),但不能无限期用 Retry 拖延。规范规定目标第一次 Retry 内存写后,必须在 10µs 内成功完成至少一个内存写数据阶段(设备初始化期间除外)。
2.8.3 仲裁延迟与 Latency Timer
仲裁延迟 指 Master 置位 REQ# 到总线进入空闲状态且 GNT# 被置位之间的时钟周期数。每个 Master 占用总线的时间受其 Latency Timer 限制------当 Master 置位 FRAME# 时即启动其延迟定时器计数。
Latency Timer 的几种情况:
- 到期前取消置位 FRAME# → 正常终止。
- 到期时 FRAME# 仍置位(非 MWI)→ Master 必须在 GNT# 被取消置位时启动事务终止。
- MWI 命令下到期时 FRAME# 仍置位,且当前传输的是非 CacheLine 的最后一个 DWORD → 在当前 CacheLine 末尾终止。
- MWI 命令下到期时 FRAME# 仍置位,且当前是 CacheLine 的最后一个 DWORD → 在下一个 CacheLine 末尾终止。
2.8.4 带宽与延迟的权衡
PCI 系统存在「低延迟」与「高带宽」之间的权衡:
- 大 Latency Timer = 高吞吐量、高延迟(允许长突发传输)。
- 小 Latency Timer = 低延迟、低吞吐量(限制最大突发长度)。
简言之,允许设备用长突发传输实现高带宽,通过减少最大突发传输长度来实现低延迟。下面这张图给出了不同突发长度下的带宽/延迟关系:
图 2-22 带宽与延迟权衡。图下方原表的计算假设:主/从设备初始延迟为 8 个时钟、后续数据阶段无延迟、数据阶段数量为 2 的幂、Latency Timer 值选在倒数第二个数据阶段到期。
2.8.5 仲裁延迟的确定与 Buffer 需求
- 确定仲裁延迟:Master 从申请 REQ# 到真正开始事务等待的时间就是仲裁延迟,没有固定答案,受系统负载、总线使用效率等因素影响。
- 确定 Buffer 需求:每个设备都需要 Buffer 以提高传输效率。规范建议低速设备单次至少传 4 个 DWORD,高速设备至少 32 个 DWORD。事务过短会导致效率低下------单次只传 1 个 DWORD 时,地址阶段、仲裁、等待等协议开销甚至会超过实际数据量。因此规范建议尽量积累数据后采用 Burst 模式一次发送。
2.9 其他总线操作
2.9.1 设备选择(Device Selection)
DEVSEL# 由当前被寻址的目标设备驱动,向 Master 响应本次访问。它必须早于或等于 TRDY#、STOP#,且必须在地址译码完成后才能驱动。解码速度分四档:
| 档位 | 时机 |
|---|---|
| Fast | 第 1 个时钟 |
| Medium | 第 2 个时钟 |
| Slow | 第 3 个时钟 |
| Subtractive | 前 3 周期无人认领时,第 4 个时钟由 Subtractive Decode Agent 置位(如 PCI-to-ISA Bridge) |
异常处理:
- Master-Abort:FRAME# 断言后 3 个时钟内无人置位 DEVSEL#,且系统中无 Subtractive Decode Agent,主设备强行中止事务。
- Target-Abort:Target 置位 DEVSEL# 后发现无法处理,立即撤销 DEVSEL# 并以目标中止响应。

图 2-23 DEVSEL# 解码时序。
2.9.2 Special Cycle
Special Cycle 是 PCI 提供的一种简单消息广播机制:没有显式寻址,广播至同总线段的所有 Agent,由接收方自行判断是否适用。它的几个特性:
- Special Cycle 不会穿越 PCI-to-PCI Bridge。若需在特定桥下生成,必须用 Type 1 配置写定位到具体总线。
- 没有任何代理通过置位 DEVSEL# 响应,因此 Special Cycle 以 Master-Abort 正常结束。
信号定义:
- 地址周期:C/BE# = 0001(Special Cycle 命令编码),AD 总线驱动为任意值(忽略)。
- 数据周期(低 16 位):AD15:00 → 消息类型。
- 数据周期(高 16 位):AD31:16 → 可选数据字段。
图 2-24 Special Cycle 时序。
2.9.3 Interrupt Acknowledge
外部设备发生中断后,CPU 需要得到中断向量以确定中断来源和要执行的中断服务程序。Interrupt Acknowledge 就是 CPU 向中断控制器获取向量(Vector)的过程:
- 地址周期中 AD31:00 被驱动为稳定值(无有效地址),但 PAR 必须有效并参与奇偶校验。
- 中断控制器必须在 FRAME# 置位后 3 个时钟内置位 DEVSEL#。
- 最后中断控制器置位 TRDY# 并返回中断向量。
图 2-25 Interrupt Acknowledge 时序。
2.10 错误功能
PCI 提供了检测和报告奇偶校验及其他系统错误的机制,但只负责发现和报告错误,不负责规定如何恢复------底层硬件把异常检测出来传递给软件,软件再决定如何处理。
2.10.1 奇偶校验的生成与检查
PCI 采用偶校验解决传输中的数据准确性问题。参与校验的信号包括 AD 线、C/BE# 以及 PAR 位:所有参与计算的位中「1」的个数必须为偶数。
PAR 信号「谁驱动 AD 线谁负责」,且比对应的 AD 数据晚一个时钟周期出现。
2.10.2 地址奇偶校验错误
地址奇偶校验错误往往比数据错误更危险,因为可能访问到非预期的 Target。由于 Target 并不知道正确的地址是什么,规范定义它可以做三种操作:
- 认领事务并正常终止;
- Target-Abort 终止;
- 不响应事务,Master-Abort 终止。
图 2-26 奇偶校验相关机制。
2.10.3 错误报告:PERR# 与 SERR#
PERR# 与 SERR# 是两条错误报告信号,简单说:PERR# 代表数据错误,SERR# 代表系统错误。
- PERR#:专用于数据阶段报告数据奇偶校验错误,触发原则是「谁发现数据错误谁拉低 PERR#」。它通常意味着局部数据问题,系统可恢复(Recoverable)。
- SERR#:代表更严重的系统级错误,涵盖地址奇偶校验错误、Special Cycle 奇偶校验等。系统收到 SERR# 时通常会直接触发 NMI、Machine Check,甚至崩溃。
配置空间里有两个奇偶校验相关寄存器位:
- Status 寄存器 Bit 8(Master Data Parity Error):指示当前设备作为 Master 时发现了数据奇偶校验错误。
- Status 寄存器 Bit 15(Detected Parity Error):表示设备检测到了奇偶校验错误。只要设备检测到错误,无论 Command 寄存器里的 Parity Error Response 是否使能,都必须无条件把 Bit 15 置位。
2.10.4 延迟事务与数据奇偶校验错误
- 读事务:数据源由目标提供,错误在 Master 完成阶段发现,Master 检测奇偶错并置位 PERR#,按常规非延迟事务处理。
- 写事务:数据源由 Master 在每次 Retry 时重复提供,错误可能在初始请求阶段或后续重试阶段发生,性质可能是 constant(持续)或 transient(瞬时)。当 Target 在写延迟事务中检测到奇偶校验错误时:① 完成当前数据相位;② 报告错误;③ 丢弃该事务,但 Target 仍持有之前已完成的数据,Discard Timer 到期后丢弃。
2.11 64 位总线扩展与 64 位寻址
2.11.1 64 位总线扩展
PCI 支持 64 位扩展总线,需要额外引脚:REQ64#、ACK64#、AD63::32、C/BE7::4# 和 PAR64。
- Master 拉低 REQ64# 发起 64 位传输请求,目标采样到后通过拉低 ACK64# 响应。
- 64 位事务运行时,所有 PCI 总线协议和时序规则保持不变;且只有内存事务适合用 64 位数据传输。
当 Master 请求 64 位数据传输时,目标有三种基础响应:
- 拉低 ACK64#,用完整 64 位数据通路完成该事务;
- 保持 ACK64# 无效,用 32 位数据通路完成该事务;
- 保持 ACK64# 无效,同时拉低 STOP#,仅完成单次 32 位数据传输。
64 位奇偶校验信号 PAR64 覆盖 AD63::32 与 C/BE7::4#,逻辑与 32 位 PAR 一致。
图 2-27 64 位读事务(目标拉低 ACK64# 响应,64 位数据相位完成)。
图 2-28 64 位写事务(目标未拉低 ACK64#,降级为 32 位模式)。
2.11.2 64 位寻址
- 32 位或 64 位设备要支持 64 位寻址,无需额外新增引脚;仅支持 32 位地址的设备会被映射到 4GB 以下地址空间,且只有内存类事务支持 64 位寻址。
- 即使主从设备都支持 64 位总线,Master 也必须固定用 2 个时钟传输 64 位地址------因为地址阶段无法提前获知从设备侧的总线位宽。
- 32 位寻址设备可与 64 位寻址规则透明兼容:支持 64 位寻址的 32 位从设备会先暂存 AD31::00 的低 32 位地址,下一时钟锁存高 32 位;64 位从设备则第一个地址周期就锁存完整地址。
- 完整地址传输完成、命令被锁存后,从设备再判断是否拉低 DEVSEL#。
- Master 在完整地址传输完成前无法判断从设备是否支持 64 位数据通路,因此地址传输阶段必须默认按 32 位从设备的兼容逻辑执行。
图 2-29 64 位寻址。
第 3 章 配置空间
3.1 配置空间组织
每个 PCI 设备的每个 Function 都有一段 256 字节的配置空间(Configuration Space),里面定义了符合 PCI 标准设备的配置寄存器的编程模型与使用规则。本章内容覆盖:配置空间组织、配置寄存器功能、PCI 扩展 ROM、Capabilities List 和 MSI。
下图是配置空间的预定义头部(Header),包含 PCI 规定的特定寄存器:

图 3-1 配置空间预定义头部。
标准 Type 0 配置空间的寄存器布局如下(这是 PCI 规范定义的固定布局):
| 偏移 | 寄存器 | 说明 |
|---|---|---|
| 00h | Vendor ID | 厂商标识,0FFFFh 为无效值 |
| 02h | Device ID | 设备型号标识 |
| 04h | Command | 设备控制寄存器 |
| 06h | Status | 设备状态寄存器 |
| 08h | Revision ID | 设备版本标识 |
| 09h--0Bh | Class Code | 设备类型(三个字节字段) |
| 0Ch | Cache Line Size | 缓存行大小(以 DWORD 计) |
| 0Dh | Latency Timer | 延迟定时器(以 CLK 计) |
| 0Eh | Header Type | Bit7 单/多 Function,Bits6:0 头部类型 |
| 0Fh | BIST | 内置自测试控制与状态 |
| 10h--27h | BAR0--BAR5 | 六个基地址寄存器 |
| 28h | CardBus CIS Pointer | CardBus 卡信息结构指针 |
| 2Ch | Subsystem Vendor ID | 子系统厂商 ID |
| 2Eh | Subsystem ID | 子系统 ID |
| 30h | Expansion ROM Base Address | 扩展 ROM 基地址 |
| 34h | Capabilities Pointer | 能力列表指针 |
| 3Ch | Interrupt Line | 中断线路由信息 |
| 3Dh | Interrupt Pin | 中断引脚(1--4 对应 INTA#--INTD#) |
| 3Eh | MIN_GNT | 最小突发持续时长(1/4µs 单位) |
| 3Fh | MAX_LAT | 最大总线访问间隔(1/4µs 单位) |
3.2 配置寄存器详解
3.2.1 设备识别(Device Identification)
- Vendor ID :标识设备生产厂商,
0FFFFh是无效的厂商 ID 值。 - Device ID:标识具体设备型号。
- Revision ID:设备专属的版本标识。
- Header Type :Bit7 为 0 代表单 Function 设备、1 代表多 Function 设备;Bits6:0 中
00h是普通设备、01h是 PCI-to-PCI 桥、02h是 CardBus 桥。 - Class Code:标识设备类型,分三个单字节字段(如内存控制器类、SATA 类、支持 AHCI 规范等)。
3.2.2 设备控制寄存器(Command)
Command 寄存器(偏移 04h)控制设备的多项行为:
| 位 | 字段 | 含义 |
|---|---|---|
| --- | I/O Space | 是否支持 I/O 空间访问请求 |
| --- | Memory Space | 是否支持内存空间访问请求 |
| --- | Bus Master | 是否可作为 Master 发起访问 |
| --- | Special Cycles | 是否响应 Special Cycle |
| --- | Memory Write and Invalidate Enable | 是否支持 MWI |
| --- | Parity Error Response | 是否触发 PERR# |
| --- | SERR# Enable | 是否使能 SERR# 输出(地址奇偶错误需此位与 Parity Error Response 同时为 1 才上报) |
| --- | Fast Back-to-Back Enable | 作为 Master 能否向不同设备发起 Fast Back-to-Back |
| --- | Interrupt Disable | 是否禁用 INTx# 中断信号 |
图 3-2 设备控制寄存器。
3.2.3 设备状态寄存器(Status)
Status 寄存器(偏移 06h)反映设备的状态:
| 位 | 字段 | 含义 |
|---|---|---|
| --- | Interrupt Status | 设备/功能的中断状态 |
| --- | Capabilities List | 是否实现 Capabilities List |
| --- | 66 MHz Capable | 是否有 66MHz 能力 |
| --- | Fast Back-to-Back Capable | 是否支持接收非同一代理的 Fast Back-to-Back |
| Bit 8 | Master Data Parity Error | 仅 Bus Master 需实现;满足①检测到 PERR# ②出错的 Bus Master 置位 ③Command 奇偶错误响应位为 1 三个条件时置 1 |
| --- | DEVSEL Timing | 00b=Fast、01b=Medium、10b=Slow、11b=保留 |
| --- | Signaled Target Abort | Target 以 Target-Abort 状态终止 |
| --- | Received Target Abort | Master 事务被 Target-Abort 终止 |
| --- | Received Master Abort | Master 事务被 Master-Abort 终止(Special Cycle 除外) |
| --- | Signaled System Error | 设备触发 SERR# |
| Bit 15 | Detected Parity Error | 检测到奇偶错误时置 1(即使错误处理功能被禁用) |
图 3-3 设备状态寄存器。
3.2.4 杂项寄存器(Miscellaneous Registers)
- CacheLine Size(0Ch):以 DWORD 为单位指定缓存行大小。所有支持生成 MWI 命令的 Master 必须实现;支持 CacheLine Wrap 寻址的从设备也必须实现,以此识别突发序列何时回绕到缓存行起始。
- Latency Timer(0Dh):以 CLK 为单位,设置延迟定时器大小。
- BIST(0Fh):控制内置自测试功能、反馈自测试状态。
- CardBus CIS Pointer(28h):供需要在 CardBus 与 PCI 间复用芯片的设备使用,指向 CardBus 卡的卡信息结构(CIS)。
- Interrupt Line(3Ch):传递中断线路由信息,数值标识设备中断引脚连接到系统中断控制器的哪一路输入,供驱动和 OS 确定优先级与向量。
- Interrupt Pin(3Dh):数值 1--4 对应 INTA#--INTD#。
- MIN_GNT / MAX_LAT(3Eh/3Fh):单位为 1/4µs。MIN_GNT 指定 33MHz 时钟下设备单次突发传输的最短时长;MAX_LAT 指定设备两次获得总线访问权之间的最大允许间隔。
- Subsystem Vendor ID / Subsystem ID(2Ch/2Eh):标识制造设备的公司(需申请)/自家不同型号的卡。
- Capabilities Pointer(34h):指向 Capabilities List,最低两位保留必须为 00b。
图 3-4 杂项寄存器。
3.2.5 基地址寄存器(BAR)
BAR(Base Address Register) 存放空间的基址。BAR0--BAR5 共 6 个,偏移 10h--27h。它的关键位定义:
- Bit0:只读位,标识该寄存器映射到内存空间(0)还是 I/O 空间(1)。
- 内存空间:Bit2:1 表示可映射到 32 位或 64 位空间的任意位置;Bit3 表示是否支持预取(Prefetchable,读操作不受字节使能影响、返回全部字节)。
由于 I/O 空间较小,规范强烈建议 BAR 映射在内存空间。
BAR 空间大小的计算方法(「写 1 回读」法):向该寄存器写入全 1,再读回,清除低字节无关位后取反加 1,即得到 BAR 所需空间大小。
例:1MB 的 32 位内存基址寄存器
写入 0xFFFFFFFF → 回读(低 4 位是只读标志位)
清除低 4 位 → 0xFFF00000
取反加 1 → 0x00100000 = 1MB ✓

图 3-5 BAR 寄存器。
3.2.6 扩展 ROM 基地址寄存器
该寄存器(偏移 30h)指向设备板载的 BIOS 代码:
- 高 21 位:对应扩展 ROM 基地址的高 21 位。例如需要 64KB 空间映射扩展 ROM 的设备,只需实现高 16 位,剩余低位硬件硬连线为 0。
- Bit0:扩展 ROM 地址空间是否被禁用。
- Command 寄存器的内存空间位优先级高于扩展 ROM 使能位:只有内存空间位和扩展 ROM 使能位同时置 1 时,设备才响应针对自身扩展 ROM 的访问。
图 3-6 扩展 ROM 基地址寄存器。
3.3 PCI 扩展 ROM
PCI 规范允许设备提供扩展 ROM(Expansion ROM) 代码,用于完成设备专属初始化。该机制支持 ROM 内存储多份不同固件镜像,以适配不同的主机与处理器架构。
扩展 ROM 头部信息支持的核心功能:
- 提供长度编码字段,标识 ROM 镜像在初始化阶段所需的连续地址空间总大小。
- 提供类型标识位,指明每份镜像中可执行或解释型代码的具体类型。
- 提供代码与数据的版本修订级别字段。
- ROM 内置了所支持 PCI 设备的厂商 ID 与设备 ID。
一个关键点:PCI 扩展 ROM 的代码绝不会在 ROM 芯片中原位执行,必须先拷贝到系统 RAM,再从 RAM 加载运行。
图 3-7 PCI 扩展 ROM。
3.3.1 扩展 ROM 内容
芯片内可按需存放任意数量的代码镜像,分别适配不同系统与处理器架构。每份代码镜像必须从 512 字节对齐的地址边界开始。
PCI 扩展 ROM 头部格式(Header Format)的关键字段:
- ROM Signature:必须位于每份 ROM 镜像 ROM 地址空间的最开头两个字节。
- Pointer to PCI Data Structure:指向对应的 PCI Data Structure。


图 3-8 扩展 ROM 内容与头部格式。
3.3.2 PCI Data Structure
PCI Data Structure 必须位于 ROM 镜像的前 64KB 地址范围内,其关键字段:
| 字段 | 说明 |
|---|---|
| Signature | 固定字符串 "PCIR" |
| Vendor Identification | 与配置空间 Vendor ID 一致 |
| Device Identification | 与配置空间 Device ID 一致 |
| PCI Data Structure Length | 结构总长度 |
| PCI Data Structure Revision | 结构版本号 |
| Class Code | 同配置空间 |
| Image Length | 当前镜像总长度,单位 512 字节 |
| Revision Level of Code/Data | 代码版本号 |
| Code Type | 代码类型,3 代表 EFI |
| Indicator | 标记是否是最后一个 Image |
图 3-9 PCI Data Structure 格式。
3.3.3 POST 代码
POST(Power-on Self Test,上电自检)代码通过两个步骤检测设备是否存在 Option ROM:
- 先判断设备配置空间是否实现了扩展 ROM 基址寄存器。若已实现,POST 必须将 ROM 映射并启用到一段未使用的地址空间,随后检查 ROM 起始位置前两个字节是否为
AA55h签名。 - 若确认存在 ROM,POST 在 ROM 内搜索代码类型正确、且 Vendor ID / Device ID 与配置空间完全匹配的固件镜像,将其拷贝到系统 RAM 并执行初始化代码。
3.3.4 PC 兼容扩展 ROM
PC 兼容系统中,ROM 镜像有附加要求。「Code Type」字段指定为「Intel x86, PC-AT compatible」。
ROM 头部扩展:为适配 PC 兼容系统,标准头部小幅扩展了两个字段:
- 偏移
02h:提供该镜像的初始化空间大小。 - 偏移
03h:指定扩展 ROM 初始化(INIT)函数的入口地址。

图 3-10 PC 兼容扩展 ROM 头部。
POST 代码扩展:POST 将「初始化大小」指定的字节数拷贝到 RAM,随后调用偏移 03h 处的 INIT 函数。针对每个扩展 ROM,PC 兼容系统的 POST 执行以下步骤:
- 将扩展 ROM 映射并启用到一段未被占用的内存地址空间;
- 在 ROM 中找到匹配的固件镜像,按「初始化大小」拷贝到 RAM 兼容区域(通常为
0xC0000~0xDFFFF); - 禁用扩展 ROM 基址寄存器;
- 保持 RAM 区域可写,调用 INIT 函数;
- 读取偏移 02h 处的字节(可能被 INIT 修改),确定系统运行时实际占用的内存大小。
在系统正式引导启动前,POST 必须将包含扩展 ROM 代码的 RAM 区域设置为只读。
INIT 函数扩展:INIT 执行期间,存放代码的 RAM 区域保持可写,因此 INIT 被允许使用部分扩展能力:
- INIT 可在自身占用的 RAM 区域存储静态参数,供运行态固件或驱动调用(该区域运行期间被设为不可写)。
- INIT 可修改 02h 处的长度字节,调整运行态 RAM 占用量,节省
0xC0000~0xDFFFF区域内有限的内存资源。 - INIT 必须保证镜像长度范围内校验和合法;若修改了 RAM 区域内容,必须重新计算校验和并写回。
- 若 INIT 想完全移除自身,只需把初始化大小字段(偏移 02h)写 0。
- INIT 被调用时传入三个参数:设备的总线号、设备号、功能号,用于访问待初始化的目标设备。
图 3-11 INIT 函数扩展。
3.4 系统复位
系统复位(System Reset)之后,处理器必须能够访问 Boot Code 以及完成系统启动所需的各种设备 Option ROM。因此有些 PCI 设备必须具有上电即可工作的默认映射,例如 Boot ROM、SPI Flash Controller、Host Bridge 等。这些设备可能在 Reset 后已经能响应某个固定地址(Fixed Address)------设备在 BIOS 尚未配置 BAR 之前,就已经固定响应某个地址范围(如 FFFFFFFFFFF00000h 可能固定映射到 Boot ROM)。
图 3-12 系统复位后的默认映射。
3.5 Capabilities List
某些在 PCI 2.1 版本之后新增的功能(Capabilities)通过配置空间里新增的一组寄存器实现,这些寄存器按链表 形式组织,称为 Capabilities List。
- 若设备支持能力列表,应将 Status 寄存器中的 Capabilities List 位置 1,表示偏移 34h 处存在 Capabilities Pointer,指向能力列表中的第一个能力结构。
- 当 Capabilities Pointer 值为
00h时,表示当前能力结构已是列表最后一个节点。

图 3-13 Capabilities List 结构。
3.6 MSI / MSI-X
3.6.1 什么是 MSI
MSI(Message Signaled Interrupts,消息信号中断) 是一项可选功能:设备功能通过向系统指定地址写入一个系统指定数据值来请求服务,而不是拉低传统的中断引脚。系统软件在设备配置期间初始化消息地址和消息数据(Vector),为每个支持 MSI 的功能分配一个或多个向量。
MSI 的核心思想是把中断转化为一次特殊的 PCI 内存写事务:
sequenceDiagram participant Dev as 设备 Function participant APIC as APIC 中断控制器 participant CPU Note over Dev: 中断事件发生 Dev->>APIC: 内存写事务:地址=FEE00000h,数据=中断向量 APIC->>CPU: 通知 CPU(识别中断来源)
设备不再拉低引脚,而是向系统指定地址(如 FEE00000h)写入预设数据;APIC 收到该特殊地址的写操作即识别为中断并通知 CPU。写入的数据对应不同中断向量,使 CPU 直接辨别中断来源。这两个关键的 Address 和 Data 值由 BIOS 或 OS 在初始化时填入设备的 MSI Capability。
关于 MSI 的几个要点:
- 由于事务目标无法区分 MSI 写事务与其他写事务,因此支持所有事务终止条件。
- 规范建议设备实现中断引脚,以便在不支持 MSI 的系统中提供兼容。
- MSI-X 是 MSI 的一个独立可选扩展,支持每个功能更多的向量数;MSI 与 MSI-X 均支持 per-vector masking。
- 一个功能可同时实现 MSI 和 MSI-X,但系统软件禁止同时启用两者。
3.6.2 MSI Capability 结构
要请求服务,MSI 功能把 Message Data 寄存器的内容写入 Message Address 寄存器(64 位地址还包括 Message Upper Address)指定的地址。支持 MSI 的功能按所支持的可选功能实现三种结构之一:
- 32 位消息寻址;
- 64 位消息寻址;
- 64 位消息寻址 + Per-vector Masking。
如果功能作为 Master 支持 64 位寻址(DAC),或支持 per-vector masking,则必须实现 64 位寻址。

图 3-14 MSI Capability 结构。
MSI Capability 的字段:
| 字段 | 说明 |
|---|---|
| Capability ID | 值 05h 表示支持 MSI(只读) |
| Next Pointer | 指向 Capabilities List 的下一项 |
| Message Control Bit8 | Per-vector masking capable(若为 1,Mask/Pending Bits 寄存器必须存在) |
| Message Control Bit7 | 64 bit address capable(是否支持 64 位消息地址) |
| Message Control Bits6:4 | Multiple Message Enable(软件写入,选择实际启用的向量数) |
| Message Control Bits3:1 | Multiple Message Capable(硬件固化,软件读取以确定可请求的向量数) |
| Message Control Bit0 | MSI Enable(是否启用 MSI) |
| Message Address Bits31:2 | MSI 内存写事务的 DWORD 对齐地址 |
| Message Address Bits1:0 | Reserved |
| Message Upper Address | 高位地址,仅支持 64 位消息地址时实现 |
| Message Data Bits15:0 | 写事务 Data Phase 的 AD15:00(AD31:16 为 0),低位表示 vector |
| Mask Bits / Pending Bits | 每个向量对应一位;屏蔽期间中断发生则 Pending 置 1,解除屏蔽后补发并清 Pending |
3.6.3 MSI-X Capability 与表结构
MSI-X Capability 包含一个 BAR 指示符寄存器(BIR) 和一个 Table Offset 寄存器。单独的 MSI-X 表结构通常包含多个条目,每个条目由一个 Message Address 字段和一个 Message Data 字段组成。MSI-X 表由 BIR 指示的 BAR 映射。

图 3-15 MSI-X Capability 与表结构。
MSI-X Capability 字段:
| 字段 | 说明 |
|---|---|
| Capability ID | 11h |
| Next Pointer | 列表中的下一项 |
| Message Control Bit15 | MSI-X 使能 |
| Message Control Bits10:0 | Table size = 值 + 1 |
| Message Upper Address | 为 0 生成 SAC(单地址周期)消息,非 0 生成 DAC(双地址周期)消息 |
| Table Offset/BIR Bits31:3 | 表偏移 |
| Table Offset/BIR Bits2:0 | BIR |
MSI-X 表条目字段:
| 字段 | 说明 |
|---|---|
| Message Address Bits31:2 | 消息地址,AD1:0 在地址阶段被驱动为 0 |
| Bit1 | 屏蔽位(Mask) |
| Bit0 | 挂起位(Pending) |
| Message Data Bits31:0 | 内存写事务 Data Phase 上 AD31:0 的数据,C/BE# 置位 |
3.6.4 MSI / MSI-X 的配置与操作
MSI 配置:
- 系统软件读取 Message Control 寄存器确定 MSI 能力;
- 读取 Multiple Message Capable 字段确定可请求的向量数;
- 写入 Multiple Message Enable 字段分配全部或部分向量;
- 若 Per-vector Mask 能力位被置位,可写入 Mask Bits 屏蔽一个或多个向量;
- 若 64 位地址能力位被置位,用系统指定地址初始化 Message Address 和 Message Upper Address。
MSI-X 配置:
- 系统软件为 Function 的 BAR 分配地址空间,其中某个 BAR 包含 MSI-X 表空间;
- 配置 MSI-X 能力与表结构以使用合适的向量;
- 从 Message Control 读取 Table Size 确定表大小(最大 2048);
- 读取 Table Offset/BIR 计算 MSI-X 表基地址;
- 根据 Message Upper Address 判断 SAC/DAC;
- 为每个要使用的表条目填写 Message Address、Mask 位和 Message Data 字段。
配置时,系统软件遍历 Function 的能力列表:找到能力 ID 05h 则实现 MSI,找到 11h 则实现 MSI-X。
使能操作 :MSI 的 MSI Enable 位与 MSI-X 的 MSI-X Enable 位在复位后均被清除,系统软件设置其一,从不同时启用两者。MSI、MSI-X 与 INTx# 是互斥的。
生成消息:
- 对于 MSI,所写的双字由 Message Data 寄存器值(低 2 字节)和高 2 字节的 0 组成;若 Multiple Message Enable 非零,功能可修改消息数据低阶位以生成多个向量。
- 对于 MSI-X,从选定表条目取 32 位 Message Data,不对低阶位做修改。
Per-vector Masking:在 MSI 中是可选功能,在 MSI-X 中是标准功能。Mask 置位时功能禁止生成相关消息,若本应生成则置相应 Pending 位;软件清除 Mask 后,功能立即补发并清 Pending 位。
软硬件同步:功能发出 Vector A 后,在驱动明确允许前不能再次发出 Vector A;若无数据丢失风险则无需握手。功能可在无握手情况下连续发 Vector A、Vector B(都会被服务)。系统保证功能在发送中断消息前写入的数据,在中断服务程序访问前已到达最终目的地。
消息事务终止:MSI 事务的目标无法将其与其他内存写区分,终止要求与普通内存写相同。若 MSI/MSI-X 写事务以 Master-Abort 或 Target-Abort 终止,发起方 Master 必须断言 SERR# 报告错误,目标则忽略该事务。若 MSI/MSI-X 写事务导致数据奇偶校验错误,发起方 Master 必须断言 SERR#。
消息接收与排序 :与所有内存写事务一样,中断接收器作为目标必须完成所有中断消息事务,无需等待其他事务。每次收到中断消息可设置内部寄存器的一位,随后调度相应中断服务程序。虽然中断消息在整个 PCI 层次结构中严格有序,但消息接收顺序不能保证中断被服务的顺序。
小结
本文从信号定义、总线操作、配置空间三条主线,系统梳理了 PCI Local Bus Specification 3.0 的核心内容。回顾几个贯穿全文的关键概念:
- 地址/数据复用 与以 FRAME#/IRDY#/TRDY# 为核心的握手,构成了 PCI 事务的基本骨架。
- Posting 与事务排序是桥接器行为的核心,也是理解数据一致性与死锁规避的钥匙。
- 延迟事务、仲裁、Latency Timer 共同决定了总线的带宽与延迟特性。
- 配置空间通过 IDSEL 而非地址线选中设备,承载了设备识别、资源分配(BAR)、扩展 ROM 与 MSI/MSI-X 中断等现代 PCI 能力。
PCI 是理解 PCIe 的重要铺垫------PCIe 虽然在物理层改用了串行点对点链路,但配置空间模型、BAR、MSI/MSI-X 这些软件可见的概念几乎原封不动地继承了下来。理解了本文的内容,再去看 PCIe 会轻松很多。
本文内容主要整理自PCI Local Bus Specification学习笔记。
参考资料
- PCI Local Bus Specification, Revision 3.0