【FPGA零基础学习之旅#9】状态机基础知识

这篇具有很好参考价值的文章主要介绍了【FPGA零基础学习之旅#9】状态机基础知识。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🎉欢迎来到FPGA专栏~状态机基础知识


  • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
  • 博客主页:小夏与酒的博客
  • 🎈该系列文章专栏:FPGA学习之旅
  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️
    【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

一、效果演示

🔸Hello状态机例程
RTL视图:
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析
状态转移:
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

🔸 流水灯状态机例程
使用小精灵V2实现的效果:
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析
小精灵V2基础使用记录:【FPGA-Spirit_V2】小精灵V2开发板初使用。
RTL视图:
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析状态转移:
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

🔸循迹小车pwm状态机例程
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

二、状态机基础知识

状态机全称是有限状态机(Finite State Machine,FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

状态机分为摩尔(Moore)型有限状态机米利(Mealy)型有限状态机。摩尔状态机的输出只由输入确定(不直接依赖于当前状态)。米利有限状态机的输出不止与其输入有关,还与它的当前状态相关,这也是与摩尔有限状态机的不同之处。

对于状态机的描述方式,可分为一段式、两段式以及三段式。
一段式:整个状态机写到一个 always 模块里面。在该模块中既描述状态转移,又描述状态的输入和输出。
两段式:用两个 always 模块来描述状态机。其中一个 always 模块采用同步时序描述状态转移,另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律及其输出。
三段式:在两个 always 模块描述方法基础上,使用三个 always 模块。一个 always 模块采用同步时序描述状态转移,一个 always 采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 模块描述状态输出(可以用组合电路输出,也可以时序电路输出)。

本篇文章主要以三个简单案例为主入门状态机,需要深入学习状态机相关的知识可以参考大佬的文章:FPGA状态机(一段式、二段式、三段式)、摩尔型(Moore)和米勒型(Mealy)。

三、Hello例程分析

Hello例程说明:使用状态机对“Hello”字符串进行检测,当检测到完整的“Hello”字符串时,改变led的电平。

状态转移过程说明:在状态H时,当检测到字符H,跳转到状态e,否则,一直保持在H状态;在状态e时,当检测到字符e,跳转到状态l,否则,跳转回H状态;…同理,在状态o时,当检测到字符o,跳转到状态H,同时改变led的电平,否则,跳转回H状态且不改变led电平。

为了便于转移过程的分析,使用独热码对状态进行编码:

localparam ST_H 	= 5'b00001;
localparam ST_e 	= 5'b00010;
localparam ST_la 	= 5'b00100;
localparam ST_lb 	= 5'b01000;
localparam ST_o 	= 5'b10000;

同时,需要定义一个状态机寄存器,用于判断当前的状态

//状态机寄存器
reg[4:0] curr_st;

由于对“Hello”字符串的检测分为5个状态,使用独热码进行编码占用了5个位宽,因此也需要定义一个5个位宽的状态机寄存器。

为了便于模块的维护和管理,在此定义好led的亮和灭:

//定义led状态
parameter led_on  = 1'b0;
parameter led_off = 1'b1;

根据上述状态转移的说明分析,编写状态机主程序

//状态机主程序
always@(posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		curr_st <= ST_H;
	else begin 
		case(curr_st)
			ST_H:begin
				if(data == "H")
					curr_st <= ST_e;
				else
					curr_st <= ST_H;
			end
			ST_e:begin
				if(data == "e")
					curr_st <= ST_la;
				else
					curr_st <= ST_H;
			end
			ST_la:begin
				if(data == "l")
					curr_st <= ST_lb;
				else
					curr_st <= ST_H;
			end
			ST_lb:begin
				if(data == "l")
					curr_st <= ST_o;
				else
					curr_st <= ST_H;
			end
			ST_o:begin
				if(data == "o")
					curr_st <= ST_H;
				else
					curr_st <= ST_H;
			end
			default:curr_st <= ST_H;
		endcase
	end
end

“Hello”例程的完整代码:

FSM_Hello.v:

/
//模块:状态机例程-Hello
//作者:CSDN-小夏与酒

module FSM_Hello(
	input 			Clk,
	input 			Rst_n,
	input 		 	[7:0]data,
	output 	reg 	led
);

	//状态机状态定义
	//使用独热码的编码方式
	//写法一:
	localparam ST_H 	= 5'b00001;
	localparam ST_e 	= 5'b00010;
	localparam ST_la 	= 5'b00100;
	localparam ST_lb 	= 5'b01000;
	localparam ST_o 	= 5'b10000;

	//写法二:
	//	localparam
	//			ST_H 	= 5'b00001,
	//          ST_e 	= 5'b00010,
	//          ST_la 	= 5'b00100,
	//          ST_lb 	= 5'b01000,
	//			ST_o 	= 5'b10000;
				
	//状态机寄存器
	reg[4:0] curr_st;
	
	//定义led状态
	parameter led_on  = 1'b0;
	parameter led_off = 1'b1;
	
	//状态机主程序
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			curr_st <= ST_H;
		else begin 
			case(curr_st)
				ST_H:begin
					if(data == "H")
						curr_st <= ST_e;
					else
						curr_st <= ST_H;
				end
				ST_e:begin
					if(data == "e")
						curr_st <= ST_la;
					else
						curr_st <= ST_H;
				end
				ST_la:begin
					if(data == "l")
						curr_st <= ST_lb;
					else
						curr_st <= ST_H;
				end
				ST_lb:begin
					if(data == "l")
						curr_st <= ST_o;
					else
						curr_st <= ST_H;
				end
				ST_o:begin
					if(data == "o")
						curr_st <= ST_H;
					else
						curr_st <= ST_H;
				end
				default:curr_st <= ST_H;
			endcase
		end
	end

	//给led赋值,如果进入ST_o状态,并且data = "o",则led电平改变
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led <= led_off;
		else if(curr_st == ST_o && data == "o")
			led <= led_on;
		else
			led <= led_off;
	end

endmodule

RTL视图:

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

编写测试激励文件:

FSM_Hello_tb.v:

`timescale 1ns/1ns
`define clock_period 20

module FSM_Hello_tb;

	reg Clk;
	reg Rst_n;
	reg [7:0]ASCII;
	
	wire led;
	
	FSM_Hello FSM_Hello0(
		.Clk(Clk),
		.Rst_n(Rst_n),
	 	.data(ASCII),
	 	.led(led)
	);

	initial Clk = 1;
	always #(`clock_period/2) Clk = ~Clk;
	
	initial begin
		Rst_n = 0;
		ASCII = 0;
		#(`clock_period*20);
		Rst_n = 1;
		#(`clock_period*20 + 1);
		
		ASCII = "I";
		#(`clock_period);
		ASCII = "A";
		#(`clock_period);
		ASCII = "M";
		#(`clock_period);
		ASCII = "X";
		
		#(`clock_period);
		ASCII = "H";
		#(`clock_period);
		ASCII = "E";
		#(`clock_period);
		ASCII = "M";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);

		ASCII = "H";
		#(`clock_period);
		ASCII = "E";
		#(`clock_period);
		ASCII = "L";
		#(`clock_period);
		ASCII = "L";
		#(`clock_period);
		ASCII = "O";
		#(`clock_period);
		
		ASCII = "H";
		#(`clock_period);
		ASCII = "e";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "o";
		
		#(`clock_period);
		ASCII = "l"; 
		#(`clock_period);
		
		ASCII = "H";
		#(`clock_period);
		ASCII = "e";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "l";
		#(`clock_period);
		ASCII = "o";
		
		#(`clock_period);
		ASCII = "l"; 
		
		#(`clock_period);
		
		$stop;
	end		

endmodule

仿真结果:

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

四、简单例程分析

4.1 使用状态机实现流水灯

使用Verilog实现一个流水灯并不难,但是使用状态机的方式实现,就提供了新的编程思路。

在状态机的文章中加入流水灯的实现,主要是为了学习 【小月电子】 大佬的状态机写法风格,大佬博客主页链接:Moon_3181961725。

先上完整代码:

FSM_01_led.v:

/
//模块:状态机例程-LED-分析
//作者:CSDN-小夏与酒

module FSM_01_led(
	input 		Clk,
	input 		Rst_n,
	output reg 	led1,
	output reg 	led2,
	output reg 	led3
	);
	
	//定义状态机
	parameter	ST1		= 1;
	parameter	ST2		= 2;
	parameter	ST3		= 3;
	
	reg[3:0]	curr_st;//状态机寄存器
	reg[7:0]	cnt1;//定义计数寄存器
	reg[7:0]	cnt2;//定义计数寄存器
	reg[7:0]	cnt3;//定义计数寄存器
	
	//状态机主程序
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			curr_st <= ST1;
		else case(curr_st)
			ST1:begin
				if(cnt1 == 8'd9)
					curr_st <= ST2;
				else;	
			end
			ST2:begin
				if(cnt2 == 8'd9)
					curr_st <= ST3;
				else;	
			end
			ST3:begin
				if(cnt3 == 8'd9)
					curr_st <= ST1;
				else;	
			end
			default:;
		endcase
	end
	
	//状态机ST1的计数器,当状态机等于ST1时,cnt1加1,否则cnt1等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt1 <= 8'b0;
		else if(curr_st == ST1)
			cnt1 <= cnt1 + 1'b1;
		else
			cnt1 <= 8'b0;	
	end
	
	//状态机ST2的计数器,当状态机等于ST2时,cnt2加1,否则cnt2等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt2 <= 8'b0;
		else if(curr_st == ST2)
			cnt2 <= cnt2 + 1'b1;
		else
			cnt2 <= 8'b0;	
	end
	
	//状态机ST3的计数器,当状态机等于ST3时,cnt3加1,否则cnt3等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt3 <= 8'b0;
		else if(curr_st == ST3)
			cnt3 <= cnt3 + 1'b1;
		else
			cnt3 <= 8'b0;	
	end
	
	//给LED1赋值,当状态等于ST1时,LED1等于0,即点亮LED灯,否则LED1等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led1 <= 1'b1;
		else if(curr_st == ST1)
			led1 <= 1'b0;
		else
			led1 <= 1'b1;	
	end
	
	//给LED2赋值,当状态等于ST2时,LED2等于0,即点亮LED灯,否则LED2等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led2 <= 1'b1;
		else if(curr_st == ST2)
			led2 <= 1'b0;
		else
			led2 <= 1'b1;	
	end
	
	//给LED3赋值,当状态等于ST3时,LED3等于0,即点亮LED灯,否则LED3等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led3 <= 1'b1;
		else if(curr_st == ST3)
			led3 <= 1'b0;
		else
			led3 <= 1'b1;	
	end
	
endmodule
	

RTL视图与状态转移:

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

FSM_01_led_tb.v:

`timescale 1ns/1ns
`define clock_period 20

module FSM_01_led_tb;
	
	reg Clk;
	reg Rst_n;
	
	wire led1;
	wire led2;
	wire led3;
	
	FSM_01_led FSM_01_led0(
		.Clk(Clk),
		.Rst_n(Rst_n),
		.led1(led1),
		.led2(led2),
		.led3(led3)
	);
	
	initial Clk = 1;
	always #(`clock_period/2) Clk = ~Clk;
	
	initial begin
	Rst_n = 0;
	
	#(`clock_period*30);
	Rst_n = 1;
	
	#(`clock_period*300);
	
	$stop;
	
	end
	
endmodule

仿真结果:

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

实现效果:

在上板验证前记得修改计数器的计数值,即:

/
//模块:状态机例程-LED
//作者:CSDN-小夏与酒

module FSM_01_led(
	input 		Clk,
	input 		Rst_n,
	output reg 	led1,
	output reg 	led2,
	output reg 	led3
	);
	
	//定义状态机
	parameter	ST1		= 1;
	parameter	ST2		= 2;
	parameter	ST3		= 3;
	
	reg[3:0]	curr_st;//状态机寄存器
	reg[24:0]	cnt1;//定义计数寄存器
	reg[24:0]	cnt2;//定义计数寄存器
	reg[24:0]	cnt3;//定义计数寄存器
	
	//定义计数值范围
	parameter cnt_max = 25'd24_999_999;	//定时器最大值
	parameter cnt_min = 25'd0;			//定时器最小值
	parameter cnt_add = 1'b1;			//定时器定时增量
	
	//定义led状态
	parameter led_on  = 1'b0;
	parameter led_off = 1'b1;
	
	//状态机主程序
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			curr_st <= ST1;
		else case(curr_st)
			ST1:begin
				if(cnt1 == cnt_max)
					curr_st <= ST2;
				else;	
			end
			ST2:begin
				if(cnt2 == cnt_max)
					curr_st <= ST3;
				else;	
			end
			ST3:begin
				if(cnt3 == cnt_max)
					curr_st <= ST1;
				else;	
			end
			default:;
		endcase
	end
	
	//状态机ST1的计数器,当状态机等于ST1时,cnt1加1,否则cnt1等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt1 <= cnt_min;
		else if(curr_st == ST1)
			cnt1 <= cnt1 + cnt_add;
		else
			cnt1 <= cnt_min;	
	end
	
	//状态机ST2的计数器,当状态机等于ST2时,cnt2加1,否则cnt2等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt2 <= cnt_min;
		else if(curr_st == ST2)
			cnt2 <= cnt2 + cnt_add;
		else
			cnt2 <= cnt_min;	
	end
	
	//状态机ST3的计数器,当状态机等于ST3时,cnt3加1,否则cnt3等于0
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			cnt3 <= cnt_min;
		else if(curr_st == ST3)
			cnt3 <= cnt3 + cnt_add;
		else
			cnt3 <= cnt_min;	
	end
	
	//给LED1赋值,当状态等于ST1时,LED1等于0,即点亮LED灯,否则LED1等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led1 <= led_off;
		else if(curr_st == ST1)
			led1 <= led_on;
		else
			led1 <= led_off;	
	end
	
	//给LED2赋值,当状态等于ST2时,LED2等于0,即点亮LED灯,否则LED2等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led2 <= led_off;
		else if(curr_st == ST2)
			led2 <= led_on;
		else
			led2 <= led_off;	
	end
	
	//给LED3赋值,当状态等于ST3时,LED3等于0,即点亮LED灯,否则LED3等于1,关闭LED灯
	always@(posedge Clk or negedge Rst_n)begin
		if(!Rst_n)
			led3 <= led_off;
		else if(curr_st == ST3)
			led3 <= led_on;
		else
			led3 <= led_off;	
	end
	
endmodule
	

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

学习方法:

将状态机主程序分割开来单独写,会使整个程序思路清晰。状态机主程序部分:

//状态机主程序
always@(posedge Clk or negedge Rst_n)begin
	if(!Rst_n)
		curr_st <= ST1;
	else case(curr_st)
		ST1:begin
			if(cnt1 == 8'd9)
				curr_st <= ST2;
			else;	
		end
		ST2:begin
			if(cnt2 == 8'd9)
				curr_st <= ST3;
			else;	
		end
		ST3:begin
			if(cnt3 == 8'd9)
				curr_st <= ST1;
			else;	
		end
		default:;
	endcase
end

4.2 使用状态机实现循迹小车的pwm

关于FPGA实现简单的循迹小车链接:【FPGA-Spirit_V2】基于FPGA的循迹小车-小精灵V2开发板。

现在讲解小车中的pwm模块

关于模块的端口列表:

module ctrl_moto_pwm(
	input				clk,//时钟50M
	input				rst_n,//复位,低电平有效
	input	[7:0]		spd_high_time,//输入高电平持续时间
	input	[7:0]		spd_low_time,//输入低电平持续时间
	output				period_fini,//一个pwm周期结束的标志位
	output	reg			pwm//脉冲信号									
);

该模块需要输入时钟信号、复位信号、高电平持续时间和低电平持续时间;输出一个pwm周期结束的标志位和pwm信号

定义pwm产生的三个状态

//状态机
parameter	idle		= 8'h0;//空闲状态
parameter	step_high 	= 8'h1;//脉冲高电平状态,当为该状态时,pwm为高电平
parameter	step_low  	= 8'h2;//脉冲低电平状态,当为该状态时,pwm为低电平

为了debug的方便,在模块中加入判断产生了一个pwm的标志位

//产生一个pwm周期的标志位,当一个pwm产生后,输出高电平,否则输出低电平
assign period_fini = (step_low_cnt == step_low_time)?1'b1:1'b0;

同样,与上文相同,为了编程思路的清晰,将状态机主程序单独写

//状态机主程序
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		curr_st <= idle;
	else case(curr_st)
		idle:curr_st <= step_high;
		step_high:begin
				//当高电平计数时间到达输入值时,进行状态跳转
				if(step_high_cnt == step_high_time)
					curr_st <= step_low;
				else;
		end
		step_low:begin
				//当低电平计数时间到达输入值时,进行状态跳转
				if(step_low_cnt == step_low_time)
					curr_st <= step_high;
				else;
		end
		default:;
	endcase
end

在不同的状态下(空闲、高电平、低电平),pwm的信号输出:

//该always块描述pwm的输出
always@(posedge clk or negedge rst_n)begin
	if(!rst_n)
		pwm <= 0;
	else if(curr_st == idle)
		pwm <= 0;
	else if(curr_st == step_high)
		pwm <= 1;
	else if(curr_st == step_low)
		pwm <= 0;
	else
		pwm <= 1;
end

完整代码如下:

ctrl_moto_pwm.v:

//脉冲生成模块,通过控制输出脉冲频率及占空比来控制小车的速度
module ctrl_moto_pwm(
	input				clk,//时钟50M
	input				rst_n,//复位,低电平有效
	input	[7:0]		spd_high_time,//输入高电平持续时间
	input	[7:0]		spd_low_time,//输入低电平持续时间
	output				period_fini,//一个pwm周期结束的标志位
	output	reg			pwm//脉冲信号									
);
				
	//状态机
	parameter	idle		= 8'h0;//空闲状态
	parameter	step_high 	= 8'h1;//脉冲高电平状态,当为该状态时,pwm为高电平
	parameter	step_low  	= 8'h2;//脉冲低电平状态,当为该状态时,pwm为低电平

	reg	[7:0]	curr_st;
	reg	[10:0]	step_high_time;
	reg	[10:0]	step_low_time;
	reg	[10:0]	step_high_cnt;
	reg	[10:0]	step_low_cnt;
	
	//产生一个pwm周期的标志位,当一个pwm产生后,输出高电平,否则输出低电平
	assign period_fini = (step_low_cnt == step_low_time)?1'b1:1'b0;
	
	//将输入值(高、低电平持续时间)存入寄存器中
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)begin
				step_high_time <= 0;
				step_low_time <= 0;
		end
		else begin
				step_high_time <= spd_high_time;
				step_low_time <= spd_low_time;
		end
	end
	
	//状态机主程序
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			curr_st <= idle;
		else case(curr_st)
			idle:curr_st <= step_high;
			step_high:begin
					//当高电平计数时间到达输入值时,进行状态跳转
					if(step_high_cnt == step_high_time)
						curr_st <= step_low;
					else;
			end
			step_low:begin
					//当低电平计数时间到达输入值时,进行状态跳转
					if(step_low_cnt == step_low_time)
						curr_st <= step_high;
					else;
			end
			default:;
		endcase
	end
	
	//高电平持续时间计数器,当持续时间到达输入值时,进行状态跳转
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			step_high_cnt <= 0;
		else if(curr_st == idle)
			step_high_cnt <= 0;
		else if(curr_st == step_high)
			step_high_cnt <= step_high_cnt + 1;
		else
			step_high_cnt <= 0;
	end
	
	//低电平持续时间计数器,当持续时间到达输入值时,进行状态跳转
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			step_low_cnt <= 0;
		else if(curr_st == idle)
			step_low_cnt <= 0;
		else if(curr_st == step_low)
			step_low_cnt <= step_low_cnt + 1;
		else
			step_low_cnt <= 0;
	end
	
	//该always块描述pwm的输出
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n)
			pwm <= 0;
		else if(curr_st == idle)
			pwm <= 0;
		else if(curr_st == step_high)
			pwm <= 1;
		else if(curr_st == step_low)
			pwm <= 0;
		else
			pwm <= 1;
	end

endmodule

pwm产生模块的代码中我每一部分都写了对应的注释,可供大家参考,该模块可以直接在需要用到pwm的地方直接调用

该模块对应的RTL视图和状态转移:
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析
【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析
我们编写一个简单的测试激励文件:

ctrl_moto_pwm_tb.v:

`timescale 1ns/1ns
`define clock_period 20

module ctrl_moto_pwm_tb;
	
	reg clk;
	reg rst_n;
	reg [7:0]spd_high_time;
	reg [7:0]spd_low_time;
	
	wire period_fini;
	wire pwm;
	
	ctrl_moto_pwm Uctrl_moto_pwm0(
		.clk(clk),//时钟50M
		.rst_n(rst_n),//复位,低电平有效
		.spd_high_time(spd_high_time),//输入高电平持续时间
		.spd_low_time(spd_low_time),//输入低电平持续时间
		.period_fini(period_fini),//一个pwm周期结束的标志位
		.pwm(pwm)//脉冲信号									
	);

	initial clk = 1;
	always #(`clock_period/2) clk = ~clk;
	
	initial begin
	
		rst_n = 0;
		spd_high_time = 0;
		spd_low_time = 0;
		#(`clock_period*100);
	
		rst_n = 1;
		spd_high_time = 15;
		spd_low_time = 5;
		#(`clock_period*2000);
		
		$stop;
	
	end

endmodule

仿真结果:

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

🧸结尾文章来源地址https://www.toymoban.com/news/detail-598652.html


  • ❤️ 感谢您的支持和鼓励! 😊🙏
  • 📜您可能感兴趣的内容:
  • 【FPGA-Spirit_V2】基于FPGA的循迹小车-小精灵V2开发板
  • 【Verilog HDL】FPGA-Verilog文件的基本结构
  • 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
  • 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
    【FPGA零基础学习之旅#9】状态机基础知识,FPGA学习之旅,fpga开发,学习,状态机,Verilog HDL,FPGA,例程分析

到了这里,关于【FPGA零基础学习之旅#9】状态机基础知识的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [FPGA] 7系列FPGA的基础知识

    Virtex-7 Family是Xilinx公司推出的一系列FPGA器件,采用了28纳米工艺制造。它是Xilinx公司的第一个采用28纳米工艺的FPGA系列,提供了高性能、低功耗和灵活性的特点。 Virtex-7 Family提供了不同规模的器件,包括Virtex-7 XT、Virtex-7 HT、Virtex-7 H580T、Virtex-7 VXT和Virtex-7 VX系列,每个系列都

    2024年02月09日
    浏览(34)
  • FPGA基础知识

    FPGA是在PAL、PLA和CPLD等可编程器件的基础上进一步发展起来的一种更复杂的可编程逻辑器件。它是ASIC领域中的一种半定制电路,既解决了定制电路的不足,又克服了原有可编程器件门电路有限的缺点。 由于FPGA需要被反复烧写,它实现组合逻辑的基本结构不可能像ASIC那样通过

    2024年02月03日
    浏览(30)
  • 第一篇 FPGA基础知识

    FPGA的全称为Field-ProgrammableGateArray,即现场可编程门阵列。 在开始学习FPGA之前,同学们首先应该清楚地了解FPGA的概念,明白FPGA到底是什么东西,可以用来做什么。FPGA是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物,是作为专用集成电路(ASIC)领域中的一种半定制

    2024年02月07日
    浏览(51)
  • FPGA基础知识点

    基础知识 逻辑值 逻辑0:表示低电平,也就是对应电路GND 逻辑1:表示高电平,也就是对应电路VCC 逻辑X:表示未知,有可能是高电平也有可能是低电平 逻辑Z:表示高阻态,外部没有激励信号,是一个悬空状态 数字进制格式 Verilog数字进制格式包括 二进制(b) , 八进制(

    2024年02月03日
    浏览(40)
  • FPGA基础知识-任务和函数

    目录 学习目标 学习内容 1.任务和函数的区别 2.任务 3.函数 学习时间 学习总结 1.理解任务和函数之间的区别。 2.理解定义任务所需的条件,学会任务的声明和调用。 3.理解定义函数所需的条件.学会函数的声明和调用。 提示:这里可以添加要学的内容      在 Verilog中,任务

    2024年02月10日
    浏览(30)
  • FPGA基础知识-行为级建模

    目录 学习目标 学习内容 1.结构化过程语句 2.过程赋值语句 3.时序控制 4.条件语句 5.多路分支语句 6.循环语句 7.顺序块和并行块      8.生成块 学习时间 学习产出 解释结构化过程always和initial在行为级建模中的重要性, 定义阻塞( blocking〉和非阻塞( non-blocking )过程性赋值语句

    2024年02月12日
    浏览(40)
  • FPGA基础知识-开关级建模

    目录 学习目标 学习内容 1.MOS开关 2.CMOS开关 3.双向开关  4.电源和地 5.阻抗开关 6.开关中的延迟说明 学习时间 学习总结 提示:这里可以添加学习目标 1.能够描述基本 MOS开关:nmos.pmos和cmos。 2.理解双向传输开关、电源和地的建模方法。 3.识别阻抗MOS开关。 4.解释在基本MOS 开关

    2024年02月10日
    浏览(29)
  • FPGA基础知识-时序和延迟

    目录 学习目标: 学习内容: 1.延迟模型的类型 2.路径延迟建模 3.时序检查 4.延迟反标注 学习时间: 学习总结 提示:这里可以添加学习目标 ·鉴别Verilog 仿真中用到的延迟模型的类型,分布延迟、集总( lumped)延迟和引脚到引脚〔路径)的延迟。 能解释rise. fall和 turn-off延迟

    2024年02月09日
    浏览(29)
  • FPGA基础知识-门级建模

    目录 学习目标 学习内容 1.门的类型  2.门延迟 学习时间 学习小结 学习Verilog 提供的门级原语 理解门的实例引用、门的符号以及andor,bufnot类型的门的真值表 学习如何根据电路的逻辑图来生成verilog描述 讲述门级设计中的上升、下降和关断延迟 解释门级设计中的最小、最大和

    2024年02月10日
    浏览(42)
  • FPGA基础知识-模块和端口

    目录 学习目标 学习内容  端口 端口列表 端口声明 端口链接规则 学习时间 总结 1.说明Verilog 模块定义中的各个组成部分,例如模块名、端口列表、参数、变址声明、数据流描述语句、行为语句、调用(实例引用》其他模块以及任务和函数等。 2.说明verilog模块定义中的各个组

    2024年02月08日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包