文章目录
- 一、分频器
- 二、Verilog实现任意倍分频器
- 2.1、Verilog源码
- 2.2、仿真文件
- 三、仿真波形图
一、分频器
在FPGA(可编程逻辑门阵列)中,分频器是一种用于将时钟信号的频率降低的电路或模块。它可以根据输入的时钟信号生成一个较低频率的输出时钟信号。
常见的分频器可以按照固定比例来进行分频,例如将输入时钟频率除以2、除以4等。因此,如果输入时钟信号的频率为100 MHz,并且使用一个除以2的分频器,那么输出时钟信号的频率将为50 MHz。这样就可以将高频的时钟信号降低到所需的较低频率,以满足电路设计中对时序和性能的要求。
FPGA中的分频器一般由触发器和计数器组成。触发器用于产生时钟信号的边沿触发,计数器用于对触发器的触发计数,并在达到一定计数值时生成输出时钟信号。通过调整计数器的初值和计数步长,可以实现不同的分频比例。
分频器在FPGA中非常常用,可以在时序控制、数据采样、状态机设计等方面起到重要作用。它可以帮助实现时钟域划分、减少功耗、减小时序延迟等功能。
以上是GPT3对分频器的概述。
二、Verilog实现任意倍分频器
2.1、Verilog源码
-
由于我们无法在一个always块中同时检测时钟的上升沿和下降沿,因此我们需要两个always块分别进行检测。同时博主引进了两个中间时钟信号分别是根据始终上升沿反转的clk_p,以及根据时钟下降沿进行反转的clk_n。
-
以本题为例,我们设计一个七倍分频器,因此我们需要在数到七个半个系统时钟周期时对输出的clk_out进行信号反转,因此对于计数器,我们只需要计数到7的一半,也就是3。
-
由于上升沿与下降沿相隔了半个时钟周期,因此我们可以通过对两个中间时钟信号想与从而获得相当于系统时钟周期的七个半个周期时钟信号。
-
而对于偶数倍分频器,相信已经大家十分熟悉,只需要计数时钟上升沿进行翻转即可,因此不再赘述。
-
具体请看如下代码:
module N_divider#(parameter N = 7)( //分频系数,填入几代表数到几输出的时钟信号就要翻转一次
input wire clk , //系统时钟
input wire rst_n , //系统复位
output wire clk_out //分频时钟
);
reg [N:0] cnt_p ; //时钟上升沿计数寄存器,计满信号反转
reg [N:0] cnt_n ; //时钟下降沿计数寄存器,计满信号反转
reg clk_p ; //对时钟上升沿敏感的时钟信号
reg clk_n ; //对时钟下降沿敏感的时钟信号
//时钟上升沿敏感计数器
wire add_cnt_p ;
wire end_cnt_p ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_p <= 1'd0;
end
else if(add_cnt_p)begin
if(end_cnt_p)begin
cnt_p <= 1'd0;
end
else begin
cnt_p <= cnt_p + 1'b1;
end
end
end
assign add_cnt_p = 1'b1;
assign end_cnt_p = add_cnt_p && cnt_p == N - 1'b1;
//对时钟上升沿敏感的信号反转
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_p <= 1'b0;
end
else if(cnt_n <= (N >> 1))begin
clk_p <= 1'b1;
end
else begin
clk_p <= 1'b0;
end
end
//时钟下降沿敏感计数器
wire add_cnt_n ;
wire end_cnt_n ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_n <= 'd0;
end
else if(add_cnt_n)begin
if(end_cnt_n)begin
cnt_n <= 'd0;
end
else begin
cnt_n <= cnt_n + 1'b1;
end
end
end
assign add_cnt_n = 1'b1;
assign end_cnt_n = add_cnt_n && cnt_n == N - 1'b1;
//对时钟下降沿敏感的输出信号
always@(negedge clk or negedge rst_n)begin
if(!rst_n)begin
clk_n <= 1'b0;
end
else if(cnt_p <= (N >> 1))begin
clk_n <= 1'b1;
end
else begin
clk_n <= 1'b0;
end
end
assign clk_out = (N==1) ? clk_p : N[0] ? (clk_p&clk_n) : clk_p;//在二进制中,奇数的第一位是1,偶数的第一位是0;
//N=1的情况,clk不变;N大于1,判断奇数偶数
//偶数的情况直接输出上升沿时的输出时钟
//奇数,则输出clk_p&clk_n;
//N[0]等效于N[0] == 1
endmodule
2.2、仿真文件
`timescale 1ns/1ns
module N_divider_tb#(parameter N = 7)();
reg clk ;
reg rst_n ;
wire clk_out ;
always #10 clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b1;
#20;
rst_n = 1'b0;
#20;
rst_n = 1'b1;
#1000;
$stop;
end
N_divider #(.N(N)) N_divider_u(
.clk (clk ),
.rst_n(rst_n),
.clk_out(clk_out)
);
endmodule