状态机加强练习

1、状态机
1.1、什么是状态机

    状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。有限状态机简写为FSM(Finite State Machine)。

    状态机主要分为两大类:
    

  • mealy状态机:输出跟外部输入和当前状态共同决定
    在这里插入图片描述

    

  • moore状态机:输出只和状态有关而与输入无关
    在这里插入图片描述
1.2、状态机的使用

    根据状态机的实际写法,状态机可以分为一段式、二段式和三段式状态机。

  • 一段式状态机:整个状态机写到一个 always模块里面,在该模块中既描述状态转移,又描述状态的输入和输出。

  • 二段式状态机:用两个 always 模块来描述状态机,其中一个 always 模块采用同步时序描述状态转移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出。不同于一段式状态机的是,它需要定义两个状态,现态和次态,然后通过现态和次态的转换来实现时序逻辑。

  • 三段式状态机:在两个 always 模块描述方法基础上,使用三个always 模块,一个always 模块采用同步时序描述状态转移,一个 always 采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 模块描述状态输出。

2、实现
2.1、设计思想

    设计一个实现一个测试过程,该过程包括启动准备状态、启动测试、停止测试、查询测试结果、显示测试结果、测试结束返回初始化6个状态;用时间来控制该过程,90秒内完成该过程,6个状态均用led灯展示:准备状态(3个led灯全亮)、启动测试(只点亮led1)、停止测试(点亮led2)、查询测试结果(点亮led3)、显示测试结果(点亮led1和led2)、测试结束返回初始化(点亮led1和led3)。90s内完成6个状态的切换。

2.2、代码实现

     1、首先定义6个状态空间来存储6个状态:启动准备状态(1=001)、启动测试(2=010)、停止测试(3=011)、查询测试结果(4=100)、显示测试结果(5=101)、测试结束返回初始化(6=110)
    reg [2:0] cstate;

    2、根据设置的状态的所咋子时间范围切换状态

always@ (posedge clk_50 or negedge rst_n)
begin
	if(!rst_n)
		cstate <= 3'b000;
	else if(cnt_nine > 0 && cnt_nine <= TIME_ONE)
		cstate <= 3'b001;
	else if(cnt_nine > TIME_ONE && cnt_nine <= TIME_TWO)
		cstate <= 3'b010;
	else if(cnt_nine > TIME_TWO && cnt_nine <= TIME_THREE)
		cstate <= 3'b011;
	else if(cnt_nine > TIME_THREE && cnt_nine <= TIME_FOUR)
		cstate <= 3'b100;
	else if(cnt_nine > TIME_FOUR && cnt_nine <= TIME_FIVE)
		cstate <= 3'b101;
	else if(cnt_nine > TIME_FIVE && cnt_nine <= TIME_SIX)
		cstate <= 3'b110;
	else
		cstate <= 3'b000;
		
end

    3、根据状态点亮相应的led灯

always@ (*)
begin
	if(!rst_n)
		led_r = 3'b000;
	else
	begin
		case(cstate)
			3'b001: led_r = 3'b111;             //第一个状态,点亮三盏灯
			3'b010: led_r = 3'b001;             //第二个状态,点亮led1
			3'b011: led_r = 3'b010;             //第三个状态,点亮led2
			3'b100: led_r = 3'b100;             //第四个状态,点亮led3
			3'b101: led_r = 3'b011;             //第五个状态,点亮led1和led2
			3'b110: led_r = 3'b101;             //第六个状态,点亮led1和led3
			default:   ;
		endcase
	end
end

    完整代码:

module system_six_top(
	input wire clk_50,                       //时钟信号
	input wire rst_n,                        //复位信号
	
	output wire [2:0] led                    //开发板led灯输出信号
	);
	
parameter TIME_ONE   =  33'd250_000_000;    //0~5s(33'd250_000_000)内为启动准备状态,仿真时取5us
parameter TIME_TWO   =  33'd750_000_000;    //5~15s(33'd750_000_000)内为启动测试状态,仿真时取15us
parameter TIME_THREE =  33'd1_500_000_000;  //15~30s(33'd1_500_000_000)内为启动测试状态,仿真时取30us
parameter TIME_FOUR  =  33'd2_500_000_000;  //30~50s(33'd2_500_000_000)内为查询结果状态,仿真时取50us
parameter TIME_FIVE  =  33'd3_500_000_000;  //50~70s(33'd3_500_000_000)内为显示测试结果状态,仿真时取70us
parameter TIME_SIX   =  33'd4_500_000_000;  //70~90s内为测试结束状态,也是90s(33'd4_500_000_000)计时最大值(90s),仿真时取90us
	

reg [32:0] cnt_nine;                        //90s计数寄存器
reg [2:0] led_r;

//定义6个状态空间
//启动准备状态(1=001)、启动测试(2=010)、停止测试(3=011)、查询测试结果(4=100)、显示测试结果(5=101)、测试结束返回初始化(6=110)
reg [2:0] cstate;

//90s计时模块
always@ (posedge clk_50 or negedge rst_n)
begin
	if(!rst_n)
		cnt_nine <= 33'd0;
	else if(cnt_nine == TIME_SIX - 1)
		cnt_nine <= 33'd0;
	else
		cnt_nine <= cnt_nine + 1'b1;
end

