05 状态机

状态机简介

Verilog 是硬件描述语言,它所生成的电路都是并行执行的,当需要按照流程或者步骤来完成某个功能时,可以使用多个 if 嵌套语句来实现,但是这样就增加了代码的复杂度,使得代码可读性差、维护困难,此时若通过状态机来控制程序流程即可解决这个问题。状态机相当于一个控制器,它将一项复杂流程的流程分解为若干步,每步对应于一个状态,通过预先设计的规则在各状态之间进行跳转,从而控制程序执行流程的目的。

状态机,全称是有限状态机(Finite State Machine,缩写为 FSM),是一种在有限个状态之间按一定规律跳转的时序电路(也可以认为是组合逻辑和时序逻辑的一种组合)。状态机通过控制各个状态间的跳转来控制程序执行流程,使得整个代码看上去更加清晰易懂,在控制复杂流程的时候,状态机优势非常明显。

状态机分类

根据输出与输入关系分类

根据状态机的输出是否与输入条件相关,可将状态机分为两大类,即摩尔(Moore)型状态机和米勒(Mealy)型状态机

  • 米勒(Mealy)状态机:输出不仅取决于当前状态,还取决于输入状态
  • 摩尔(Moore)状态机:输出只取决于当前状态

根据写法分类

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

  • 一段式状态机:整个状态机写到一个 always 里面,在该 always 中既描述状态转移,又描述状态的输入和输出,这种写法会使整个代码比较混乱。
  • 二段式状态机:用两个 always 来描述状态机,其中一个 always 采用时序逻辑描述状态转移,另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律,同时进行输出控制,因为这种写法的输出是由组合逻辑控制,其输出可能有毛刺。
  • 三段式状态机:一个 always 模块采用时序逻辑描述状态转移,一个 always 采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 模块描述状态输出,三段式状态机代码逻辑清晰,更有利于代码编写和维护

三段式状态机的实现

三段式状态机的基本格式

  1. 第一个 always 语句采用时序逻辑实现状态跳转
  2. 第二个 always 语句采用组合逻辑判断状态转移条件(一般结合当前状态和输入判断),确定下一个状态
  3. 第三个 always 语句描述状态输出(根据当前状态进行输出,可以用组合电路输出,也可以时序电路输出)

状态编码选择原则

状态编码一般按下面原则选取:

基于状态机的LED流水灯

下面编写一个基于状态机的LED流水灯来演示状态机的编写,系统框图如下,LED从0~3以0.5s间隔依次被点亮,共计有四种状态,状态机第一段采用时序逻辑,实现状态跳转,第二段采用组合逻辑,根据当前状态和计数器的值描述状态转移,确定下一个状态,第三段采用时序逻辑,结合当前状态控制LED。

c 复制代码
module fsm_led #(
	//参数列表
	parameter COUNT_WIDTH = 25,				//内部计数器宽度
	parameter COUNT_PERIOD = 25_000_000		//计数器最大周期,决定LED多久变化依次
)
(
	input sys_clk,
	input sys_rst_n,

	output reg [3:0] led
);

//独热码定义方式
parameter S0 = 4'b0001;
parameter S1 = 4'b0010;
parameter S2 = 4'b0100;
parameter S3 = 4'b1000;

//周期计数器
reg [COUNT_WIDTH-1:0] count;
//当前状态
reg [3:0] curr_st;
//下一个状态
reg [3:0] next_st;

//一个周期计数器
always @(posedge sys_clk) begin
	if(!sys_rst_n)
		count <= 0;
	else if(count < (COUNT_PERIOD - 1))
		count <= count + 1;
	else
		count <= 0;
end

//状态机第一段采用时序逻辑实现状态跳转
always @(posedge sys_clk) begin
	if(!sys_rst_n)
		curr_st <= S0;
	else
		curr_st <= next_st;
end

//状态机第二段采用组合逻辑判断状态转移条件,确定下一个状态
always @(*) begin
	case (curr_st)
		S0 : begin
			//输出采用时序逻辑,存在一个周期的延时,所以这里为(COUNT_PERIOD - 2)
			if(count == (COUNT_PERIOD - 2))
				next_st = S1;
			else
				next_st = S0;
		end
		S1 : begin
			if(count == (COUNT_PERIOD - 2))
				next_st = S2;
			else
				next_st = S1;
		end
		S2 : begin
			if(count == (COUNT_PERIOD - 2))
				next_st = S3;
			else
				next_st = S2;
		end
		S3 : begin
			if(count == (COUNT_PERIOD - 2))
				next_st = S0;
			else
				next_st = S3;
		end
		default: next_st = S0;
	endcase
end

//状态机第三段描述状态输出(根据当前状态进行输出,可以用组合电路输出,也可以时序电路输出)
always @(posedge sys_clk) begin
	if(!sys_rst_n)
		led <= 0;
	else begin
		case (curr_st)
			S0 : led = 4'b0001;
			S1 : led = 4'b0010;
			S2 : led = 4'b0100;
			S3 : led = 4'b1000;
		endcase
	end
end

endmodule
相关推荐
南檐巷上学4 小时前
基于FPGA的正弦信号发生器、滤波器的设计(DAC输出点数受限条件下的完整正弦波产生器)
fpga开发·数字信号处理·dsp·dds
嵌入式-老费9 小时前
Linux Camera驱动开发(fpga + csi rx/csi tx)
fpga开发
ALINX技术博客21 小时前
【202601芯动态】全球 FPGA 异构热潮,ALINX 高性能异构新品预告
人工智能·fpga开发·gpu算力·fpga
JJRainbow1 天前
SN75176 芯片设计RS-232 转 RS-485 通信模块设计原理图
stm32·单片机·嵌入式硬件·fpga开发·硬件工程
s9123601011 天前
FPGA眼图
fpga开发
北京青翼科技1 天前
【PCIe732】青翼PCIe采集卡-优质光纤卡- PCIe接口-万兆光纤卡
图像处理·人工智能·fpga开发·智能硬件·嵌入式实时数据库
minglie11 天前
verilog信号命名规范
fpga开发
XINVRY-FPGA1 天前
中阶FPGA效能红线重新划定! AMD第2代Kintex UltraScale+登场,记忆体频宽跃升5倍
嵌入式硬件·fpga开发·硬件工程·dsp开发·fpga
南檐巷上学2 天前
基于FPGA的音频信号监测识别系统
fpga开发·音频·verilog·fpga·傅立叶分析·fft·快速傅里叶变换
Aaron15882 天前
基于RFSOC的数字射频存储技术应用分析
c语言·人工智能·驱动开发·算法·fpga开发·硬件工程·信号处理