【ARM v8】如何在ARM上实现x86的rdtsc()函数

这篇具有很好参考价值的文章主要介绍了【ARM v8】如何在ARM上实现x86的rdtsc()函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。


博客内容主要围绕:
       5G/6G协议讲解
       算力网络讲解(云计算,边缘计算,端计算)
       高级C语言讲解
       Rust语言讲解



如何在ARM上实现x86的rdtsc()函数


【ARM v8】如何在ARM上实现x86的rdtsc()函数,ARMv8,arm开发

一、使用ARMv8提供的独立定时器CNTVCT_EL0

       System counter是Arm64下独立于CPU core的计数器,在系统上电时,会给此计数器设置固定的频率。一个映射System counter计数器内容的寄存器为CNTVCT_EL0,可在用户态下读取此寄存器获取counter值。而CNTFRQ_EL0保存的是counter的频率值(详细内容参考《【ARMv8】通用定时器总结》)。通过下面的函数实现获取counter值及频率值:

static inline uint64_t 
arm64_cntvct(void) 
{   
	uint64_t tsc;   
    asm volatile("mrs %0, cntvct_el0" : "=r" (tsc));   
    return tsc; 
} 

static inline uint64_t 
arm64_cntfrq(void)
{   
	uint64_t freq;   
	asm volatile("mrs %0, cntfrq_el0" : "=r" (freq));   
    return freq; 
} 

static inline uint64_t 
rdtsc(void) 
{
	return arm64_cntvct();
}

但是System counter的精度从Armv8.0到Armv8.5,范围通常在1-50MHz;从Armv8.6开始,以1GHz的固定频率递增。虽然1GHz的频率已经足够高了,但是还是达不到CPU cycle级别的精度


二、使用ARMv8的PMU计数器PMCCNTR_EL0

       在ARMv8中,有Performance Monitors Control Register系列寄存器,其中PMCCNTR_EL0就类似于x86的TSC寄存器。但是如果想在用户态访问这些寄存器,需要在内核代码中开启PMU用户态访问开关。

2.1 关键寄存器介绍

PMCCNTR_EL0(Performance Monitors Cycle Count Register)

保存了处理器周期计数器的值,其结构如下:
【ARM v8】如何在ARM上实现x86的rdtsc()函数,ARMv8,arm开发

PMCR_EL0(Performance Monitors Control Register)

PMU配置寄存器,其结果如下:

【ARM v8】如何在ARM上实现x86的rdtsc()函数,ARMv8,arm开发
其中和我们关系密切的几个参数含义:

  • LC:设置为1,表示开启64bit的周期计数器;否则,使用32bit的计数器(32bit的已经摒弃);
  • D:设置为1,表示每64个时钟周期,计时器累加一次(已经摒弃);否则,每个时钟周期计数器累加一次;
  • C:设置为1,表示重置计数器;
  • E:设置为1,表示开启计数器PMCCNTR_EL0;

PMUSERENR_EL0(Performance Monitors User Enable Register)

用于开启或关闭用户态下是否可以访问PMU寄存器,相关结构如下:

【ARM v8】如何在ARM上实现x86的rdtsc()函数,ARMv8,arm开发
其中和我们关系密切的几个参数含义:

  • ER:设置为1,表示用户态下可以读写PMU寄存器;否则不可以读写;
  • EN:设置为1,表示用户态软件可以访问所有PMU特定的寄存器;

PMCNTENCLR_EL0(Performance Monitors Count Enable Clear register)

设置启用的计数器和事件计数器,相关结构如下:

【ARM v8】如何在ARM上实现x86的rdtsc()函数,ARMv8,arm开发
其中和我们关系密切的几个参数含义:

  • C:设置为1,表示启用PMCCNTR_EL0计数器;

2.2 内核使能代码

/*                                                                             
 * Enable user-mode ARM performance counter access.                            
 */                                                                           
#include <linux/kernel.h>                                                      
#include <linux/module.h>                                                      
#include <linux/smp.h>                                                         
                                                                                                                                                       
#define ARMV8_PMCR_MASK         0x3f                                                                    
#define ARMV8_PMCR_E            (1 << 0) /* Enable all counters */                                      
#define ARMV8_PMCR_P            (1 << 1) /* Reset all counters */                                       
#define ARMV8_PMCR_C            (1 << 2) /* Cycle counter reset */                                      
#define ARMV8_PMCR_D            (1 << 3) /* CCNT counts every 64th cpu cycle */                         
#define ARMV8_PMCR_X            (1 << 4) /* Export to ETM */                                            
#define ARMV8_PMCR_DP           (1 << 5) /* Disable CCNT if non-invasive debug*/                        
#define ARMV8_PMCR_LC           (1 << 6) /* Cycle Counter 64bit overflow*/
#define ARMV8_PMCR_N_SHIFT      11       /* Number of counters supported */                             
#define ARMV8_PMCR_N_MASK       0x1f                                                                    
                                                                                                         
