FPGA——状态机专题

  • 一、何为状态机
  • 二、状态机状态检测
    • 2.1 问题描述
    • 2.2 工程创建
    • 2.3 代码
      • 2.3.1 计时器模块
      • 2.3.2 状态切换模块
      • 2.3.3 顶层文件模块
    • 2.4 原理图
  • 三、检测10010串的状态机
    • 3.1 问题描述
    • 3.2 原理图示
    • 3.3 创建工程
    • 3.4 代码
      • 3.4.1 按键消抖模块
      • 3.4.2 状态机模块
    • 3.5 原理图
  • 总结
  • 参考文献

一、何为状态机

  • 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作、完成特定操作的控制中心。有限状态机简写为FSM(Finite State Machine),主要分为2大类:
    第一类,若输出只和状态有关而与输入无关,则称为摩尔(Moore)型状态机。
    第二类,输出不仅和状态有关而且和输入有关系,则称为米莉(Mealy)型状态机。

二、状态机状态检测

2.1 问题描述

  • 根据以下描述功能用verilog编写一段代码,并用状态机来实现该功能。
    (1)状态机:实现一个测试过程,该过程包括启动准备状态、启动测试、停止测试、查询测试结果、显示测试结果、测试结束返回初始化6个状态;用时间来控制该过程,90秒内完成该过程;
    (2)描述状态跳转时间;
    (3)编码实现。

2.2 工程创建

① 首先在相应的文件夹下建立一个名为state_show(可自行命名)的文件,里面分别创建prj(存放工程的文件)、src(存放Verilog代码源文件)、sim(存放仿真文件)、tcl(引脚配置文件),除此之外还可以建立ip(存放知识产权的文件)等…

FPGA——状态机专题_第1张图片
②打开Quartus ||(我的版本是18.1),新建工程
file——New——New Quartus Prime Preject——OK
然后next,到如下图页面:

FPGA——状态机专题_第2张图片
③两个next后,根据自己的开发版型号进行选择,笔主这里是:

FPGA——状态机专题_第3张图片
④下一页面如下

FPGA——状态机专题_第4张图片

  • 然后finish,创建工程完成。

2.3 代码

  • 新建 verilog 文件,File->New->Verilog HDL File
  • 注意保存时,要保存在src中,命名要与Verilog 语句中一致

FPGA——状态机专题_第5张图片

2.3.1 计时器模块

module time_count(
	input  clk,   //50M时钟                        
	input  rst_n, //复位信号
	
	output reg sec_15//15s           
);

parameter MAX_NUM = 30'd749_999_999;//最大数15s,750_000_000-1次
reg [29:0] cnt_15;//计数寄存器

//15s计时器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		cnt_15 <= 30'd0;
	end
	else if(cnt_15 == MAX_NUM)begin
		cnt_15 <= 30'd0;	
	end
	else begin
		cnt_15 <= cnt_15 + 1'd1;
	end
end

//15s脉冲信号
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		sec_15 <= 1'b0;
	end
	else if(cnt_15 == MAX_NUM)begin
		sec_15 <= 1'b1;
	end
	else begin
		sec_15 <= 1'b0;
	end
end

endmodule

2.3.2 状态切换模块

module state_change(
	input                  clk    ,
	input                  rst_n  ,
	input  wire 		     sec_15  
);

reg [2:0] cstate;               //现态
reg [2:0] nstate;               //次态

//状态划分
parameter S0 = 0;//初始化
parameter S1 = 1;//启动准备状态
parameter S2 = 2;//启动测试
parameter S3 = 3;//停止测试
parameter S4 = 4;//查询测试结果
parameter S5 = 5;//显示测试结果
                   
//第一段:现态跟随次态
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cstate <= S0;//复位键被按下,当前状态设置为初始化
	else
		cstate <= nstate;          //下一次状态赋值给当前状态
end
		
