Skip to content

Commit 6d34761

Browse files
committed
posix: Add cpu_hold() function to better emulate code delay
In native_posix and nrf52_bsim add the cpu_hold() function, which can be used to emulate the time it takes for code to execute. It is very similar to arch_busy_wait(), but while arch_busy_wait() returns when the requested time has passed, cpu_hold() ensures that the time passes in the callers context independently of how much time may pass in some other context. Signed-off-by: Alberto Escolar Piedras <[email protected]>
1 parent 570a84c commit 6d34761

File tree

10 files changed

+154
-65
lines changed

10 files changed

+154
-65
lines changed

arch/posix/core/cpuhalt.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@
2525
#include <arch/posix/posix_soc_if.h>
2626
#include <tracing/tracing.h>
2727

28+
#if !defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
29+
#error "The POSIX architecture needs a custom busy_wait implementation. \
30+
CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT must be selected"
31+
/* Each POSIX arch board (or SOC) must provide an implementation of
32+
* arch_busy_wait()
33+
*/
34+
#endif
35+
2836
void arch_cpu_idle(void)
2937
{
3038
sys_trace_idle();

boards/posix/native_posix/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ zephyr_library_sources(
1414
tracing.c
1515
cmdline_common.c
1616
cmdline.c
17+
cpu_wait.c
1718
hw_counter.c
1819
)
1920

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2020 Oticon A/S
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdbool.h>
8+
#include "timer_model.h"
9+
#include <arch/posix/posix_soc_if.h>
10+
#include <posix_board_if.h>
11+
#include <posix_soc.h>
12+
13+
/**
14+
* Replacement to the kernel k_busy_wait()
15+
* Will block this thread (and therefore the whole Zephyr) during usec_to_wait
16+
*
17+
* Note that interrupts may be received in the meanwhile and that therefore this
18+
* thread may lose context.
19+
* Therefore the wait time may be considerably longer.
20+
*
21+
* All this function ensures is that it will return after usec_to_wait or later.
22+
*
23+
* This special arch_busy_wait() is necessary due to how the POSIX arch/SOC INF
24+
* models a CPU. Conceptually it could be thought as if the MCU was running
25+
* at an infinitely high clock, and therefore no simulated time passes while
26+
* executing instructions(*1).
27+
* Therefore to be able to busy wait this function does the equivalent of
28+
* programming a dedicated timer which will raise a non-maskable interrupt,
29+
* and halting the CPU.
30+
*
31+
* (*1) In reality simulated time is simply not advanced just due to the "MCU"
32+
* running. Meaning, the SW running on the MCU is assumed to take 0 time.
33+
*/
34+
void arch_busy_wait(uint32_t usec_to_wait)
35+
{
36+
uint64_t time_end = hwm_get_time() + usec_to_wait;
37+
38+
while (hwm_get_time() < time_end) {
39+
/*
40+
* There may be wakes due to other interrupts including
41+
* other threads calling arch_busy_wait
42+
*/
43+
hwtimer_wake_in_time(time_end);
44+
posix_halt_cpu();
45+
}
46+
}
47+
48+
/**
49+
* Will block this thread (and therefore the whole Zephyr) during usec_to_waste
50+
*
51+
* Very similar to arch_busy_wait(), but if an interrupt or context switch
52+
* occurs this function will continue waiting after, ensuring that
53+
* usec_to_waste are spent in this context, irrespectively of how much more
54+
* time would be spent on interrupt handling or possible switched-in tasks.
55+
*
56+
* Can be used to emulate code execution time.
57+
*/
58+
void posix_cpu_hold(uint32_t usec_to_waste)
59+
{
60+
uint64_t time_start;
61+
int64_t to_wait = usec_to_waste;
62+
63+
while (to_wait > 0) {
64+
/*
65+
* There may be wakes due to other interrupts or nested calls to
66+
* cpu_hold in interrupt handlers
67+
*/
68+
time_start = hwm_get_time();
69+
hwtimer_wake_in_time(time_start + to_wait);
70+
posix_change_cpu_state_and_wait(true);
71+
to_wait -= hwm_get_time() - time_start;
72+
73+
posix_irq_handler();
74+
}
75+
}

boards/posix/nrf52_bsim/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ zephyr_compile_options(
2323

2424
zephyr_library_sources(
2525
irq_handler.c
26-
k_busy_wait.c
26+
cpu_wait.c
2727
bstests_entry.c
2828
argparse.c
2929
main.c

boards/posix/nrf52_bsim/cpu_wait.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2017 Oticon A/S
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdbool.h>
8+
#include "zephyr/types.h"
9+
#include "fake_timer.h"
10+
#include "time_machine.h"
11+
#include <arch/posix/posix_soc_if.h>
12+
#include <posix_board_if.h>
13+
#include <posix_soc.h>
14+
15+
/**
16+
* Replacement to the kernel k_busy_wait()
17+
* Will block this thread (and therefore the whole Zephyr) during usec_to_wait
18+
*
19+
* Note that interrupts may be received in the meanwhile and that therefore this
20+
* thread may lose context.
21+
* Therefore the wait time may be considerably longer.
22+
*
23+
* All this function ensures is that it will return after usec_to_wait or later.
24+
*/
25+
void arch_busy_wait(uint32_t usec_to_wait)
26+
{
27+
bs_time_t time_end = tm_get_hw_time() + usec_to_wait;
28+
29+
while (tm_get_hw_time() < time_end) {
30+
/*
31+
* There may be wakes due to other interrupts or nested calls to
32+
* k_busy_wait in interrupt handlers
33+
*/
34+
fake_timer_wake_in_time(time_end);
35+
posix_halt_cpu();
36+
}
37+
}
38+
39+
/**
40+
* Will block this thread (and therefore the whole Zephyr) during usec_to_waste
41+
*
42+
* Very similar to arch_busy_wait(), but if an interrupt or context switch
43+
* occurs this function will continue waiting after, ensuring that
44+
* usec_to_waste are spent in this context, irrespectively of how much more
45+
* time would be spent on interrupt handling or possible switched-in tasks.
46+
*
47+
* Can be used to emulate code execution time.
48+
*/
49+
void posix_cpu_hold(uint32_t usec_to_waste)
50+
{
51+
bs_time_t time_start;
52+
int64_t to_wait = usec_to_waste;
53+
54+
while (to_wait > 0) {
55+
/*
56+
* There may be wakes due to other interrupts or nested calls to
57+
* cpu_hold in interrupt handlers
58+
*/
59+
time_start = tm_get_hw_time();
60+
fake_timer_wake_in_time(time_start + to_wait);
61+
posix_change_cpu_state_and_wait(true);
62+
to_wait -= tm_get_hw_time() - time_start;
63+
64+
posix_irq_handler();
65+
}
66+
}

boards/posix/nrf52_bsim/k_busy_wait.c

Lines changed: 0 additions & 29 deletions
This file was deleted.

drivers/timer/native_posix_timer.c

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -115,40 +115,6 @@ uint32_t z_clock_elapsed(void)
115115
}
116116

117117

118-
#if defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
119-
/**
120-
* Replacement to the kernel k_busy_wait()
121-
* Will block this thread (and therefore the whole zephyr) during usec_to_wait
122-
*
123-
* Note that interrupts may be received in the meanwhile and that therefore this
124-
* thread may loose context
125-
*
126-
* This special arch_busy_wait() is necessary due to how the POSIX arch/SOC INF
127-
* models a CPU. Conceptually it could be thought as if the MCU was running
128-
* at an infinitely high clock, and therefore no simulated time passes while
129-
* executing instructions(*1).
130-
* Therefore to be able to busy wait this function does the equivalent of
131-
* programming a dedicated timer which will raise a non-maskable interrupt,
132-
* and halting the CPU.
133-
*
134-
* (*1) In reality simulated time is simply not advanced just due to the "MCU"
135-
* running. Meaning, the SW running on the MCU is assumed to take 0 time.
136-
*/
137-
void arch_busy_wait(uint32_t usec_to_wait)
138-
{
139-
uint64_t time_end = hwm_get_time() + usec_to_wait;
140-
141-
while (hwm_get_time() < time_end) {
142-
/*
143-
* There may be wakes due to other interrupts including
144-
* other threads calling arch_busy_wait
145-
*/
146-
hwtimer_wake_in_time(time_end);
147-
posix_halt_cpu();
148-
}
149-
}
150-
#endif
151-
152118
#if defined(CONFIG_SYSTEM_CLOCK_DISABLE)
153119
/**
154120
*

soc/posix/inf_clock/posix_board_if.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ extern "C" {
2424
void posix_irq_handler(void);
2525
void posix_exit(int exit_code);
2626
uint64_t posix_get_hw_cycle(void);
27+
void posix_cpu_hold(uint32_t usec_to_waste);
2728

2829
#ifdef __cplusplus
2930
}

soc/posix/inf_clock/posix_soc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern "C" {
1616
void posix_interrupt_raised(void);
1717
void posix_boot_cpu(void);
1818
int posix_is_cpu_running(void);
19+
void posix_change_cpu_state_and_wait(bool halted);
1920

2021
#ifdef __cplusplus
2122
}

soc/posix/inf_clock/soc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ int posix_is_cpu_running(void)
7373
* raise a new interrupt; and how the HW models awake the CPU, and wait for it
7474
* to complete and go to idle.
7575
*/
76-
static void posix_change_cpu_state_and_wait(bool halted)
76+
void posix_change_cpu_state_and_wait(bool halted)
7777
{
7878
PC_SAFE_CALL(pthread_mutex_lock(&mtx_cpu));
7979

0 commit comments

Comments
 (0)