【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
 

相关文章:
【ARM v8】如何在ARM上实现x86的rdtsc()函数
博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…...
redis--事务
redis事务 在Redis中,事务是一组原子性操作的集合,它们被一起执行,要么全部执行成功,要么全部回滚。虽然Redis的事务并不遵循传统数据库的ACID特性,但它仍然提供了一种将多个命令打包成一组执行的机制,适用…...
111. 二叉树的最小深度
111. 二叉树的最小深度 给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明:叶子节点是指没有子节点的节点。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeN…...
SpringMVC归纳与总结
前言 Spring的核心是IOC,一种依赖反转的解耦思想。MVC是一种处理Web请求的架构模式,当两者的作用结合,就形成了SpringMVC。 组成及运行原理 1. 两次映射 2. 为什么用适配器模式 过滤器与拦截器 1. 范围 静态资源与动态资源2. 生命周期…...
Python学习笔记_进阶篇(三)_django知识(二)
本章内容 Django model Model 基础配置 django默认支持sqlite,mysql, oracle,postgresql数据库。 <1> sqlite django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 引擎名称:django.db.backends.sqlite3 <2>mysql …...
RISC-V 整型通用寄存器介绍
简介 RISC-V64位/32位提供了32个整型通用寄存器,编号是x0~x31,这些整型通用寄存器的宽度与架构位数一致。 浮点数寄存器与整形寄存器一样也提供了32个:f0~f31,位数与架构位数一致。 通用寄存器介绍 零寄存器 x0/zero x0寄存…...
学习Vue:【性能优化】异步组件和懒加载
在Vue.js应用开发中,性能优化是一个至关重要的主题,而异步组件和懒加载是提升性能的有效方法之一。本文将介绍什么是异步组件和懒加载,以及如何在Vue.js中应用这些技术来提升应用性能。 异步组件和懒加载 异步组件 异步组件是指在需要的时候…...
pdf格式文件下载不预览,云存储的跨域解决
需求背景 后端接口中返回的是pdf文件路径比如: pdf文件路径 (https://wangzhendongsky.oss-cn-beijing.aliyuncs.com/wzd-test.pdf) 前端适配是这样的 <ahref"https://wangzhendongsky.oss-cn-beijing.aliyuncs.com/wzd-test.pdf&…...
httplib + nlohmann::json上传数据时中文乱码解决
1、nlohmann::json 1.1 编码格式使用UTF-8 参考 nlohmann::json 中文乱码解决方案 (1)将数据先转为UTF-8格式 2、httplib 2.1 上传数据前 (1)调用httplib::Response对象的set_header()方法来设置编码格式 httplib::Response res…...
JavaScript中的设计模式之一--单例模式和模块
虽然有一种疯狂天才的感觉可能很诱人,但重新发明轮子通常不是设计软件的最佳方法。很有可能有人已经遇到了和你一样的问题,并以一种聪明的方式解决了它。这样的最佳实践在形式化后被称为设计模式。今天我们来看看它们的概念,并检查单例模式和…...
回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测(多指标,多图)
回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测(多指标,多图) 目录 回归预测 | MATLAB实现GAM广义加性模型多输入单输出回归预测(多指标,多图)效果一览基本介绍程序设计参考资料 效果一览 基本…...
css学习4(背景)
1、CSS中,颜色值通常以以下方式定义: 十六进制 - 如:"#ff0000"RGB - 如:"rgb(255,0,0)"颜色名称 - 如:"red" 2、background-image 属性描述了元素的背景图像. 默认情况下,背景图像进…...
二、SQL,如何实现表的创建和查询
1、新建表格(在当前数据库中新建一个表格): (1)基础语法: create table [表名]( [字段:列标签] [该列数据类型] comment [字段注释], [字段:列标签] [该列数据类型] comment [字段注释], ……,…...
大数据及软件教学与实验专业实训室建设方案
一 、系统概述 大数据及软件教学与实验大数据及软件教学与实验在现代教育中扮演重要角色,这方面的教学内容涵盖了大数据处理、数据分析、数据可视化和大数据应用等多个方面。以下是大数据及软件教学与实验的一般内容:1. 数据基础知识:教授学生…...
信创办公–基于WPS的EXCEL最佳实践系列 (公式和函数)
信创办公–基于WPS的EXCEL最佳实践系列 (公式和函数) 目录 应用背景相关知识操作步骤1、认识基本的初级函数2、相对引用,绝对引用,混合引用3、统计函数4、文本函数 应用背景 熟练掌握Excel的函数工具能让我们在日常的使用中更加方…...
【Apollo】自动驾驶感知——毫米波雷达
作者简介: 辭七七,目前大一,正在学习C/C,Java,Python等 作者主页: 七七的个人主页 文章收录专栏: 七七的闲谈 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖…...
SpringBoot部署到腾讯云
SpringBoot部署到腾讯云 此处默认已经申请到腾讯云服务器,因为本人还没有申请域名,所以就直接使用的ip地址 XShell连接到腾讯云 主机中填写腾讯云的公网ip地址 公网ip地址在下图中找到 接下来填写服务器的用户名与密码 一般centOS用户名为rootÿ…...
Git 设置代理
Git 传输分两种协议,SSH和 http(s),设置代理也需要分两种。 http(s) 代理 Command Line 使用 命令行 模式,可以在Powershell中使用以下命令设置代理: $env:http_proxy"http://127.0.0.1:7890" $env:https_proxy&quo…...
基于Spring Boot的机场VIP客户管理系统的设计与实现(Java+spring boot+MySQL)
获取源码或者论文请私信博主 演示视频: 基于Spring Boot的机场VIP客户管理系统的设计与实现(Javaspring bootMySQL) 使用技术: 前端:html css javascript jQuery ajax thymeleaf 微信小程序 后端:Java s…...
图数据库_Neo4j学习cypher语言_使用CQL_构建明星关系图谱_导入明星数据_导入明星关系数据_创建明星关系---Neo4j图数据库工作笔记0009
首先找到明星数据 可以看到有一个sheet1,是,记录了所有的关系的数据 然后比如我们搜索一个撒贝宁,可以看到撒贝宁的数据 然后这个是构建的CQL语句 首先我们先去启动服务 neo4j console 然后我们再来看一下以前导入的,可以看到导入很简单, 就是上面有CQL 看一下节点的属性...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
