ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

tsc之x86、armv8的cpu如何获取高精度时间戳以及高精度时间

2022-06-11 00:34:50  阅读:283  来源: 互联网

标签:__ x86 高精度 tsc high volatile 寄存器 asm


替换x86 rdtsc汇编指令_鲲鹏通用_TaiShan服务器代码移植参考_移植相关问题处理_嵌入式汇编类问题_华为云 (huaweicloud.com)

替换rdtsc汇编指令_鲲鹏处理器_处理器指令替换案例集_源码修改类案例_华为云 (huaweicloud.com)

http://ilinuxkernel.com/?p=1755  

在编程时,我们往往需要获取高精度的时间点,尤其是在进行性能测试的时候,当测试的代码本身执行的时间就很短的时候,用低精度的时间戳是很难进行测试的。下面将总结一下对于不同cpu在这方面的实现。

(1)x86一种读时间戳实现方式

在x86架构中,由于cpu的tsc和系统的tsc以及频率都是采用统一的频率,例如有些都是采用2500MHz。 本代码来源于pcie-lat项目。

下面这种方式计算stc是最准确的,连续两条指令之间大概在32个cycle之间。至于关于x86的寄存器以及内部实现原理希望哪位大佬帮忙普及一下,
实在是对x86的汇编以及寄存器一无所知。
#define get_tsc_top(high, low) \ asm volatile ("cpuid \n\t" \ "rdtsc \n\t" \ "mov %%edx, %0 \n\t" \ "mov %%eax, %1 \n\t" \ :"=r" (high), "=r"(low) \ : \ :"rax", "rbx", "rcx", "rdx"); \ #define get_tsc_bottom(high, low) \ asm volatile ("rdtscp \n\t" \ "mov %%edx, %0 \n\t" \ "mov %%eax, %1 \n\t" \ "cpuid \n\t" \ :"=r" (high), "=r"(low) \ : \ :"rax", "rbx", "rcx", "rdx"); \ get_tsc_top(tsc_high_before, tsc_low_before); get_tsc_bottom(tsc_high_after, tsc_low_after);

   tsc_start = ((u64) tsc_high_before << 32) | tsc_low_before;
   tsc_end = ((u64) tsc_high_after << 32) | tsc_low_after;
   tsc_diff = tsc_end - tsc_start;

实现第二种方式:这种方式的误差稍微大一下,可能是它并没有将rdtsc寄存器和rdtscp寄存器配合使用。稍后查查他们的原理。

static uint64_t Rdtsc(void)
{
  uint32_t lo,hi;
  __asm__ __volatile__ ("ifence; rdtsc": "=a" (lo), "=d" (hi));//ifence指令也可以不用
  return   (uint64_t)hi << 32 | lo;
}

  

(2)armv8--系统寄存器读系统时间戳

armv8读取时间戳有两种方式,首先是系统时间戳和系统频率。

以下是鲲鹏,注意其他cpu可能外部频率不是100MHz
static uint64_t Rdtsc()
{
  uint64_t count_num;
  current_speed = 2400;//这个是cpu的频率2400MHz
  extern_clock   = 100;//这个是系统频率,100MHz。也叫外部频率。
__asm__ __volatile__("mrs %0, cntfreq_el0\n\tisb\n\t": "=r"(extern_clock));这句话也可以读出来系统频率
  __asm__ __volatile__("mrs %0, cntcvt_el0" : "=r"(count_num)) ; 

return count_num *(current_speed / extern_clock);
}

 为什么要这样做呢?实际上cntcvt_el0寄存器读出来的时间戳是系统的时时间戳,由于系统频率和cpu频率都是已知的,那么我们就可以利用这个关系进行转换,将系统时间戳乘以这个系数。其实这里是有一些问题的,因为cntcvt_el0的时间戳是之间的时间比较长的(因为系统频率一般比较低300MHz一下),这样加入一条指令之间的时间要短于这个时间戳,系统也会自动加1,这样误差其实就很大了。 

 

(3)armv8-cpu时间戳相当于x86的rdtsc寄存器

在armv8中,有一个PMCCNTR_EL0的寄存器,默认情况下,需要内核使能才能读取。

用户态代码:

static uint64_t Rdtsc(void)
{
  uint64_t count_num;
  __asm__ __volatile_("mrs %0, pmccntr_el0": "+r"(count_num));
  return count_num;

}

 

驱动模块,在驱动中实现对寄存器的使能。不过这段代码,我尝试在init初始化函数

asm volatile("mrs %0, PMCCNTR_EL0" : "=r"(cval));

asm volatile("mrs %0, PMCCNTR_EL0" : "=r"(cval));

发现这里两个cval的值竟然相同!!!!!!!!!!!,不理解为什么!!!

/*
 * Author: Yiquan Chen这个作者的代码,此处引用。该网站有时候无法访问,所以直接粘贴了
 * http://www.ilinuxkernel.com
 * ilinuxkernel@gmail.com         
 * Enable user-mode ARM performance counter access.
 */                                  
#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/smp.h>      

#define PERF_DEF_OPTS         (1 | 16) 
#define PERF_OPT_RESET_CYCLES     (2 | 4)  
#define PERF_OPT_DIV64         (8)   
#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 inline  long long armv8_read_CNTPCT_EL0(void)
{
   long long val;
   asm volatile("mrs %0, CNTVCT_EL0" : "=r" (val));

   return val;
}


static void enable_cpu_counters(void* data) 
{
    u32 val=0;   
    asm volatile("msr pmuserenr_el0, %0" : : "r"(0xf));
    armv8pmu_pmcr_write(ARMV8_PMCR_LC|ARMV8_PMCR_E);  
    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)  
{ 
    u32 val=0; 
    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)
{
    u64 cval;
    u32 val;

    isb();
    asm volatile("mrs %0, PMCCNTR_EL0" : "=r"(cval));
    printk("\nCPU Cycle count:%llu \n", cval);
    asm volatile("mrs %0, PMCNTENSET_EL0" : "=r"(val));
    printk("PMCNTENSET_EL0:%lX ", val);
    asm volatile("mrs %0, PMCR_EL0" : "=r"(val));
    printk("\nPMCR_EL0 Register:%lX ", val);
        
    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);

 

Makefile

obj-m	:= pmu.o
KDIR	:= /lib/modules/$(shell uname -r)/build
PWD	:= $(shell pwd)

all:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

  

 

标签:__,x86,高精度,tsc,high,volatile,寄存器,asm
来源: https://www.cnblogs.com/luckdog0623/p/16361422.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有