#define ARMV8_PMUSERENR_EN_EL0  (1 << 0) /* EL0 access enable */                                        
#define ARMV8_PMUSERENR_CR      (1 << 2) /* Cycle counter read enable */                                
#define ARMV8_PMUSERENR_ER      (1 << 3) /* Event counter read enable */                                
                                                                                                         
static inline u32 armv8pmu_pmcr_read(void)                                                              
{                                                                                                       
        u64 val=0;                                                                                      
        asm volatile("mrs %0, pmcr_el0" : "=r" (val));                                                  
        return (u32)val;                                                                                
}                                                                                                       
static inline void armv8pmu_pmcr_write(u32 val)                                                         
{                                                                                                       
        val &= ARMV8_PMCR_MASK;                                                                         
        isb();                                                                                          
        asm volatile("msr pmcr_el0, %0" : : "r" ((u64)val));                                            
}       
                                                                                                         
static void                                                                                            
enable_cpu_counters(void* data)                                                                         
{                                                                                                       
    u32 val=0;                                                         
    asm volatile("msr pmuserenr_el0, %0" : : "r"(0xf));                                                   
    asm volatile("msr PMCNTENSET_EL0, %0" :: "r" ((u32)(1<<31)));
    armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMCR_E|ARMV8_PMCR_LC);   
    printk("\nCPU:%d ", smp_processor_id());
}                                                                                                       
                                                                                                         
static void                                                                                            
disable_cpu_counters(void* data)                                                                        
{                                                                                                                                                                                                   
    printk(KERN_INFO "\ndisabling user-mode PMU access on CPU #%d",                       
    smp_processor_id());                                                                                   
                                                                                                         
    /* Program PMU and disable all counters */                                                            
    armv8pmu_pmcr_write(armv8pmu_pmcr_read() |~ARMV8_PMCR_E);                                              
    asm volatile("msr pmuserenr_el0, %0" : : "r"((u64)0));                                                                                                                                                
}                                                                                                       
                                                                                                         
static int __init                                                                                       
init(void)                                                                                              
{                                                                       
	isb();
	on_each_cpu(enable_cpu_counters, NULL, 1);                                                             
    printk(KERN_INFO "Enable Access PMU Initialized");                                                       
    return 0;                                                                                              
}                                                                                                       
                                                                                                         
static void __exit                                                                                      
fini(void)                                                                                              
{                                                                                                       
    on_each_cpu(disable_cpu_counters, NULL, 1);                                                            
    printk(KERN_INFO "Access PMU Disabled");                                                          
}                                                                                                       
                                                                                                         
module_init(init);                                                                                      
module_exit(fini);
module_license("GPL");

2.3 用户态代码

#include <stdio.h>

#define u64 unsigned long long
#define isb()       asm volatile("isb" : : : "memory")
 
static inline u64 arch_counter_get_cntpct(void)
{
    u64 cval;
    isb();
    asm volatile("mrs %0, PMCCNTR_EL0" : "+r"(cval));
    return cval;
}

2.4 测试时遇到的问题

可能有同学会用下面的代码测试定时精度,

int main()
{
	u64 begin,end;
	
	begin = arch_counter_get_cntpct();
	sleep(1);
	end= arch_counter_get_cntpct();
	
	printf("The count is %llu.\n",end-begin);

	return 0;
}

但是会发现使用统计的计数值与CPU当前的始终频率计算后,时间不是1s。这是因为Linux的省电功能导致的,sleep会使当前进程让出CPU,如果此时CPU任务队列中没有任务,就会进入低功耗(例如,WFI)甚至offline,如果进入上述状态PMU计数器就会停止计数,导致计数值不准确。

毕竟PMU是为调式使用的,如果此时CPU没有任务,也确实没有必要继续统计了。所以使用PMU寄存器计数是,不应该有主动让出CPU的行为,可能会导致计数不准确。

可以尝试关闭省电模式:

echo 1 > /sys/devices/system/cpu/cpu<X>/cpuidle/state<Y>/disable


【ARM v8】如何在ARM上实现x86的rdtsc()函数,ARMv8,arm开发文章来源地址https://www.toymoban.com/news/detail-744228.html

