从以下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
执行仿真,波形图如下所示。