声明:案例和代码来自小梅哥课程,本人仅对知识点做做笔记,如有学习需要请支持官方正版。
文章目录
- 该系列目录
- 设计目标
- 设计思路
- RTL及Testbench代码
- RTL代码
- Testbench代码
- 仿真结果
- 上板视频
该系列目录
Verilog线性序列机点灯案例(一)(小梅哥课程)
Verilog线性序列机点灯案例(二)(小梅哥课程)
设计目标
使用8个拨码开关控制一个LED灯,每个拨码开关负责控制0.25秒的时间,开关为1时亮,开关为0时灭。
举例:
10101010就是亮0.25秒,灭0.25秒,如此循环
10111101就是亮0.25秒,灭0.25秒,亮1秒,灭0.25秒,亮0.25秒,如此循环
设计思路
counter0计数器每计数满0.25秒就向counter1计数器加1,
counter1计数器从0到7计数,
led灯根据当前counter1计数器的值,选中对应的拨码开关进行输出,用软件的写法就是
led = sw[counter1]
RTL及Testbench代码
RTL代码
module led_ctrl2(
clk,
rst_n,
led_out,
sw
);
input clk;
input rst_n;
input [7:0] sw;
output reg led_out;
parameter MCNT = 12500_000 - 1;
reg [26:0] counter0;
//第一个always块负责counter0
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
counter0 <= 0 ;
else if(counter0 == MCNT)
counter0 <= 0;
else
counter0 <= counter0 + 1'd1;
end
reg [2:0] counter1;
//第二个always块负责counter1
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
counter1 <= 0 ;
else if(counter0 == MCNT) begin
counter1 <= counter1 + 1'd1;
end
else
counter1 <= counter1;
end
//第三个always块负责根据counter1决定led_out
//每0.25秒切换一个拨码输出
always@(posedge clk or negedge rst_n)
if(!rst_n)
led_out <= 0;
else begin
case(counter1)
0:led_out <= sw[0];
1:led_out <= sw[1];
2:led_out <= sw[2];
3:led_out <= sw[3];
4:led_out <= sw[4];
5:led_out <= sw[5];
6:led_out <= sw[6];
7:led_out <= sw[7];
default:led_out <= led_out;
endcase
end
endmodule
Testbench代码
`timescale 1ns / 1ns
module tb_led_ctrl2();
reg clk;
reg rst_n;
wire led_out;
reg [7:0] sw;
initial clk = 1;
always #10 clk = ~clk;
led_ctrl2 led_ctrl2_inst0(
.clk(clk),
.rst_n(rst_n),
.sw(sw),
.led_out(led_out)
);
defparam led_ctrl2.MCNT = 12500 - 1;
initial begin
rst_n = 0;
sw = 8'b1010_1010;
#201;
rst_n = 1;
#4_000_000;
sw = 8'b0000_0001;
#4_000_000;
sw = 8'b1111_0001;
#20_000_000;
$stop;
end
endmodule
仿真结果
总结这两张图可以看出来,counter1是在每次counter0计满12499时进1,即counter1每个值维持的时间是counter0等于0~12499,是0.25s,而led_out依赖于counter1,比counter1延迟一拍,所以led_out每个值维持时间也是0.25秒,时序正确。
上板视频
Verilog线性序列机点灯案例(三)(小梅哥课程)