小边要日更!flag已倒。我不是把所有知识点一股脑塞给你,而是从实践中逐渐熟悉!
LED实验进化六部曲:
- LED亮0.25s,灭0.75s的状态循环亮灭
- LED亮0.25s,灭0.5s,亮0.75秒,灭1s的方式循环亮灭
- 让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。以0.25s为一个变化周期,8个变化周期为一个循环
- 让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。8个变化周期为一个循环,变化是兼职由不同应用场景决定
- 让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化
- 每隔10ms,让LED灯的一个8状态循环执行一次(每隔状态变化时间值下一点,方便测试,比如设置为10us)
热身 —— 亮灭各0.5s 直接把我们之前的flash贴过来改两下子
couter_led_0.v
module counter_led_0 (
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) //49999999
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <=0;
else if(counter == MCNT/2 -1) //24999999
Led <= 1;
else if(counter == MCNT-1)
Led <= 0;
endmodule
couter_led_0_tb.v
我们依旧把时间缩小1000倍,也就是ms变us
`timescale 1ns/1ns
module counter_led_0_tb();
//激励信号
reg Clk;
reg Reset_n;
wire Led;
counter_led_0
#(
.MCNT(50000)
)
counter_led_0_inst(
//连线
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
initial Clk = 1;
always #10 Clk = !Clk; //每延时10s,翻转
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#2000000000;
$stop;
end
endmodule
1. episode 1
LED亮0.25s,灭0.75s的状态循环亮灭
counter_led_1.v
module counter_led_1 (
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) //49999999
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <=0;
else if(counter == MCNT/2 + MCNT/4 -1) //24999999
Led <= 1;
else if(counter == MCNT-1)
Led <= 0;
endmodule
2. episode 2
LED亮0.25s,灭0.5s,亮0.75秒,灭1s的方式循环亮灭
2.5s一个周期,那么就需要计数2500000000ns/20ns = 125000000次
划分为 —— 1/10T, 1/5T, 3/5T, 2/5T
module counter_led_2 (
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) //过0.25s灭掉
Led <= 0;
else if(counter == MCNT/10 + MCNT/5-1) //过0.5s后再亮
Led <= 1;
else if(counter == MCNT/10 + MCNT/5 + 3*MCNT/10-1) //过0.75s灭掉
Led <= 0;
else if(counter == MCNT-1) //过1s后再亮
Led <= 1;
endmodule
别忘了把仿真文件改成1250000啊,2.5s → 0.025s = 25ms
哎!好像挺简单?
3. episode 3
让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。以0.25s为一个变化周期,8个变化周期为一个循环
怎么指定亮灭模式??那么我们需要有一个输入端口Ctrl
来指定亮灭~~
一个周期2s,则计数器需计数2000,000,000ns/20ns = 100,000,000次
module counter_led_3 (
Clk,
Reset_n,
Ctrl,
Led
);
input Clk;
input Reset_n;
input[7:0] Ctrl;
output reg Led;
reg[29: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 == 2*MCNT/8 - 1)
Led <= Ctrl[1];
else if(counter == 3*MCNT/8 - 1)
Led <= Ctrl[2];
else if(counter == 4*MCNT/8 - 1)
Led <= Ctrl[3];
else if(counter == 5*MCNT/8 - 1)
Led <= Ctrl[4];
else if(counter == 6*MCNT/8 - 1)
Led <= Ctrl[5];
else if(counter == 7*MCNT/8 - 1)
Led <= Ctrl[6];
else if(counter == MCNT - 1)
Led <= Ctrl[7];
endmodule
这个仿真文件要重新写啦~ 另外别忘了右键set as top哦
`timescale 1ns/1ns
module counter_led_3_tb();
//激励信号
reg Clk;
reg Reset_n;
reg[7:0] Ctrl;
wire Led;
counter_led_3
#(
.MCNT(100000)
)
counter_led_2_inst(
//连线
.Clk(Clk),
.Reset_n(Reset_n),
.Ctrl(Ctrl) ,
.Led(Led)
);
initial Clk = 1;
always #10 Clk = !Clk; //每延时10s,翻转
initial begin
Reset_n = 0;
Ctrl = 0;
#201;
Reset_n = 1;
Ctrl = 8'b1010_0101;
#2000000000;
$stop;
end
endmodule
计数器中的每个值都可以作为刻度标尺。
是的,咱们现在这个if - else 太多了,我们也可以改成case-endcase
else case(counter)
1*MCNT/8 - 1: Led <= Ctrl[0];
2*MCNT/8 - 1: Led <= Ctrl[1];
3*MCNT/8 - 1: Led <= Ctrl[2];
4*MCNT/8 - 1: Led <= Ctrl[3];
5*MCNT/8 - 1: Led <= Ctrl[4];
6*MCNT/8 - 1: Led <= Ctrl[5];
7*MCNT/8 - 1: Led <= Ctrl[6];
MCNT - 1: Led <= Ctrl[7];
default: Led <= Led;
endcase
4. episode 4
让LED灯按照指定的亮灭模式亮灭,亮灭模式由用户随机指定。8个变化周期为一个循环,变化周期(每个变化周期相同)是由不同应用场景决定—— 依然通过端口
episode1-3都是采用“化整为零”的思路,而现在我们“凑零为整”,思路类似于3-8译码器:即counter记录时间流逝,counter2记录8个状态,合并起来即为一个循环。
counter_led_4.v
module counter_led_4 (
Clk,
Reset_n,
Ctrl,
Time, //设置周期
Led
);
input Clk;
input Reset_n;
input[7:0] Ctrl;
input[31:0] Time;
output reg Led;
reg[31:0] counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == Time-1)
counter <= 0;
else
counter <= counter + 1'b1;
reg[2:0] counter2;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(counter == Time-1) //计满一个小区间+1,且counter2计满自动清0
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
counter_led_4_tb.v
`timescale 1ns/1ns
module counter_led_4_tb();
//激励信号
reg Clk;
reg Reset_n;
reg[7:0] Ctrl;
reg[31:0] Time;
wire Led;
counter_led_4 counter_led_4_inst(
//连线
.Clk(Clk),
.Reset_n(Reset_n),
.Ctrl(Ctrl) ,
.Time(Time),
.Led(Led)
);
initial Clk = 1;
always #10 Clk = !Clk; //每延时10s,翻转
initial begin
Reset_n = 0;
Ctrl = 0;
Time = 0;
#201;
Reset_n = 1;
Time = 2500;
Ctrl = 8'b1010_0101;
#2000000000;
$stop;
end
endmodule
500us → 50us
5. episode 5
让多个LED灯按照设置的模式各自在一个变化循环内独立亮灭变化
我们改成2个灯泡~ 设置2个Ctrl来分别控制灯的亮灭 —— 同时控制多个信号
counter_led_5.v
module counter_led_5 (
Clk,
Reset_n,
CtrlA,
CtrlB,
Time, //设置周期
Led
);
input Clk;
input Reset_n;
input[7:0] CtrlA, CtrlB;
input[31:0] Time;
output reg [1:0]Led;
reg[31:0] counter;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == Time-1)
counter <= 0;
else
counter <= counter + 1'b1;
reg[2:0] counter2;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(counter == Time-1) //计满一个小区间+1,且counter2计满自动清0
counter2 <= counter2 + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 0;
else case(counter2)
0: begin Led[0] <= CtrlA[0]; Led[1] <= CtrlB[0]; end
1: begin Led[0] <= CtrlA[1]; Led[1] <= CtrlB[1]; end
2: begin Led[0] <= CtrlA[2]; Led[1] <= CtrlB[2]; end
3: begin Led[0] <= CtrlA[3]; Led[1] <= CtrlB[3]; end
4: begin Led[0] <= CtrlA[4]; Led[1] <= CtrlB[4]; end
5: begin Led[0] <= CtrlA[5]; Led[1] <= CtrlB[5]; end
6: begin Led[0] <= CtrlA[5]; Led[1] <= CtrlB[6]; end
7: begin Led[0] <= CtrlA[7]; Led[1] <= CtrlB[7]; end
default: Led <= Led;
endcase
endmodule
咳咳,我没写仿真文件。。
6. episode 6 受控线性序列机
每隔10ms,让LED灯的一个8状态循环执行一次,其余时间不运行 (小区间时间段小一点,方便测试,比如设置为10us)
怎么在10ms中只计数一次呢?本质是要让小区间上的counter(和counter2)不计数,于是我们设置一个En
使能端,它的电平状态如下图所示,所谓受控就是这个意思 ——
- counter记录时间流逝
- counter2记录8个变化区间
- counter0记录10ms
其中和En
的变化时刻相关的 ——
-
10ms拉高,则需计数10,000,000ns/20ns = 50,000次,由计数器counter0控制,这个很简单~
-
8个小周期结束,拉低,由计数器counter2控制
要注意两处难点 ——
En
高电平1,counter和counter2才要计数;其余时刻无情的给0En
我们要在8个周期结束时拉低,事实上,(counter2 == 7) && (counter == Time -1)
就是恰切的时间点
counter_led_6.v
module counter_led_6 (
Clk,
Reset_n,
Ctrl,
Time, //设置周期
Led
);
input Clk;
input Reset_n;
input[7:0] Ctrl;
input[31:0] Time;
reg En;
output reg Led;
reg[31:0] counter; //记录时间的流逝
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(En) begin
if(counter == Time-1)
counter <= 0;
else
counter <= counter + 1'b1;
end
else
counter <= 0;
reg[2:0] counter2; //小状态区间计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter2 <= 0;
else if(En) begin
if(counter == Time-1)
counter2 <= counter2 + 1'b1;
end
else
counter <= 2;
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
reg[19:0] counter0; //10ms计数器
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter0 <= 0;
else if(counter0 == 499999)
counter0 <= 0;
else
counter0 <= counter0 + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
En <= 0;
else if(counter0 == 0)
En <= 1;
else if((counter2 == 7) && (counter == Time -1))
En <= 0;
endmodule
复用4的test bench,counter_led_6_tb.v
10us,则需计数10,000ns/20ns = 500次
`timescale 1ns/1ns
module counter_led_4_tb();
//激励信号
reg Clk;
reg Reset_n;
reg[7:0] Ctrl;
reg[31:0] Time;
wire Led;
counter_led_4 counter_led_4_inst(
//连线
.Clk(Clk),
.Reset_n(Reset_n),
.Ctrl(Ctrl) ,
.Time(Time),
.Led(Led)
);
initial Clk = 1;
always #10 Clk = !Clk; //每延时10s,翻转
initial begin
Reset_n = 0;
Ctrl = 0;
Time = 0;
#201;
Reset_n = 1;
Time = 499;
Ctrl = 8'b1010_0101;
#2000000000;
$stop;
end
endmodule