目录
1. 概述
时钟分频器按照分频系数可以为整数或小数,整数中又分为奇数和偶数,本文将分析以上几种分频器的设计方法。
2. 整数分频器
偶数分频器的实现比较简单,通过计数器即可实现,奇数分频器略微复杂。
2.1 偶数分频器
2.1.1 设计原理及结构
偶数分频器的结构如下图所示,主要由计数器和时钟生成模块组成。其中分频系数为N时,计数器的计数范围为[0,N/2-1],时钟生成模块输出时钟在计数器输出为N/2-1时取反。
2.1.2 代码实现
//=================================================================================
// module : clk_even_div.v
// description : clock divide by even number
// data : 2022/3/16
// author : souther meditating
//=================================================================================
module clk_even_div{
clk_i,
rsti_n,
clk_o
};
//=================================================================================
// parameter & localparam
//=================================================================================
//=================================================================================
// parameter
parameter DIV_NUM = 'd4;
//=================================================================================
// localparam
localparam DIV_NUM_WIDTH = clog2(DIV_NUM);
localparam DIV_NUM_HALF = DIV_NUM/2;
//=================================================================================
// I/O
//=================================================================================
input clk_i;
input rsti_n;
output clk_o;
//=================================================================================
// signal
//=================================================================================
reg [DIV_NUM_WIDTH-1:0] count;
wire [DIV_NUM_WIDTH-1:0] div_num_half_m1;
//=================================================================================
// main body
//=================================================================================
//=================================================================================
// 1. count div_num_half
assign div_num_half_m1 = DIV_NUM_HALF - 'd1;
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
count <= {DIV_NUM_WIDTH{1'b0}};
end
else if(count == div_num_half_m1) begin
count <= {DIV_NUM_WIDTH{1'b0}};
end
else begin
count <= count + 'b1;
end
end
//=================================================================================
// 2. generate output clock
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
clk_o <= 1'b0;
end
else if (count == div_num_half_m1) begin
clk_o <= ~clk_o;
end
end
endmodule
2.2 奇数分频器
奇数分频器有两种实现方法,一种是Mohit Arora提出的“生成两个相位相差90度,频率为目标频率一半的时钟(该时钟为源时钟的偶数分频)”,另一种方法是“产生两个相位相差90度,频率为目标频率一半的时钟,但该时钟占空比不等于50%”。
2.2.1 方法一(Mohit Arora)
2.2.1.1 设计原理及结构
Mohit Arora的方法分为四步:
1) 产生一个计数器,计数范围为[0,N-1];
2) 根据计数值产生两个信号,cnt=0时tff1_en有效,cnt=(N+1)/2时tff2_en有效;
3) 根据tff1_en和tff2_en产生两个分频时钟,div1在时钟上升沿且tff1_en有效时取反,div2在时钟下降沿且tff2_en有效时取反;
4) 将div1和div2异或输出。
下图为分频系数等于3时的波形,通过90度的相位差得到奇数分频的系数,而且最后的输出时钟不会存在毛刺。
奇数分频的结构图如下所示,共包含3个寄存器。
2.2.1.2 代码实现
// module : clk_div_odd_func0.v
// description : clock divide by odd number
// data : 2022/3/16
// author : souther meditating
//=================================================================================
module clk_div_odd_func0{
clk_i,
rsti_n,
clk_o
};
//=================================================================================
// parameter & localparam
//=================================================================================
//=================================================================================
// parameter
parameter DIV_NUM = 'd5;
//=================================================================================
// localparam
localparam DIV_NUM_WIDTH = clog2(DIV_NUM);
localparam DIV_NUM_M1 = DIV_NUM - 1;
//=================================================================================
// I/O
//=================================================================================
input clk_i;
input rsti_n;
output clk_o;
//=================================================================================
// signal
//=================================================================================
reg [DIV_NUM_WIDTH-1:0] count;
wire tff1_en;
wire tff2_en;
reg div1;
reg div2;
//=================================================================================
// main body
//=================================================================================
//=================================================================================
// N divide clock,duty clock not 50%
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
count <= {DIV_NUM_WIDTH{1'b0}};
end
else if (count == DIV_NUM_M1) begin
count <= {DIV_NUM_WIDTH{1'b0}};
end
else begin
count <= count + 'd1;
end
end
assign tff1_en = (count == {DIV_NUM_WIDTH{1'b0}});
assign tff2_en = (count == (DIV_NUM+1)/2);
//=================================================================================
// generate two clock,difference of phase is 90 deg
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
div1 <= 1'b0;
end
else if (tff1_en == 1'b1) begin
div1 <= ~div1;
end
end
always @(negedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
div2 <= 1'b0;
end
else if (tff2_en == 1'b1) begin
div2 <= ~div2;
end
end
assign clk_o = (div1 ^ div2);
endmodule
2.2.2 方法二
2.2.2.1 设计原理及结构
方法二也可以分为4步:
1) 产生一个计数器,计数范围[0,N-1];
2) 产生占空比为((N-1)/2)/N的N分频时钟;
3) 分别在上升沿和下降沿对该时钟采样,得到div1和div2,此时div1和div2为相位差90度的N分频时钟,但占空比仍为((N-1)/2)/N;
4) Div1和div2进行“或”操作,得到占空比为50%的N分频时钟。
方法二的奇数分频结构图如下所示。
2.2.2.2 代码实现
// module : clk_div_odd_func1.v
// description : clock divide by odd number
// data : 2022/3/16
// author : souther meditating
//=================================================================================
module clk_div_odd_func1{
clk_i,
rsti_n,
clk_o
};
//=================================================================================
// parameter & localparam
//=================================================================================
//=================================================================================
// parameter
parameter DIV_NUM = 'd5;
//=================================================================================
// localparam
localparam DIV_NUM_WIDTH = clog2(DIV_NUM);
localparam DIV_NUM_M1 = DIV_NUM - 1;
localparam DIV_NUM_M1_HALF = (DIV_NUM - 1)/2;
//=================================================================================
// I/O
//=================================================================================
input clk_i;
input rsti_n;
output clk_o;
//=================================================================================
// signal
//=================================================================================
reg [DIV_NUM_WIDTH-1:0] count;
reg clk_div_n;
wire clk_turn_flag;
reg div1;
reg div2;
//=================================================================================
// main body
//=================================================================================
//=================================================================================
// N divide clock,duty clock is ((N-1)/2)/N
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
count <= {DIV_NUM_WIDTH{1'b0}};
end
else if (count == DIV_NUM_M1) begin
count <= {DIV_NUM_WIDTH{1'b0}};
end
else begin
count <= count + 'd1;
end
end
assign clk_turn_flag = ((count == {DIV_NUM_WIDTH{1'b0}}) || (count == DIV_NUM_M1_HALF));
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
clk_div_n <= 1'b0;
end
else if (clk_turn_flag == 1'b1) begin
clk_div_n <= ~clk_div_n;
end
end
//=================================================================================
// generate two clock,difference of phase is 90 deg
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
div1 <= 1'b0;
end
else begin
div1 <= clk_div_n;
end
end
always @(negedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
div2 <= 1'b0;
end
else begin
div2 <= clk_div_n;
end
end
assign clk_o = (div1 | div2);
endmodule
3. 小数分频器
3.1 设计原理及结构
Mohit Arora提出了一种基于移位寄存器的小数分频器的设计方法,但时钟占空比不等于50%。以4.5分频为例,在9个时钟内要产生两个重复的脉冲,该方法可以分为三步:
1) 定义9bit寄存器count,复位时count=9’b0_0000_0001,在时钟的上升沿对count进行逻辑左移;
2) 在时钟的下降沿对count[0]进行采样得到count_0_deg90,将count[0]、count[1]、count_0_deg90相与,得到第一个脉冲的高电平;
3) 在时钟的下降沿对count[4]、count[5]进行采样得到count_4_deg90、count_5_deg90,将count[5]、count_4_deg90、count_5_deg90相与,得到第二个脉冲的高电平。
引用Mohit Arora的波形图,其中波形中的第0bit为count[1],需要特别注意。
3.2 代码实现
// module : clk_div_decimal.v
// description : clock divide by decimal
// data : 2022/3/16
// author : souther meditating
//=================================================================================
module clk_div_decimal{
clk_i,
rsti_n,
clk_o
};
//=================================================================================
// parameter & localparam
//=================================================================================
//=================================================================================
// parameter decimal = numerator/denominator
parameter DIV_DENOM = 'd2;
parameter DIV_NUMER = 'd9;
parameter DIV_COUNT = 'd9;
//=================================================================================
// localparam
//=================================================================================
// I/O
//=================================================================================
input clk_i;
input rsti_n;
output clk_o;
//=================================================================================
// signal
//=================================================================================
reg [DIV_COUNT-1:0] count;
reg ps_count0;
reg ps_count4;
reg ps_count5;
//=================================================================================
// main body
//=================================================================================
//=================================================================================
// count implementation , reset value is 9'd0_0000_0001
always @(posedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
count <= 9'b0_0000_0001;
end
else begin
count <= (count << 1);
end
end
//=================================================================================
// count bit 0,4,5 phase shifter 180
always @(negedge clk_i or negedge rsti_n) begin
if (rsti_n == 1'b0) begin
ps_count0 <= 1'b0;
ps_count4 <= 1'b0;
ps_count5 <= 1'b0;
end
else begin
ps_count0 <= count[0];
ps_count4 <= count[4];
ps_count5 <= count[5];
end
end
//=================================================================================
// generate decimal clock
assign clk_o = (ps_count0 || count[0] || count[1]) || // first pluse
(ps_count4 || ps_count5 || count[5]); // second pluse
endmodule