如何计算 Verilog 中的特定序列?

How do I count a specific sequence in Verilog?

我需要设计一个可以计算 1011 位序列的电路。例如,如果序列如图所示,计数应该是1,但不是。我知道我正在读取输入 w 不是按顺序,但我也不知道该怎么做。

w is our input sequence and CS and NS are current state and next state. and parameters are our states. second picture's second state machine is what I used there is a slight difference between those two SMs.

主要逻辑:

module serialCounter(w,clk,resetn,z);
input w,clk,resetn;
output reg z;
reg [2:0] CS,NS;
reg [7:0] count;
parameter S0=3'b000, S1=3'b001, S2=3'b010, S3=3'b011, S4=3'b100;
always @(w,CS)
  case(CS)
    S0:if(w==1)
    begin
      NS=S1;
    end
       else
    begin
      NS=S0;
    end
    S1:if(w==1)
    begin
      NS=S2;
    end
       else
    begin
      NS=S0;
    end
    S2:if(w==1)
    begin
      NS=S2;
    end
       else
    begin
      NS=S3;
    end
    S3:if(w==1)
    begin
      NS=S4; 
    end
       else
    begin
      NS=S0;
    end
    S4:begin NS=S0;end
endcase

always @(posedge clk, negedge resetn)
begin
  if(resetn == 0)
    CS<=S0;
  else
    CS<=NS;
end
always @(CS) 
begin
  if(CS==S4)
    begin 
      z=1;
      count<=count+1;
    end
end
endmodule


测试台:

`timescale 1ns/1ns
module counterTB();
reg w,clk,resetn;
reg [2:0] CS,NS;
wire z;
wire [7:0] count;
integer i;
serialCounter sk(.w(w),.clk(clk),.resetn(resetn),.z(z));
initial
  begin
    resetn = 1'b0;
    clk = 1'b0;
    CS=3'b000;
  end
initial
begin
  for(i=0;i<255;i=i+1)
    @(posedge clk, negedge resetn)  w=i;
end
always #5 clk = ~clk;


always @(posedge clk, negedge resetn) $monitor("w=%b, z=%b, count=%d", w,z,count);

endmodule

有几个问题。

您断言重置,但您从未释放它(resetn 保持为 0)。您需要在几个时钟周期后将其设置为 1。

您需要在测试台中以不同方式驱动您的输入w。您的设计总是在它为 0 时对其进行采样。一种快速的方法是随机驱动它。

您错误地建模了 z 输出,我认为这是您的“检测”信号。我还从 always 块中删除了 count 逻辑,因为它似乎没有做任何重要的事情。

这是现在运行并检测 w 上的 1011 模式的代码:

module serialCounter(w,clk,resetn,z);
input w,clk,resetn;
output reg z;
reg [2:0] CS,NS;
reg [7:0] count;
parameter S0=3'b000, S1=3'b001, S2=3'b010, S3=3'b011, S4=3'b100;
always @(w,CS)
  case(CS)
    S0:if(w==1)
    begin
      NS=S1;
    end
       else
    begin
      NS=S0;
    end
    S1:if(w==1)
    begin
      NS=S2;
    end
       else
    begin
      NS=S0;
    end
    S2:if(w==1)
    begin
      NS=S2;
    end
       else
    begin
      NS=S3;
    end
    S3:if(w==1)
    begin
      NS=S4; 
    end
       else
    begin
      NS=S0;
    end
    S4:begin NS=S0;end
endcase

always @(posedge clk, negedge resetn)
begin
  if(resetn == 0)
    CS<=S0;
  else
    CS<=NS;
end

always @* begin
  if(CS==S4) begin 
        z=1;
  end else begin
        z=0;
  end
end

endmodule



module counterTB();
reg w,clk,resetn;
reg [2:0] CS,NS;
wire z;
wire [7:0] count;
integer i;
serialCounter sk(.w(w),.clk(clk),.resetn(resetn),.z(z));

initial
  begin
    resetn = 1'b0;
    clk = 1'b0;
    CS=3'b000;
    #30 resetn=1;
    #5000 $finish;
  end

always #5 clk = ~clk;

always @(posedge clk, negedge resetn) $monitor("w=%b, z=%b, count=%d", w,z,count);

always @(posedge clk, negedge resetn) begin
    w <= $random;
end

endmodule

对代码进行了一些修复,首先,我们应该去掉主逻辑(z 和 count)中的推断锁存器,这样结束:

module serialCounter(w,clk,resetn,z);
input w,clk,resetn;
output reg z;
reg [2:0] CS,NS;
reg [7:0] count;
parameter S0=3'b000, S1=3'b001, S2=3'b010, S3=3'b011, S4=3'b100;
always @(w,CS)
  case(CS)
    S0:if(w==1)
    begin
      NS=S1;
    end
       else
    begin
      NS=S0;
    end
    S1:if(w==1)
    begin
      NS=S2;
    end
       else
    begin
      NS=S0;
    end
    S2:if(w==1)
    begin
      NS=S2;
    end
       else
    begin
      NS=S3;
    end
    S3:if(w==1)
    begin
      NS=S4;
    end
       else
    begin
      NS=S0;
    end
    S4:begin NS=S0;end
    default: NS=S0;
endcase

always @(posedge clk, negedge resetn)
begin
  if(resetn == 0)
    CS<=S0;
  else
    CS<=NS;
end
always @(posedge clk, negedge resetn)
begin
  if(resetn == 0)
    begin
      z<=0;
      count<=0;
    end
  if(CS==S4)
    begin
      z<=1;
      count<=count+1;
    end
end
endmodule

其次,在测试台模块中,resetn 从未从 0 变为 1,因为我们有一个异步低电平有效复位,它始终处于复位状态,同样,如果你用最少的驱动“w”计数器的重要位,它将在每个周期切换。我把它改成一个带位计数器的移位寄存器。测试台是这样工作的:

`timescale 1ns/1ns
module counterTB();
localparam WIDTH = 8;
reg clk,resetn;
reg [2:0] CS,NS;
wire w;
wire z;
wire [WIDTH-1:0] count;
reg  [WIDTH-1:0] w_reg;
reg  [WIDTH-1:0] w_nxt;
reg  [$clog2(WIDTH)-1:0] bit_count;
integer i;
serialCounter sk(.w(w),.clk(clk),.resetn(resetn),.z(z));
initial
  begin
    resetn = 1'b0;
    clk = 1'b0;
    CS=3'b000;
    w_reg = 0;
    w_nxt = 0;
  end

