ARM cortex-r8 CP 15

arm cortexr8 获取cpu cycle clock的方法是什么?
在 ARM Cortex-R 系列里,可以用 ARM Performance Monitor Unit (PMU) 或 Generic Timer 来获取周期计数。

方法 1:使用 PMU Cycle Counter (CCNT)

Cortex-R8 内置 PMU (Performance Monitor Unit),其中有一个 Cycle Counter Register (PMCCNTR),它能直接计数 CPU cycle。
主要步骤:

启用 PMU

在 PMCR (Performance Monitor Control Register) 里设置 E 位,打开 PMU。

在 PMCNTENSET 里设置对应位,启用 cycle counter。

清零计数器(可选)

写 PMCCNTR 为 0。

读计数器

通过 MRC/MRS 指令读 PMCCNTR,得到 cycle 数。

代码示例(ARMv7-R/ARMv8-R 通用,需在特权模式):

static inline void enable_pmu(void) {
uint32_t value;

// Enable user-mode access to PMU (optional, if running in EL0)
asm volatile("MRC p15, 0, %0, c9, c14, 0" : "=r"(value));
value |= 1;
asm volatile("MCR p15, 0, %0, c9, c14, 0" :: "r"(value));

// Enable all counters
asm volatile("MRC p15, 0, %0, c9, c12, 0" : "=r"(value));
value |= 1;  // enable all
asm volatile("MCR p15, 0, %0, c9, c12, 0" :: "r"(value));

// Enable cycle counter
value = 1 << 31;
asm volatile("MCR p15, 0, %0, c9, c12, 1" :: "r"(value));

}

static inline uint32_t get_cyclecount(void) {
uint32_t value;
asm volatile(“MRC p15, 0, %0, c9, c13, 0” : “=r”(value));
return value;
}

调用:

enable_pmu();
uint32_t start = get_cyclecount();
// … code to measure …
uint32_t end = get_cyclecount();
uint32_t cycles = end - start;

方法 2:使用 Generic Timer (ARMv8-R 特性)

Cortex-R8 属于 ARMv8-R 架构,支持 Generic Timer,其中有一个 64-bit CNTVCT (virtual count register),通常以固定频率(不是 CPU clock,而是 CNTFRQ 指定的时钟)递增。

读取方法:

static inline uint64_t get_cntvct(void) {
uint64_t cnt;
asm volatile(“MRS %0, CNTVCT_EL0” : “=r”(cnt));
return cnt;
}

static inline uint32_t get_cntfrq(void) {
uint32_t frq;
asm volatile(“MRS %0, CNTFRQ_EL0” : “=r”(frq));
return frq; // Hz
}

注意:CNTVCT 并不是严格的 CPU cycle counter,而是固定频率计数器。但它常用于计时,因为不受 CPU 变频影响。

✅ 结论:

如果你要精确获取 CPU 周期 (cycle clock) → 用 PMU Cycle Counter (PMCCNTR)。

如果你只要一个稳定的 时间基准 → 用 Generic Timer CNTVCT_EL0。

在 Cortex-R8 上,PMU cycle counter (PMCCNTR) 获取到的 CPU cycle 是基于 CPU core clock (CPU pipeline 的输入时钟),也就是 处理器运行指令的时钟频率。

细节说明:

PMCCNTR 的递增速率

默认情况下,它每经过 一个 CPU 时钟周期 递增 1。

如果 PMCR.D 位(Divide by 64)被置位,那么计数器会以 CPU_clk / 64 的速率递增。

所以正常情况下就是 CPU 周期数,除非你启用了除64分频。

CPU core clock 与外部时钟

Cortex-R8 的 CPU clock 由 SoC 内部时钟树生成,通常来自 PLL。

这个频率不一定等于外设时钟,也可能和 CNTFRQ(Generic Timer 的基准频率)不同。

所以:

PMCCNTR → 跟随 CPU pipeline 时钟 (CPU frequency)。

CNTVCT (Generic Timer) → 跟随固定 reference clock (CNTFRQ),独立于 CPU。

验证方法
如果你不确定 CPU 的实际运行频率,可以:

先读 PMCCNTR,

再延时一段已知的时间(例如用 CNTVCT 计时 1 ms),

再次读 PMCCNTR,差值除以时间就是 CPU cycle 频率(Hz)。

✅ 总结:

PMCCNTR 反映 CPU 核心时钟 (core clock) 的周期数。

如果你的 SoC CPU 频率是 600 MHz,那么 PMCCNTR 每秒会递增 600M。

如果打开了 Divide by 64,则变为 600M / 64 ≈ 9.375M 每秒。

