2400864_zh-CN

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

2400864_zh-CN

2400864_zh-CN

MCU-Link - 链路服务器 - 性能测试中注入不必要的周期计数

你好呀,

首先:所有代码都是由 Claude 代理编写的,因为我所有的工作都在 Obsidian 仓库中完成,让代理编写代码速度更快。  

我正在对 FRDM-MCXN947 进行周期精确计时测量,作为我的硕士论文的一部分,我在其中比较了一些实时操作系统的调度开销。我报告的每个数字都是两次 DWT->CYCCNT 读数的差值,因此测量结果必须能够重复到单个循环。事实并非如此,经过相当长时间的调查,我已将原因缩小到一种我无法从文档中确认的机制。我想请问一下我的解释是否正确,因为如果我的解释是错误的,那么我已经排除了所有其他可能性,就没有剩下的候选人了。

问题的简短版本是:当调试会话打开时,LinkServer 会定期读取 DHCSR 和 CPACR,以确定核心是否已停止运行。这两个都是私有外设总线内的 SCS 寄存器,因此访问是在内核内部进行的,永远不会出现在代码总线或系统总线上。我的假设是,这种访问仍然会与核心内部的指令获取和数据访问发生竞争,而这正是每次轮询落在测量窗口内时,我的测量都会消耗几个周期的原因。这个假设正确吗?


设置

该板为 FRDM-MCXN947,我只使用了 core0,实际测量频率为 150 MHz,而对照实验(详见下文)频率为 12 MHz。我通过板载 MCU-Link 和 LinkServer 进行调试,它通过 USB 批量传输 CMSIS-DAP v2,我已经尝试了 50 和 500 的 SWD 线时钟。用上市,不用发布配置传递了 --no-rtos 和 --semihost-port=-1,并且 liveWatch 已禁用,因此在核心运行时,IDE 不应该从目标读取任何内容。

被测代码从 RAM 的 0x2000_0000 处执行,这意味着它是通过系统总线获取的。缓存和内存纠错功能已关闭。图像进入 RAM 的方式还有第二种不同的变化:对于大多数工作,探针会直接将其加载到 RAM 中,但在一个实验中,我将图像存储在闪存中,并在启动时将其复制到 RAM 中,因为如果没有探针首先加载它,仅存在于 RAM 中的图像就无法启动。这种区别只对那一次实验有意义,我现在提一下是为了避免以后的设置产生歧义。

被测量的是一个空的计数循环,执行给定的次数,并通过在执行前后读取 CYCCNT 来计时。每项测量重复64次。


问题


两次进行相同的测量并不会得到相同的结果。在空循环中,64 个值分布在三到六个周期内;而在实际应用程序代码中,大约每十万次迭代中就有九次比其他所有迭代多出一到十个周期。偏差只会向上,不会向下,最小值完全稳定且可重复。最有启发性的是,价差会随着测量窗口长度的增加而增长,而不是每次测量调用都有固定成本,这已经排除了我的仪器中存在恒定开销的可能性。

以下是全部内容。每个单元格是空循环的 64 次测量;“异常值”统计了 64 次测量中有多少次高于第一次测量值,最小值和最大值以高于循环干净值的循环次数表示。

核心循环计数 SWD 50:异常值最小值最大值 SWD 500:异常值最小值最大值

核心 F循环每 64 次测量中的异常值最小抖动计数最大抖动计数异常值/64mea。最小抖动最大抖动
12 MHz1000+13+130+13+13
 10004+13+157+13+16
 1000024+13+1618+13+17
 10000055+13+2357+13+22
 100万56+40+6959+45+102
150 MHz1000+13+130+13+13
 10000+13+131+13+15
 100004+13+153+13+15
 10000013+13+1516+13+17
 100万53+13+2060+13+25

从这张表格中可以得出三点结论。首先,常数 +13 不是问题的一部分。在两种核心时钟和两种线速下,它在每个未饱和的单元格中都显示为最小值,它只是循环序言加上 CYCCNT 读取本身。因为它与核心时钟和探针都无关,所以我减去的是一个固定的偏置,而所有高于 +13 的值都是我实际追求的目标。

其次,也是最重要的一点,干扰遵循的是实际时钟时间,而不是执行的周期数。如果比较两个时钟在相同循环次数下的情况,12 MHz 的异常值始终比 150 MHz 的异常值多:在 1000 次迭代时,12 MHz 的异常值比 150 MHz 的异常值多 4 个,150 MHz 的异常值比 0 个;在 10000 次迭代时,12 MHz 的异常值比 150 MHz 的异常值多 7 个,150 MHz 的异常值比 1 个;在 10000 次迭代时,12 MHz 的异常值比 150 MHz 的异常值多 24 ...50 MHz 的异常值多 24 个,150 MHz 的异常值比 150 MHz 的异常值多 24 个,150 MHz两种情况下指令流完全相同,因此执行的周期数也相同,唯一的区别是 12 MHz 的运行时间比 12 MHz 的运行时间长 12.5 倍。