//6个状态的切换
always@ (posedge clk_50 or negedge rst_n)
begin
	if(!rst_n)
		cstate <= 3'b000;
	else if(cnt_nine > 0 && cnt_nine <= TIME_ONE)
		cstate <= 3'b001;
	else if(cnt_nine > TIME_ONE && cnt_nine <= TIME_TWO)
		cstate <= 3'b010;
	else if(cnt_nine > TIME_TWO && cnt_nine <= TIME_THREE)
		cstate <= 3'b011;
	else if(cnt_nine > TIME_THREE && cnt_nine <= TIME_FOUR)
		cstate <= 3'b100;
	else if(cnt_nine > TIME_FOUR && cnt_nine <= TIME_FIVE)
		cstate <= 3'b101;
	else if(cnt_nine > TIME_FIVE && cnt_nine <= TIME_SIX)
		cstate <= 3'b110;
	else
		cstate <= 3'b000;
		
end

//根据不同的状态点亮不同的led灯
always@ (*)
begin
	if(!rst_n)
		led_r = 3'b000;
	else
	begin
		case(cstate)
			3'b001: led_r = 3'b111;             //第一个状态,点亮三盏灯
			3'b010: led_r = 3'b001;             //第二个状态,点亮led1
			3'b011: led_r = 3'b010;             //第三个状态,点亮led2
			3'b100: led_r = 3'b100;             //第四个状态,点亮led3
			3'b101: led_r = 3'b011;             //第五个状态,点亮led1和led2
			3'b110: led_r = 3'b101;             //第六个状态,点亮led1和led3
			default:   ;
		endcase
	end
end

assign led = led_r;                           //将led_r的值赋给led
	
	
endmodule

    4、仿真测试:测试时为了方便,将状态周期改为90us,其它的时间间隔相应改小
    代码:

 `timescale 1ns/1ns            //时间单位/时间精度 后者不能比前者大

module system_six_top_tb;

reg clk_50;
reg rst_n;               
wire [2:0] led;                     
	
//激励信号
always #10 clk_50 = ~clk_50;    //每过10ns时钟取反

//初始化
initial 
begin
	clk_50 = 1'b0;
	rst_n = 1'b0;
	#20 rst_n = 1'b1;
	#180000
	$stop;
end
	
//实例化
system_six_top     instance_system_six_top(
.clk_50       (clk_50),                       //时钟信号
.rst_n        (rst_n ),                       //复位信号
.led          (led   )                        //开发板外设led灯
);



endmodule


    仿真结果
在这里插入图片描述

    5、板极测试:同样为方便测试,将90s时间改为9s,其它时间间隔相应改小
在这里插入图片描述

3、检测10010串
3.1、检测10010串的状态图

    1、根据需求画出状态图
在这里插入图片描述

    2、其中IDLE为初始状态,S1表示检测到1、S2表示检测到10、S3表示检测到100、S4表示检测到1001、S5表示检测到10010,当检测到串10010时会输出高电平cout = 1.

3.2、代码部分

    1、定义六个状态空间

parameter IDLE = 3'd0;           //初始状态
parameter S1   = 3'd1;           //检测到1
parameter S2   = 3'd2;           //检测到10
parameter S3   = 3'd3;           //检测到100
parameter S4   = 3'd4;           //检测到1001
parameter S5   = 3'd5;           //检测到10010

    2、完整代码

module detection_top(
	input wire clk_50,            //时钟信号
	input wire rst_n,             //复位信号
	input wire cin,               //输入
	
	output reg cout               //输出
	);
	
//定义六个状态空间
parameter IDLE = 3'd0;           //初始状态
parameter S1   = 3'd1;           //检测到1
parameter S2   = 3'd2;           //检测到10
parameter S3   = 3'd3;           //检测到100
parameter S4   = 3'd4;           //检测到1001
parameter S5   = 3'd5;           //检测到10010

//当前状态
reg [2:0] cstate;
reg [2:0] nstate;
 
always@ (posedge clk_50 or negedge rst_n)
begin
	if(!rst_n)
	begin
		cstate <= IDLE;
	end
	else
		cstate <= cstate;
end

//状态的转换
always@ (*)
begin
	case(cstate)
		IDLE:
		begin
			if(cin == 1)
				nstate = S1;
			else
				nstate = IDLE;
		end
		S1:
		begin
			if(cin == 0)
				nstate = S2;
			else
				nstate = IDLE;
		end
		S2:
		begin
			if(cin == 0)
				nstate = S3;
			else
				nstate = IDLE;
		end
		S3:
		begin
			if(cin == 1)
				nstate = S4;
			else
				nstate = IDLE;
		end
		S4:
		begin
			if(cin == 0)
				nstate = S5;
			else
				nstate = IDLE;
		end
		default: nstate = IDLE;
	endcase
	
end

always@ (posedge clk_50 or negedge rst_n)
begin
	if(!rst_n)
		cout <= 1'b0;
	else if(cstate == S5)
		cout <= 1'b1;
	else
		cout <= 1'b0;
end



endmodule

    3、仿真代码

`timescale 1ns/1ns
module detection_top_tb();
	reg clk_50;
   reg rst_n ;
   wire cin   ;
   reg [9:0] data;
   wire cout ; 
always #20 clk_50 = ~clk_50;
assign cin = data[9];

initial
begin
	clk_50 = 1'b0;
	rst_n  = 1'b1;
	#20 rst_n = 1'b1;
	data = 10'b1_100_100_100;
	#1000 $stop;
end

always@ (posedge clk_50)
begin
	#2 data = {data[0],data[9:1]};
end


detection_top     detection_top_inst(
.clk_50      (clk_50),            
.rst_n       (rst_n ),            
.cin         (cin   ),               
              
.cout        (cout  )              
	);

detection_top m(.clk_50(clk_50),.rst_n(rst_n),.cin(cin),.cout(cout));

endmodule

    4、仿真结果
在这里插入图片描述

参考:https://blog.csdn.net/wuzhikaidetb/article/details/119421783
          https://blog.csdn.net/qq_41982581/article/details/82949012

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值