序列检测与状态机


记录一些学习心得与经典电路,欢迎大佬批评指正不吝赐教
本篇记录分别采用两段式和三段式状态机的写法,用米里型和摩尔型状态机实现序列检测功能

状态机结构图

两段式状态机结构图

摘自Verilog HDL实用精解

三段式状态机结构图

摘自Verilog HDL实用精解
本文要检测的序列是1101,并且可以实现重复检测,因此要注意最终状态之后不一定回到初始状态。

摩尔型状态机

摩尔型状态机与输入无关,只与当前状态有关

两段式状态机

状态转换图如图所示:
在这里插入图片描述

代码如下:

`timescale 1ns / 1ps
module seq(
input clk,
input rst_n,
input din,
output reg sign
    );
reg [4:0] state;
reg [4:0] state_n;

parameter
    IDLE = 5'b00001,
    s0   = 5'b00010,
	s1   = 5'b00100,
	s2   = 5'b01000,
	s3   = 5'b10000;

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    state <= IDLE;
  else 
    state <= state_n;
end
 
always@(*)
begin 
  case(state)
    IDLE:begin
	       if(din == 1'b1)  state_n = s0;
		   else state_n = IDLE;
		 end
    s0:  begin
	       if(din == 1'b1)  state_n = s1;
		   else state_n = IDLE;
		 end
    s1:  begin
	       if(din == 1'b0)  state_n = s2;
		   else state_n = s1;
		 end
    s2:  begin  
	       if(din == 1'b1)  state_n = s3;
		   else state_n = IDLE;
		 end
	s3:  begin 
	       if(din == 1'b1)  state_n = s1;
		   else state_n = IDLE;		   
		 end
	default: begin
	       state_n = IDLE;
		 end
  endcase
end

always@(*)
begin
  if(!rst_n)
    sign = 1'b0;
  else 
  begin
    if(state == s3)
	  sign = 1'b1;
	else 
	  sign = 1'b0;
  end
end
		 
endmodule

testbench如下:采用了循环的方式重复赋值

`timescale 1ns / 1ps
module tb();

reg clk;
reg rst_n;
wire din;
wire sign;

reg[17:0] data;

initial begin
clk = 0;
rst_n =0;
#40
rst_n = 1;
#3000
$stop;
end
always #5 clk = ~clk;

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    data <= 18'b1101_1011_1001_1011_00;
  else
    data <= {data[16:0],data[17]};
end

assign din = data[17];

seq seq1(
  .clk(clk),
  .rst_n(rst_n),
  .din(din),
  .sign(sign)
);

endmodule

仿真结果如图所示,可以发现当state处于s3状态时,输出sign信号为持续一个周期的高电平,并且第一个sign高电平与第二个sign高电平之间实现了连续检测。此时的sign输出高电平是在1101这个待检测序列已经传递完之后才有效的,注意与下面的米里型进行区分,米里型的sign信号是在收到第四个数字为1的同时就输出为1的。
波形图

三段式状态机

状态转换图同上
代码如下:

