分频器的设计(Verilog HDL)
在fpga应用中,有时需要对系统时钟进行分频。通常情况下有两种情况,一种是偶分频,一种是奇分频。
偶分频
偶分频指的是分频系数是偶数的分频器。比如,假设输入时钟是100MHz,分频系数是2,则输出的方波频率为50MHz。
偶分频的实现比较简单。分频系数实际上代表的是输出信号的周期同输入信号的周期的倍数。因此,设分频系数为N,则每隔N/2个输入信号周期,输出信号电平发生一次反转,即可。
8分频的参考代码如下:
`timescale 1ns/10ps
module clk_div(
input wire i_clk, // 输入时钟
input wire rst_n, // 复位信号
input wire en, // 使能信号,低电平有效
output wire o_clk // 输出时钟
);
/* ================= 内部引线定义 ================= */
reg [ 2:0] cnt_q;
reg clk_q;
reg en_reg;
reg en_reg0;
assign o_clk = clk_q;
/* ================= 实例生成 ================= */
always @(posedge i_clk) begin
en_reg0 <= en;
en_reg <= en_reg0;
end
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
if(~rst_n | ~en_reg) begin
cnt_q <= 0;
end
else if(cnt_q == (8 - 1))
cnt_q <= 0;
else
cnt_q <= cnt_q + 1;
end
always @(posedge i_clk or negedge rst_n or negedge en_reg) begin
if(~rst_n | ~en_reg) begin
clk_q <= 0;
end
else if((cnt_q == 0) || (cnt_q == (8 >> 1))) begin // 计数到分频因子或分频因子的一半时,信号反转
clk_q <= ~clk_q;
end
else begin // 信号保持
clk_q <= clk_q;
end
end
endmodule
奇分频
奇分频的实现相对而言比较复杂,原因在于,如果只在输入信号的上升沿或下降沿处,触发电平反转,会导致输出信号的占空比不为50%。因此,需要采用两个计数器进行计数:
- 一个在输入信号上升沿时计数,在 1 ∼ N − 1 2 1\sim\frac{N-1}{2} 1∼2N−1个时钟周期内输出一个电平,在 N + 1 2 ∼ N \frac{N+1}{2}\sim N 2N+1∼N个时钟周期内,输出反转后的电平。
- 一个在输入信号下降沿时计数,在 1 ∼ N − 1 2 1\sim\frac{N-1}{2} 1∼2N−1个时钟周期内输出一个电平,在 N + 1 2 ∼ N \frac{N+1}{2}\sim N 2N+1∼N个时钟周期内,输出反转后的电平。
这计数器输出的信号的相位差,刚好差半个输出信号周期,将两个信号取或逻辑操作,即可得到占空比为50%的奇分频信号了。7细分的参考代码如下:
`timescale 1ns/10ps
module clk_div(
input wire i_clk, // 输入时钟
input wire rst_n, // 复位信号
input wire en, // 使能信号,低电平有效
output wire o_clk // 输出时钟
);
/* ================= 内部引线定义 ================= */
reg [ 2:0] cnt_q;
reg clk_neg;
reg clk_pos;
reg en_reg;
reg en_reg0;
assign o_clk = clk_neg | clk_pos;
/* ================= 实例生成 ================= */
always @(posedge i_clk) begin
en_reg0 <= en;
en_reg <= en_reg0;
end
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
if(~rst_n | ~en_reg) begin
cnt_q <= 0;
end
else if(cnt_q == (7 - 1)) begin
cnt_q <= 0;
end
else begin
cnt_q <= cnt_q + 1;
end
end
// 奇分频的思路是,相位相差90°,然后取或组合得到占空比为50%的方波
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin:posedge_trigger
if(~rst_n | ~en_reg) begin
clk_pos <= DEF_LEVEL;
end
else if((cnt_q == (7 - 1)) || (cnt_q == ((7 - 1) >> 1))) begin
clk_pos <= ~clk_pos;
end
else begin
clk_pos <= clk_pos;
end
end
always@(negedge i_clk or negedge rst_n or negedge en_reg) begin:negedge_trigger
if(~rst_n | ~en_reg) begin
clk_neg <= DEF_LEVEL;
end
else if((cnt_q == (7 - 1)) || (cnt_q == ((7 - 1) >> 1))) begin
clk_neg <= ~clk_neg;
end
else begin
clk_neg <= clk_neg;
end
end
通用分频器
基于偶分频和奇分频的原理,我们可以将二者整合在一起,得到一个通用的分频器模块,这样在调用的时候就无需考虑,是需要例化哪一种分频器了。只需要修改分频因子即可。
`timescale 1ns/10ps
module clk_div(
input wire i_clk, // 输入时钟
input wire rst_n, // 复位信号
input wire en, // 使能信号,低电平有效
output wire o_clk // 输出时钟
);
// 位宽求解函数
function integer bw_solve(input integer value);
begin
if(value == 0)
value = 1;
else if(value != 0)
for (bw_solve = 0; value > 0;bw_solve = bw_solve + 1)
value = value >> 1;
end
endfunction
/* ================= 参数定义 ================= */
parameter integer DIV_FACT = 20; // 细分因子
parameter integer DEF_LEVEL = 0; // 初始电平
localparam integer BW = bw_solve(DIV_FACT); // 分频因子位宽
localparam integer ODD_OR_EVEN = DIV_FACT % 2; // 奇偶分频判断,0为偶分频,1为奇分频
/* ================= 内部引线定义 ================= */
reg [BW-1:0] cnt_q;
reg clk_pos;
reg clk_neg;
reg clk_q;
reg en_reg;
reg en_reg0;
/* ================= 实例生成 ================= */
always @(posedge i_clk) begin
en_reg0 <= en;
en_reg <= en_reg0;
end
generate
if(!ODD_OR_EVEN) begin // 偶分频
assign o_clk = clk_q;
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
if(~rst_n | ~en_reg) begin
cnt_q <= 0;
end
else if(cnt_q == (DIV_FACT - 1))
cnt_q <= 0;
else
cnt_q <= cnt_q + 1;
end
always @(posedge i_clk or negedge rst_n or negedge en_reg) begin
if(~rst_n | ~en_reg) begin
clk_q <= DEF_LEVEL;
end
else if((cnt_q == 0) || (cnt_q == (DIV_FACT >> 1))) begin // 计数到分频因子或分频因子的一半时,信号反转
clk_q <= ~clk_q;
end
else begin // 信号保持
clk_q <= clk_q;
end
end
end
else begin // 奇分频
assign o_clk = clk_neg | clk_pos;
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin
if(~rst_n | ~en_reg) begin
cnt_q <= 0;
end
else if(cnt_q == (DIV_FACT - 1)) begin
cnt_q <= 0;
end
else begin
cnt_q <= cnt_q + 1;
end
end
// 奇分频的思路是,生成两个占空比是25%的方波,相位相差半个输入时钟周期,然后取或组合得到占空比为50%的方波
always@(posedge i_clk or negedge rst_n or negedge en_reg) begin:posedge_trigger
if(~rst_n | ~en_reg) begin
clk_pos <= DEF_LEVEL;
end
else if((cnt_q == (DIV_FACT - 1)) || (cnt_q == ((DIV_FACT - 1) >> 1))) begin
clk_pos <= ~clk_pos;
end
else begin
clk_pos <= clk_pos;
end
end
always@(negedge i_clk or negedge rst_n or negedge en_reg) begin:negedge_trigger
if(~rst_n | ~en_reg) begin
clk_neg <= DEF_LEVEL;
end
else if((cnt_q == (DIV_FACT - 1)) || (cnt_q == ((DIV_FACT - 1) >> 1))) begin
clk_neg <= ~clk_neg;
end
else begin
clk_neg <= clk_neg;
end
end
end
endgenerate
endmodule