如何用 PMCCNTR + CNTVCT 自动测出 Cortex-R8 当前的 CPU 频率
ChatGPT 说:

可以的 👍。我们可以利用:

PMCCNTR:记录 CPU cycle 数。

CNTVCT + CNTFRQ:提供稳定的时间基准。

思路:

读 CNTFRQ → 得到 Generic Timer 的频率(Hz)。

读 CNTVCT(起始时间)、PMCCNTR(起始 cycles)。

等待一段时间(比如 CNTVCT 增加一段固定值)。

再读 CNTVCT(结束时间)、PMCCNTR(结束 cycles)。

根据 ΔPMCCNTR / Δ时间 = CPU 频率。

示例代码(ARMv8-R / Cortex-R8)
#include <stdint.h>

static inline void enable_pmu(void) {
uint32_t value;

// 允许 PMU 使用
asm volatile("MRC p15, 0, %0, c9, c12, 0" : "=r"(value));
value |= 1; // E = 1, 使能 PMU
asm volatile("MCR p15, 0, %0, c9, c12, 0" :: "r"(value));

// 使能 cycle counter
value = 1 << 31;
asm volatile("MCR p15, 0, %0, c9, c12, 1" :: "r"(value));

// 清零 cycle counter
asm volatile("MCR p15, 0, %0, c9, c13, 0" :: "r"(0));

}

static inline uint32_t get_cyclecount(void) {
uint32_t value;
asm volatile(“MRC p15, 0, %0, c9, c13, 0” : “=r”(value));
return value;
}

static inline uint64_t get_cntvct(void) {
uint64_t cnt;
asm volatile(“MRS %0, CNTVCT_EL0” : “=r”(cnt));
return cnt;
}

static inline uint32_t get_cntfrq(void) {
uint32_t frq;
asm volatile(“MRS %0, CNTFRQ_EL0” : “=r”(frq));
return frq;
}

// 计算 CPU 频率 (Hz)
uint64_t measure_cpu_freq(void) {
enable_pmu();

uint32_t freq_ref = get_cntfrq();  // Generic Timer 频率 (Hz)

uint64_t t1 = get_cntvct();
uint32_t c1 = get_cyclecount();

// 等待一段参考时间,比如 1/100 秒
uint64_t wait_ticks = freq_ref / 100; // ~10ms
uint64_t target = t1 + wait_ticks;
while (get_cntvct() < target);

uint64_t t2 = get_cntvct();
uint32_t c2 = get_cyclecount();

uint64_t delta_t = t2 - t1;        // Generic Timer ticks
uint64_t delta_c = (uint32_t)(c2 - c1);  // CPU cycles

// 换算 CPU Hz
uint64_t cpu_hz = delta_c * freq_ref / delta_t;
return cpu_hz;

}

使用示例
uint64_t cpu_hz = measure_cpu_freq();
printf(“CPU Frequency = %llu Hz\n”, cpu_hz);

⚠️ 注意:

PMCCNTR 只有 32-bit,如果 CPU >1GHz,长时间测量可能溢出,所以等待时间别太长。

如果你的 SoC 打开了 PMCR.D (divide by 64),记得再乘 64 才是实际 CPU cycle。

大部分 Cortex-M 系列 MCU 没有像 Cortex-A/R 那样的 PMU(Performance Monitor Unit,带 cycle counter 的寄存器组)。
但是,Cortex-M 内核一般都提供了一个叫做 DWT (Data Watchpoint and Trace Unit) 的调试功能,其中包含了 CYCCNT 寄存器,它能实现类似 PMU cycle counter 的功能。

SysTick Timer(替代方案)

所有 Cortex-M 内核都有 SysTick 定时器。

它基于 CPU clock 运行,可以当作一个“简易周期计数器”,不过是 递减计数器,而且通常是 24-bit。

精度比 DWT_CYCCNT 差一些,但在没有 DWT 的内核上可用。

总结

Cortex-M3/M4/M7/M33 等中高端 MCU → 有 DWT_CYCCNT,等价于 cycle counter。

Cortex-M0/M0+ 等低端 MCU → 没有 DWT,只能用 SysTick 来近似计时。

Cortex-M 系列没有完整的 PMU 事件统计功能(比如 cache miss 计数、branch 预测统计等),只能用 CYCCNT 来做 CPU cycle 计数。

u32value = __MRC(15, 0, 9, 13, 0); 和asm volatile(“MRC p15, 0, %0, c9, c13, 0” : “=r”(value)); 写法有何区别
ChatGPT 说:

好问题 👌,你提到的两种写法:

u32value = __MRC(15, 0, 9, 13, 0);