`timescale 1ns / 1ps
module seq(
input clk,
input rst_n,
input din,
output reg sign
    );
reg [4:0] state;
reg [4:0] state_n;

parameter
    IDLE = 5'b00001,
    s0   = 5'b00010,
	s1   = 5'b00100,
	s2   = 5'b01000,
	s3   = 5'b10000;

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    state <= IDLE;
  else 
    state <= state_n;
end
 
always@(*)
begin 
  case(state)
    IDLE:begin
	       if(din == 1'b1)  state_n = s0;
		   else state_n = IDLE;
		 end
    s0:  begin
	       if(din == 1'b1)  state_n = s1;
		   else state_n = IDLE;
		 end
    s1:  begin
	       if(din == 1'b0)  state_n = s2;
		   else state_n = s1;
		 end
    s2:  begin  
	       if(din == 1'b1)  state_n = s3;
		   else state_n = IDLE;
		 end
	s3:  begin 
	       if(din == 1'b1)  state_n = s1;
		   else state_n = IDLE;		   
		 end
	default: begin
	       state_n = IDLE;
		 end
  endcase
end

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    sign <= 1'b0;
  else 
  begin
    if(state_n == s3)
	  sign <= 1'b1;
	else 
	  sign <= 1'b0;
  end
end
		 
endmodule

testbench同上
仿真结果如下:
仿真结果
同样可以发现,当状态为s3时候,输出sign为持续一个周期的高电平。

米里型状态机

米里型状态机不仅与当前状态有关,也与输入信号有关

两段式状态机

状态转移图如图所示:
在这里插入图片描述
代码如下:

module seq(
input clk,
input rst_n,
input din,
output reg sign
    );
reg [3:0] state;
reg [3:0] state_n;

parameter
    s0 = 4'b0001,
	s1 = 4'b0010,
	s2 = 4'b0100,
	s3 = 4'b1000;

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    state <= s0;
  else 
    state <= state_n;
end
 
always@(*)
begin 
  case(state)
    s0:  begin
	       if(din == 1'b1)  state_n = s1;
		   else state_n = s0;
		 end
    s1:  begin
	       if(din == 1'b1)  state_n = s2;
		   else state_n = s0;
		 end
    s2:  begin  
	       if(din == 1'b0)  state_n = s3;
		   else state_n = s2;
		 end
	s3:  begin 
	       if(din == 1'b1)  state_n = s1;
		   else state_n = s0;
		 end
	default: begin
	       state_n = s0;
		 end
  endcase
end

always@(*)
begin
  if(!rst_n)
    sign = 1'b0;
  else 
  begin
    if((state == s3) && (din == 1'b1))
	  sign = 1'b1;
	else 
	  sign = 1'b0;
  end
end
		 
endmodule

testbench和摩尔型的一样
仿真结果如图所示:
在这里插入图片描述

三段式状态机

代码如下:

module seq(
input clk,
input rst_n,
input din,
output reg sign
    );
reg [3:0] state;
reg [3:0] state_n;

parameter
    s0 = 4'b0001,
	s1 = 4'b0010,
	s2 = 4'b0100,
	s3 = 4'b1000;

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    state <= s0;
  else 
    state <= state_n;
end
 
always@(*)
begin 
  case(state)
    s0:  begin
	       if(din == 1'b1)  state_n = s1;
		   else state_n = s0;
		 end
    s1:  begin
	       if(din == 1'b1)  state_n = s2;
		   else state_n = s0;
		 end
    s2:  begin  
	       if(din == 1'b0)  state_n = s3;
		   else state_n = s2;
		 end
	s3:  begin 
	       if(din == 1'b1)  state_n = s1;
		   else state_n = s0;
		 end
	default: begin
	       state_n = s0;
		 end
  endcase
end

always@(posedge clk or negedge rst_n)
begin
  if(!rst_n)
    sign <= 1'b0;
  else 
  begin
    if((state_n == s3) && (din == 1'b1))
	  sign <= 1'b1;
	else 
	  sign <= 1'b0;
  end
end
		 
endmodule

此时本来用的依然是上面的testbench,但是仿真后发现sign输出始终为0,经过改正testbench,发现如果采用下面的这种写法或者一位一位激励din都不会有问题,猜测可能是因为原先的这种写法是在时钟上升沿来临时才赋值给对data进行操作,然后再对din进行操作,编译过程中可能会导致在时钟上升沿处判断错误,如果有大佬有不同的想法,欢迎不吝赐教批评指正,万分感谢。
testbench写法:

`timescale 1ns / 1ps
module tb();

reg clk;
reg rst_n;
reg din;
wire sign;
integer i;

reg[17:0] data;

initial begin
clk = 0;
rst_n = 0;
data = 18'b1101_1011_1001_1011_00;
din = 0;
#10
rst_n = 1;
#5

for(i = 17 ; i >= 0 ; i = i-1)
begin
#10
din = data[i];
end
#3000
$stop;
end

always #5 clk = ~clk;

seq seq1(
  .clk(clk),
  .rst_n(rst_n),
  .din(din),
  .sign(sign)
);

endmodule

仿真结果如图所示:
在这里插入图片描述
米里型状态机的检测信号输出的位置是在最后一个1输入的同时拉高的,与摩尔型不同。
如果想实现单独检测的功能,将最后一个状态都指回到最初的状态即可。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值