共计 3355 个字符,预计需要花费 9 分钟才能阅读完成。
背景痛点:为什么需要优先编码器?
在数字系统设计中,我们经常会遇到多个信号同时请求资源的情况。比如:

- 中断控制器需要处理多个设备的中断请求
- 总线仲裁器需要决定哪个主设备获得总线使用权
- 键盘扫描电路需要识别同时按下的多个按键
传统编码器在处理这类问题时有个致命缺陷:当多个输入信号同时有效时,输出会出现混乱。比如用普通 8 - 3 编码器处理键盘输入时,如果同时按下两个键,输出的二进制码就是两个按键编码的 ” 与 ” 或者 ” 或 ”,这显然不是我们想要的结果。
原理对比:普通编码器 vs 优先编码器
普通 8 - 3 编码器的真值表很简单:
| 输入 | 输出 |
|---|---|
| 00000001 | 000 |
| 00000010 | 001 |
| … | … |
| 10000000 | 111 |
但当输入为 00001100 时,输出就变得不确定了。优先编码器的聪明之处在于它引入了优先级概念:
- 预先定义好每个输入信号的优先级(通常高位优先级更高)
- 当多个输入有效时,只响应优先级最高的那个
- 输出编码始终对应当前最高优先级的有效输入
8- 3 优先编码器的典型应用场景包括:
- 中断优先级管理
- 总线请求仲裁
- 多路数据选择
Verilog 实现详解
基础版本实现
module pri_encoder_8to3 (input [7:0] din,
output reg [2:0] dout,
output reg valid
);
// 优先级编码逻辑
always @(*) begin
valid = 1'b0;
dout = 3'b000;
casez (din)
8'b1???????: begin dout = 3'b111; valid = 1'b1; end
8'b01??????: begin dout = 3'b110; valid = 1'b1; end
8'b001?????: begin dout = 3'b101; valid = 1'b1; end
8'b0001????: begin dout = 3'b100; valid = 1'b1; end
8'b00001???: begin dout = 3'b011; valid = 1'b1; end
8'b000001??: begin dout = 3'b010; valid = 1'b1; end
8'b0000001?: begin dout = 3'b001; valid = 1'b1; end
8'b00000001: begin dout = 3'b000; valid = 1'b1; end
default: begin dout = 3'b000; valid = 1'b0; end
endcase
end
endmodule
参数化改进版本
module pri_encoder #(
parameter INPUT_WIDTH = 8,
parameter OUTPUT_WIDTH = $clog2(INPUT_WIDTH)
) (input [INPUT_WIDTH-1:0] din,
output reg [OUTPUT_WIDTH-1:0] dout,
output reg valid
);
// 使用 for 循环实现可扩展性
integer i;
always @(*) begin
dout = {OUTPUT_WIDTH{1'b0}};
valid = 1'b0;
for (i = INPUT_WIDTH-1; i >= 0; i = i-1) begin
if (din[i]) begin
dout = i;
valid = 1'b1;
// 找到第一个高优先级就退出循环
disable loop;
end
end
// 命名块用于退出循环
loop: begin end
end
endmodule
Testbench 设计要点
完整的验证需要覆盖以下场景:
- 全 0 输入测试
- 单 bit 有效测试(遍历所有位)
- 多 bit 冲突测试(验证优先级)
- 随机输入测试
module tb_pri_encoder();
reg [7:0] test_in;
wire [2:0] test_out;
wire test_valid;
pri_encoder_8to3 uut(.din(test_in), .dout(test_out), .valid(test_valid));
initial begin
// 场景 1:全 0 输入
test_in = 8'b00000000;
#10;
// 场景 2:单 bit 测试
test_in = 8'b00000001;
#10;
test_in = 8'b00000010;
#10;
// ... 其他单 bit 测试
// 场景 3:多 bit 冲突
test_in = 8'b00001100; // 应识别 bit3
#10;
test_in = 8'b00110000; // 应识别 bit5
#10;
// 场景 4:随机测试
repeat(20) begin
test_in = $random;
#10;
end
$finish;
end
endmodule
优化进阶:组合逻辑 vs 时序逻辑
组合逻辑实现特点
- 延迟:取决于关键路径(通常 3 - 4 级 LUT)
- 面积:较小时(约 8 个与门 + 3 个或门)
- 优点:单周期完成编码
- 缺点:可能产生毛刺
时序逻辑实现变种
// 流水线版本(适合高频场景)always @(posedge clk) begin
if (rst) begin
dout_r <= 3'b0;
valid_r <= 1'b0;
end else begin
// 组合逻辑部分
casez (din)
// ... 同前
endcase
// 寄存器输出
dout_r <= dout;
valid_r <= valid;
end
end
时序实现的特点:
- 延迟:增加 1 个周期
- 面积:增加 3 + 1 个触发器
- 优点:消除毛刺,提高时序裕量
- 缺点:需要额外时钟周期
避坑指南
常见问题与解决方案
- 不定态 (x) 传播问题
- 现象:当输入包含 x 态时,casez 语句可能导致整个输出变为 x
-
解决方案:
// 在 casez 前添加 x 检测 if (^din === 1'bx) begin dout = 3'b0; valid = 1'b0; end else begin casez (din) // ... endcase end -
综合警告处理
- “incomplete case” 警告:添加 default 分支
-
“multi-driven net” 警告:检查 always 块重叠
-
调试技巧
- 使用 one-hot 编码辅助调试:
wire [7:0] one_hot; assign one_hot = (1 << dout) & {8{valid}}; - 添加 assertion 检查优先级:
assert property (@(posedge clk) valid |-> (din[dout] && !(|din[7:dout+1])));
延伸思考:如何扩展到 16- 4 编码器
改造思路:
- 输入位宽扩展到 16 位
- 输出位宽增加到 4 位(因为 log2(16)=4)
- 优先级规则可以灵活定义:
- 高位优先(与 8 - 3 一致)
- 可编程优先级(添加优先级配置寄存器)
- 分组处理技巧:
- 将 16 位分成两个 8 位组
- 先判断哪个组有请求
- 再在组内进行 8 - 3 编码
示例代码框架:
module pri_encoder_16to4 (input [15:0] din,
output reg [3:0] dout,
output reg valid
);
wire group1_valid = |din[15:8];
wire [2:0] group1_out;
wire [2:0] group0_out;
// 高位组编码
pri_encoder_8to3 u_high(.din(din[15:8]), .dout(group1_out), .valid(group1_valid));
// 低位组编码
pri_encoder_8to3 u_low(.din(din[7:0]), .dout(group0_out), .valid());
// 最终输出选择
always @(*) begin
if (group1_valid) begin
dout = {1'b1, group1_out};
valid = 1'b1;
end else begin
dout = {1'b0, group0_out};
valid = |din[7:0];
end
end
endmodule
实战心得
通过这个 8 - 3 优先编码器的实现,我总结了几点经验:
- 参数化设计可以大大提高代码复用性,比如我们的参数化版本可以轻松支持 4 -2、16- 4 等各种变体
- 验证时要特别注意边界情况,特别是全 0 输入和多 bit 冲突场景
- 在 FPGA 实现时,组合逻辑版本通常足够用,但在 ASIC 设计中可能需要考虑时序版本
- 优先级定义要根据实际应用场景灵活调整,有时可能需要动态优先级配置
希望这个实现能帮助大家解决实际项目中的信号优先级问题。如果你们在项目中遇到有趣的变种需求,比如加权优先级或者轮询调度,也欢迎交流讨论!
正文完
发表至: 未分类
近一天内