//DISABLE RESET
always #10 resetn = 1'b1;

always @ (posedge clk, negedge resetn) begin
  if(resetn == 0) begin
    w_reg <= 0;
    w_nxt <= 1;
    bit_count <= 0;
  end
  else begin
    if(bit_count == (WIDTH-1)) begin
      if(&w_nxt) begin
        $finish;
      end
      else begin
        w_reg <= w_nxt;
        w_nxt <= w_nxt + 1;
        bit_count <= 0;
      end
    end
    else begin
      w_reg <= w_reg >> 1;
      bit_count <= bit_count + 1;
    end
  end
end

assign w = w_reg[0];

always #5 clk = ~clk;

always @(posedge clk, negedge resetn) $monitor("w=%b, z=%b, count=%d, w_reg=%x, w_nxt=%h, bit_count=%d, reset=%b", w,z,sk.count,w_reg,w_nxt,bit_count,resetn);

endmodule

模拟输出的最后一部分(累计到255):

w=0, z=1, count=104, w_reg=fa, w_nxt=fb, bit_count=0, reset=1
w=1, z=1, count=104, w_reg=7d, w_nxt=fb, bit_count=1, reset=1
w=0, z=1, count=104, w_reg=3e, w_nxt=fb, bit_count=2, reset=1
w=1, z=1, count=105, w_reg=1f, w_nxt=fb, bit_count=3, reset=1
w=1, z=1, count=105, w_reg=0f, w_nxt=fb, bit_count=4, reset=1
w=1, z=1, count=105, w_reg=07, w_nxt=fb, bit_count=5, reset=1
w=1, z=1, count=105, w_reg=03, w_nxt=fb, bit_count=6, reset=1
w=1, z=1, count=105, w_reg=01, w_nxt=fb, bit_count=7, reset=1
w=1, z=1, count=105, w_reg=fb, w_nxt=fc, bit_count=0, reset=1
w=1, z=1, count=105, w_reg=7d, w_nxt=fc, bit_count=1, reset=1
w=0, z=1, count=105, w_reg=3e, w_nxt=fc, bit_count=2, reset=1
w=1, z=1, count=105, w_reg=1f, w_nxt=fc, bit_count=3, reset=1
w=1, z=1, count=105, w_reg=0f, w_nxt=fc, bit_count=4, reset=1
w=1, z=1, count=106, w_reg=07, w_nxt=fc, bit_count=5, reset=1
w=1, z=1, count=106, w_reg=03, w_nxt=fc, bit_count=6, reset=1
w=1, z=1, count=106, w_reg=01, w_nxt=fc, bit_count=7, reset=1
w=0, z=1, count=106, w_reg=fc, w_nxt=fd, bit_count=0, reset=1
w=0, z=1, count=106, w_reg=7e, w_nxt=fd, bit_count=1, reset=1
w=1, z=1, count=106, w_reg=3f, w_nxt=fd, bit_count=2, reset=1
w=1, z=1, count=106, w_reg=1f, w_nxt=fd, bit_count=3, reset=1
w=1, z=1, count=106, w_reg=0f, w_nxt=fd, bit_count=4, reset=1
w=1, z=1, count=106, w_reg=07, w_nxt=fd, bit_count=5, reset=1
w=1, z=1, count=106, w_reg=03, w_nxt=fd, bit_count=6, reset=1
w=1, z=1, count=106, w_reg=01, w_nxt=fd, bit_count=7, reset=1
w=1, z=1, count=106, w_reg=fd, w_nxt=fe, bit_count=0, reset=1
w=0, z=1, count=106, w_reg=7e, w_nxt=fe, bit_count=1, reset=1
w=1, z=1, count=106, w_reg=3f, w_nxt=fe, bit_count=2, reset=1
w=1, z=1, count=106, w_reg=1f, w_nxt=fe, bit_count=3, reset=1
w=1, z=1, count=107, w_reg=0f, w_nxt=fe, bit_count=4, reset=1
w=1, z=1, count=107, w_reg=07, w_nxt=fe, bit_count=5, reset=1
w=1, z=1, count=107, w_reg=03, w_nxt=fe, bit_count=6, reset=1
w=1, z=1, count=107, w_reg=01, w_nxt=fe, bit_count=7, reset=1
w=0, z=1, count=107, w_reg=fe, w_nxt=ff, bit_count=0, reset=1
w=1, z=1, count=107, w_reg=7f, w_nxt=ff, bit_count=1, reset=1
w=1, z=1, count=107, w_reg=3f, w_nxt=ff, bit_count=2, reset=1
w=1, z=1, count=108, w_reg=1f, w_nxt=ff, bit_count=3, reset=1
w=1, z=1, count=108, w_reg=0f, w_nxt=ff, bit_count=4, reset=1
w=1, z=1, count=108, w_reg=07, w_nxt=ff, bit_count=5, reset=1
w=1, z=1, count=108, w_reg=03, w_nxt=ff, bit_count=6, reset=1
w=1, z=1, count=108, w_reg=01, w_nxt=ff, bit_count=7, reset=1
w=1, z=1, count=108, w_reg=01, w_nxt=ff, bit_count=7, reset=1

