目录
偶数分频器电路设计
1、偶数分频器电路简介
2、实验任务
3、程序设计
方法1:
3.1、8分频电路代码如下:
3.2、仿真验证
3.2.1、编写 TB 文件
3.2.2、仿真验证
方法2:
4、计数器进行分频
4.1、仿真测试
偶数分频器电路设计
分频器在逻辑设计中一直都担任着很重要的角色,分频器一般包括计数分频和偶数分频。实现偶数分频可通过一个简单计数器实现,而如果需要三分频,五分频,七分频等奇数分频,一个计数器是不够的。我们先来看下偶数分频。
1、偶数分频器电路简介
实现分频一般有两个方法,一个方法是直接使用 PLL 进行分频,比如 FPGA 或者 ASIC 设计中,都可以直接使用 PLL 进行分频,但是这种分频倍数有时候受限于 PLL 本身的特性,比如输入 100Mhz 时钟,很多 PLL 都实现不了分频到 1Mhz 的时钟,这个就是 PLL 本身特性限制的。还有一种实现方法就是直接使用逻辑实现,即使用代码实现分频设计。我们本节介绍的是使用代码进行设计分频器。本节我们先看下偶数分频设计。
偶数分频,顾名思义,是说分频后的频率和分频前的频率比例是偶数,比如 100Mhz 时钟,进行二分频后,就是 50Mhz。
实现偶数分频可通过一个简单计数器实现,而如果需要三分频,五分频,七分频等奇数分频,一个计数器是不够的。
偶数分频实现比较简单,假设为 N(偶数)分频,只需计数到 N/2-1,然后时钟翻转、计数器清零,如此循环就可以得到 N(偶)分频。举个例子,比如晶振时钟是 100Mhz 时钟,想得到一个 25Mhz 的时钟, 那么这个是一个 100/25=4 的四分频设计,那么按照我们刚说的计数到 4/2-1=1,然后时钟翻转、计数器清零,就可以得到一个 24Mhz 的时钟。
2、实验任务
使用 Verilog 语言设计一个任意偶数分频电路,默认进行 8 分频。
3、程序设计
偶数分频器:2分频设计,只需要使用基准时钟在第1个时钟周期输出高电平(或低电平),在第2个时钟周期输出相反电平,如此反复即可。
同理,4分频设计:使用基准时钟在第1、2个时钟周期输出高电平(或低电平),在第3、4个时钟周期输出相反电平,如此反复即可。
同理,8分频设计:使用基准时钟在第1、2、3、4个时钟周期输出高电平(或低电平),在第5、6、7、8个时钟周期输出相反电平,如此反复即可。
由此可以推导出偶数分频设计的一般方法:假设为N分频,只需设计一个计数器从0计数到 N/2-1(一共N/2个基准时钟),然后将输出分频时钟翻转、计数器清零,如此循环就可以得到 N分频。
根据简介介绍的分频电路设计思路,假设为 N(偶数)分频,只需计数到 N/2-1,然后时钟翻转、计数清零,如此循环就可以得到 N(偶)分频,可以通过改变参量 N 的值和计数变量 cnt 的位宽实现任意偶分频,由于默认为 8 分频,因此 N 初始值为 8。由此可以写出如下代码。
方法1:
3.1、8分频电路代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/05/13 14:45:37
// Design Name:
// Module Name: divider_8
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//
//8分频电路设计
module divider_8(
// input sys_clk, //50MHz系统时钟(一个周期是20ns:1/50MHz=0.02us=20ns)
//differential system clocks //200MHz系统时钟(一个周期是5ns:1/200MHz=0.005us=5ns)
input sys_clk_p, //system clock positive
input sys_clk_n, //system clock negative
input sys_rst_n, //复位信号,低电平有效
output reg clk_8 //输出8分频信号(50MHz/8=6.25MHz,周期160ns)(200MHz/8=25MHz,周期40ns)
);
//reg define //define the time counter
reg [1:0] cnt;
wire sys_clk;
//差分输入时钟缓冲器(黑金FPGA)
IBUFDS sys_clk_ibufgds //generate single end clock
(
.O (sys_clk ),
.I (sys_clk_p ),
.IB (sys_clk_n )
);
//计数模块
//从0计数到3,共计4个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 2'd0; //复位清零
else if(cnt==2'd3) //从0开始计数,所以要-1
cnt <= 2'd0; //计数满,则清零。
else
cnt <= cnt + 2'd1; //没计满,就一直计数
end
//8分频时钟输出模块
//满足计数条件则对8分频时钟进行反转
//8分频时钟每隔4个周期反转一次,所以8分频的周期,即为8个时钟周期
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
clk_8 <= 1'b0; //复位清零
else if(cnt==2'd3) //计满4个时钟周期
clk_8 <= ~clk_8; //计满,则输出反转
else
clk_8 <= clk_8; //没计满,就保持原状态
end
endmodule
接下来我们使用 Vivado 的 RTL ANALYSIS,来看一下我们编写代码的 RTL 视图。
从上图可以看出,cnt 是一个 2bit 的计数器,在计数器计到 3 的时候,out_clk 进行取反操作,即得到一个八分频时钟。
3.2、仿真验证
3.2.1、编写 TB 文件
只需要对时钟以及复位信号进行激励,代码编写如下:
`timescale 1ns / 1ps //时间刻度:单位1ns,精度1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/05/13 15:04:35
// Design Name:
// Module Name: tb_divider_8
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_divider_8(); //仿真测试模块
//parameter T = 20; //50MHz时钟周期为20ns
parameter T = 5; //200MHz时钟周期为5ns
//input define
//reg sys_clk; //时钟信号
reg sys_clk_p; //时钟信号
wire sys_clk_n;
reg sys_rst_n;
//output define
wire clk_8;
//设置初始化条件
initial begin
// sys_clk = 1'b0; //初始时钟为0
sys_clk_p = 1'b0; //初始时钟为0
sys_rst_n <= 1'b0; //初始复位为0
#10 //10个时间单位后
sys_rst_n <= 1'b1; //拉高复位
end
//always代表重复进行,#10代表每10个时间单位
//每10个时间单位反转时钟,即时钟周期为20个时间单位(20ns)
//always #10 sys_clk = ~sys_clk; //每半个周期后,电平取反一次。
always #(T/2) sys_clk_p <= ~sys_clk_p; //每半个周期后,电平取反一次。
assign sys_clk_n=~sys_clk_p;
//例化被测试模块
divider_8 u_divider_8(
// .sys_clk (sys_clk),
.sys_clk_p (sys_clk_p),
.sys_clk_n (sys_clk_n),
.sys_rst_n (sys_rst_n),
.clk_8 (clk_8)
);
endmodule
3.2.2、仿真验证
测试程序在 Xilinx 的 Vivado 软件 或者其他仿真工具运行后的波形如下显示,可以看出,N 初始为 8,当复位撤销(复位信号低有效)之后,cnt 即开始计数,在计数器计到 3 的时候,out_clk 进行取反操作,即得到一个八分频时钟,大家可以改变 N 参数看下,看下 N 参数不一样,最终分频的时钟是多少。
方法2:
4、计数器进行分频
此处再给大家介绍一个分频设计方法,就是直接使用计数器即可进行分频。 直接使用计数器分配的代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/06/20 11:12:13
// Design Name:
// Module Name: divider_8CNT
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//8分频电路
//直接使用计数器即可进行分频。
module divider_8CNT(
input clk , // system clock 50Mhz on board
input rst_n, // system rst, low active
output reg [2:0] y // output signal
);
//===========================================================================
// ------------------------- MAIN CODE --------------------------------------
//===========================================================================
always @ (posedge clk or negedge rst_n) begin
if (rst_n == 1'b0)
y <= 3'b0 ;
else
y <= y + 1'b1 ;
end
endmodule
4.1、仿真测试
testbech 测试电路,这个 testbech 激励只需要提供时钟和复位就好了,通过仿真来看下计数器的波形。
测试代码如下
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/06/20 11:22:19
// Design Name:
// Module Name: tb_divider_8CNT
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
//testbech 测试电路,这个 testbech 激励只需要提供时钟和复位就好了,
//通过仿真来看下计数器的波形。
//测试代码如下
module tb_divider_8CNT();
reg sys_clk;
reg sys_rst_n;
wire [2:0] y;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200 //等待200ns
sys_rst_n = 1'b1;
end
always #10 sys_clk = ~sys_clk;
divider_8CNT u_divider_8CNT(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.y (y )
);
endmodule
可以看出,当复位撤销(复位信号低有效)之后,计数器 y 就从 0 开始一直累加,每次加 1,直到加到 7 之后,进位溢出为 0,然后再次从 0 开始一直累加,如此反复循环。
从波形中,我们能看到一个分频的效果,计数器最低 bit 的 y[0]是每 2 个周期在循环跳变,而计数器次低 bit 的 y[1] 是每 4 个周期在循环跳变,计数器最高 bit 的 y[2] 是每 8 个周期在循环跳变,这个也是常有的分频方法,比如直接把计数器的各个 bit 赋值给一个时钟信号,那么计数器的 y[0] 实现的是 2 分频,计数器的 y[1] 实现的是 4 分频,计数器的 y[2] 实现的是 8 分频。这个也是最简单和最常用的偶数分频方法。