USB sof统计方法

打时间戳方式

  1. ktime_get_raw_ns() Linux标准接口。
  2. meson_timestamp() Amlogic实现的硬件定时器接口。

sof打时间戳的时机

  1. 直接在SOF中断handle里打时间戳,然后将其保存到一个fifo里。
  2. 通过一个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以内基本上是可用接受的。

最终的算法

  1. 通过proc文件系统获取sof的timestamp
  2. 计算两个时间戳之间的间隔,期望是1ms
  3. 将该时间戳与1ms做差,进行积分,得到累计误差err_integral
  4. 对累计误差进行中值滤波得到median_filter_err_integral中值滤波后的累计误差
  5. 对中值滤波的结果进行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 协议规范》