asm volatile(“MRC p15, 0, %0, c9, c13, 0” : “=r”(value));

实际上都在执行同一个 ARM CP15 协处理器寄存器访问指令:

MRC p15, 0, Rt, c9, c13, 0

只是写法层面有所不同:

  1. __MRC() 写法

这是 编译器内置 intrinsic(内建函数)。

通常由 ARMCC(ARM Compiler)、Keil、IAR 等编译器提供。

它会被编译器展开成对应的 MRC 指令。

优点:

可读性更高,直接表达“读 CP15 的寄存器”。

编译器能更好地做语义检查,移植性较好(只要支持 ARM intrinsic)。

缺点:

依赖编译器,某些 GCC 环境可能没有 __MRC。

  1. asm volatile(“MRC …”) 写法

这是 GCC 内联汇编(或 clang 内联汇编)。

直接告诉编译器发出 MRC 指令,并把结果放到 C 变量里。

优点:

完全可控,和编译器无关。

在 GCC/clang 等工具链里通用。

缺点:

可读性稍差,需要写寄存器名。

编译器对语义不了解,可能不如 intrinsic 优化友好。

  1. 生成的汇编比较

两种方式编译后生成的指令是一样的(都是 MRC p15, 0, rX, c9, c13, 0)。
区别只在 代码可移植性 & 可读性:

如果你写的是 Keil/ARMCC 工程 → 推荐用 __MRC。

如果你写的是 GCC 工程(如裸机、Linux 内核) → 推荐 asm volatile(“MRC …”)。

✅ 总结

本质一样:两者都会生成相同的 MRC 指令。

差异:__MRC 是编译器内置函数,更高层;asm volatile 是手写汇编,更通用

ARM CP15 协处理器是什么?位置在哪里?作用是什么?为什么设计这个协处理器
ChatGPT 说:

好问题 👍,这涉及 ARM 经典架构里一个很重要的东西。

  1. CP15 协处理器是什么?

CP15,全称 System Control Coprocessor(系统控制协处理器)。

它是 ARMv4/v5/v6/v7(特别是 Cortex-A/R)架构里一个 专门用于系统控制和配置的协处理器。

在汇编里你常看到的 MRC p15,…、MCR p15,… 就是对 CP15 的访问。

  1. 位置在哪里?

CP15 不是外部芯片,而是 ARM CPU 内核里的一部分硬件单元。

它和主流水线、MMU、Cache 控制逻辑等紧密结合。

程序员不能直接通过地址总线访问 CP15(它不在 memory map 中),只能通过专门的指令(MRC/MCR)读写。

  1. 作用是什么?

CP15 主要提供系统级控制和状态寄存器,常见功能包括:

MMU(Memory Management Unit)配置

页表基地址(TTBR)

域访问控制

内存属性(cacheable/bufferable 等)

Cache/TLB 管理

开启/关闭 I-Cache / D-Cache

清空 / 失效 TLB / Cache line

CPU 系统控制

启用/禁用 MMU、Cache、Branch Prediction

异常向量表基地址

性能监控 (PMU)

Cycle counter

Event counters

安全与调试功能

控制 TrustZone

调试寄存器

总之,CP15 是 ARM 处理器的大管家,负责系统层面的控制,而不是普通的通用寄存器运算。

  1. 为什么设计成“协处理器”?

历史原因 + 架构扩展性:

历史背景:
ARM 早期架构支持最多 16 个“协处理器”(CP0~CP15),CPU 主核通过 MCR/MRC 指令和协处理器交互。

CP0:通常留作浮点协处理器 (FPU)。

CP14:调试相关。

CP15:固定分配为系统控制协处理器。

优势:

指令集简单化:不用为系统寄存器设计新的 ARM 指令,而是用通用的 MCR/MRC 接口。

扩展性强:厂商可以在 CP10、CP11 挂载 FPU/NEON,在 CP14 挂调试逻辑,不影响主核指令集。

权限控制:访问 CP15 通常需要特权模式(Supervisor/Hypervisor),保证用户态程序无法随意改 MMU/Cache。

  1. 现状

在 ARMv8-A (AArch64) 里,协处理器机制被弱化,CP15 寄存器统一并入系统寄存器空间,通过 MRS/MSR 指令访问,而不再用 MRC/MCR。

但是在 ARMv7-A/R 和之前的 32-bit ARM 中,CP15 依然是系统控制的核心。

✅ 一句话总结:
CP15 = ARM CPU 内部的“系统控制协处理器”,负责 MMU/Cache/异常向量/性能监控等全局控制。设计成协处理器是为了扩展性和架构简洁性。
在这里插入图片描述
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值