FPGA:有限状态机

从以下6个实验理解状态机的概念
在这里插入图片描述
开发板频率为 50 M H z 50MHz 50MHz,一个时钟周期是 20 n s 20ns 20ns

1、实验一:LED灯亮0.25秒、灭0.75秒的状态循环

通过之前的分析,我们实现频闪灯时,是让led灯在0.5秒实现一次翻转,而这里虽然总时长都是一秒,但是要求亮的时间和不亮的时间缺不是相等的。 1 s ÷ 20 n s = 5 × 1 0 7 T 1s÷20ns=5\times10^7T 1s÷20ns=5×107T,即一秒钟有 50000000 50000000 50000000个时钟周期,而 ⌈ l o g 2 50000000 ⌉ = 26 ⌈log_250000000⌉=26 log250000000=26,所以计数器需要26位。我们可以在前0.75秒保持灯灭,在0.75秒-1秒保持灯亮,即可实现对应的要求。
对应的verilog代码如下:

module led_flash1(
	clk,Reset_n,led
);
input clk;
input Reset_n;
output reg led;
reg [25:0] counter;
parameter MCNT=50000000;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter<=0;
	else if(counter==MCNT-1)
		counter<=0;
	else
		counter<=counter+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		led<=0;
	else if(counter==(3*MCNT)/4-1)  //0.75秒时1秒的3/4
		led<=1;
	else if(counter==MCNT-1)
		led<=0;
endmodule

对应的测试文件如下:

`timescale 1ns/1ps
module led_flash_tb();
reg clk;
reg Reset_n;
wire led;
led_flash1 led_flash_inst(
	.clk(clk),
	.Reset_n(Reset_n),
	.led(led)
);
defparam led_flash_inst.MCNT=50000;
initial clk=1;
always #10 clk=!clk;
initial begin
	Reset_n=0;
	#201;
	Reset_n=1;
	#2000000;
	$stop;
	end
endmodule

进行仿真,对应的波形图如下所示:
在这里插入图片描述

和我们分析的结果一致,前面还有对应的零头使我们复位的用时。但是通过add_maker可以发现和预想结果一致。

2、实验二:LED灯亮0.25秒,灭0.5秒,亮0.75秒,灭1秒顺序循环

此时总时间周期为 0.25 + 0.5 + 0.75 + 1 = 2.5 s 0.25+0.5+0.75+1=2.5s 0.25+0.5+0.75+1=2.5s
2.5 s ÷ 20 n s = 125000000 T 2.5s \div 20ns=125000000T 2.5s÷20ns=125000000T,而 ⌈ l o g 2 125000000 ⌉ = 27 ⌈log_2125000000⌉=27 log2125000000=27,所以计数器需要27位,其余实现思路类似。
比如从开始分析,复位时令led为1,然后亮0.25秒,即十分之一个周期,然后灭0.5秒,即在0.25秒到0.75秒为灭,0.75秒为十分之三个周期,0.75秒到1.5秒为亮,1.5秒为五分之三个总周期,1.5秒-2.5秒为灭。这就是一个周期。对应的verilog代码如下:

module led_flash2(
	clk,Reset_n,led
);
input clk;
input Reset_n;
output reg led;
reg [26:0] counter;
parameter MCNT=125000000;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter<=0;
	else if(counter==MCNT-1)
		counter<=0;
	else
		counter<=counter+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		led<=1;
	else if(counter==MCNT/10-1)
		led<=0;
	else if(counter==(MCNT)*3/10-1)
		led<=1;
	else if(counter==(MCNT)*3/5-1)
		led<=0;
	else if(counter==MCNT-1)
		led<=1;
endmodule

测试文件如下

`timescale 1ns/1ps
module led_flash_tb();
reg clk;
reg Reset_n;
wire led;
led_flash2 led_flash_inst(
	.clk(clk),
	.Reset_n(Reset_n),
	.led(led)
);
defparam led_flash_inst.MCNT=125000;
initial clk=1;
always #10 clk=!clk;
initial begin
	Reset_n=0;
	#201;
	Reset_n=1;
	#20000000;
	$stop;
	end
endmodule

打开仿真如下所示:
在这里插入图片描述

3、实验三:LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机制定,以0.25秒为一个变化周期,8个变化状态为一个循环。

这个实验的思路就是加上一个端口[7:0]ctrl,用来记录状态,每过0.25秒的时候,转换到下一个状态,计算可得,单个总时长为 0.25 × 8 = 2 s 0.25×8=2s 0.25×8=2s,单个周期内的时钟周期数: 2 × 1 0 9 ÷ 20 = 100000000 T 2×10^9÷20=100000000T 2×109÷20=100000000T,需要27位计数器,所以可以定义一个变量让其等于 1000000000 1000000000 1000000000,每经过八分之一个周期切换一次状态即可。具体实现的verilog代码如下:

module led_flash3(
	clk,Reset_n,led,ctrl
);
input clk;
input Reset_n;
output reg led;
input [7:0] ctrl;
reg [26:0] counter;
parameter MCNT=100000000;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter<=0;
	else if(counter==MCNT-1)
		counter<=0;
	else
		counter<=counter+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		led<=0;
	else if(counter==MCNT/8-1)
		led<=ctrl[0];
	else if(counter==(MCNT)*2/8-1)
		led<=ctrl[1];
	else if(counter==(MCNT)*3/8-1)
		led<=ctrl[2];
	else if(counter==(MCNT)*4/8-1)
		led<=ctrl[3];
	else if(counter==(MCNT)*5/8-1)
		led<=ctrl[4];
	else if(counter==(MCNT)*6/8-1)
		led<=ctrl[5];
	else if(counter==(MCNT)*7/8-1)
		led<=ctrl[6];
	else if(counter==MCNT-1)
		led<=ctrl[7];
endmodule

测试文件代码如下:

`timescale 1ns/1ps
module led_flash3_tb();
reg clk;
reg Reset_n;
reg[7:0] ctrl;
wire led;
led_flash3 led_flash_inst(
	.clk(clk),
	.Reset_n(Reset_n),
	.led(led),
	.ctrl(ctrl)
);
defparam led_flash_inst.MCNT=100000;
initial clk=1;
always #10 clk=!clk;
initial begin
	Reset_n=0;
	#201;
	Reset_n=1;
	ctrl = 8'b0101_0110;    //初始的ctrl状态
	#20000000;
	$stop;
	end
endmodule

仿真波形如下:
注意高低位的判断,ctrl[0]=0,ctrl[1]=1,ctrl[2]=1,ctrl[3]=0,ctrl[4]=1,ctrl[5]=0,ctrl[6]=1,ctrl[7]=0,所以显示应该为01101010,测试文件里面我们定义MCNT为100000, 100000 × 20 ÷ 8 = 250 μ s 100000×20÷8=250μs 100000×20÷8=250μs,所以仿真内一个变化周期为 250 μ s 250μs 250μs,观察可知相符。
在这里插入图片描述

4、实验四:让LED灯按照指定的亮灭模式亮灭,亮灭模式未知,由用户随机制定,8个变化状态为一个循环,每个变化状态的时间值可以根据不同的应用场景选择。

实验思路:加入一个时间端口Time,计数器1专门用来计数,计数器2专门用来计状态,当计数器1满一个Time则计数器2加1。此处设Time位宽为32位,即[31:0]。所以计数器1要和它一样长,否则会出现溢出,只有8种状态,需要3位二进制表示。具体verilog代码如下:

module led_flash3(
	clk,Reset_n,led,ctrl,Time
);
input clk;
input Reset_n;
output reg led;
input [7:0] ctrl;
input [31:0] Time;
reg [31:0] counter;
reg [2:0] counter2;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter<=0;
	else if(counter==Time-1)
		counter<=0;
	else
		counter<=counter+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter2<=0;
	else if (counter==Time-1)
		counter2<=counter2+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		led<=0;
	else
		case(counter2)
			0:led<=ctrl[0];
			1:led<=ctrl[1];
			2:led<=ctrl[2];
			3:led<=ctrl[3];
			4:led<=ctrl[4];
			5:led<=ctrl[5];
			6:led<=ctrl[6];
			7:led<=ctrl[7];
			default:led<=led;
		endcase
endmodule

测试文件如下:

`timescale 1ns/1ps
module led_flash4_tb();
reg clk;
reg Reset_n;
reg[7:0] ctrl;
reg [31:0] Time;
wire led;
led_flash4 led_flash_inst(
	.clk(clk),
	.Reset_n(Reset_n),
	.led(led),
	.ctrl(ctrl),
	.Time(Time)
);
initial clk=1;
always #10 clk=!clk;
initial begin
	Reset_n=0;
	ctrl=0;
	Time=0;
	#201;
	Reset_n=1;
	Time = 2500;
	ctrl = 8'b1110_1101;
	#20000000;
	$stop;
	end
