Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

micros() is non-monotonic #65

Open
Khalinda opened this issue Feb 21, 2024 · 2 comments
Open

micros() is non-monotonic #65

Khalinda opened this issue Feb 21, 2024 · 2 comments

Comments

@Khalinda
Copy link

I was benchmarking some floating point operations on a CH32V307 chip and I was getting crazy results. The following sketch illustrates the problem:

uint32_t au32Time[1000];

void setup()
{
	int i;

	Serial.begin(115200);
	delay(1000);
	for(i=0;i<1000;i++)
		au32Time[i]=micros();
	for(i=0;i<1000;i++)
		Serial.println(au32Time[i]);
}

void loop(){}

The output starts out with:

1000998
1000996
1000994
1000992
1000990

And about half way down, this happens:

1000005
1000003
1000001
1001998
1001996
1001994

The counts should be strictly increasing. I haven't looked at the CH32 core, but in the Arduino core, micros() is essentially calculated from millis(). There seems to be an arithmetic error in the CH32 version. I tested this sketch on versions 1.0.3 and 1.0.4 and I obtained the same results.

@Khalinda
Copy link
Author

I tracked down the CH32 source code for micros(). It just calls getCurrentMicros, which is found in clock.c. The code appears to have been written assuming that SysTick counts down. However, in hw_config.c, the SysTick CTRL register is set to 0xF, which configures SysTick to count up. I modified getCurrentMicros, replacing two lines of code as follows:

uint32_t getCurrentMicros(void)
{
  
  uint64_t m0 = GetTick();
  __IO uint64_t u0 = SysTick->CNT;
  uint64_t m1 = GetTick();
  __IO uint32_t u1 = SysTick->CNT;   //may be a interruption
   uint64_t tms = SysTick->CMP + 1;

  if (m1 != m0) {
//    return (m1 * 1000 + ((tms - u1) * 1000) / tms);
    return m1 * 1000 + u1 * 1000 / tms;
  } else {
//    return (m0 * 1000 + ((tms - u0) * 1000) / tms);
    return m0 * 1000 + u0 * 1000 / tms;
  }
}

That appears to work correctly, at least on the CH32V307 chip.

The SysTick timer on the CH32V10x processors is different, and it is configured differently. Unfortunately, I do not have a CH32V103 chip to test with, and the RISC-V SysTick registers are not documented in the CH32V10x Reference Manual, so exercise caution if you decide to apply this change.

@Khalinda
Copy link
Author

I purchased a CH32V103C8T6 board, and I am experiencing the same problem with non-monotonic micros() output. The CH32V103 Systick device has 32-bit high and low count registers, so it is not code compatible with the other CH32V chips. There is separate code for the CH32V103 chips further down in clock.c. I made a similar change to that code and it seems to correct the problem.

uint32_t getCurrentMicros(void)
{
  
  uint64_t m0 = GetTick();
  uint64_t u0 = *((__IO uint32_t *)SYSTICK_CNTH);  
           u0 = (u0 << 32) + *((__IO uint32_t *)SYSTICK_CNTL);
  
  uint64_t m1 = GetTick();
  uint64_t u1 = *((__IO uint32_t *)SYSTICK_CNTH); //may be a interruption
           u1 = (u1 << 32) + *((__IO uint32_t *)SYSTICK_CNTL);

  uint64_t tms = *((__IO uint32_t *)SYSTICK_CMPH);
           tms = (tms << 32) + *((__IO uint32_t *)SYSTICK_CMPL) + 1;     

  if (m1 != m0) {
//    return (m1 * 1000 + ((tms - u1) * 1000) / tms);
    return m1 * 1000 + u1 * 1000 / tms;

  } else {
//    return (m0 * 1000 + ((tms - u0) * 1000) / tms);
    return m0 * 1000 + u0 * 1000 / tms;
  }
}

In my previous post, I noted that the Systick device was not documented in the WCH reference manuals. The Systick device is part of the RISC-V core, so it is documented in the (V2, V3, etc.) QingKe RISC-V microprocessor manuals instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant