@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
什么问题?
离线
发现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)
离线
"把左移操作放到运算符右边"还不行吧,参照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)
离线
@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)
离线