您尚未登录。

楼主 # 2024-08-09 14:59:53

海石生风
会员
所在地: 深圳
注册时间: 2019-07-02
已发帖子: 645
积分: 778
个人网站

52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

想用GTC实现1us精度的时基计时,搞完才发现这玩意是格雷码的!格雷码转自然码要耗费几十上百个指令周期,即根本实现不了微秒级精度。

格雷码用途何在?!

离线

#1 2024-08-09 22:01:45

dykxjh
会员
注册时间: 2020-03-25
已发帖子: 169
积分: 174

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

格雷码可以避免冒险与竞争,抗干扰能力更强

离线

#2 2024-08-10 14:56:28

xdlkliang
会员
所在地: 珠海
注册时间: 2024-04-18
已发帖子: 7
积分: 37

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

1、格雷码主要是为了解决跨时钟域异步问题。GTC寄存器自然数->格雷码->CPU寄存器自然数,格雷码两端对自然数的转换都是由硬件进行转换,不需要软件处理。两头寄存器看到的值,都是咱们直接可读的自然数值。
2、GTC自然数的读取有两种方式,一种是读取GTC寄存器GTC_CNTVL GTC_CNTVH的值,一种是读取RISC-V CPU寄存器的值,通过汇编指令(csrr a0, time)(csrr a1, timeh) 获取;第一种方式由于CPU访问GTC寄存器的访问进行各级总线的同步,会占有百纳秒级别的开销,实际不推荐这么使用;建议使用第二种方式,访问CPU寄存器自然数的值,其开销取决于CPU指令的时间。
3、GTC时基250ns,通过读取CPU寄存器的值判断实现1us延迟是可行的。这里还需要特别注意延迟后执行动作耗费的时间,函数调用和寄存器读写都会占用一些时间。

离线

楼主 #3 2024-08-11 10:31:59

海石生风
会员
所在地: 深圳
注册时间: 2019-07-02
已发帖子: 645
积分: 778
个人网站

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

@xdlkliang
感谢解答。我上面的需求说法有误,我要的是分辨率为1us,精度是us级(10us左右)的时基计时。
我测试了第二种方式,发现读取timeh寄存器时会再现cpu异常。寄存器读取函数如下:

static inline uint32_t _read_csr_time_lo(void)
{
    uint32_t value;

    __asm__ __volatile__ ("csrr %0, time\n\t"
            : "=r" (value) :
            : "memory");

    return value;
}

static inline uint32_t _read_csr_time_hi(void)
{
    uint32_t value;

    __asm__ __volatile__ ("csrr %0, timeh\n\t"
            : "=r" (value) :
            : "memory");

    return value;
}

RTOS SDK 1.0.5下经测试读寄存器time没有问题,读寄存器timeh时出现cpu异常:

CPU Exception: NO.2                                                                                                                                                                                               
x1(ra)   : 00000000400338b8     x2(sp)   : 000000004022d2c8     x3(gp)   : 00000000401ba318     x4(tp)   : 00000000deadbeef                                                                                       
x5(t0)   : 00000000401bbef8     x6(t1)   : 0000000000000001     x7(t2)   : 00000000deadbeef     x8(s0/fp): 0000000000000006                                                                                       
x9(s1)   : 00000000400bb7e0     x10(a0)  : 0000000000000001     x11(a1)  : 000000004022d2e8     x12(a2)  : ffffffff00000000                                                                                       
x13(a3)  : 0000000040223228     x14(a4)  : 0000000000000000     x15(a5)  : 0000000000000000     x16(a7)  : 0000000000000009                                                                                       
x17(a7)  : 0000000040223228     x18(s2)  : 0000000040223222     x19(s3)  : 0000000040150570     x20(s4)  : 00000000401829f8                                                                                       
x21(s5)  : 0000000040150900     x22(s6)  : 000000004015bd28     x23(s7)  : 00000000401c2400     x24(s8)  : 000000000000000d                                                                                       
x25(s9)  : 0000000040223222     x26(s10) : 0000000040222dd0     x27(s11) : 00000000deadbeef     x28(t3)  : 0000000000000022                                                                                       
x29(t4)  : 000000000000005c     x30(t5)  : 000000000000000a     x31(t6)  : 00000000deadbeef                                                                                                                       
mcause   : 0000000000000002                                                                                                                                                                                       
mtval    : 00000000c8102773                                                                                                                                                                                       
mepc     : 00000000400bb7f0                                                                                                                                                                                       
mstatus  : 8000000a00007880

