- Mux sync
- 门控单元:clock gating
- 动态的时钟切换:glith free clock switch
mux sync
enable信号同步两拍,以保证信号的稳定。信号不打拍。enable信号到时,把信号同步过去。
ICG:Integrate Clock Gating Cell
- 减少时钟功耗,clock和data的跳变的功耗叫做dynamic power,当数据不变时,需要将clock停住(gating),以减少功耗,可以用AND和OR gate,前者时0是enable,后者是1是enale
ICG在关断和打开的时候如果时机不对,会产生glitch(短时脉冲),为了避免这种情况,可在AND gate或OR gate前加上一级latch,保证enable信号只在时钟低电平或者高电平时才传到gate上去。
Verilog Behavior Model
module cell_clock_gating(
input TE, //test enable which is used for test mode in DFT
input E, //enable
input CP, //clock
output Q
);
reg E_lat;
assign E_or = E | TE;
always @ (CP or E) //latch
if(!CP) begin
E_lat <= E_or;
end
assign Q = E_lat & CP;
endmodule
Clock switch:glitch due to simple mux
时钟切换时会产生毛刺,合理的设计要使切换发生目标时钟的低电平处。
SoC系统中时钟切换应用场景
软件配置需要CPU去下达命令,所以当在切换CPU时钟的时候不适用软件配置切换
CPU动态切换时钟,并且不能出现glitch的方法
- 用打三拍的方式,使其他时钟提前关断,并且用ICG电路保证在时钟信号低电平时刻关断。
- 其他时钟关断后,载使用ICG使选中的时钟在低电平处释放。
毛刺消除
module glitch_free(
output clk_out,
input [1:0] cgm_sel,
input clk_in0,
input clk_in1,
input clk_in2,
input rst_clk_n,
input icg_scan_mode,
input scan_dc_mode,
input clk_scan
);
reg in0_en_sync1, in0_en_sync2, in0_en_sync3;
reg in1_en_sync1, in1_en_sync2, in1_en_sync3;
reg in2_en_sync1, in2_en_sync2, in2_en_sync3;
/*
* 切换时钟模式选择
*/
assign clk_in0_scan = scan_dc_mode ? clk_scan : clk_in0;
assign clk_in1_scan = scan_dc_mode ? clk_scan : clk_in1;
assign clk_in2_scan = scan_dc_mode ? clk_scan : clk_in2;
/*
* cgm_sel[1:0]选择时钟输入,in0_sel是选择信号
*/
assign in0_sel = (cgm_sel[1:0] == 2'b00);
assign in1_sel = (cgm_sel[1:0] == 2'b01);
assign in2_sel = (cgm_sel[1:0] == 2'b10 || (cgm_sel[1:0] == 2'b11));
/*********************************使能信号的转变**********************************/
// 三级同步器中每一级都是0,表示该时钟不被使用,有一个是1,表示正在切换至或正
// 正在使用
assign in0_used = in0_sel | in0_en_sync1 | in0_en_sync2 | in0_en_sync3;
assign in1_used = in1_sel | in1_en_sync1 | in1_en_sync2 | in1_en_sync3;
assign in2_used = in2_sel | in2_en_sync1 | in2_en_sync2 | in2_en_sync3;
// inx_en信号转变后,时钟信号至少在本时钟2.5个周期后完成停止或起振。
// 因此转变目标时钟要在inx_en转变的3个时钟周期后发生起振。
assign in0_en = ~in1_used & ~in2_used;
assign in1_en = ~in0_used & ~in2_used;
assign in2_en = ~in0_used & ~in1_used;
/*********************************************************************************/
/*
*ICG使能信号,打三拍处理
*/
always @(posedge clk_in0_scan or negedge rst_clk_n) begin
if(!rst_clk_n) begin
in0_en_sync1 <= 1'b0;
in0_en_sync2 <= 1'b0;
in0_en_sync3 <= 1'b0;
end
else begin
in0_en_sync1 <= in0_en;
in0_en_sync2 <= in0_en_sync1;
in0_en_sync3 <= in0_en_sync2;
end
end
always @(posedge clk_in1_scan or negedge rst_clk_n) begin
if(!rst_clk_n) begin
in1_en_sync1 <= 1'b0;
in1_en_sync2 <= 1'b0;
in1_en_sync3 <= 1'b0;
end
else begin
in1_en_sync1 <= in1_en;
in1_en_sync2 <= in1_en_sync1;
in1_en_sync3 <= in1_en_sync2;
end
end
always @(posedge clk_in2_scan or negedge rst_clk_n) begin
if(!rst_clk_n) begin
in2_en_sync1 <= 1'b0;
in2_en_sync2 <= 1'b0;
in2_en_sync3 <= 1'b0;
end
else begin
in2_en_sync1 <= in2_en;
in2_en_sync2 <= in2_en_sync1;
in2_en_sync3 <= in2_en_sync2;
end
end
assign ind_in0 = in0_en_sync2;
assign ind_in1 = in1_en_sync2;
assign ind_in2 = in2_en_sync2;
/*
* 打三拍处理后的使能信号接入ICG模块
*/
cell_clock_gating u_clk_gate_out0(
.TE (icg_scan_mode),
.E (ind_in0),
.CP (clk_in0),
.Q (clk_out0)
);
assign ind_in1_scan = -scan_dc_mode && ind_in1;
cell_clock_gating u_clk_gate_out1(
.TE (1'b0),
.E (ind_in1_scan),
.CP (clk_in1),
.Q (clk_out1)
);
assign ind_in2_scan = -scan_dc_mode && ind_in2;
cell_clock_gating u_clk_gate_out2(
.TE (1'b0),
.E (ind_in2_scan),
.CP (clk_in2),
.Q (clk_out2)
);
assign clk_out = clk_out0 | clk_out1 | clk_out2;
endmodule
testbench
module glitch_free_tb();
reg clk_in0; //输出clk1
reg clk_in1; //输出clk1
reg clk_in2; //输出clk2
reg clk_scan;
reg scan_dc_mode;
reg icg_scan_mode;
reg rst_clk_n = 0;
reg [1:0] clk_in0;
initial begin
clk_in0 = 0;
clk_in1 = 0;
clk_in2 = 0;
rst_clk_n = 0;
clk_scan = 0;
icg_scan_mode = 0;
scan_dc_mode = 1;
#200
rst_clk_n = 1;
cgm_sel = 0;
repeat(50) @(posedge clk_in2); //clk2输出50个周期后,切换clk
cgm_sel = 1;
repeat(50) @(posedge clk_in2);
cgm_sel = 2;
repeat(50) @(posedge clk_in2);
icg_scan_mode = 1;
scan_dc_mode = 0;
sgm_sel = 0;
repeat(1000) @(posedge clk_in0);
icg_scan_mode = 0;
scan_dc_mode = 1;
cgm_sel = 2;
repeat(1000) @(posedge clk_in0);
$finish;
end
initial begin
$fsdbDumpfile("glitch_free_tb.fsdb");//将名为glitch_free_tb.fsdb的fsdb波形文件dump下来
$fsdbDumpvars;
end
always #20 clk_in0 = ~clk_in0; //clk0周期为40个时间单位
always #70 clk_in1 = ~clk_in1; //clk1周期为140个时间单位
always #110 clk_in2 = ~clk_in2; //clk2周期为220个时间单位
always #1000 clk_scan = ~clk_scan; //clk_scan周期为2000个时间单位
glitch_free u_glitch_free(
.clk_out (clk_out),
.cgm_sel (cgm_sel),
.clk_in0 (clk_in0),
.clk_in1 (clk_in1),
.clk_in2 (clk_in2),
.rst_clk_n (rst_clk_n),
.icg_scan_mode (icg_scan_mode),
.scan_dc_mode (scan_dc_mode),
.clk_scan (clk_scan)
);
endmodule
仿真
dve对应的波形文件叫vpd文件
verdi对应的波形文件叫fsdb文件