流水灯笔记

亮灯

点亮一个led灯

  • 设计流程

    设计规划—波形绘制—代码编写—代码编译—逻辑仿真—波形对比—绑定管脚—分析综合,布局布线—上板验证

  • 代码实现

1
2
3
4
5
6
7
module led(
input wire key_in,
output wire led_out
);
assign led_out = key_in;

endmodule
  • 仿真文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module tb_led();
wire led_out;
reg key_in;

//初始化输入信号
initial key_in <= 1'b0; //非阻塞赋值
// key_in:产生输入随机数,模拟按键的输入情况
always #10 key_in <= {$random} % 2;
//取模求余数,产生非负随机数0、1,每隔10ns产生一次随机数

led led_inst( //实例化
.key_in(key_in)
.led_out(led_out)
);
endmodule

基础流水灯

  • 单模块直接计数,采用单一verilog模块,通过直接计数 50MHz 时钟周期的方式实现每秒一次的流水灯效果,核心思想是:

    每当计数器累积到50,000,000(即1s)时,立即对led寄存器执行一次循环左移,并将计数器清零

关键机制

  • 计数范围:0 - 49,999,999
  • 移位时机

各模块作用

顶层模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
`timescale 1ns / 1ps
//描述工程大致框架
module flash_led_top(
input clk, //时钟
input rst_n, //复位(低电平)
input sw0, //开关控制led灯的方向
output [15:0]led //输出十六个led灯
);
wire clk_bps; //分频后的基准时钟
wire rst; //转换后的高电平有效复位信号
assign rst = ~rst_n;
//assign:将低电平有效信号转换为高电平有效
//转换原因:避免复位逻辑混乱

//实例化counter
counter counter( //分频计数器
.clk( clk ), //输入:系统时钟(连接顶层模块clk)
.rst( rst ), //输入:高电平有效复位(连接转换过的rst)
.clk_bps( clk_bps ) //输出分频后的基准时钟
);

//实例化led闪烁控制模块
flash_led_ctl flash_led_ctl(
.clk( clk ),
.rst( rst ),
.dir( sw0 ),
.clk_bps( clk_bps ),
.led( led )
);
endmodule

时钟分频模块 counter

  • clk_bps:仅当两级计数器都计满时输出 1 个时钟周期的高电平,其余时间为低电平。

  • 该模块接收两个输入:时钟信号clk与复位信号(reset,简写为rst)rst

  • 并且输出一个脉冲时 钟信号clk_bps。该模块定义了两个计数器,cnt_first与cnt_second,负责计数工作。

  • 每过一个时 钟周期都会使某一个或两个计数器自增,直到达到特定条件两个计数器均会清零。此时将会拉高脉 冲时钟信号clk_bps一个周期。

  • 我们可以认为,clk_bps的频率是计数器两次达到特定条件的时间间隔之倒数,其占空比将随频率变化,但高电平的持续时间永远为一个时钟周期的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
`timescale 1ns / 1ps

module counter(
input wire clk, //输入系统高频时钟
input wire rst, //高电平复位
output wire clk_bps //输出分频后的基准时钟
);
reg [13: 0] cnt_first, cnt_second;

always @(posedge clk)
if(rst)
cnt_first <= 14'd0;
else if(cnt_first == 14'd10000)
cnt_first <= 14'd0;
else
cnt_first <= cnt_first + 1'b1;

always @(posedge clk)
if(rst)
cnt_second <= 14'd0;
else if(cnt_second == 14'd10000)
cnt_second <= 14'd0;
else if(cnt_first == 14'd10000)
cnt_second <= cnt_second + 1'b1;

assign clk_bps = cnt_second == 14'd10000 ? 1'b1 : 1'b0;
endmodule

控制模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
`timescale 1ns / 1ps

module flash_led_ctl(
input clk,
input rst,
input dir,
input clk_bps,
output reg[15:0]led
);
always @( posedge clk or posedge rst )
if( rst )
led <= 16'h8000;
//初始化1000 0000 0000 0000
else
case( dir )
1'b0: //led左移
if( clk_bps )
if( led != 16'd1 )
led <= led >> 1'b1;
else
led <= 16'h8000;
1'b1:
if( clk_bps )
if( led != 16'h8000 )
led <= led << 1'b1;
else
led <= 16'd1;
endcase
endmodule