//第二段:组合逻辑,阻塞赋值
always@(*)begin
	if(!rst_n)
		begin
			nstate = S0;
		end
	else
		case(cstate)
			S0: begin
								if(sec_15 == 1'b1)       
									nstate = S1;
								else
									nstate = nstate;
							end
			S1: begin
								if(sec_15 == 1'b1)
									nstate = S2;
								else
									nstate = nstate;
							end
			S2: begin
								if(sec_15 == 1'b1)
									nstate = S3;
								else
									nstate = nstate;
							end
			S3: begin
								if(sec_15 == 1'b1)
									nstate = S4;
								else
									nstate = nstate;
							end
			S4: begin
								if(sec_15 == 1'b1)
									nstate = S5;
								else
									nstate = nstate;
							end
			S5: begin
								if(sec_15 == 1'b1)
									nstate = S0;
								else
									nstate = nstate;
							end
			default:    nstate = S0;
      endcase
end
endmodule

2.3.3 顶层文件模块

module state_top(
	input  clk,
	input  rst_n
);

wire sec_15;

//实例化计时器模块
time_count inst_time_count(
.clk    (clk   ),                 
.rst_n  (rst_n ),
                     
.sec_15 (sec_15)         
);

//实例化状态切换模块
state_change inst_state_change(
.clk    (clk   ),                
.rst_n  (rst_n ),
.sec_15 (sec_15)               
);

endmodule

2.4 原理图

FPGA——状态机专题_第6张图片

三、检测10010串的状态机

3.1 问题描述

  • 画出可以检测10010串的状态图, 并用verilog编程实现之。

3.2 原理图示

FPGA——状态机专题_第7张图片

3.3 创建工程

  • 同上2.2

3.4 代码

3.4.1 按键消抖模块

module key_debounce(
	input 		clk,
	input			rst_n,	
	input 		key,
	
	output		key_value, //按键稳定信号
	output		flag		  //抖动结束标识(0表示未结束,1表示结束)
	
);

parameter TIME_20MS = 20'd1_000_000; //20ms

reg [19:0] cnt_20ms;
reg key_value_r;
reg flag_r;
reg key_reg; //保存前一次取的key值


always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		cnt_20ms <= 20'd0;
		key_reg  <= 1'b1;
	end
	else begin
		key_reg <= key;
		if(key_reg == 1'b1 && key == 1'b0)
			cnt_20ms <= TIME_20MS;
		else if(cnt_20ms <= 20'd0)
			cnt_20ms <= 20'd0;
		else
			cnt_20ms <= cnt_20ms -20'd1;
	end
end


always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		flag_r <= 1'b0;
	else if(cnt_20ms == 20'd1)
		flag_r <= 1'b1;
	else
		flag_r <= 1'b0;
end
assign flag = flag_r;


always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		key_value_r <= 1'b1;
	else if(cnt_20ms == 20'd1)
		key_value_r <= ~key;
	else
		key_value_r <= key_value_r;
		
end

assign key_value = key_value_r;

endmodule

3.4.2 状态机模块

module state(
	input                  clk,
	input                  rst_n,
	input           [1:0]  key,
	
	output   wire   [3:0]  led
);
parameter MAX_TIME   = 26'd49_999_999;//1s
parameter TIME_200MS = 24'd9_999_999; //200ms 

reg [2:0] cstate;               //现态
reg [2:0] nstate;               //次态
parameter S0 = 0;//初始状态
parameter S1 = 1;//1状态
parameter S2 = 2;//10状态
parameter S3 = 3;//100状态
parameter S4 = 4;//1001状态
parameter S5 = 5;//10010状态

reg [3:0] 	led_r;
reg [25:0] 	cnt_1s;
reg [23:0]	cnt_200ms;

//1s计数器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt_1s <= 26'd0;
	else if(cnt_1s == MAX_TIME)
		cnt_1s <= 26'd0;
	else
		cnt_1s <= cnt_1s + 26'd1;
end

//200ms计数器
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cnt_200ms <= 24'd0;
	else if(cnt_200ms == TIME_200MS)
		cnt_200ms <= 24'd0;
	else
		cnt_200ms <= cnt_200ms + 24'd1;
end
//第一段
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		cstate <= S0;
	else 
		cstate <= nstate;
end

//状态切换模块
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		begin
			nstate <= S0;
		end
	else
		case(cstate)
			S0: begin
								if(key[1] )           
									nstate <= S1;
								else if(key[0])
									nstate <= S0;
								else
									nstate <= nstate;
							end
			S1: begin
								if(key[0] )
									nstate <= S2;
								else if(key[1])            
									nstate <= S0;
								else
									nstate <= nstate;
							end
			S2: begin
								if(key[0] )
									nstate <= S3;
								else if(key[1])            
									nstate <= S0;
								else
									nstate <= nstate;
							end
			S3: begin
								if(key[1] )
									nstate <= S4;
								else if(key[0] )
									nstate <= S0;
								else
									nstate <= nstate;
							end
			S4: begin
								if(key[0] )
									nstate <= S5;
								else if(key[1] )
									nstate <= S0;
								else
									nstate <= nstate;
							end
			S5:	begin
								if(cnt_1s == MAX_TIME)
									nstate <= S0;
								else
									nstate <= S5;
				end
								
			default:   nstate <= S0; 
      endcase
end

//第三段:跟随状态输出
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
	 led_r <=4'b0000;
	else
		case(cstate)
			S5:begin
			 if(cnt_200ms == TIME_200MS)
					led_r <= ~led_r;
				else 
					led_r <= led_r;
			end
			default:	led_r <= 4'b0000;
		endcase
end

assign led = led_r;

endmodule

3、顶层模块

module state_top(
	input wire        clk,
	input wire 		   rst_n,
	input wire  [1:0] key,
	
	output wire [3:0] led
);
wire [1:0] flag;
wire [1:0] key_value;

//实例化按键KEY0
key_debounce inst_key_debounce(
.clk      (clk         ),
.rst_n    (rst_n       ),
.key      (key[0]      ),
		
.flag     (flag[0]     ),               
.key_value(key_value[0])         
);

//实例化按键KEY1
key_debounce inst_key_debounce1(
.clk      (clk         ),
.rst_n    (rst_n       ),
.key      (key[1]      ),
		
.flag     (flag[1]     ),              
.key_value(key_value[1])          
);

//实例化状态切换模块
state  inst_state(
.clk  (clk         ),
.rst_n(rst_n       ),
.key  ({key_value[1]&&flag[1],key_value[0]&&flag[0]}),
	     	
.led  (led)
);
endmodule

3.5 原理图

FPGA——状态机专题_第8张图片

总结

  • 状态机分为一段式、两段式、三段式,一般建议用三段式最好。三段式第一段主要就是现态跟随次态,第二段是不同的状态及切换条件等,第三段就是不同状态的输出。
  • 状态机部分不算很难,懂了就简单,只是有时候写得可能会有些混,写之前可以画个流程图梳理一下。

参考文献

  1. 百度百科——状态机
  2. 【FPGA入门九】状态机实验

你可能感兴趣的:(FPGA,fpga开发)