系列文章目录
第二章 时钟频率检查
前言
众所周知,时钟模块为整个SOC系统中各个模块提供相应的时钟,在大部分情况下,各个模块的时钟频率各不相同,这也是为了更好的管理芯片的功耗问题,当系统需要跑到高频阶段时,软件可以通过配置分频控制字将时钟切换到高频,同理当芯片需要进行相对低的功耗时,就需要把各个模块的频率切换到低频去工作。因此,在系统工作过程中,软件会频繁切换相关模块的时钟频率。关于时钟无毛刺切换设计,我已在其他篇文章中做了详细介绍,这里我们主要介绍如何验证时钟切换过程中频率是否符合要求的问题。
针对系统中进行时钟频率切换,本文介绍了一种验证方法,专门对切换过程中时钟频率是否满足要求添加了自动化的验证checker,不管是验证debug过程中,还是后续回归测试,该自动化检查checker可以随时上报频率异常的问题,极大的加快了芯片验证的流程。
一、什么是时钟频率检查?
所谓时钟频率检查,即是根据设计需求,对所有设计支持的频率范围进行全方位的动态检查,一旦系统运行过程中,时钟频率违反了指定的频率,即刻向testbench上报时钟频率错误信息,方便debug。
二、使用步骤
1.自动化check代码代码如下:
`define CLK_FREQ_CHK(chk_clk,chk_freq,sign)
begin
real t_last,t_now,t_starat,cycle_now,chk_cycle;
int cnt;
chk_cycle = 1000.000/chk_freq;
t_last = 0.0;
t_now = 0.0;
cnt = 0;
t_start = $realtime;
fork
begin
while(1)begin
@(posedge clk_ref);
if(clk_check==0)begin
cnt = 0;
break;
end
end
end
begin
while(1)begin
@(posedge chk_clk);
t_now = $realtime;
if(cnt==0)begin
end
else if((t_last >0) &&(cnt>1))begin
cycle_now = t_now - t_last;
if((cycle_now > (0.99*chk_cycle)) &&(cycle_now < (1.01*chk_cycle)))begin
end
else begin
$display("clk_freq_err, %0s, exp_freq=%0f,act_freq=%0f,expect cycle=%0f,
actual cycle=%0f, at time
%0f",sign,chk_freq,1000.000/cycle_now,chk_cycle,cycle_now,%time);
err_num++;
end
end
begin
t_last = $realtime;
cnt ++;
end
end
end
join_any
disable fork;
end
2. 代码解析
首先是这部分代码的主体结构,如下:
`define CLK_FREQ_CHK(chk_clk,chk_freq,sign)
begin
real t_last,t_now,t_starat,cycle_now,chk_cycle;
int cnt;
chk_cycle = 1000.000/chk_freq;
t_last = 0.0;
t_now = 0.0;
cnt = 0;
t_start = $realtime;
fork
begin
xxx;
end
begin
xxx;
end
join_any
disable fork;
end
这部分主体代码建立了一个fork join的并行执行语句,当两个begin end语句中任意一个执行完毕后退出检查。这样做的目的是为了更好的控制整个flow,第一个begin end语句中我们添加了使能信号,当clk_check为0时,退出检查,这样可以在更上层testbench中随时使能或关闭,第二个begin end为计算频率的算法结构部分,通过计算的时钟频率与目标时钟频率进行比较,当误差范围超过设定的偏差值时,向testbench上报频率检查错误。
以下代码为第二个begin end算法结构:
while(1)begin
@(posedge chk_clk);
t_now = $realtime;
if(cnt==0)begin
end
else if((t_last >0) &&(cnt>1))begin
cycle_now = t_now - t_last;
if((cycle_now > (0.99*chk_cycle)) &&(cycle_now < (1.01*chk_cycle)))begin
end
else begin
$display("clk_freq_err, %0s, exp_freq=%0f,act_freq=%0f,expect cycle=%0f,
actual cycle=%0f, at time
%0f",sign,chk_freq,1000.000/cycle_now,chk_cycle,cycle_now,%time);
err_num++;
end
end
begin
t_last = $realtime;
cnt ++;
end
end
首先,当待check的时钟chk_clk上升沿到来时,我们记录下当前的时间,然后下一个时钟上升沿到来后记录当前时间为t_last,后续根据每个当前时钟时间减去上一个时钟沿的时间,来计算时钟周期cycle_now,最后根据cycle_now的数值跟传参chk_cycle进行比较,当误差范围小于0.01倍的时钟周期时,检查通过;否则检查fail,并向testbench上报错误信息。
`CLK_FREQ_CHK(clk_target,100,"clk_target freq")
在整个验证体统中通过调用上述宏定义来对所有待检查时钟进行实例化。如上是我们需要检查clk_target的时钟频率为100M的实例化调用。
总结
这里对文章进行总结:
通过宏定义的方式对检查进行主体建模,然后在验证bench中对所有待检查的时钟进行扩展建模,很大程度上减少代码的复杂度,使代码通俗易懂,易维护。同时,通过这种自动化的时钟频率检查,可以检查出任意时刻,当时钟频率不满足设定的目标频率时随时向testbench上报错误信息,大大提高验证效率。