4、VC配置寄存器详解:VC Capability结构、VC Resource Control寄存器、VC Resource Status寄存器
好,咱们接着聊VC的配置寄存器。说实话,这部分内容在PCIe Spec里写得挺枯燥的,一堆bit位堆在一起,看着就头疼。但你要是真正动手配过Switch,就会发现这些寄存器其实挺有规律的。我当年第一次调VC的时候,就是对着Spec一个一个bit啃下来的,踩了不少坑。今天我把这些经验整理出来,希望能帮你少走弯路。
4.1 VC Capability结构——VC的“身份证”
每个PCIe设备,只要支持VC,就必须实现VC Capability结构。这个结构说白了就是告诉软件:我这设备能支持几个VC、每个VC有什么能力。它位于配置空间的Capability链表中,Capability ID是0x10。
我个人习惯把VC Capability结构分成三块来看:
- Capability Header:标准的PCIe Capability头,包含Cap ID、Next Cap Pointer、Cap Version。
- Port VC Capability Register 1:描述端口支持的VC数量、TC映射能力等。
- Port VC Capability Register 2:描述VC Arbitration能力、端口仲裁能力等。
嗯,这里要注意一个细节:VC Capability结构里还有一个VC Arbitration Table,它是个可选的查表结构。如果你的设备支持复杂的VC仲裁算法,就需要实现这个表。我在一个项目里见过一个Switch芯片,它支持8个VC,仲裁表有128个entry,配起来那叫一个酸爽。
核心字段速查:
| 字段 | 偏移 | 说明 |
|---|---|---|
| Extended VC Count | 0x0C[4:0] | 支持的扩展VC数量(不含VC0) |
| Port Arbitration Table Entry Size | 0x0C[7:6] | 端口仲裁表每个entry的bit数 |
| Reference Clock | 0x0E[3] | VC仲裁参考时钟选择 |
4.2 VC Resource Control寄存器——VC的“方向盘”
这个寄存器才是真正干活的地方。每个VC对应一组VC Resource Control寄存器,用来控制这个VC的使能、TC映射、仲裁策略等。说白了,你想让哪个VC干活,怎么干活,全在这里配。
我记得第一次配这个寄存器时,犯了个低级错误:我把VC0的Enable位清掉了,结果整个端口都不通了。后来才明白,VC0是必须使能的,你不能把它关掉。这是Spec的硬性规定。
VC Resource Control寄存器的主要字段:
- VC Enable:使能该VC。VC0必须为1,其他VC可选。
- TC/VC Map:将Traffic Class映射到该VC。每个bit对应一个TC,置1表示该TC走这个VC。
- Load Arbitration Table:加载仲裁表的触发位。写1后,硬件会重新加载仲裁表。
- Arb Select:选择VC仲裁算法。比如硬件固定优先级、轮询、加权轮询等。
实战小技巧:
配置TC/VC Map时,我建议你先把所有VC的Map清空,然后一个一个配。这样能避免TC被多个VC同时映射——虽然Spec允许这么做,但实际项目中很容易搞乱优先级。我曾经见过一个团队,因为TC0同时映射到了VC0和VC1,导致低优先级流量占用了高优先级VC的buffer,最后整条链路性能崩了。
4.3 VC Resource Status寄存器——VC的“仪表盘”
配完控制寄存器,总得看看效果吧?VC Resource Status寄存器就是干这个的。它主要提供两个信息:VC的当前状态和仲裁表是否加载完成。
关键字段就两个:
- VC Negotiation Pending:VC协商是否正在进行。如果这个位是1,说明VC还在协商中,你不能开始发数据。
- VC Arbitration Table Status:仲裁表是否加载完成。写1到Load Arbitration Table后,需要轮询这个位,直到它变成0。
嗯,这里有个坑:VC Negotiation Pending这个位,在链路训练完成后会自动清零。但如果你在热插拔或者链路重训练时去读它,可能会读到1。我建议你在配置VC之前,先确认这个位是0,否则写控制寄存器可能会被忽略。
避坑指南:
我曾经在一个项目里,因为没等VC Negotiation Pending清零就写了TC/VC Map,结果TC映射根本没生效。排查了两天才发现是时序问题。后来我养成了一个习惯:每次配置VC前,先读Status寄存器,确认Negotiation Pending为0,再写Control寄存器。写完Control后,再读Status确认仲裁表加载完成。三步走,稳得很。
4.4 知识体系结构图
下面这张图是我自己整理的VC配置寄存器知识体系,你可以把它当作一个快速索引。每次配VC前扫一眼,心里就有数了。
4.5 配置流程总结
好了,把上面这些串起来,一个标准的VC配置流程应该是这样的:
- 读VC Capability:确认设备支持几个VC,仲裁表大小是多少。
- 读VC Resource Status:确认VC Negotiation Pending为0。
- 写VC Resource Control:使能VC、配置TC映射、选择仲裁算法。
- 触发仲裁表加载:写1到Load Arbitration Table位。
- 轮询Status:等待Arbitration Table Status变为0。
- 重复步骤2-5:为每个需要配置的VC执行上述操作。
你想想看,这个流程其实跟咱们平时做项目初始化很像:先看文档(Capability),再设参数(Control),最后看日志确认(Status)。没什么高深的,就是细心点。
个人习惯:
我一般会在驱动初始化阶段,把所有VC的配置一次性写好,然后统一触发仲裁表加载。这样比一个一个配要快,而且不容易出错。当然,前提是你得把每个VC的TC映射规划清楚,别搞冲突了。
嗯,关于VC配置寄存器,今天就聊这么多。这部分内容虽然看起来琐碎,但它是整个VC和QoS配置的基础。你把这些寄存器吃透了,后面配仲裁策略、配流量整形,都会顺手很多。