共计 2039 个字符,预计需要花费 6 分钟才能阅读完成。
优先编码器在中断控制器、键盘扫描等场景中非常实用,它能将多个输入信号中优先级最高的那个转换成二进制编码输出。今天我们就用 Verilog 来实现一个 16 输入、4 位输出的优先编码器,并讨论仿真中的常见问题。
行为级 vs 门级建模
在数字电路设计中,我们通常有两种建模方式:
- 行为级建模:用高级语言描述电路功能,代码简洁但可能不够直观
- 门级建模:用基本逻辑门搭建电路,接近实际硬件但代码量大
对于初学者,我建议先从行为级建模入手,等理解电路原理后再尝试门级实现。
Verilog 实现
核心代码
module pri_encoder_16_4 (input [15:0] din,
output reg [3:0] dout,
output reg valid
);
always @(*) begin
casex(din) // casex 允许 x 和 z 参与比较
16'b1xxxxxxxxxxxxxxx: begin dout = 4'b1111; valid = 1'b1; end
16'b01xxxxxxxxxxxxxx: begin dout = 4'b1110; valid = 1'b1; end
16'b001xxxxxxxxxxxxx: begin dout = 4'b1101; valid = 1'b1; end
// 中间省略部分 case...
16'b0000000000000001: begin dout = 4'b0000; valid = 1'b1; end
default: begin dout = 4'b0000; valid = 1'b0; end // 无有效输入
endcase
end
endmodule
这里用了 casex 语句来处理优先级,最高位 (din[15]) 优先级最高。valid 信号用于指示是否有有效输入。
测试平台
module tb_pri_encoder();
reg [15:0] din;
wire [3:0] dout;
wire valid;
// 实例化待测设计
pri_encoder_16_4 uut(.din(din), .dout(dout), .valid(valid));
// 测试任务封装
task test_case;
input [15:0] test_input;
input [3:0] expected_output;
input expected_valid;
begin
din = test_input;
#10; // 等待稳定
if(dout !== expected_output || valid !== expected_valid) begin
$display("Error: input=%b, got out=%b(valid=%b), expected=%b(valid=%b)",
test_input, dout, valid, expected_output, expected_valid);
end
end
endtask
initial begin
// 测试用例
test_case(16'b1000000000000000, 4'b1111, 1'b1);
test_case(16'b0100000000000000, 4'b1110, 1'b1);
test_case(16'b0000000000000001, 4'b0000, 1'b1);
test_case(16'b0000000000000000, 4'b0000, 1'b0); // 无输入
test_case(16'b0001000000001000, 4'b1011, 1'b1); // 多个输入
$display("Test complete");
$finish;
end
endmodule
波形分析
在 ModelSim 中仿真时,要特别注意几个关键点:
- 当多个输入同时有效时,输出是否正确响应最高优先级
- valid 信号在无输入时是否拉低
- 输入变化时是否存在毛刺(竞争冒险)

上图中红色标记处显示了输入变化时可能出现的短暂毛刺,这是组合逻辑的典型特征。
性能优化
传播延迟
16- 4 优先编码器的关键路径是从 din[0]到 dout[3]的路径。在典型 0.18μm 工艺下,传播延迟约为:
- 与门延迟:约 0.1ns
- 或门延迟:约 0.15ns
- 总延迟:约 4 级逻辑门 → 0.6-0.8ns
面积优化
使用 Design Compiler 时,可以通过以下命令优化面积:
compile -map_effort high -area_effort high
set_max_area 0
避坑指南
X 态传播
仿真时如果出现 x 态传播,通常是因为:
- 未初始化寄存器
- case 语句未覆盖所有可能
- 多驱动冲突
解决方法:
- 给所有寄存器赋初值
- 确保 case/default 覆盖所有情况
- 检查是否有多个 always 块驱动同一信号
多时钟域同步
如果编码器需要在不同时钟域使用,需要:
- 对输入信号进行两级触发器同步
- 对输出信号使用握手协议或 FIFO
扩展思考
如何将设计扩展为 32- 5 优先编码器?主要修改点包括:
- 输入位宽改为 32 位
- 输出改为 5 位
- casex 语句需要相应扩展
- 可能需要流水线设计以提高时钟频率
希望这篇入门指南能帮助你理解优先编码器的实现。在实际项目中,记得先用小规模测试验证基本功能,再逐步扩展复杂度。
正文完
发表至: 未分类
近三天内
