时序逻辑电路 计数器的实现
1 D触发器
分析:
特性:输出端Q只在CK处于上升沿的时候变化
图中波形的形成过程:
当D处于高电平时,CK未处于上升沿时,Q仍处于低电平
当CK来到上升沿,Q需要根据D发生变化,由于D是高电平,所以Q要从低电平变化成高电平
D从高电平变化成低电平,但是此时CK未来到上升沿,所以Q不随着D变化(虽然D变了,Q仍保持刚刚的状态——存储功能)
CK来到上升沿,此时D又恢复了高电平,所以Q不变
2 计数器
原理:加法器基本结构图
这样就能实现 每一个时序上升沿,计数器结果都加1
今天我们的任务是:
设计一个以1秒频率闪烁的LED灯(亮灭各500ms)
怎么知道500ms亮,500ms灭呢?
-
我们就需要一个计数器,计数到什么时候到了500ms
-
时钟的频率我们设置为
50MHz 20ns
,也就是两个上升沿(一个周期)的时间是20ns
-
每到一个上升沿,计数器的值加一次
-
由于500ms亮和灭,所以我们需要计数器计数 500ms/20ns = 25000000次
-
而一个四位计数器肯定计数不了这么多的值,那么需要多少位呢?
- 源代码
module led_flash(
clk,
reset_n,//reset是复位信号, _n 表示低电平有效,也就是低电平时复位
led
);
input clk;
input reset_n;
output reg led;
reg [24:0] counter;//25位计数器
always@(posedge clk or negedge reset_n) //当clk处于上升沿或者reset处于下降沿的时候下面的代码就生效
if(!reset_n) // if(reset_n == 0)
counter <= 0; // <= 是非阻塞赋值的意思
else if (counter == 2500_0000) //-1)
counter <= 0;
else
counter <= counter + 1'd1;
//需要让 led 灯每500ms翻转一次
always@(posedge clk or negedge reset_n)
if(!reset_n)
led <= 0;
else if (counter == 2500_0000) //-1) 要减1
led <= !led; //翻转
endmodule
仿真代码
`timescale 1ns / 1ps
module led_flash_tb(
);
reg clk;
reg reset_n;
wire led;
led_flash UUT(
.clk(clk),
.reset_n(reset_n),
.led(led)
);
initial clk = 1;
always #10 clk =! clk;
initial begin
reset_n = 0;
#201; //避开上升沿
reset_n = 1;
#20_0000_0000; // 2s
end
endmodule
- 仿真
从仿真的结果看
![image-20221112114920018](https://blogliving.oss-cn-nanjing.aliyuncs.com/img/202211121149115.png)
比预想的500ms 多了 20ns
原因是这里如果想计数2500_0000次,应该写2499_9999
这里以计数到4为例,如果要计数到4的话,实际上不是计数了4次,而是5次
- 0-1
- 1-2
- 2-3
- 3-4
- 4-0
所以应该改为 2500_0000-1
改过之后 就可以了
![image-20221112120351268](https://i-blog.csdnimg.cn/blog_migrate/6dce38b7e2309386fc0b96a7dde48c94.png)
改过之后 就可以了
![image-20221112120351268](https://i-blog.csdnimg.cn/blog_migrate/6dce38b7e2309386fc0b96a7dde48c94.png)