Search code examples
verilogstate-machine

How do I count a specific sequence in Verilog?


I need to design a circuit that can count 1011 in a sequence of bits. For example, if the sequence is as shown in the picture, the count should be 1, but it isn't. I know I'm reading input w not in a sequence way, but I don't know how to do it either.

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.

picture of circuit

picture of state machines. second one is what I used

main logic:

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


testbench:

`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

Solution

  • I think, there is an easier way to created this state machine. The following example reg [3:0] pattern register which keeps 4 bits of the pattern. Then it just compares the pattern with the desired one (seq in this case).

    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
    

    I also fixed a couple of things in the tb module, e.g. $monitor should be used int an initial block. I changed the way you generate w as well.