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

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

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


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

文章目录

  • 如何在ARM上实现x86的rdtsc()函数
  • 一、使用ARMv8提供的独立定时器CNTVCT_EL0
  • 二、使用ARMv8的PMU计数器PMCCNTR_EL0
    • 2.1 关键寄存器介绍
      • PMCCNTR_EL0(Performance Monitors Cycle Count Register)
      • PMCR_EL0(Performance Monitors Control Register)
      • PMUSERENR_EL0(Performance Monitors User Enable Register)
      • PMCNTENCLR_EL0(Performance Monitors Count Enable Clear register)
    • 2.2 内核使能代码
    • 2.3 用户态代码
    • 2.4 测试时遇到的问题



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


在这里插入图片描述

一、使用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)

保存了处理器周期计数器的值,其结构如下:
在这里插入图片描述

PMCR_EL0(Performance Monitors Control Register)

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

在这里插入图片描述
其中和我们关系密切的几个参数含义:

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

PMUSERENR_EL0(Performance Monitors User Enable Register)

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

在这里插入图片描述
其中和我们关系密切的几个参数含义:

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

PMCNTENCLR_EL0(Performance Monitors Count Enable Clear register)

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

在这里插入图片描述
其中和我们关系密切的几个参数含义:

  • 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


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/82683.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Laravel 框架构造器的排序分组.子查询 JOIN 查询 构造器的增删改 ⑦

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…

文件上传xxx