什么问题?

离线

楼主 #4 2024-08-11 15:37:40

海石生风
会员
所在地: 深圳
注册时间: 2019-07-02
已发帖子: 645
积分: 778
个人网站

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

发现RTOS SDK里有这个API:aic_get_time_us,其实现就是读time寄存器,于是找到上面问题的原因了。

我用的芯片是D21x,其CSR寄存器time是64位的,所以此芯片应该没有timeh寄存器,只需读time寄存器即可。
那么,基本上只需两条指令就能获取分辨率为1us的时基,不错!

最后D21x上读取内核时基的函数如下,D13x等32位MCU就没那么便利了:要读取寄存器2次且需处理读取过程中的进位情况

static inline uint64_t _read_csr_time(void)
{
    uint64_t value;

    __asm__ __volatile__ ("csrr %0, time\n\t"
            : "=r" (value) :
            : "memory");

    return value;
}

PS:RTOS SDK里d13x、d12x等读取系统时基的函数 aic_get_ticks,没有处理读取过程中可能出现的进位情况(读取时低32位寄存器发生32位进位),是否是一个BUG?!

u64 aic_get_ticks(void)
{
    return (((u64)csi_coret_get_valueh() << 32U) | csi_coret_get_value());
}

最近编辑记录 海石生风 (2024-08-11 15:48:30)

离线

#5 2024-08-13 11:13:07

xdlkliang
会员
所在地: 珠海
注册时间: 2024-04-18
已发帖子: 7
积分: 37

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

感谢感谢,这个的确是个bug来着,需要把左移操作放到运算符右边,下一版SDK修改。

离线

楼主 #6 2024-08-13 12:05:51

海石生风
会员
所在地: 深圳
注册时间: 2019-07-02
已发帖子: 645
积分: 778
个人网站

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

"把左移操作放到运算符右边"还不行吧,参照Linux SDK那边(linux-5.10/arch/riscv/include/asm/timex.h)应该改成如下才行吧:

u64 aic_get_ticks(void)
{
	u32 hi, lo;

	do {
		hi = csi_coret_get_valueh();
		lo = csi_coret_get_value();
	} while (hi != csi_coret_get_valueh());

	return ((u64)hi << 32) | lo;
}

最近编辑记录 海石生风 (2024-08-13 12:07:26)

离线

#7 2024-08-13 13:22:28

xdlkliang
会员
所在地: 珠海
注册时间: 2024-04-18
已发帖子: 7
积分: 37

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

展开来看的话,代码是这样
{
    u64 tick_high_64;
    u32 tick_low_32;
   

    /* 1.先获取高32位,但是在执行这段的时候,低32位的tick实际还是在增加的 */
    tick_high_64 = csi_coret_get_valueh() << 32;

    /* 2.后获取低32位,如果在执行"1"之前,低32位已经是满量程0xFFFFFFFF。则这里就有可能溢出成0的概率 */
    /* 所以这一段需要在1之前执行 */
    tick_low_32 = csi_coret_get_value();

    tick_high_64 |= tick_low_32;

    return     tick_high_64;
}

离线

#8 2024-08-13 13:51:38

xdlkliang
会员
所在地: 珠海
注册时间: 2024-04-18
已发帖子: 7
积分: 37

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

您上面的修改应该也是对的

离线

楼主 #9 2024-08-13 14:36:47

海石生风
会员
所在地: 深圳
注册时间: 2019-07-02
已发帖子: 645
积分: 778
个人网站

Re: 52位的GTC计时器数值为什么要特意转成格雷码?根本没法用!

@xdlkliang
不行呀,在读取过程中产生进位,即是高低两个寄存器都已经变化了呀,那么两个寄存器都要重新读取。

所以要用do while流程来或以下的if流程来处理:

	hi = csi_coret_get_valueh();
	lo = csi_coret_get_value();

	// 产生进位的时间间隔是很长的,不可能在短时间内产生两次进位
	if(hi != csi_coret_get_valueh()) {
		hi = csi_coret_get_valueh();
		lo = csi_coret_get_value();
	}

最近编辑记录 海石生风 (2024-08-13 14:37:40)

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn