前言
本节包含了2个任务,任务3和任务4.下面介绍这两个任务。
任务3
其实也就根据拨码开关的开通和关闭来决定LED灯的亮灭。
程序不难,但有实际的意义,这是一个在程序的运行过程中能够改变程序的状态的一个程序。
任务4
程序进入正常的工作状态后,LED熄灭1s,1s之后再让LED输出新一轮8位的状态。
一、任务过程
通过Vivado软件实现对LED控制的仿真。
二、任务3
1.模块构建
代码如下(示例):
`timescale 1ns / 1ps
module led_ctrl2(
Clk,
Reset_n,
SW,
Led
);
input Clk;
input Reset_n;
input [7:0]SW;
output reg Led;
parameter MCNT =12500_000 - 1;
reg [26:0]counter0;
//counter0计数计的是什么。是0.25s的时间间隔。
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
counter0 <= 0;
else if(counter0 == MCNT) //对应的是整个LED变化完整的结束所需要的时间。
counter0 <= 0;
else
counter0 <= counter0 + 1'd1;
end
reg [2:0]counter1; //7对应的是0111
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
counter1 <= 0;
else if(counter1 == 7)
counter1 <= 0;
else
counter1 <= counter1 + 1'd1;
end
//根据counter1不同的值指定LED的状态,通过SW不同的位对应不同的LED的值。
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 0;
else begin
case(counter1)
0: Led <= SW[0];
1: Led <= SW[1];
2: Led <= SW[2];
3: Led <= SW[3];
4: Led <= SW[4];
5: Led <= SW[5];
6: Led <= SW[6];
7: Led <= SW[7];
default:Led <= Led;
endcase
end
endmodule
2.激励部分
代码如下(示例):
`timescale 1ns / 1ps
module led_ctrl_tb();
reg Clk;
reg Reset_n;
wire Led; //起连接作用所以是wire类型的
reg [7:0]SW;
led_ctrl2 led_ctrl2(
.Clk(Clk),
.Reset_n(Reset_n),
.SW(SW),
.Led(Led)
);
// defparam led_ctrl1.TIME_UNIT_MS = 1;
defparam led_ctrl2.MCNT = 12_500-1;
initial Clk = 1; //初始化时钟Clk信号为逻辑高电平
always #10 Clk = !Clk; //每隔10ns翻转一次,模拟时钟的周期。
initial begin //模拟初始化的过程
Reset_n = 0; //Reset_n初始化为0
SW = 8'b1010_1010;
#201; //过了201ns,Reset_n被置1
Reset_n = 1;
#4000000;
SW = 8'b0000_0001;
#20_000_000; //8个LED灯每个间隔0.5s。所以是4s,对应的就是4000_000_000ns,减少了100倍缩短仿真的时间。
$stop;
end
endmodule
问题:
1.存在一个未设置顶层,看不到波形的问题。
2.调counter1的波形观察的时候没有看见,counter计数,发现模块代码中,else if(counter1 == 7),之前未修改,修改后解决问题。
得到仿真波形。
三、任务4
基于第4个任务,引出可控序列机。
1.模块构建
`timescale 1ns / 1ps
module led_ctrl3(
Clk,
Reset_n,
SW,
Led
);
input Clk;
input Reset_n;
input [7:0]SW;
output reg Led;
parameter MCNT0 =12500_000 - 1; //对应的是0.25s。
parameter MCNT2 =50_000_000 - 1; //1s对应的时间值。
reg [26:0]counter0;
reg [26:0]counter2;
//en_counter对应的是控制信号。用来使用不同的计数器。过程赋值,所以使用reg
reg en_counter0;
reg en_counter2;
reg [2:0]counter1; //7对应的是0111,计数输出第几个LED状态的计数器
//counter0
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter0 <= 0;
else if(en_counter0)begin
if(counter0 == MCNT0)
counter0 <= 0;
else
counter0 <= counter0 + 1'd1;
end
else
counter0 <= 0;
//en_counter2部分
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
en_counter2 <= 1'd1;
else if((counter1 == 7) && (counter0== MCNT0))
en_counter2 <= 1'd1;
else if(counter2 == MCNT2)
en_counter2 <= 1'd0;
//en_counter0部分
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
en_counter0 <= 1'd0;
else if(counter2 == MCNT2)
en_counter0 <= 1'd1;
else if((counter1 == 7)&& (counter0 == MCNT0))
en_counter0 <= 1'd0;
//counter1部分。
always@(posedge Clk or negedge Reset_n)begin
if(!Reset_n)
counter1 <= 0;
else if(counter1 == 7)
counter1 <= 0;
else
counter1 <= counter1 + 1'd1;
end
//counter2部分
always@( posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(en_counter2)begin
if(counter2 == MCNT2)
counter2 <= 0;
else
counter2 <= counter2+1'd1;
end
else
counter2 <= 0;
// LED部分:根据counter1不同的值指定LED的状态,通过SW不同的位对应不同的LED的值。
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 0;
else if(en_counter2 == 1)
Led <= 1'd0;
else begin
case(counter1)
0: Led <= SW[0];
1: Led <= SW[1];
2: Led <= SW[2];
3: Led <= SW[3];
4: Led <= SW[4];
5: Led <= SW[5];
6: Led <= SW[6];
7: Led <= SW[7];
default:Led <= Led;
endcase
end
endmodule
解法3:
分别计时。新的模式中,计数器有静有动。何为有静有动,另一个在计数的时候,另外一个就暂停计数。
不同需要的计数器
en_counter0的计数原理。
2.激励部分
`timescale 1ns / 1ps
module led_ctrl_tb();
reg Clk;
reg Reset_n;
wire Led; //起连接作用所以是wire类型的
reg [7:0]SW;
led_ctrl3 led_ctrl3(
.Clk(Clk),
.Reset_n(Reset_n),
.SW(SW),
.Led(Led)
);
// defparam led_ctrl1.TIME_UNIT_MS = 1;
defparam led_ctrl3.MCNT0 = 12_500-1;
defparam led_ctrl3.MCNT2 = 50_000-1;
initial Clk = 1; //初始化时钟Clk信号为逻辑高电平
always #10 Clk = !Clk; //每隔10ns翻转一次,模拟时钟的周期。
//怎么知道这些值的赋值也很重要,这些赋的值要能够我们方便观察结果。
initial begin //模拟初始化的过程
Reset_n = 0; //Reset_n初始化为0
SW = 8'b1010_1001;
#201; //过了201ns,Reset_n被置1
Reset_n = 1;
#40_000_000; //8个LED灯每个间隔0.5s。所以是4s,对应的就是4000_000_000ns,减少了100倍缩短仿真的时间。
$stop;
end
endmodule
//怎么知道这些tb文件中值的赋值也很重要,这些赋的值要能够我们方便的观察结果。
这样在状态切换的过程中就一定会出现,高电平变低电平的过程。
难点就是计数器
总结
任务3:
这是一个在程序的运行过程中能够改变程序的状态的一个程序。
任务4:
难点在于计数器的一个工作模式,计数器的工作模式不再是简单的持续计数,而是会有暂停态,有计数态。同时,计数器之间的关系也变得复杂,相互关联,相互制约。这些是做FPGA设计逻辑的时候,经常会遇到的问题。