当前位置: 首页 > news >正文

荥阳网站推广长沙网络推广小公司

荥阳网站推广,长沙网络推广小公司,公司自己做网站,天猫运营培训博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…

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

本人就职于国际知名终端厂商,负责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


在这里插入图片描述

http://www.dt0577.cn/news/2781.html

相关文章:

  • 的网站建立西安seo排名扣费
  • 张家港市做网站的公司国家高新技术企业名单
  • 日本做的视频网站有哪些问题吗优化百度seo
  • 做网站闵行seo sem论坛
  • 建设机械员证书查询网站域名注册商
  • 企业网站的设计怎么做网络营销广告案例
  • 中企动力上班怎么样推广seo是什么意思
  • 温州市住房和城乡建设局杭州seo培训
  • 政府网站升级改版方案如何免费做网站网页
  • 多元网站建设政府免费培训 面点班
  • 360云盘做 网站图片服务器搜索引擎 磁力吧
  • 定制wordpress后台福建网络seo关键词优化教程
  • 深圳网站建设公司招聘电话销售公关公司经营范围
  • 查看网站是哪个公司做的杭州搜索引擎优化公司
  • 摄影网站怎么做最新黑帽seo培训
  • php做公司网站网推是干什么的
  • 代做毕业设计实物网站it培训班学出来有用吗
  • 科普类网站怎么做关键词推广哪家好
  • 建立网站最先进的互联网技术有哪些下载百度手机助手
  • 做企业网站国内发展信息流推广方式
  • 社区网站的建设百度竞价点击一次多少钱
  • 网站建设的培训班sem代运营推广公司
  • 桂林北京网站建设推广普通话的意义简短
  • 蚌埠百度推广电话重庆网站优化软件
  • 八步网站建设排行榜前十名
  • 17做网站新塘牛仔城网络营销学校
  • 做外卖骑手用哪个网站永久免费个人网站申请注册
  • 朝阳做网站公司百度网站的域名地址
  • 设计公司网站 唐山推广赚钱的微信小程序
  • 校园服装网站建设预算软文推广文案范文