[Verilog combat] (About a netizen privately messaged me yesterday...) Design and functional verification of serial data stream specific sequence loop detector (with source code)

Written in the front, yesterday a netizen asked me about the state machine, so as to make a record: design a sequence detector record that can be detected cyclically.


Virtual machine: VMware -14.0.0.24051
Environment: ubuntu 18.04.1 Script
: makefile ( click to go )
Utility: vcs and verdi



1. Demand

  The function of the module is to detect the occurrence of a 5-digit binary 10010 in the serial data stream, and realize cyclic detection (after detecting 10010, if the subsequent data input is 010), that is, the sequence is 10010010xxx, and the sequence needs to be detected twice. , build a test platform for functional verification.

2. Spec

(1)Analyze

  According to the requirements, the width of the sequence to be detected is 5, then including the IDLE state, the state machine has a total of 6 states, which can be set as IDLE, S1, S2, S3, S4, S5. One-hot encoding is used. Each time a sequence is detected, a signal is output, and the counter is incremented by 1 to record how many times it has been detected.

In the low-speed system, the number of states in the state machine is less than 4, using binary code encoding
In the low-speed system, the number of states in the state machine is 4~24, using one-hot code encoding
In the low-speed system, the state machine The number of states in the middle is > 24, using Gray code encoding

(2)Interface Description

Signal Name Width Direction Description
clk 1 input System clk signal, xxMhz
rst 1 input System reset signal
data 1 input Detected data
result 1 output Detection result signal
res_cnt 2 output The number of occurrences of the sequence

(3)FSM

insert image description here

三、Design and Functional Verification

(1)RTL

//-- modified by xlinxdu, 2022/04/24
module check #(
  parameter WIDHT = 2
)(
  input                   clk_i    ,
  input                   rst_n_i  ,

  //-- interface
  input                   data_i   ,
  output reg              result_o ,
  output reg [WIDHT-1:0]  res_cnt_o //(sum)
);

//-- state define
parameter  IDLE = 6'b00_0001;
parameter  S1   = 6'b00_0010;
parameter  S2   = 6'b00_0100;
parameter  S3   = 6'b00_1000;
parameter  S4   = 6'b01_0000;
parameter  S5   = 6'b10_0010;

reg  [5:0] cur_state;//current state
reg  [5:0] nxt_state;//next state

wire       result_s ;//sum
reg        nres_cnt ;//


/*-----------------------------------------------\
 -----------  updata of current state  ----------
\-----------------------------------------------*/

always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    cur_state <= 6'b0;
  end
  else begin
    cur_state <= nxt_state;
  end
end


/*-----------------------------------------------\
 --------------  transfer of state  -------------
\-----------------------------------------------*/

always @ (*) begin
  case(cur_state) 
    IDLE: if(data_i) nxt_state = S1  ;
          else       nxt_state = IDLE;

    S1  : if(data_i) nxt_state = S1  ;
          else       nxt_state = S2  ; 

    S2  : if(data_i) nxt_state = S1  ;
          else       nxt_state = S3  ;

    S3  : if(data_i) nxt_state = S4  ;
          else       nxt_state = IDLE;

    S4  : if(data_i) nxt_state = S1  ;
          else       nxt_state = S5  ;

    S5  : if(data_i) nxt_state = S1  ;
          else       nxt_state = S3  ;

    default:         nxt_state = IDLE;
  endcase
end

/*-----------------------------------------------\
 ---------  updata of result counter  -----------
\-----------------------------------------------*/
always @ (*) begin
  if(nxt_state == S5) begin
    nres_cnt = 1'b1;
  end
  else begin
    nres_cnt = 1'b0;//clear 
  end
end

always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    res_cnt_o <= 2'b0;
  end
  else begin
    res_cnt_o <= res_cnt_o + nres_cnt;
  end
end

/*-----------------------------------------------\
 ---------- updata of result_o   ----------
\-----------------------------------------------*/

//assign result_s = (cur_state == S4) ? 1'b1 : 1'b0;
assign result_s = (nxt_state == S5) ? 1'b1 : 1'b0;

always @ (posedge clk_i or negedge rst_n_i) begin
  if (!rst_n_i) begin
    result_o <= 1'b0;
  end
  else begin
    result_o <= result_s;
  end
end

endmodule

(2)Test Bench

module tb_check;
  reg         clk_i    ;
  reg         rst_n_i  ;
  reg         data_i   ;

  wire        result_o ;
  wire  [1:0] res_cnt_o;

reg [17:0] data_check;

initial begin
  clk_i = 0 ;
  rst_n_i = 1;
  data_i = 0;
  data_check = 18'b10_1001_0001_0010_0100;

  #5 rst_n_i = 0;
  #10 rst_n_i = 1;
end

always begin
  data_i = data_check[17];
  #60 data_check = (data_check << 1'b1);
end


check tb_check(
               .clk_i    (clk_i    ),
               .rst_n_i  (rst_n_i  ),
               .data_i   (data_i   ),
               .result_o (result_o ),
               .res_cnt_o(res_cnt_o)
);

always begin
  #30 clk_i = ~clk_i;
end



initial begin
  #2000 $finish;
  
  $fsdbDumpfile("check.fsdb");
  $fsdbDumpvars            ;
  $fsdbDumpMDA             ;
end

endmodule

4. Result

insert image description here
  In the test platform, the input data is data_check = 18'b10_1001_0001_0010_0100, which is converted into a serial data stream and input to the DUT under test. See the three boxes in the figure above, and 10010 appears three times. The function realizes that one signal is output at each detection, and the number is recorded once, which ensures the basic data path of the module.


Author: xlinxdu
Copyright: This article is the original author, and the copyright belongs to the author.
Reprinting: Reprinting is prohibited without the permission of the author. Reprinting must retain this statement, and a link to the original text must be given in the article.

Guess you like

Origin blog.csdn.net/qq_43244515/article/details/124389916