1、什么是分频器
在数字系统的设计中经常会碰到需要使用多个时钟的情况。时钟信号的产生通常具有两种方法,一种是使用PLL(Phase Locked Loop,锁相环),可生成倍频、分频信号;另一种则是使用硬件描述语言构建一个分频电路。
分频器的设计通常分为以下三类:奇数分频器、偶数分频器及小数分频器。接下来分别对偶数分频器及奇数分频器的设计仿真进行说明。
2、偶数分频器
2.1、设计方法
如图所示,分别画出了基准时钟、2分频时钟、4分频时钟、8分频时钟。
通过对波形的观察,不难得到:
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分频。
2.2、8分频电路设计实例
接下来将设计一个8分频电路并对其进行仿真验证。
8分频电路模块框图如下:
输入:
sys_clk:基准时钟信号(这里设定为50MHz,周期20ns)
sys_rst_n:低电平有效的复位信号
输出:
clk_8:基准时钟信号的8分频信号(50/8=6.25MHz,周期160ns)
根据前文提到的方法,设计过程如下:
- 设计一个计数器进行计数(一直从0计数到3(8/2-1),清零再重复)
- 每次计数器计数到最大值,将8分频信号进行反转
理论上8分频模块的各个信号波形应该如下图所示:
2.2、Verilog
根据上图可以编写Verilog代码如下:
//8分频电路设计
module divider_8 //模块名
(
input sys_clk, //时钟(设定为 50MHz)
input sys_rst_n, //复位信号(n 表示低电平有效)
output reg clk_8 //输出8分频信号
);
reg [1:0] cnt; //reg 定义
//计数模块
//从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
2.3、Testbench
Testbench文件的编写比较简单,不需要定义或者干其他什么事情,只需要实例化一下我们刚刚设计好的8分频模块就好了。
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module tb_divider_8(); //仿真模块
//输入reg 定义
reg sys_clk;
reg sys_rst_n;
//输出wire定义
wire clk_8;
//设置初始化条件
initial begin
sys_clk = 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;
//例化被测试模块
divider_8 divider_8_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_8 (clk_8 )
);
endmodule
2.4、仿真结果
使用ModelSim执行仿真,仿真出来的波形如所示:
从波形图可以看到
- 10ns后停止复位
- 计数器cnt一直在从0计数到3
- 每当cnt计数到3,8分频时钟输出就会翻转一次
- 输出的8分频信号周期刚好为8个基准时钟周期
3、奇数分频器
3.1、设计方法
如图所示,分别画出了基准时钟、3分频时钟、5分频时钟、7分频时钟。
不难想到3分频信号的周期为基准时钟信号周期的3倍,那么3分频时钟信号的边沿变化肯定发生在第1.5个时钟周期(第2个时钟周期的下降沿),同理5分频的边沿变化肯定发生在第2.5个时钟周期(第3个时钟周期的下降沿),同理7分频的边沿变化肯定发生在第3.5个时钟周期(第4个时钟周期的下降沿)。同时我们会发现既然边沿的变化都发生在0.5个时钟周期,那么肯定要利用到时钟的下降沿。
3.2、7分频电路设计实例
根据上图的7分频为例,可以得到以下结论:
- L3到L7为7分频的1个完整的时钟周期,同时也是7个基准时钟周期,即7分频
- L3(红线3,其他红线命名类推)到L4为7分频的半个时钟周期(高电平),同时也是3.5个基准时钟周期
- L4到L7为7分频的半个时钟周期(低电平),同时也是3.5个基准时钟周期
- clk_pos为上升沿敏感信号,分为两段,前半段占3个基准时钟周期(0~2),后半段占4个基准时钟周期(3~6)
- clk_neg为下降沿敏感信号,分为两段,前半段占3个基准时钟周期(0~2),后半段占4个基准时钟周期(3~6)
- 将 clk_pos 和 clk_neg 相与(&&)就可以得到7分频电路
从7分频电路可以推导出奇数分频设计的一般方法:
- 假设为 N分频,需从0计数到 N-1(一共N/2个基准时钟),一直循环
- 设计一个对基准时钟上升沿敏感的信号,每当计数到(N-1)/2-1时,时钟翻转;计数到计数器最大值时再反转
- 设计一个对基准时钟下降沿敏感的信号,每当计数到(N-1)/2-1时,时钟翻转;计数到计数器最大值时再反转
- 将 上升沿敏感的信号和 下降沿敏感的信号相与(&&)即N分频电路
7分频电路模块框图如下:
输入:
sys_clk:基准时钟信号(这里设定为50MHz,周期20ns)
sys_rst_n:低电平有效的复位信号
输出:
clk_7:基准时钟信号的7分频信号(50/7=7.14MHz,周期140ns)
3.3、Verilog
根据上文分析不难编写Verilog代码如下:
module divider_7 //模块名
(
input sys_clk, //时钟(设定为 50MHz)
input sys_rst_n, //复位信号(n 表示低电平有效)
output clk_7 //输出7分频信号
);
//reg 定义
reg [2:0] cnt; //最大为6,所以需要3位位宽
reg cnt_pos; //上升沿敏感信号
reg cnt_neg; //下降沿敏感信号
assign clk_7 = cnt_pos && cnt_neg; //组合逻辑 与
//计数模块,从0计数到6共计7个时钟周期
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt <= 3'd0; //复位清零
else if(cnt == 3'd6) //从0开始计数,所以需要 -1
cnt <= 3'd0; //计满则清零
else
cnt <= cnt + 3'd1; //没记满就一直计数
end
//cnt_pos:上升沿触发
//低电平维持3个基准时钟周期,高电平维持4个时钟周期
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_pos <= 1'b0; //复位清零
else if(cnt == 3'd2) //记满3个时钟周期
cnt_pos <= 1'b1; //前3个时钟周期输出为0,满足条件则输出1
else if(cnt == 3'd6) //记满7个时钟周期
cnt_pos <= 1'b0; //后4个时钟周期输出为1,满足条件则输出0
else
cnt_pos <= cnt_pos; //不满足条件就保持原来状态
end
//cnt_neg:下降沿触发
//低电平维持3个基准时钟周期,高电平维持4个时钟周期
always@(negedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_neg <= 1'b0; //复位清零
else if(cnt == 2'd2) //记满3个时钟周期
cnt_neg <= 1'b1; //前3个时钟周期输出为0,满足条件则输出1
else if(cnt == 3'd6) //记满7个时钟周期
cnt_neg <= 1'b0; //后4个时钟周期输出为1,满足条件则输出0
else
cnt_neg <= cnt_neg; //不满足条件就保持原来状态
end
endmodule
3.4、Testbench
Testbench文件比较简单,只需要实例化一下我们刚刚设计好的7分频模块就好了。
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module tb_divider_7(); //仿真模块
//输入reg 定义
reg sys_clk;
reg sys_rst_n;
//输出wire定义
wire clk_7;
//设置初始化条件
initial begin
sys_clk = 1'b0; //初始时钟为0
sys_rst_n <= 1'b0; //初始复位
#10 //10个时间单位后
sys_rst_n <= 1'b1; //拉高复位(此时复位无效)
end
//always代表重复进行,#10代表每10个时间单位
//每10个时间单位反转时钟,即时钟周期为20个时间单位(20ns)
always #10 sys_clk = ~sys_clk;
//例化被测试模块
divider_7 divider_7_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_7 (clk_7 )
);
endmodule
3.5、仿真结果
使用ModelSim执行仿真,仿真出来的波形如所示:
从波形图可以看到
- 10ns后停止复位
- 计数器cnt一直在从0计数到6
- 每当cnt计数到3的上升沿(L2),cnt_pos信号输出翻转,每当cnt计数清零(L4),cnt_pos信号输出翻转
- 每当cnt计数到3的下降沿(L1),cnt_neg信号输出翻转,每当cnt计数清零(L3),cnt_neg信号输出翻转
- 从L2到L3为7分频信号的半个时钟周期(高电平),从L2到L3为8分频信号的半个时钟周期(低电平)
- 从L2到L6为7分频信号的1个完整的时钟周期,同时也是7个基准时钟周期
4.其他
版本信息
文件:V1.0
编号:5
Vivado:无
Modelsim:Modelsim SE-64 10.4
Quartus II:无