Verilog中generate块内模块实例化与赋值问题求助
序列检测器Verilog实现问题排查
问题背景
尝试用2个D触发器实现1010序列检测器,推导得到逻辑表达式:
- D1 = (Q2&~X | Q1&~Q2&X)
- D2 = X
- Z = Q1&Q2&~X
其中X为输入,Z为输出。编写的代码出现以下问题: - q1、q2初始时刻为X,未正确赋值为inp[9]和inp[8]
- 疑惑是否该使用非阻塞赋值
- 不清楚如何解决generate块在仿真前运行导致clk无效的问题
原代码
module d_flipflop(d, clk, q); input d, clk; output reg q; always @ (posedge clk) q <= d; endmodule module seq_detector (clk, inp, outp); input [9:0] inp; input clk; output reg [9:0] outp; wire d1 = 0; wire q1 = inp[9], q2 = inp[8]; generate genvar i; for (i=9; i>=0; i=i-1) begin d_flipflop ff2 (.d(inp[i]), .clk(clk), .q(q2) ); // X <=> inp[i] assign d1 = (q2 & ~inp[i]) | (q1 & ~q2 & inp[i] ); d_flipflop ff1 (.d(d1), .clk(clk), .q(q1) ); assign outp[i] = q1 & q2 & ~inp[i]; end endgenerate endmodule module TB_seq_detector(); reg [9:0] inp, clk; wire [9:0] outp; seq_detector DUT (.clk(clk), .inp(inp), .outp(outp) ); // Generating clock: always #10 clk = ~clk; // Initializing clk: initial clk=0; // Test case: initial begin inp = 10'b1000101011; end initial begin $display("input: %b", inp[9:0]); $display("output: %b", outp[9:0]); end initial #100 $finish; endmodule
问题分析与修正方案
1. Generate块的误用
你的generate循环会实例化10组D触发器和逻辑,这完全违背了序列检测器的时序串行处理需求——序列检测器需要在时钟驱动下逐位处理输入序列,而非并行处理所有输入位。generate块在这里是错误的设计思路,直接移除即可。
2. 状态变量的定义与初始化问题
- q1、q2是D触发器的输出,必须定义为
reg类型(而非wire),因为触发器的输出是寄存状态,需要时序驱动更新。 - 原代码中
wire q1 = inp[9], q2 = inp[8];是组合逻辑赋值,没有时钟触发,无法实现触发器的寄存功能,自然会出现X态。 - 必须通过复位信号或initial块为q1、q2设置初始状态,避免仿真初始时的未知值。
3. 时钟逻辑的正确实现
序列检测器的状态更新和输出计算必须放在always @(posedge clk)(带复位的话加上negedge rst_n)的时序块中,用**非阻塞赋值<=**更新状态,这是Verilog时序电路设计的标准规范,避免竞争冒险。
修正后的代码
module seq_detector (clk, rst_n, inp, outp); input clk; input rst_n; // 新增复位信号,用于初始化状态 input inp; // 改为单bit串行输入,符合序列检测器工作方式 output reg outp; reg q1, q2; // 触发器输出,reg类型 wire d1, d2; // 实现推导的逻辑表达式 assign d2 = inp; assign d1 = (q2 & ~inp) | (q1 & ~q2 & inp); // 时序更新触发器状态 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin // 复位时初始化状态,这里设为0(可根据需求调整) q1 <= 1'b0; q2 <= 1'b0; end else begin q2 <= d2; q1 <= d1; end end // 计算输出 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin outp <= 1'b0; end else begin outp <= q1 & q2 & ~inp; end end endmodule module TB_seq_detector(); reg clk; reg rst_n; reg [9:0] inp_seq; // 存储待测试的10bit序列 reg inp; // 串行输入到检测器的单bit信号 wire outp; seq_detector DUT (.clk(clk), .rst_n(rst_n), .inp(inp), .outp(outp)); // 生成10ns周期的时钟 always #10 clk = ~clk; initial begin // 初始化所有信号 clk = 1'b0; rst_n = 1'b0; inp = 1'b0; inp_seq = 10'b1000101011; // 复位20ns后释放 #20 rst_n = 1'b1; // 逐周期串行输入序列的每一位 for (int i=9; i>=0; i=i-1) begin #20 inp = inp_seq[i]; $display("时间: %0t, 当前输入: %b, 输出: %b", $time, inp, outp); end // 仿真结束 #20 $finish; end endmodule
关键说明
- 串行输入设计:原代码的10bit并行输入不符合序列检测器的工作逻辑,改为单bit串行输入,在测试台中逐时钟周期输入序列的每一位。
- 复位初始化:通过复位信号
rst_n确保q1、q2在仿真开始时处于已知状态,解决初始X态问题。 - 非阻塞赋值:时序块中必须用
<=更新状态,保证时钟边沿触发的操作顺序正确,避免逻辑竞争。
内容的提问来源于stack exchange,提问作者Ishaan
相关产品推荐
相关产品推荐

