使用 DWT CYCCNT 在 STM32MP1 中进行精确时间测量
Precise Time Measurement in STM32MP1 with DWT CYCCNT
我在生产模式下使用 OSD32MP1(基于 STM32MP157c),在 Core A7 上使用 OpenSTLinux,在 M4 上使用 FreeRTOS。其中一项任务是以非常高的速度非常精确地为 M4 获取的 ADC 数据添加时间戳(认为它是纳秒到微秒的数量级)。请注意,只有测量之间的时间差很重要。
片上 RTC 可用(它分配给 A7,但 M4 可以访问寄存器)。然而,亚秒级精度约为 0.003 秒(PREDIV_S 为 255 - 详情请参阅 Reference Manual),所以它不够好。
, this and this Whosebug 帖子导致使用 DWT_CYCCNT 即 CPU 循环计数器来测量时间差。相关部分代码如下:
在 M4 侧:
typedef struct tTimeStamp
{
uint32_t nCPUFreq;
uint32_t nCPUCycles;
...
}tTimeStamp;
...
tTimeStamp oTimeStamp;
...
oTimeStamp.nCPUCycles = DWT->CYCCNT;
oTimeStamp.nCPUFreq = HAL_RCC_GetSystemCoreClockFreq();
最后 2 条语句在读取 ADC 值之前在 FreeRTOS 任务中运行。时间戳和其他数据一起交给A7。
在 A7 侧(假设在时间 T0 有 tTimeStamp,然后在时间 T1 有 tTimeStamp):
// Second to NanoSecond Conversion
#define SECTONS 1000000000
...
float ComputeTimeDiffNS(tTimeStamp oTS0, tTimeStamp oTS1)
{
// to avoid reporting time diff at t0
// and in case CPU frequency changes
if (oTS0.nCPUFreq != oTS1.nCPUFreq)
return -1;
// in case of counter overflow
if (oTS0.nCPUCycles > oTS1.nCPUCycles)
{
float fCyclesDiff = float(UINT32_MAX- oTS0.nCPUCycles + oTS1.nCPUCycles);
return fCyclesDiff * SECTONS / float(oTS0.nCPUFreq) / 2;
}
// base case
else
{
float fCyclesDiff = float(oTS1.nCPUCycles - oTS0.nCPUCycles);
return fCyclesDiff * SECTONS / float(oTS0.nCPUFreq);
}
}
- 这是使用 DWT->CYCCNT 和 HAL_RCC_GetSystemCoreClockFreq() 测量非常精确的时间差的正确方法吗?有没有更好更精确的方法?
- 上述方法给了我两倍的时间。在读取 DWT->CYCCNT 时,我还切换一个引脚并使用逻辑分析仪测量切换之间的间隔。假设这次 tActual 是 2ms。然而上面的公式即 CPU_Cycles / CPU_Frequency returns tMeasured = 4ms.
这似乎暗示公式应该是CPU_Cycles / (2*CPU_Frequency)。所以要么频率需要加倍,要么周期需要减半。
在读数中,nCPUFreq 为 208878528(每个 Reference Manual 允许的最大值为 209000000),因此这必须是正确的并且不能乘以 2。
CPU_Cycles 可能被除以 2,但这不会表明 CPU 每个时钟周期要经过 2 个周期吗?这可能吗(CPU 在上升沿和下降沿都循环??)
TLDR:M4 和 A7 之间的数据包丢弃。
嗨,在 PatrikF at ST Forum 的大量帮助下,我最终解决了自己的问题,他们建议 DWT 应该按照 ARM 指定的方式工作。
原来问题是 M4 和 A7 之间非常一致的数据包丢失,恰好是 2 倍,导致 CYCCNT 的两倍。我浪费了太多时间寻找错误的方向,但在一天结束时,我了解了数据包计数器的重要性。
请注意,Partrik 还在 STM 的高精度计数器上添加了一些 recommendations:
Maybe using STGENR is another option independant of Cortex-M4
frequency.
STGEN is running by default on HSI 64MHz which give you a resolution
of about 15ns, but HSI is not an high precision oscillator (+/-1%).
alternatively, using STGEN on HSE 24MHz which is more precise (few ten
of ppm) but give a resolution of about 40ns.
See also this post:
https://community.st.com/s/question/0D53W00000oXAqhSAG/how-can-i-get-access-to-m4-timers-from-a7-linux-is-it-possible-
As STGEN is read using AXI bus thru async buses from Cortex-m4, it
must suffer some ns of additional latency.
我在生产模式下使用 OSD32MP1(基于 STM32MP157c),在 Core A7 上使用 OpenSTLinux,在 M4 上使用 FreeRTOS。其中一项任务是以非常高的速度非常精确地为 M4 获取的 ADC 数据添加时间戳(认为它是纳秒到微秒的数量级)。请注意,只有测量之间的时间差很重要。
片上 RTC 可用(它分配给 A7,但 M4 可以访问寄存器)。然而,亚秒级精度约为 0.003 秒(PREDIV_S 为 255 - 详情请参阅 Reference Manual),所以它不够好。
在 M4 侧:
typedef struct tTimeStamp
{
uint32_t nCPUFreq;
uint32_t nCPUCycles;
...
}tTimeStamp;
...
tTimeStamp oTimeStamp;
...
oTimeStamp.nCPUCycles = DWT->CYCCNT;
oTimeStamp.nCPUFreq = HAL_RCC_GetSystemCoreClockFreq();
最后 2 条语句在读取 ADC 值之前在 FreeRTOS 任务中运行。时间戳和其他数据一起交给A7。
在 A7 侧(假设在时间 T0 有 tTimeStamp,然后在时间 T1 有 tTimeStamp):
// Second to NanoSecond Conversion
#define SECTONS 1000000000
...
float ComputeTimeDiffNS(tTimeStamp oTS0, tTimeStamp oTS1)
{
// to avoid reporting time diff at t0
// and in case CPU frequency changes
if (oTS0.nCPUFreq != oTS1.nCPUFreq)
return -1;
// in case of counter overflow
if (oTS0.nCPUCycles > oTS1.nCPUCycles)
{
float fCyclesDiff = float(UINT32_MAX- oTS0.nCPUCycles + oTS1.nCPUCycles);
return fCyclesDiff * SECTONS / float(oTS0.nCPUFreq) / 2;
}
// base case
else
{
float fCyclesDiff = float(oTS1.nCPUCycles - oTS0.nCPUCycles);
return fCyclesDiff * SECTONS / float(oTS0.nCPUFreq);
}
}
- 这是使用 DWT->CYCCNT 和 HAL_RCC_GetSystemCoreClockFreq() 测量非常精确的时间差的正确方法吗?有没有更好更精确的方法?
- 上述方法给了我两倍的时间。在读取 DWT->CYCCNT 时,我还切换一个引脚并使用逻辑分析仪测量切换之间的间隔。假设这次 tActual 是 2ms。然而上面的公式即 CPU_Cycles / CPU_Frequency returns tMeasured = 4ms.
这似乎暗示公式应该是CPU_Cycles / (2*CPU_Frequency)。所以要么频率需要加倍,要么周期需要减半。
在读数中,nCPUFreq 为 208878528(每个 Reference Manual 允许的最大值为 209000000),因此这必须是正确的并且不能乘以 2。
CPU_Cycles 可能被除以 2,但这不会表明 CPU 每个时钟周期要经过 2 个周期吗?这可能吗(CPU 在上升沿和下降沿都循环??)
TLDR:M4 和 A7 之间的数据包丢弃。
嗨,在 PatrikF at ST Forum 的大量帮助下,我最终解决了自己的问题,他们建议 DWT 应该按照 ARM 指定的方式工作。
原来问题是 M4 和 A7 之间非常一致的数据包丢失,恰好是 2 倍,导致 CYCCNT 的两倍。我浪费了太多时间寻找错误的方向,但在一天结束时,我了解了数据包计数器的重要性。
请注意,Partrik 还在 STM 的高精度计数器上添加了一些 recommendations:
Maybe using STGENR is another option independant of Cortex-M4 frequency.
STGEN is running by default on HSI 64MHz which give you a resolution of about 15ns, but HSI is not an high precision oscillator (+/-1%).
alternatively, using STGEN on HSE 24MHz which is more precise (few ten of ppm) but give a resolution of about 40ns.
See also this post: https://community.st.com/s/question/0D53W00000oXAqhSAG/how-can-i-get-access-to-m4-timers-from-a7-linux-is-it-possible-
As STGEN is read using AXI bus thru async buses from Cortex-m4, it must suffer some ns of additional latency.