本地保存文件 将文件保存到服务器本地硬盘中 max-request-size 多个文件总大小不能大于100M PostMapping("/upload")public Result upload(String username,Integer age,MultipartFile image) throws IOException {log.info("用户名:{},牛叔&#xff1a;{},文件…

ARM--day4(电灯实验、分析RCC、GPIO控制器,PMOS管、NMOS管的基本原理)

电灯实验代码&#xff1a; .text .global _start _start: /**********LED1点灯**************/RCC_INIT:1.使能GPIOE组控制器&#xff0c;通过RCC_AHB4ENSETR寄存器设置第&#xff3b;5:4&#xff3d;位写&#xff11;---->0x50000A28[4]1ldr r0,0x50000A28ldr r1,[r0]orr…

H5: div与textarea输入框的交互(聚焦、失去焦点、键盘收起)

简介 本文是基于 VUE3TS 的代码说明。 记录自己遇到的 div 与 textarea 输入框交互的聚焦、失去焦点、键盘收起、表情插入不失去焦点的需求实现。 需求分析 1.固定在页面底部&#xff1b; 2.默认显示纯文字与发送图标按钮&#xff0c;文字超出的省略显示&#xff1b; 3.点击…

无涯教程-Perl - syswrite函数

描述 此函数尝试将SCALAR中的LENGTH个字节写入与FILEHANDLE相关的文件。如果指定了OFFSET,则从提供的SCALAR中的OFFSET字节中读取信息。该函数使用C /操作系统的write()函数,该函数绕过普通缓冲。 语法 以下是此函数的简单语法- syswrite FILEHANDLE, SCALAR, LENGTH, OFFS…

【2023新教程】树莓派定时自动拍照并上传腾讯云对象存储COS

1 换源 仅适用于Release date: May 3rd 2023、Debian version: 11 (bullseye)这个树莓派OS版本&#xff0c;其他版本不保证有效。 首先使用如下命令&#xff0c;查看自己树莓派的架构。 uname -a结果如下&#xff1a; 如果红圈处显示为aarch64&#xff0c;使用命令sudo na…

个性化定制界面 VS 极简版原装界面:你更喜欢哪一个?为什么?

文章目录 每日一句正能量前言自己的喜好使用这种界面的原因这种界面对你的影响后记 每日一句正能量 不管昨天、今天、明天&#xff0c;能豁然开朗就是最美好的一天。 前言 个性化定制界面和极简版原装界面&#xff0c;哪一个你用起来更加顺手呢&#xff0c;相比之下你更喜欢哪一…

NFTScan NFT API 在 DID Protocol 开发中的应用

自互联网发展以来&#xff0c;Web2.0 时代产生了网络社会&#xff0c;社会已经不再局限于地理边界&#xff0c;而 Web 3.0 引入了去中心化的理念&#xff0c;强调个体数据隐私和可信互操作性。在这个新的时代中&#xff0c;去中心化身份&#xff08;Decentralized Identifier 即…

爬虫逆向实战(十八)--某得科技登录

一、数据接口分析 主页地址&#xff1a;某得科技 1、抓包 通过抓包可以发现数据接口是AjaxLogin 2、判断是否有加密参数 请求参数是否加密&#xff1f; 查看“载荷”模块可以发现有一个password加密参数和一个__RequestVerificationToken 请求头是否加密&#xff1f; 无…

FifthOne:计算机视觉提示和技巧

一、说明 欢迎来到我们每周的FiftyOne提示和技巧博客&#xff0c;我们回顾了最近在Slack&#xff0c;GitHub&#xff0c;Stack Overflow和Reddit上弹出的问题和答案。FiftyOne是一个开源机器学习工具集&#xff0c;使数据科学团队能够通过帮助他们策划高质量数据集、评估模型、…

NVIDIA Jetson 项目:机器人足球比赛

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑器的3D应用场景 事实上&#xff0c;整个比赛都致力于这个想法。RoboCup小型联盟&#xff08;SSL&#xff09;视觉停电技术挑战赛鼓励团队“探索本地传感和处理&#xff0c;而不是非车载计算机和全球摄像机感知环境的…

数据结构 - 语句的频度和时间复杂度

一、语句频度&#xff1a; 算法的运行时间 Σ每条语句的执行次数X该语句执行一次所需的时间每条语句的执行次数&#xff0c;也称为&#xff1a;语句的频度结合上面两点&#xff0c;可知&#xff1a;算法的运行时间 Σ每条语句的频度X该语句执行一次所需的时间 二、语句执行…

element时间选择器el-date-picter使用disabledDate指定禁用的日期

需要的效果 <el-date-pickerclass"selectstyle"v-model"year"value-format"yyyy"type"year":picker-options"disabledCli"placeholder"选择年"> </el-date-picker>data() {return {disabledCli: {/…

Android SDK 上手指南|| 第三章 IDE:Android Studio速览

第三章 IDE&#xff1a;Android Studio速览 Android Studio是Google官方提供的IDE&#xff0c;它是基于IntelliJ IDEA开发而来&#xff0c;用来替代Eclipse。不过目前它还属于早期版本&#xff0c;目前的版本是0.4.2&#xff0c;每个3个月发布一个版本&#xff0c;最近的版本…

7-1 选择法排序

分数 20 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 本题要求将给定的n个整数从大到小排序后输出。 输入格式&#xff1a; 输入第一行给出一个不超过10的正整数n。第二行给出n个整数&#xff0c;其间以空格分隔。 输出格式&#xff1a; 在一行中输出从大到小有序…

Springboot 实践(4)swagger-ui 测试controller

前文项目操作&#xff0c;完成了项目的创建、数据源的配置以及数据库DAO程序的生成与配置。此文讲解利用swagger-ui界面&#xff0c;测试生成的数据库DAO程序。目前&#xff0c;项目swagger-ui界面如下&#xff1a; 以”用户管理”为例&#xff0c;简单讲述swagger-ui测试数据库…

WPF入门到精通:2.WPF常用控件及布局

WPF&#xff08;Windows Presentation Foundation&#xff09;是一个用于构建 Windows 应用程序的框架&#xff0c;它提供了丰富的控件和布局方式&#xff0c;帮助开发者快速构建出现代化的应用程序。 WPF常用控件 Button 控件 WPF 中最常用的控件之一。它由一个文本标签和一个…

flinksql实时统计程序背压延迟优化

问题&#xff1a; flinkcdcflinksql做实时读取sls日志和实时统计业务指标&#xff0c;今天发现程序背压了&#xff0c;业务延迟了6个小时。解决办法&#xff1a; 1、资源优化 作业并发大时&#xff1a;在作业的高级配置的资源配置中&#xff0c;增加JobManager的资源&#xf…

MySQL系统变量 会话变量,用户变量

系统变量 分类 全局系统变量需要添加 global 关键字&#xff0c;有时把全局系统变量简称 全局变量 会话系统变量需要添加 session 关键字&#xff0c;有时也把会话系统变量称为 local 变量 局部变量 如果不写&#xff08;global、session&#xff09;默认会话级别。 静态变量在…