endmodule

这里Time定义为2500,即一个小周期2500个时钟周期,即 2500 × 20 n s = 50 μ s 2500×20ns=50μs 2500×20ns=50μs,仿真波形如下
在这里插入图片描述

5、实验五:让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变换

实验思路:和之前一样,这里在输入端口上加上8位的LED控制对应的LED灯即可。(为了简化输入输出,在这里time和ctrl都设置为定值),对应verilog代码如下:

module led_flash5(
	clk,Reset_n,led,ctrl
);
input clk;
input Reset_n;
input [7:0] ctrl;
output reg[7:0] led;
parameter Time=24999999;
reg [31:0] counter;
reg [2:0] counter2;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter<=0;
	else if(counter==Time-1)
		counter<=0;
	else
		counter<=counter+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		counter2<=0;
	else if (counter==Time-1)
		counter2<=counter2+1'b1;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		led<=0;
	else
		case(counter2)
			0:begin
			led[0]<=ctrl[0];
			led[1]<=ctrl[0];
			led[2]<=ctrl[0];
			led[3]<=ctrl[0];
			led[4]<=ctrl[0];
			led[5]<=ctrl[0];
			led[6]<=ctrl[0];
			led[7]<=ctrl[0];
			end
			1:begin
			led[0]<=ctrl[1];
			led[1]<=ctrl[1];
			led[2]<=ctrl[1];
			led[3]<=ctrl[1];
			led[4]<=ctrl[1];
			led[5]<=ctrl[1];
			led[6]<=ctrl[1];
			led[7]<=ctrl[1];
			end
			2:begin
			led[0]<=ctrl[2];
			led[1]<=ctrl[2];
			led[2]<=ctrl[2];
			led[3]<=ctrl[2];
			led[4]<=ctrl[2];
			led[5]<=ctrl[2];
			led[6]<=ctrl[2];
			led[7]<=ctrl[2];
			end
			3:begin
			led[0]<=ctrl[3];
			led[1]<=ctrl[3];
			led[2]<=ctrl[3];
			led[3]<=ctrl[3];
			led[4]<=ctrl[3];
			led[5]<=ctrl[3];
			led[6]<=ctrl[3];
			led[7]<=ctrl[3];
			end
			4:begin
			led[0]<=ctrl[4];
			led[1]<=ctrl[4];
			led[2]<=ctrl[4];
			led[3]<=ctrl[4];
			led[4]<=ctrl[4];
			led[5]<=ctrl[4];
			led[6]<=ctrl[4];
			led[7]<=ctrl[4];
			end
			5:begin
			led[0]<=ctrl[5];
			led[1]<=ctrl[5];
			led[2]<=ctrl[5];
			led[3]<=ctrl[5];
			led[4]<=ctrl[5];
			led[5]<=ctrl[5];
			led[6]<=ctrl[5];
			led[7]<=ctrl[5];
			end
			6:begin
			led[0]<=ctrl[6];
			led[1]<=ctrl[6];
			led[2]<=ctrl[6];
			led[3]<=ctrl[6];
			led[4]<=ctrl[6];
			led[5]<=ctrl[6];
			led[6]<=ctrl[6];
			led[7]<=ctrl[6];
			end
			7:begin
			led[0]<=ctrl[7];
			led[1]<=ctrl[7];
			led[2]<=ctrl[7];
			led[3]<=ctrl[7];
			led[4]<=ctrl[7];
			led[5]<=ctrl[7];
			led[6]<=ctrl[7];
			led[7]<=ctrl[7];
			end
			default:begin
			led[0]<=led[0];
			led[1]<=led[1];
			led[2]<=led[2];
			led[3]<=led[3];
			led[4]<=led[4];
			led[5]<=led[5];
			led[6]<=led[6];
			led[7]<=led[7];
			end
		endcase
endmodule

测试文件如下:

`timescale 1ns/1ps
module led_flash5_tb();
reg clk;
reg Reset_n;
reg [7:0] ctrl;
wire [7:0]led;
led_flash5 led_flash_inst(
	.clk(clk),
	.Reset_n(Reset_n),
	.led(led),
	.ctrl(ctrl)
);
defparam led_flash_inst.Time=2500;
initial clk=1;
always #10 clk=!clk;
initial begin
	Reset_n=0;
	ctrl=0;
	#201;
	Reset_n=1;
	ctrl=8'b1101_1010;
	#20000000;
	$stop;
	end
endmodule

Time依旧采用2500,即 50 μ s 50μs 50μs,在测试文件中,令ctrl=1101_1010,注意这是ctrl[7]-ctrl[0]顺序,仿真波形如下:
在这里插入图片描述
这里我们进行引脚分配并进行班级调试,将ctrl分配给八个开关,led分配给对应的led灯,Reset_n分配给按键S0,clk分配给时钟单元。
引脚分配
生成比特流,烧到开发板进行调试,分析一下:当我们按键上推表示在对应小周期就是亮的,如果SW7-SW0是按照10101010,那就是之前所说的频闪灯,只不过这里是0.5s,前0.25秒亮,后0.25秒灭,如果是0000_1111,那就是后一秒亮,前1秒不亮,频闪周期2秒。全0则全不亮,全1则全亮。接下来验证一下上面的分析:

在这里插入图片描述
在这里插入图片描述

6、实验六:每隔10ms,让LED灯的一个8状态循环执行一次(每10ms执行一次)

10 m s ÷ 20 n s = 500000 T 10ms÷20ns=500000T 10ms÷20ns=500000T,8个状态最多在10ms内运行完成 500000 ÷ 8 = 62500 T 500000÷8=62500T 500000÷8=62500T,即每个小周期最多有62500个时钟周期。 ⌈ l o g 2 62500 ⌉ = 16 ⌈log_262500⌉=16 log262500=16,即Time最多16位,接下来我们定义一个计时器为10ms, ⌈ l o g 2 500000 ⌉ = 19 ⌈log_2500000⌉=19 log2500000=19,即最多需要19位。再增加一共标志位,当计数状态慢8状态时,标志位为0,即阻塞不再执行,当计数器3(计时10毫秒)满时,标志位重新恢复为1.对应verilog代码如下:

module led_flash6(
	clk,Reset_n,led,ctrl,Time
);
input clk;
input Reset_n;
output reg led;
input [7:0] ctrl;
input [15:0] Time;
reg [15:0] counter;
reg [2:0] counter2;
reg [18:0]counter3;
reg logo; //标志位
always @(posedge clk or negedge Reset_n)   //单个小周期计数器
	if(!Reset_n)
		counter<=0;
	else if(logo)
		begin
		if(counter==Time-1)
			counter<=0;
		else
			counter<=counter+1'b1;
		end
	else
		counter<=0;
always @(posedge clk or negedge Reset_n)   // 状态计数器
	if(!Reset_n)
		counter2<=0;
	else if (logo)begin
		if(counter==Time-1)
			counter2<=counter2+1'b1;
		end
	else
		counter2<=0;
always @(posedge clk or negedge Reset_n)   //计数10毫秒
	if(!Reset_n)
		counter3<=0; //复位
	else if(counter2 == 500000-1)   //
		counter3<=0;
	else
		counter<=counter+1;
always @(posedge clk or negedge Reset_n)  //标志位
	if(!Reset_n)
		logo<=0;
	else if(counter3==0)
		logo<=1;
	else if((counter2==7)&&(counter==Time-1))
		logo<=0;
always @(posedge clk or negedge Reset_n)
	if(!Reset_n)
		led<=0;
	else case(counter2)
			0:led<=ctrl[0];
			1:led<=ctrl[1];
			2:led<=ctrl[2];
			3:led<=ctrl[3];
			4:led<=ctrl[4];
			5:led<=ctrl[5];
			6:led<=ctrl[6];
			7:led<=ctrl[7];
			default:led<=led;
		endcase
endmodule                                                                                                                                                                                                                              

测试文件代码如下:

`timescale 1ns/1ps
module led_flash6_tb();
reg clk;
reg Reset_n;
reg[7:0] ctrl;
reg [15:0] Time;
wire led;
led_flash6 led_flash_inst(
	.clk(clk),
	.Reset_n(Reset_n),
	.led(led),
	.ctrl(ctrl),
	.Time(Time)
);
initial clk=1;
always #10 clk=!clk;
initial begin
	Reset_n=0;
	ctrl=0;
	Time=0;
	#201;
	Reset_n=1;
	Time = 2500;
	ctrl = 8'b1110_1101;
	#20000000;
	$stop;
	end
endmodule

执行仿真,波形图如下所示。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值