Reading notes about mult and shift pairs
Author: Yang Honggang(Joseph) <eagle.rtlinux@gmail.com>
Kernel Version: Linux 3.0.4
===================================================================================================================
关于mult和shift这一对儿值,在内核代码中有X处存在。
clocksource,clock_event_device, timekeeper,cyclecounter, clock_data
下面,我们先了解一下它在clocksource中的应用。
clocks_calc_mult_shift()函数用来根据具体应用参数计算mult 和 shift值。我们从这里开始入手分析。
/* clocks_calc_mult_shift - calculatemult/shift factors for scaled math of clocks
* @mult: pointer to mult variable
* @shift: pointer to shift variable
* @from: frequency to convert from
* @to: frequency to convert to
* @maxsec: guaranteed runtimeconversion range in seconds
* The function evaluates theshift/mult pair for the scaled math
* operations of clocksources andclockevents.
* @to and @from are frequency valuesin HZ. For clock sources @to is
* NSEC_PER_SEC == 1GHz and @from isthe counter frequency. For clock
* event @to is the counter frequencyand @from is NSEC_PER_SEC.
* The @maxsec conversion rangeargument controls the time frame in
* seconds which must be covered by theruntime conversion with the
* calculated mult and shift factors.This guarantees that no 64bit
* overflow happens when the inputvalue of the conversion is
* multiplied with the calculated multfactor. Larger ranges may
* reduce the conversion accuracy bychosing smaller mult and shift factors.
*/
void
clocks_calc_mult_shift(u32 *mult, u32*shift, u32 from, u32 to, u32 maxsec)
{
u64 tmp;
u32 sft, sftacc= 32;
/*
* Calculate the shift factor whichis limiting the conversion
* range:
*/
tmp = ((u64)maxsec * from) >>32; [1]
while (tmp) { ……[*]
tmp >>=1;
sftacc--;
} [-1]
/*
* Find the conversion shift/multpair which has the best
* accuracy and fits the maxsecconversion range:
*/
for (sft = 32; sft > 0; sft--){[2]
tmp = (u64) to << sft;
tmp += from / 2;
do_div(tmp, from);
if ((tmp >> sftacc) ==0)[3]
break;
}
*mult = tmp;
*shift = sft;
}
在分析这段代码的原理之前,先看如下的关系式。
假设时钟频率为freq,那么时钟周期为1/freq秒,即 1/freq * NSEC_PER_SEC 纳秒。
cyc个时钟的ns数为,
ns = cyc *(NSEC_PER_SEC / freq) ……[a]
实际计算时,由于内核不支持浮点运算,只支持整数的除法运算,会带来很大精度损失,所以对上面式子进行变换,如下
ns = (cyc * ((NSEC_PER_SEC << N)/ freq) >> N ……[b]
令
mult = (NSEC_PER_SEC << N) / freq
N=shift
那么,
ns = (cyc * mult) >> shift ……[c]
下面开始分析上面代码:
下面分析,基于
clocks_calc_mult_shift(&cs->mult,&cs->shift, freq,
NSEC_PER_SEC, sec);
调用实例。
首先,[1]得到可能使用mult,shift来处理的最大的cyc值
解释如下,
由式子[a],
因为maxsec为可以用来使用mult,shift转化的最大的时间范围(单位为:秒),这个是已知的。
有,
maxsec= CYC / freq
可以推出,
CYC= maxsec * freq
所以,我们的结论是[1]处代码的作用是得到可以使用mult,shift来转化的最大的cyc值。
[*]处的while循环用来得到CYC最多可以左移多少位,能够保证不出现64位溢出。
结果保存在sftacc中。//ns = ((cyc << N) * NSEC_PER_SEC/freq) >> N
然后,[2]找到能够得到最好转换精度的mult,shift值对。
shift值,在符合32位整数时,越大越好。
解释如下,
tmp = (to << sft) / from
= (NSEC_PER_SEC << sft) /freq
其实,tmp即为mult
但是,可以有多个mult,shift对,但是只有一对是最佳的。
由式子[c]可以找到转化精度好的标准,
如果, mult>> (sftacc+x), x为大于1的整数,那么,显然,cyc的最大限将会减小,
如果, mult>> (sftacc – x), x 为大于1的整数,那么,显然,cyc在数比较小的情况下,经过
[c]转换的结果将为0,那么会影响转换精度。
所以,好的标准就是,
能够满足(mult>> sftacc == 0)的最大mult定位最终的mult,对应的shift也可以得到。