3. 固定优先级仲裁:原理、陷阱与工程权衡
固定优先级仲裁,说白了就是给每个请求者排个队。谁优先级高,谁就先拿总线。这个机制在教科书里讲得挺简单,但实际工程里坑不少。我这些年调试过的总线系统,有一半以上的性能问题都跟优先级设置不当有关。
3.1 原理与实现
固定优先级仲裁的核心逻辑就一句话:所有请求者按优先级排序,仲裁器永远选择优先级最高的那个。优先级一旦设定,运行期间不会改变。
实现方式其实很直接。假设有 N 个请求者,优先级从高到低排列。仲裁器只需要一个优先级编码器和一个比较器网络。我习惯用下面这种结构:
// 固定优先级仲裁器核心逻辑(Verilog 描述)
module fixed_priority_arbiter #(
parameter NUM_MASTERS = 4
) (
input wire [NUM_MASTERS-1:0] request,
output wire [NUM_MASTERS-1:0] grant,
output wire grant_valid
);
// 优先级:master 0 最高,master N-1 最低
wire [NUM_MASTERS-1:0] priority_mask;
assign priority_mask = request & (~(request - 1)); // 取最低有效位
// 实际上就是找最右边的1
assign grant = priority_mask;
assign grant_valid = |request;
endmodule
这段代码里有个经典技巧:request & (~(request - 1)) 能直接取出最低位的1。为什么?你想想看,减1操作会把最低位的1变成0,后面的0变成1,取反后再与,就只剩那个最低位的1了。这个技巧我在很多项目里都用过,省逻辑又省时序。
实际工程中,我更推荐用查找表或者 case 语句来实现,尤其是当请求者数量不多时。这样代码可读性更好,调试也方便:
always_comb begin
case (1'b1)
request[0]: grant = 4'b0001;
request[1]: grant = 4'b0010;
request[2]: grant = 4'b0100;
request[3]: grant = 4'b1000;
default: grant = 4'b0000;
endcase
end
3.2 优先级反转问题
这是固定优先级仲裁最头疼的问题。什么叫优先级反转?就是低优先级的请求者占着总线不放,导致高优先级请求者拿不到总线。听起来反直觉,对吧?
我举个例子。假设有三个主设备:
| 主设备 | 优先级 | 任务 |
|---|---|---|
| CPU | 高 | 读取关键数据 |
| DMA | 中 | 批量传输 |
| GPIO | 低 | 低速外设 |
场景是这样的:GPIO 先拿到了总线,开始一个长传输。CPU 突然需要读关键数据,发出请求。按道理 CPU 优先级高,应该立刻拿到总线。但 GPIO 的传输还没结束,总线被它占着。CPU 只能等。这时候 DMA 也来凑热闹,发出请求。仲裁器一看,CPU 优先级最高,但 CPU 在等总线释放;DMA 优先级比 GPIO 高,所以 DMA 拿到了总线。结果呢?CPU 被两个低优先级的设备堵住了。
优先级反转的典型特征:高优先级请求者的等待时间不取决于它自己的优先级,而取决于低优先级请求者的传输长度。这完全违背了固定优先级的初衷。
3.3 实际工程中的权衡
既然优先级反转这么坑,为什么还要用固定优先级?因为它简单、确定、面积小。在以下场景中,我仍然会选它:
- 请求者数量少(2-4个),优先级反转概率低
- 传输长度可控,每个请求者都能在短时间内释放总线
- 实时性要求不高,偶尔的延迟抖动可以接受
- 面积和功耗敏感,比如物联网芯片
但如果系统对实时性有硬要求,就得做权衡。我常用的几种优化手段:
| 优化手段 | 原理 | 代价 |
|---|---|---|
| 限制传输长度 | 每个请求者最多连续占用N个时钟 | 增加传输延迟 |
| 优先级提升 | 等待时间过长的请求者临时提升优先级 | 增加逻辑复杂度 |
| 抢占机制 | 高优先级可以打断低优先级传输 | 需要保存/恢复状态 |
| 混合仲裁 | 固定优先级+轮询结合 | 设计复杂度上升 |
我个人最常用的是限制传输长度。比如规定每个请求者最多连续占用16个时钟周期。这样即使发生优先级反转,高优先级请求者的最大等待时间也是可控的。代价是低优先级设备的吞吐量会下降,但通常可以接受。
还有一种情况值得注意:当请求者的优先级与它的传输长度成反比时,固定优先级反而表现不错。比如CPU(高优先级)每次只读几个字,而DMA(低优先级)做长块传输。CPU能快速拿到总线,DMA在空闲时慢慢传。这种场景下,优先级反转几乎不会发生。
下面这张图展示了固定优先级仲裁的决策流程:
最后说一句,固定优先级仲裁不是银弹。它简单、高效,但需要工程师对系统行为有清晰的预判。如果你不确定系统里会不会出现优先级反转,那就先做仿真,把最坏情况下的等待时间跑出来。我见过太多项目,因为贪图固定优先级的简单,最后在系统集成阶段吃尽苦头。