系统中的时钟设计通常有两种设计要求,一是静态配置,这种硬件设计要求时钟的分频控制字提前配置好,之后在整个工作过程中不允许切换分频控制字,这种设计结构相对来说比较简单,中后端约束也相对比较简单,做出来的时钟tree会好一些,缺点就是实际使用起来不够灵活;二是动态配置,这种硬件设计要求系统在实际工作过程中,软件可以任意改写时钟分频控制字,这种动态切换时钟分频控制字的需求,要求硬件做特殊处理,尤其是从高低频进行切换的时刻,不能出现glitch或者超频的情况,通常情况下这个切换节点对中后端来说timing往往会出现比较紧的情况。
本文主要介绍第二种设计实现的方法,该方法比较通用,可以支持奇数分频、偶数分频。
module clk_div
#(parameter NUM_WD=4) //分频控制字位宽
(
input wire clk, //时钟源
input wire rst_n, //时钟源同步释放后的复位
input wire clk_div_en, //时钟分频模块使能,主要用于低功耗设计
input wire[NUM_WD-1:0] clk_div_num, //时钟分频控制字,这里举例2bit,最多进行3分频
output wire clk_div_out //分频时钟输出
);
wire clk_nodiv; //支持分频控制字为0,即不分频
wire clk_div_out_pre; //临时变量
reg[NUM_WD-1:0] clk_div_cnt; //内部分频计数器
reg clk_div; //分频寄存器输出
reg[NUM_WD-1:0] clk_div_num_smp; //临时变量,用于动态更新软件配置的分频控制字
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_div_num_smp <= 'd2; //default 2 分频
else if(clk_div_cnt == (clk_div_num_smp - 1'b1)) //在即将归0的前一拍更新新的分频控制字
clk_div_num_smp <= clk_div_num;
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_div_cnt <= {(NUM_WD-1){1'b0}};
else if(!clk_div_en || (clk_div_num_smp == 1))
clk_div_cnt <= {(NUM_WD-1){1'b0}};
else
clk_div_cnt <= (clk_div_cnt == clk_div_num_smp - 1'b1) ? {(NUM_WD-1){1'b0}} : clk_div_cnt + 1'b1;
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_div = 1'b0;
else if(!clk_div_en || (clk_div_num_smp == 1))
clk_div = 1'b0;
else
//当计数器小于总计数的一半时输出高电平,其他输出低电平
clk_div = (clk_div_cnt <= clk_div_num_smp[NUM_WD-1:1] - 1'b1) ? 1'b1 : 1'b0;
end
assign clk_nodiv_en = clk_div_en & (clk_div_num_smp == 'd1) & !clk_div;
cgc_cell u_cgc_cell_clk_nodiv (.clk_in(clk), .gate(clk_nodiv_en), .clk_out(clk_nodiv));//need scan
assign clk_div_out_pre = clk_nodiv | clk_div;
assign clk_div_out = clk_div_out_pre; //理论上需要做完scan_mux后输出
endmodule