第三,SWD 线时钟没有任何作用。在上述十对匹配数据中,计数结果在两个方向上分散,并且保持在计数噪声范围内,因此即使线路速率变化十倍也不会影响它们。决定节奏的因素并非电线。


排除根本原因


我想坦白地说,很多可能性都是通过在会话打开的情况下实际从目标读取相关寄存器来排除的,而不是假设RESET默认值仍然有效。

在 SoC 端,CPU1、eDMA0、eDMA1 和 SmartDMA 都被禁用,RAM ECC 也已关闭,代码缓存也已关闭,因此没有其他任何东西会争用内存。在跟踪方面,ITM 已禁用,未启用激励端口,时间戳已关闭,跟踪暂停位已清除,ETM 也已关闭,这意味着没有任何东西会生成跟踪数据包,因此也没有任何东西会暂停以传递它们。在 DWT 中,PC 采样已关闭,异常跟踪已关闭,所有事件计数器均已关闭,监视点比较器未使用,性能监测也已关闭。TRCENA 当然是设置好的,但是无论是否受到干扰,都必须设置 TRCENA,因为 CYCCNT 依赖于它,所以它不可能是两者之间的区别。

仅从大小角度来看,中断和异常就被排除在外。任何对这一核心的例外情况都会花费二十五到五十个周期,而我的偏差是一到十个周期。没有小版本的例外情况,因此无论中断掩码如何规定,整个类都会被排除在外。出于类似的原因,断点也被弃用了:FPB 比较器在匹配之前不消耗任何资源,而匹配时会停止核心运行,而不是稍微延迟核心运行。

由于需要进行对照实验,被测代码已移除。空循环不包含任何与数据相关的工作,因此它本身不会发生变化,但它仍然会发生变化。所以问题出在平台上,而不是我所衡量的指标上。

这样看来,调试器内存读取是目前为止最有希望的解释,我想解释一下我为什么放弃了它,因为这是大多数人都会合理怀疑的机制。对 SRAM 的调试读取会离开内核,并且会与内核争夺总线端口,因此该机制可以完美地工作。问题是,这种情况并没有发生。IDE 执行的每次读取操作都会以 GDB m 数据包的形式传输,但在 gdbserver 日志中,从开始执行的 $vCont;c 到 32 秒后的主机中断之间,没有一个数据包。日志中读取的每一次内存都是在程序停止后才出现的,这只是 IDE 在核心停止后填充其视图的过程。同样的论点也驳斥了调试器正在读取 CYCCNT 本身这一更具吸引力的理论,该理论将直接与我的测量结果相冲突:它也会显示为 m 数据包,但事实并非如此,而且 liveWatch 无论如何都被禁用了。


它看起来像是


为了将探针与会话分离,我运行了相同的二进制文件,同时将探针物理连接到电脑上。
但是没有打开调试会话。这项实验需要在启动时进行闪存到内存的复制。
如前所述,由于仅 RAM 映像没有探测就无法加载。探测器仍在运行
两次运行都连接上了,所以它们之间的唯一区别是是否附加了会话。

如果没有调试会话,抖动就会消失。相同的二进制文件、相同的时钟、相同的启动路径。所以这不是……
探针已连接,但并非版本:探针只是连接上但没有发出任何信号。
交易无需任何费用,但附加会话又会恢复抖动。

那时我受到了干扰,这肯定是由会话引起的,而且会话的节奏是实时的。
而不是通过核心时钟,为此我已经排除了我能想到的所有机制。


为了找出 LinkServer 在核心运行时发送的内容,我使用 USBPcap 和 Wireshark 捕获了 MCU-Link 的 CMSIS-DAP 批量端点。两个 DAP_Transfer 请求持续重复,大约每隔 50 毫秒重复一次。

我应该立即对这个数字进行限定。轮询看起来不像是一个固定的计时器,因为主机似乎只有在收到前一个响应后才会发送下一个请求,这使得间隔至少部分是由事件驱动的——主机周转时间加上 USB 延迟加上主机自行插入的任何延迟。所以 50 毫秒是一个数量级数字,表示它重复的频率,而不是一个我想引用的周期,这也意味着我不能从速率中清晰地反推。

第一个请求同时读取了 DHCSR 和 CPACR。线路上的信息为 05 00 06 08 00 00 00 00 05 f0 ed 00 e0 0f 08 00 00 00 00 05 88 ed 00 e0 0f,解码后为:

# 请求解码数据目标访问

108DP 写入 SELECT0x00000000无(DP内部)
205AP 写 TAR0xE000EDF0 (DHCSR)无(AP内部)
30FAP 阅读 DRW1次阅读,PPB
408DP 写入 SELECT0x00000000无(DP内部)
505AP 写 TAR0xE000ED88(CPACR)无(AP内部)
60FAP 阅读 DRW1次阅读,PPB

