UAC(八)PPM 评估
USB sof统计方法
打时间戳方式
- ktime_get_raw_ns() Linux标准接口。
- meson_timestamp() Amlogic实现的硬件定时器接口。
sof打时间戳的时机
- 直接在SOF中断handle里打时间戳,然后将其保存到一个fifo里。
- 通过一个hrtimer,主动查询SOF中断寄存器查看是否有SOF包,如果有SOF包则打上时间戳保存到fifo里。
sof时间戳导出到应用层
通过seq file创建一个proc(/proc/sof_ts)文件,应用通过读取该文件来获取sof时间戳。
USB sof统计数据方案验证
中断handle里用ktime_get_raw_ns()打时间戳统计数据
数据:
| 样本数量 | 平均值(ns) | 最小值(ns) | 最大值(ns) | 方差 | 标准差 | | --- | --- | --- | --- | --- | --- | | 8192 | 125001.0327 | 94125 | 156125 | 2281694.791 | 1510.5279 |
sof间隔图表:
统计直方图(124-126us):
上图在124us-126us之间的概率分布,两头分别是低于124us和高于126us的概率。低于124us的概率为3.0%,高于126us的概率为2.8%.
统计直方图(120-130us):
上图将直方图统计口径放大到120us-130us,那么低于120us的概率为1.1%,高于130us的概率为1.2%.
通过以上数据来看,没有使用hrtimer捕获sof的必要,直接在中断handler里捕获得到的数据精度完全足够评估ppm了,整体数据分布符合高斯噪声分布曲线,可以配合一定的消除高斯噪声的滤波算法得到更为稳定的数据。
中断handle里用meson_timestamp()打时间戳统计数据
数据:
样本数量 | 平均值(ns) | 最小值(ns) | 最大值(ns) | 方差 | 标准差 |
---|---|---|---|---|---|
8192 | 125000.8546 | 111000 | 141000 | 1501036.994 | 1225.168 |
sof间隔图表
sof直方图:
由于meson_timestamp的精度只到us,因此只统计120us-130us之间的概率:如下:
其中低于120us的概率为1.2%,高于130us的概率为0.78%。
结论
- 统计方式可以直接在中断handle里打印时间戳
- 获取时间戳接口meson_timestamp()数据更稳定一些,但差别不大,这里直接采用Linux标准函数ktime_get_raw_ns()获取时间戳
滤波
前面确定了在sof中断handle里通过ktime_get_raw_ns()获取sof时间戳,然后将其保存到fifo里,应用层通过/proc/sof_ts文件读取fifo里的时间戳。下面是对数据进行处理的方法:
实验一:无中值滤波,卡尔曼滤波器err_integral与kalman_filter_err_integral对比,kalman参数Q=0.01,R=1
放大细节
滤波后数据仍然存在较大的波动,这个波动主要是由一些突变很大的值引入的,想要得到更加平滑的数据,可以先用中值滤波器将这些极值率除掉。
实验二:加入中值滤波器(err_integral与kalman_filter_err_integral和kalman_filter_err_integral对比,中值滤波窗口大小100个sample,kalman参数Q=0.01,R=1)
放大看细节
可以看到经过中值滤波器后毛刺基本上都去掉了,但是数据还是不够平滑,再加入karman滤波器后数据就很平滑了.
如下图所示是经过kalman滤波后得到的ppm值:
范围在7.56-7.64ppm,在经过一定的时间后kalman系数基本收敛完毕,数据量也累计到一定程度后ppm基本稳定在7.6-7.62ppm之间。
Issue
测试中发现一个问题,我们的USB一般跑在高速模式下,在高速模式下是每125us一个sof中断,也就是在开启sof中断后arm core会每秒多接收8000次usb控制器发来的中断,这个中断是非常频繁的,会频繁触发usb中断过于频繁的check的。 可以尝试在ISO传输的数据中断中打印时间戳,理论上这种方式存在更大的波动性,需要验证数据是否可用。
ISO中断统计
下图是以1ms为基准统计的iso中断累计误差:
下图是通过上图的kalman滤波曲线计算的ppm曲线:范围在6.1-6.5之间,其中波动最大的在于有一段连续时间的偏差游走,但是经过滤波后造成ppm的偏差也在0.5pp以内。
滤波和未滤波ppm值对比:
其实在这里可以看到,将统计时长拉到足够长,不采用滤波也是可以的,比如将统计时长拉长到10分钟以上,误差也基本上不会很大。采用滤波的算法的好处是基本上2分钟以内就可以得到很高精度的值了。
结论
在sof中断里获取时间戳pipeline更短,引入的误差更小,精度更高,但是开启sof中断后usb的中断会过于频繁,不利于usb的稳定性。作为对比,直接统计usb iso传输中断不会带来额外的中断开销,牺牲的是ppm评估精度。对于我们的公版来讲基本上都是采用±20ppm的晶振,波动在1个ppm以内基本上是可用接受的。
最终的算法
- 通过proc文件系统获取sof的timestamp
- 计算两个时间戳之间的间隔,期望是1ms
- 将该时间戳与1ms做差,进行积分,得到累计误差err_integral
- 对累计误差进行中值滤波得到median_filter_err_integral中值滤波后的累计误差
- 对中值滤波的结果进行kalman滤波得到kalman_filter_err_integral
数据验证
我们可用通过示波器或者频谱仪来测量晶振的真实ppm值,但是我们上面算法评估出的不是设备的真实ppm值,而是以pc的时钟为基准,device的时钟相对于pc时钟的ppm值。我们想要验证算法的精度就需要同时测量pc和device各自的ppm,然后再计算得到device相对于pc的相对ppm值。 由于pc的ppm我们不太好获取,因此这里通过测量两台device的ppm值,然后用我们的额算法评估出两个device的相对于pc的ppm值,看两则之间的差值是否相同来验证算法的准确性。这里通过频谱仪测量得到的两台设备的ppm值分别是:
DeviceA:24.000135Mhz,ppm=(24.000135 - 24)/24 * 1000000 = 5.62ppm DeviceB:23.999953Mhz,ppm = (23.999953 - 24)/24 * 1000000 = -1.9ppm
两个设备的ppm差值为:5.62+1.9 = 7.52ppm 我们算法评估得到的DeviceA相对于pc的ppm为7.7ppm。DeviceB相对于pc的ppm为1.6ppm,两者之间的差值为6.1ppm。 这里算法得到的ppm相较于频谱仪得到的数据差有1.42ppm。 我们把算法的时间线拉长DeviceA相对于pc的ppm基本在7.4-8.4之间来回震荡,DeviceB相对于pc的ppm在1.2-1.9之间来回震荡,而且ppm值会受温度随着时间的推移也会在一个小范围内来回震荡,因此评估的数据可以认为精度在2ppm以内。
最后
如果Device相对于pc的ppm在10以上是可以采取一定的补偿措施的,如果Device相对于pc的ppm在5以内,补偿的意义就不大了,客户软件可以通过评估ppm大小判断下范围,如果评估出来在10以上可以采取一些措施来补偿ppm,比如调节clk或者调节fifo。 对于A1,由于时钟分频导致的采样率不是整数,这个可以通过async机制来解决,ppm相对于clk的分频不准确性就太小了,没必要进行ppm评估。A4、A5分频得到的ppm是整数,可以用ppm estimate来评估并做一定的补偿。当然直接用async也可以。但是A4、A5用的CRG的usb控制器,端点数量只有3对,uac+FU+Async Feedback就把3对用完了,其他usb功能都得关闭了。需要根据需求来决定采用哪种方案了。
Tips
excel中方差函数:VARPA() excel中标准差函数:STDEVPA()
参考文献
《USB2.0 协议规范》
《UAC2 协议规范》