写在前面,昨天一网友问了我关于状态机的问题,借此做一个记录:设计一个可循环检测的序列检测器记录,该模块使用moore型状态机实现,使用独热码编码。
虚拟机:VMware -14.0.0.24051
环 境:ubuntu 18.04.1
脚 本:makefile(点击直达)
应用工具:vcs 和 verdi
文章目录
- 一、Demand
- 二、Spec
- (1)Analyze
- (2)Interface Description
- (3)FSM
- 三、Design and Functional Verification
- (1)RTL
- (2)Test Bench
- 四、Result
一、Demand
模块功能是检测串行数据流中,一个5位数二进制10010出现的情况,实现循环检测(检测到10010之后,如果后续数据输入为010),即序列为10010010xxx,需检测到出现2次该序列,搭建测试平台进行功能验证。
二、Spec
(1)Analyze
依需求可知,需检测的序列宽度为5,那么算上IDLE状态,状态机一共有6个状态,可设定为IDLE,S1,S2,S3,S4,S5。采用独热码编码。每检测到一次序列,输出一个信号,并且计数器自加1,以此记录一共检测到多少次。
在低速系统中,状态机中状态的个数 < 4个,使用二进制码编码
在低速系统中,状态机中状态的个数在4~24个,使用独热码编码
在低速系统中,状态机中状态的个数>24个,使用格雷码编码
(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
三、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
四、Result
在测试平台中,输入的数据为data_check = 18’b10_1001_0001_0010_0100,转换成串行数据流输入到被测试模块DUT中,见上图三个框,10010出现了三次。功能实现了在每检测一次输出一个信号,记一次数,保证了模块基本的数据通路。
作者:xlinxdu
版权:本文是作者原创,版权归作者所有。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。