到了这里,关于【ARM v8】如何在ARM上实现x86的rdtsc()函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RISC-V是如何与X86、ARM三分天下

    目录 1.行业CPU指令集格局 2.汽车中的RISC-V进展 2.1 国际进展 2.2 国内进展 3.小结 2023年3月2日,在平头哥牵头举办的玄铁RISC-V生态大会上,工程院院士倪光南表示,基于RISC-V模块化、可扩展、容易定制、不受垄断制约等优势,成为了中国CPU领域最受欢迎的架构。 那么RISC-V到底是

    2024年01月15日
    浏览(37)
  • 带wiringPi库的交叉编译 ---宿主机x86Ubuntu,目标机ARMv8 aarch64(香橙派)

    带wiringPi库的交叉编译如何进行 先交叉编译wiringPi库,编译出的库适合香橙派,这时候交叉编译可执行程序的平台和链接库的格式也是正确的 ,然后通过-I和-L来指定链接的wiringPi库的头文件和库的位置,但是现在还没有学习过,后面学了补上 此时如果把wiringPi库拿到Ubuntu上进

    2024年02月15日
    浏览(36)
  • X86和arm的区别

    硬件上的区别 x86 系统中的硬件组件(如声卡、显卡、内存、存储器和 CPU)都是相互独立的。大多数组件都有单独的芯片,称为控制器。我们可以对这些组件进行更改或扩展,而不会影响连接性或整个硬件平台。 ARM 处理器没有单独的 CPU。相反,处理单元与其他硬件控制器位

    2024年02月03日
    浏览(32)
  • 【ARM架构】ARMv8-A 系统中的安全架构概述

    一个安全或可信的操作系统保护着系统中敏感的信息,例如,可以保护用户存储的密码,信用卡等认证信息免受攻击。 安全由以下原则定义: 保密性:保护设备上的敏感信息,防止未经授权的访问。有以下几种方法可以做到,比如密码和加密密钥。 完整性:使用公钥来保护

    2024年02月21日
    浏览(36)
  • x86 平台运行 arm 的方法

    参考: https://github.com/multiarch/qemu-user-static 核心是使用 binfmt_misc 设定运行 arm 的默认程序为 qemu-aarch64-static 1.先下载 arm64 即 aarch64 的运行文件 2.设置默认打开方式, 即设置 binfmt_misc 以支持 arm64 程序的运行 # --reset 会删除同名条目后重新设置 3.测试 4.不用映射qemu-*-static也可以使用

    2024年02月12日
    浏览(207)
  • X86、X64和ARM

    X86架构和X64架构(也称为x86-64、AMD64或Intel 64)都是计算机处理器架构的名称,它们都属于x86家族的一部分。 这些架构主要用于描述计算机中处理器的指令集和寻址能力。   X86指的是支持32位的指令集架构处理器, 最初由英特尔在1978年发布的 intel 8086,这个系统主要为X86结尾

    2024年02月15日
    浏览(29)
  • x86 和 x64 arm的区别

    x86和x64是基于英特尔x86架构的复杂指令集架构(ISA),而ARM是一种精简指令集架构。 假设我们现在要开发一个cpu,就好比说我们去修建一栋楼,开发商会先将图纸设计好。设计好之后由施工单位按照设计图去建造。在建造的这个过程中,施工方是要按照国家一定的规范来设计

    2024年01月19日
    浏览(33)
  • CPU 架构:ARM 和 x86 架构区别

    计算机有两种主要的 CPU 架构可供选择。Windows PC 通常建立在 Intel 和 AMD 使用的 x86 平台上,而 Apple 的计算机则使用该公司自己的基于 ARM 架构的 M1 和 M2 处理器。 这些方法之间存在差异,并且对性能的意义具有重大影响。 x86 和 ARM 处理器平台做相同的事情,但它们以完全不

    2024年04月27日
    浏览(32)
  • 『ARM』和『x86』处理器架构解析指南

    如果问大家是否知道 CPU,我相信不会得到否定的答案,但是如果继续问大家是否了解 ARM 和 X86 架构 ,他们的区别又是什么,相信 可能部分人就会哑口无言 了 目前随着深度学习、高性能计算、NLP、AIGC、GLM、AGI 的技术迭代,助力大模型快速发展,对于 多元算力结合(CPU+GP

    2024年02月08日
    浏览(43)
  • docker在x86平台下载arm的镜像

    6、进入该arm版本的详细页面,在该页面的上方有本版本镜像的sha校验值。 7、复制该值,使用如下命令在x86平台上进行下载即可。 docker pull nginx:latest@sha256:687e0e4a235ee770533f6c25fb5791b14d7b6aa603ba4ed724abbd2ed51ee11a 8、完成下载,如图:

    2024年02月12日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包