任意奇偶分频器
分频器是一种基本电路,是在所给信号频率的基础上,减少单位时间内脉冲个数的电路,根据分频的需求不同,将分频器分为偶数分频和奇数倍分频,非整数倍分频。整数倍频主要依赖于计数器来是实现,已输入的内部时钟信号为计数脉冲,按照计数次数规律地输出脉冲信号从而得到所要求的信号。
能够实现输入N的分频,其中偶分频的原理不同于奇分频,因为同时只有一个分频器有效,所以最终频率的输出为两者频率相或,其中主要框图如下:
偶分频的实现原理
对于任意的偶数分频,其输出已分频的信号的半周期一定是待分频信号周期的整数倍,也就是说已分频信号的跳变瞬间总是对应着原信号单一的跳变状态,因此,要实现偶数分频器,只要使用一个计数器对clk的跳变状态进行计数,便能控制输出信号的跳变。
当en有效,且N为偶数时,开始计数,在同一个分频后的时钟周期中计数二轮(N/2 - 1),当检测到N == 0时,偶分频信号翻转。
奇数分频的实现原理
对于任意的奇数倍分频,其输出已分频信号的半个周期一定是待分频信号周期的非整数倍,就是说已分频信号的跳变瞬间,并非对应原信号的单一跳变状态,因此需要使用两个计数器分别在上升沿和下降沿时触发计数,为实现按占空比50%分频,可选用于CLK相差1/2周期的两分频错位信号相“或”来实现。
刚开始我在计数分频时任然采用偶分频的方式,但是有错误,原理上采用上升沿和下降沿两种并行的计数,每个计数的原理都是相同的,各自触发时钟下以N为单位递减计数,当COUNT > (N+1)/2为高电平,否则为低电平,当count为0时,重新递减计数。
Verilog 实现
顶层模块div_clk.v
module div_clk(
input clk,
input rst_n,
input [7:0] N,
input en,
output clk_out
);
wire clk_out_e;
wire clk_out_o;
assign clk_out = clk_out_e | clk_out_o;
dive_clk inst_dive_clk(
.clk(clk),
.rst_n(rst_N),
.en(en),
.N(N),
.clk_out_e(clk_out_e)
);
divo_clk inst_divo_clk(
.clk(clk),
.rst_n(rst_N),
.en(en),
.N(N),
.clk_out_o(clk_out_o)
);
endmodule
偶数分频模块dive_clk.v
module dive_clk(
input clk,
input rst_n,
input en,
input [7:0] N,
output reg clk_out_e
);
reg [7:0] cnt;
wire E_enable;
assign E_enable = ~N[0];
always@(posedge clk)begin
if(rst_n == 0)begin
cnt <= 8'b0;
clk_out_e <= 1'b0;
end
else begin
if(en & ~N[0]) begin //偶分频
if(cnt == 8'b0)begin
clk_out_e <= ~clk_out_e;
cnt <= N/2 -1;
end
else begin
clk_out_e <= clk_out_e;
cnt <= cnt - 1'b1;
end
end
else begin //是奇分频,或者没有使能
clk_out_e <= 1'b0;
cnt <= 8'b0;
end
end
end
endmodule
奇数分频器模块divo_clk.v
module divo_clk(
input clk,
input rst_n,
input [7:0] N,
input en,
output clk_out_o
);
reg [7:0] cnt_a,cnt_b;
reg clk_a,clk_b;
wire O_enable ;
assign O_enable = N[0];
assign clk_out_o = clk_a | clk_b;
always@(posedge clk)begin
if(rst_n == 0)begin
cnt_a <= 8'b0;
clk_a <= 1'b0;
end
else begin
if(en & N[0]) begin //奇数分频
if(cnt_a == 8'b0) begin
cnt_a <= N -1;
clk_a <= 1'b1;
end
else if(cnt_a > ((N + 1) /2)) begin
cnt_a <= cnt_a - 1'b1;
clk_a <= 1'b1;
end
else begin
cnt_a <= cnt_a - 1'b1;
clk_a <= 1'b0;
end
end
else begin
clk_a <= 1'b0;
cnt_a <= 8'b0;
end
end
end
always@(negedge clk)begin
if(rst_n == 0 )begin
cnt_b <= 8'b0;
clk_b <= 1'b0;
end
else begin
if(en & N[0]) begin
if(cnt_b == 8'b0)begin
cnt_b <= N - 1;
clk_b <= 1'b1;
end
else if(cnt_b > ((N + 1)/2)) begin
cnt_b <= cnt_b - 1'b1 ;
clk_b <= 1'b1;
end
else begin
cnt_b <= cnt_b - 1'b1;
clk_b <= 1'b0;
end
end
else begin
clk_b <= 1'b0;
cnt_b <= 8'b0;
end
end
end
endmodule
testbench文件
module div_clk_tb();
reg clk;
reg rst_n;
reg en;
reg [7:0] N;
wire clk_out;
initial begin
rst_n <= 1'b1;
#10
rst_n <= 1'b0;
#10
rst_n <= 1'b1;
end
initial begin
clk = 0;
forever begin
#5 clk = ~clk;
end
end
initial begin
#30;
en <= 1'b1;
N <= 8'd7;
#300;
N <= 8'd2;
end
div_clk inst_div_clk_tb(
.clk(clk),
.rst_n(rst_n),
.en(en),
.N(N),
.clk_out(clk_out)
);
endmodule
仿真结果
首先第一个是错误的图,可以看到如果奇数分频如果采用偶分频只计数半轮的方法不可行。
修改后可以清楚的看见仿真正确,这里要特别说明的是如果在前一种N参数分频一个周期还未结束时,如果N改变,会导致一段时钟频率的混乱。
任意小数分频
对于任意小数分频,如果有PLL的话,直接倍频再分频即可;或常用的方法有双模前置小数分频和脉冲删除小数分频。前一种方法设计较为复杂,因此主要以第二种方式为主设计了一下。
这里主要通过将任意小数化为分数进行实现,首先需要理解下面的概念,我通过举例来说明:
假设我们需要实现8.7的分频,那么意思是我们先实现10分频,然后其中8.7的高电平,但通常0.*在时钟分频中不好出现,(0.5还好实现),那么我们实现的办法是在85个时钟周期中,出现10次高电平,但这高电平不是连续出现的,而说的是整体的占比,如果这样的话,就实现了87/10的时钟分频。
有一个计算公式,可以让87个周期中出现10次高电平,计算公式如下(好像称作双模前置小数分频原理)原文链接:
T=8.7 对应于M.N(即M=8|N = 7)T也可以表示为 T = M + b/(a+b) 这里的a+b就是值”输出的分频信号2个时钟周期这一平均概念“将M通分至分母(a+b),则T = ( Ma+(M+1)b )/ a+b,这里我们发现组成小数分频使用了a个M分频和b个M+1分频的整数分频电路。
这个方法被称作《双模前置小数分频》其中最重要的核心是M分频和M+1分频这个相近频率
T = 8.7= 8+ 0.7 = 8+ 7/10 = = (8a + 9b )/a+b
{a + b = 10
{ma + (m+1)b = 87
这是我对这个公式的理解,其实这里a,b的取值是有多种取法的,但是公式的取法更加好理解(a + b = 10),同时要保持了最大化的50%的占空比(8和9之间的差较小),这里公式带进去之后,求得的a,b总是整数,尽管仿真图出来我觉得这种小数分频没有什么太大的意义,最终的公式如下:
{ 3 + 7 = 10
{ 83 + 9 7 = 85
参考的博主是我看的那么多将的比较清楚的了,所以我这里直接截图了,为了减少抖动,那我采取的策略是9 9 8 9 9 8 9 9 8 9 这要的顺序来产生高电平。
verilog代码
###1.1 异步复位小数计数器
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/06/26 15:03:38
// Design Name:
// Module Name: fraction_divide
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module fraction_divide(
input clk,
input rst_n, //同时练习异步复位同步释放
output clk_out
);
reg rst_n1,rst_n2;
reg [4:0] count; //大循环用于控制偶分频还是奇分频
reg [4:0] even_count; //偶分频计数
reg [4:0] odd_count; //奇分频计数
assign clk_out = (even_count == 7 || odd_count == 8);
always@(posedge clk or negedge rst_n) begin //0~9
if(rst_n == 0) begin
count <= 5'b0;
even_count <= 5'b0;
odd_count <= 5'b0;
end
else if (count == 9)begin
if(odd_count < 8)
odd_count <= odd_count + 1'b1;
else begin
odd_count <= 5'b0;
count <= 5'b0;
end
end
else if (count == 2 || count == 5 || count == 8) begin
if(even_count < 7)
even_count <= even_count + 1'b1;
else begin
even_count <= 5'b0;
count <= count + 1'b1;
end
end
else if(count != 2 || count != 5 )begin
if(odd_count < 8)
odd_count <= odd_count + 1'b1;
else begin
odd_count <= 5'b0;
count <= count + 1'b1;
end
end
else begin
count <= 5'b0;
even_count <= 5'b0;
odd_count <= 5'b0;
end
end
endmodule
1.2 testbench
module fraction_divide_tb();
reg clk;
reg rst_n;
wire clk_out;
fraction_divide u1 (.clk(clk),.rst_n(rst_n),.clk_out(clk_out));
always #5 clk =~clk;
initial
begin
clk = 0;
rst_n = 1;
#15
rst_n = 0;
#25
rst_n = 1;
#4000;
end
endmodule
1.3仿真图
79 + 38 = 87,其中高电平出现10次,符合要求。
2.1 异步复位,同步释放的小数分频
在复位信号到来的时候不受时钟信号的同步,而是在复位信号释放的时候受到时钟信号的同步。
###2.2 verilog
module fraction_divide(
input clk,
input rst_n, //同时练习异步复位同步释放
output clk_out
);
reg rst_n1,rst_n2;
reg [4:0] count; //大循环用于控制偶分频还是奇分频
reg [4:0] even_count; //偶分频计数
reg [4:0] odd_count; //奇分频计数
assign reset_n = rst_n2;
assign clk_out = (even_count == 7 || odd_count == 8);
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rst_n1 <= 1'b0;
rst_n2 <= 1'b0;
end
else begin
rst_n1 <= 1'b1;
rst_n2 <= rst_n1;
end
end
always@(posedge clk or negedge rst_n) begin //0~9
if(reset_n == 0) begin
count <= 5'b0;
even_count <= 5'b0;
odd_count <= 5'b0;
end
else if (count == 9)begin
if(odd_count < 8)
odd_count <= odd_count + 1'b1;
else begin
odd_count <= 5'b0;
count <= 5'b0;
end
end
else if (count == 2 || count == 5 || count == 8) begin
if(even_count < 7)
even_count <= even_count + 1'b1;
else begin
even_count <= 5'b0;
count <= count + 1'b1;
end
end
else if(count != 2 || count != 5 )begin
if(odd_count < 8)
odd_count <= odd_count + 1'b1;
else begin
odd_count <= 5'b0;
count <= count + 1'b1;
end
end
else begin
count <= 5'b0;
even_count <= 5'b0;
odd_count <= 5'b0;
end
end
endmodule
2.3 仿真消除了亚稳态的同时,可以与上图对比,发现,在第一个时钟周期中能够计数没有误差。