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

Differences in cycles between k_busy_wait and k_sleep #23600

Closed
starmachine24 opened this issue Mar 19, 2020 · 11 comments
Closed

Differences in cycles between k_busy_wait and k_sleep #23600

starmachine24 opened this issue Mar 19, 2020 · 11 comments
Assignees

Comments

@starmachine24
Copy link

Hi,

We are trying to create two tasks that should preempt each other whenever they are close to their deadline and sleep to the deadline when they have finished their execution. But we have noticed that both k_busy_wait and k_sleep takes different amount of cycles, even though they are given the same amount of time as an argument. This seems odd to us, because shouldn't the task sleep for the specified amount of time and that the cycle counter still advances forward the same amount as if the task was busy doing work? This is run on the UP squared board.

CPU specs:
https://ark.intel.com/content/www/us/en/ark/products/95598/intel-celeron-processor-n3350-2m-cache-up-to-2-4-ghz.html

Ex. Both functions should run for 10ms (1 tick)

Configuration in Zephyr: 
HW_CYC_PER_SEC: 19200000
TICKS_PER_SEC: 100

#Testing sleep
int start = k_cycle_get_32();
k_sleep(10);
int end = k_cycle_get_32();
int delta = end - start;
delta: 211160 cycles

#Testing busy wait
int start = k_cycle_get_32();
k_busy_wait(10000);
int end = k_cycle_get_32();
int delta = end - start;
delta: 192006 cycles <--- This is more or less correct as one tick equals 192000 cycles

From that test we can see that the busy_wait runs for the specified amount of time in cycles, but the sleep function actually goes beyond 10ms.

We have ran the timer_monotonic test several times with different parameters but we always get a delta of 68%, no matter what we set the HW cycles per second or the number of ticks per second. We also ran the Schedule_api test and when we used the default settings for the UPsqaured board, we failed the test_slice_reset test, but when we changed the number of ticks per second to 100 we passed the test.

@gon1332
Copy link
Contributor

gon1332 commented Mar 19, 2020

It maybe connects to #21762.

@gon1332
Copy link
Contributor

gon1332 commented Mar 19, 2020

On which version of Zephyr did you perform the tests?

@starmachine24
Copy link
Author

starmachine24 commented Mar 19, 2020

We are using version 2.2.99.0, It might be related to the issue you mentioned in your thread, but we have no actual idea whats causing this so can't say for sure

@gon1332
Copy link
Contributor

gon1332 commented Mar 19, 2020

Later on it was fixed. But as it seems now, it re-appeared.

@starmachine24
Copy link
Author

ok, do you know which version it was fixed in?

@gon1332
Copy link
Contributor

gon1332 commented Mar 19, 2020

I don't know the place of the fix. Neither the assignees as it seems. But in #21762 you can see that in f5d2b32 it is fixed.

@starmachine24
Copy link
Author

starmachine24 commented Mar 19, 2020

ah, ok i must have missed that when i read your thread

When we tried to run that version of Zephyr the problem seem to be the same, with the sleep still taking more cycles than the busy wait.

@nashif
Copy link
Member

nashif commented Mar 19, 2020

@andyross can you please provide your input here

@andyross
Copy link
Contributor

It's just a precision issue. Timer subsystem calls like k_sleep() are documented as sleeping for at least as long as the passed argument. But they also can wake up only on tick boundaries (10ms in this case).

So by asking k_sleep() to sleep for at least 10ms, that becomes "at least one tick", so under normal circumstances[1] that means you will wake up anywhere between 1 and 2 ticks in the future, depending on where "within the tick period" you made the call. You happened to measure 1.1 ticks, which looks very reasonable.

You can address precision issues like this by increasing the tick rate in CONFIG_SYS_CLOCK_TICKS_PER_SEC (most hardware platforms these days are using 10 kHz ticks by default -- 100 Hz is needed only by emulated systems that interact with a system clock), but you can't fix the underlying behavior, which is as-designed. Timer handling in the kernel happens in ticks, and by definition it isn't going to be any more accurate.

And of course k_busy_wait() isn't a timer call and isn't subject to those restrictions. It spins (!!) on the CPU until the cycle counter increments by the amount requested, so it can get arbitrarily high precision limited only by the precision of the hardware counter and the CPU time required for the function call. That's why it exists, after all.

[1] There are a few spots in other subsystems (not blocking calls like sleep!) where code knows it is registering a timeout underneath a timer ISR and thus can be exact. See k_timer for an example, also the announce_remaining handling inside z_clock_announce() has some similar semantics.

@starmachine24
Copy link
Author

Thank you for the answers

@carlescufi
Copy link
Member

closing since this is now answered

necostew referenced this issue in nrfconnect/sdk-sidewalk Nov 23, 2022
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

5 participants