从以下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;elsecounter<=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/4led<=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 beginReset_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;elsecounter<=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 beginReset_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;elsecounter<=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 beginReset_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;elsecounter<=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;elsecase(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 beginReset_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;elsecounter<=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;elsecase(counter2)0:beginled[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];end1:beginled[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];end2:beginled[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];end3:beginled[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];end4:beginled[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];end5:beginled[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];end6:beginled[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];end7:beginled[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];enddefault:beginled[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];endendcase
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 beginReset_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)beginif(counter==Time-1)counter<=0;elsecounter<=counter+1'b1;endelsecounter<=0;
always @(posedge clk or negedge Reset_n) // 状态计数器if(!Reset_n)counter2<=0;else if (logo)beginif(counter==Time-1)counter2<=counter2+1'b1;endelsecounter2<=0;
always @(posedge clk or negedge Reset_n) //计数10毫秒if(!Reset_n)counter3<=0; //复位else if(counter2 == 500000-1) //counter3<=0;elsecounter<=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 beginReset_n=0;ctrl=0;Time=0;#201;Reset_n=1;Time = 2500;ctrl = 8'b1110_1101;#20000000;$stop;end
endmodule
执行仿真,波形图如下所示。