You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 05:45:36