我想,有一种更简单的方法来创建这个状态机。下面的示例 reg [3:0] 模式寄存器保留了模式的 4 位。然后它只是将模式与所需模式(在本例中为 seq)进行比较。

module serialCounter(w, clk, resetn, z, counter);
    input w, clk, resetn;
    output reg z;
    output reg[7:0] counter;
  
  reg[3:0] pattern, tmp;
  parameter seq = 4'b1011;
  
  always@(posedge clk) begin
    if (!resetn) begin
      pattern <= 0;
      counter <= 0;
    end
    else begin
      tmp = (pattern << 1) | w;
      
      if (tmp == seq) begin
        counter <= counter + 1;
        z <= 1;
      end
      else
        z <= 0;
      
      pattern <= tmp;
    end
  end   
endmodule
  
module counterTB();
   reg w,clk,resetn;
   reg [2:0] CS,NS;
   wire z;
   reg [7:0] count;

  integer i;
  serialCounter sk(.w(w), .clk(clk), .resetn(resetn), .z(z), .counter(count));

initial
  begin
    resetn = 1'b0;
    clk = 1'b0;
    CS=3'b000;
    #30 resetn=1;
    #5000 $finish;
  end

always #5 clk = ~clk;

  initial $monitor("clk=%b, w=%b, z=%b, count=%d pattern=%b", clk, w,z, count, sk.pattern);

initial begin
  w = 0;
  forever #10 w = $random;
end
  
endmodule

我还修复了 tb 模块中的一些问题,例如$monitor 应该在初始块中使用。我也更改了您生成 w 的方式。