答案是 DHCSR = 0x01100001,表示内核正在启用安全调试并执行指令,CPACR = 0x00F00003,表示 FPU 完全可访问。第二个请求是 05 00 03 08 00 00 00 00 05 f0 ed 00 e0 0f,它只是上面请求的前半部分,只询问核心是否已停止运行,而没有进行 FPU 检查。

因此,在我的测量窗口期间到达目标的全部流量为两次 PPB 读取,或者在较短的请求情况下为一次。SELECT 和 TAR 写入操作根本不会产生任何目标访问,因为它们分别是 DP 和 AP 内部的,只有两个 DRW 读取操作才会真正访问目标。我检查了传输计数与数据字计数,确认了该适配器上存在直接的值到寄存器映射,没有读取后移位,因此我确信哪个值属于哪个地址。

这两个地址都是 PPB 内部的 SCS 寄存器,并且都没有出现在 SoC 总线矩阵上。其中没有任何 SRAM 访问,也没有对核心寄存器文件的访问,因为没有任何东西会通过 DCRSR/DCRDR 密钥孔进行访问,这需要先停止核心运行。为了完整起见:捕获中确实出现了 S0 到 S31 和 FPSCR 的单独 71 次传输转储,但仅在核心在 stopAtSymbol 断点处停止时出现,而不是在运行期间出现。我的猜测是,CPACR 与 DHCSR 捆绑在一起,正是为了使适配器在内核停止运行的那一刻就已经知道浮空寄存器是否处于活动状态。


待检验的假设:


我目前认为正在发生的事情是这样的。请求通过 D-AHB 调试进入核心。
端口,然后由核心内部互连仲裁,以对抗核心自身的指令。
获取和数据访问。在核心内部解决并不意味着访问路径一定是这样的
虽然与执行过程脱节,但它仍然在共享的内部阶段与执行过程相遇。核心优先。
因此,在正常情况下,调试访问会等待,而核心程序会继续运行而不受影响。剩余成本
仅当调试节拍已在进行中且无法被抢占时才会出现,在这种情况下,核心
必须等到那个节拍结束。我观察到的正是这种等待:几个周期,总是如此。
向上,并且仅在民意调查恰好落入的测量窗口的一部分上。

问题:


  1. 我上面描述的机制是否正确?通过 D-AHB 调试端口到达的 DAP 发起的对 PPB 或 SCS 寄存器(例如 DHCSR 或 CPACR)的读取操作,是否会与 M33 内部正在运行的内核自身的访问操作发生冲突,从而导致 M33 停滞几个周期?或者,调试端口是否真的与 PPB 目标的执行路径不相干?如果是这样,我已经排除了所有可能的原因,但仍然找不到真正的原因。
  2. 这方面有任何书面记录吗?我有 Cortex-M33 TRM (100230_0100_07_en),可以看到其中的 D-AHB 和内部互连,但我没有找到任何关于在内核运行时对内核内部 PPB 空间进行调试访问的成本的说明。如果能提供指向正确章节的链接,或者任何关于 MCXN947 中 DAP 如何集成到该部件上的具体信息,将对我有很大的帮助。
  3. CYCCNT 会出现这样的停滞吗?换句话说,核心是否真的被阻塞,以至于这些是 CYCCNT 计算的真实执行周期,而不是 CYCCNT 受到其他途径干扰的周期?
  4. 有没有办法减慢或禁用 LinkServer 中的停止状态轮询?这样就能给我想要的明确确认:改变轮询率,看看异常值率是否随之改变。我一直没能找到相关的设置,任何能在核心运行时减少或停止周期性 DHCSR 读取的操作都适用于此测试。
  5. 如果该机制是真实存在的,那么对于该零件而言,在不附加任何测量步骤的情况下进行测量是否就是进行周期精确工作的正确做法?这就是我现在所做的,如果无法避免进行会话,我会取多个窗口中的最小值,并报告有多少窗口偏离了最小值以及偏离了多少。我想知道这是否是公认的答案,或者是否有官方支持的方法可以在实时会话中进行干净利落的测量。
核心与内存开发板Re: MCU-Link - Linkserver - Injection of unwanted cyclecounts at performance tests

你好@hms-isyu

感谢您详尽的分析。

很抱歉,这个问题超出了恩智浦MCU应用支持的通常范围。我可以解答有关 MCXN947、MCU-Link、SDK 和设备使用方面的问题。但是,您正在调查的行为涉及 Cortex-M33 调试架构和周期精确性能分析,这超出了我的专业领域。

对于有关在性能测量期间注入额外循环计数的问题,我建议联系 Arm 支持部门以获取进一步指导。

感谢您的理解。


BR

爱丽丝

Tags (1)
No ratings
Version history
Last update:
2 weeks ago
Updated by: