Verilog 4位并行输入极值检测代码仿真异常问题排查
嘿,我来帮你拆解这段代码里的几个关键问题,这些正是导致你仿真时pk_low始终为0、pk_high和pp_voltage处于未知态(X)的原因:
1. 组合逻辑错误使用非阻塞赋值(<=)
你在always @(*)这个组合逻辑块里,给pk_low和pk_high用了非阻塞赋值<=——这是Verilog开发里的典型错误!
非阻塞赋值<=是专门用于时序逻辑(带时钟沿触发的always块)的,它会在时钟沿到来后才完成赋值更新;而组合逻辑块里必须用阻塞赋值=,否则会导致信号更新延迟、竞争,甚至始终保持初始的未知态或错误值。你的pk_high一直是X,很大程度上就是这个原因导致的。
修改后的组合逻辑块应该是这样:
always @(*) begin case (state) st0: begin pk_low = pk_low; pk_high = pk_high; end st1: begin pk_low = pk_low; pk_high = parallel_in; end st2: begin pk_low = parallel_in; pk_high = pk_high; end endcase end
2. 初始化方式的问题(仿真与硬件实现的差异)
你用initial块来初始化state、pk_high和pk_low,这里存在两个致命问题:
- 仿真启动时,
parallel_in的初始值通常是未知态(X),所以pk_high <= parallel_in;和pk_low <= parallel_in;会直接把这两个寄存器初始化为X,后续的比较逻辑自然全部出错。 - 在实际FPGA硬件中,
initial块的行为是不确定的(多数FPGA不支持用initial初始化寄存器,或需要特殊工具配置),正确的做法是用复位信号(比如同步复位)来完成初始化。
建议添加复位端口,改用时钟沿触发的块做初始化:
module peak_voltage (clk, rst_n, parallel_in, pk_high, pk_low, pp_voltage); input clk; input rst_n; // 低电平复位 input wire [3:0] parallel_in; output reg [3:0] pk_high; output reg [3:0] pk_low; output wire [3:0] pp_voltage; reg state; parameter st0 = 'd0; parameter st1 = 'd1; parameter st2 = 'd2; always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin state <= st0; pk_high <= parallel_in; // 复位时用当前输入初始化,也可固定为4'hF/4'h0 pk_low <= parallel_in; end else begin if (parallel_in > pk_high)begin state <= st1; end else if (parallel_in < pk_low) begin state <= st2; end else begin state <= st0; end end end // 组合逻辑用阻塞赋值 always @(*) begin case (state) st0: begin pk_low = pk_low; pk_high = pk_high; end st1: begin pk_low = pk_low; pk_high = parallel_in; end st2: begin pk_low = parallel_in; pk_high = pk_high; end endcase end assign pp_voltage = pk_high - pk_low; endmodule
如果想让仿真初始值不是X,可以在测试台里给parallel_in设置初始值,比如initial parallel_in = 4'h0;。
3. 状态机逻辑的冗余与优化空间
你的状态机把“更新最大值”“更新最小值”“保持不变”拆成了三个状态,其实完全可以去掉状态机,直接在时钟沿的时序块里完成最值更新,逻辑更简洁,也避免了组合逻辑的潜在问题:
always @ (posedge clk or negedge rst_n) begin if (!rst_n) begin pk_high <= parallel_in; pk_low <= parallel_in; end else begin if (parallel_in > pk_high) begin pk_high <= parallel_in; end if (parallel_in < pk_low) begin pk_low <= parallel_in; end // 输入既不是新最大值也不是新最小值时,保持原值 end end
这种方式直接在时序逻辑里完成更新,符合Verilog最佳实践,出错概率更低。
4. 关于pk_low始终为0的猜测
如果你的仿真中parallel_in初始值是0,且后续输入都是4位无符号数(范围0-15),那么parallel_in < pk_low的条件永远不成立,state不会走到st2,pk_low就会一直保持初始的0。但只要修复了组合逻辑的赋值问题,后续如果有更小的输入(当然4位无符号数最小就是0),逻辑就能正常响应。
内容的提问来源于stack exchange,提问作者eesweng

