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 <alpi@oticon.com>
This commit is contained in:
Alberto Escolar Piedras 2020-12-10 11:49:43 +01:00 committed by Alberto Escolar
commit 6d3476117b
10 changed files with 154 additions and 65 deletions

View file

@ -25,6 +25,14 @@
#include <arch/posix/posix_soc_if.h>
#include <tracing/tracing.h>
#if !defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
#error "The POSIX architecture needs a custom busy_wait implementation. \
CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT must be selected"
/* Each POSIX arch board (or SOC) must provide an implementation of
* arch_busy_wait()
*/
#endif
void arch_cpu_idle(void)
{
sys_trace_idle();

View file

@ -14,6 +14,7 @@ zephyr_library_sources(
tracing.c
cmdline_common.c
cmdline.c
cpu_wait.c
hw_counter.c
)

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "timer_model.h"
#include <arch/posix/posix_soc_if.h>
#include <posix_board_if.h>
#include <posix_soc.h>
/**
* Replacement to the kernel k_busy_wait()
* Will block this thread (and therefore the whole Zephyr) during usec_to_wait
*
* Note that interrupts may be received in the meanwhile and that therefore this
* thread may lose context.
* Therefore the wait time may be considerably longer.
*
* All this function ensures is that it will return after usec_to_wait or later.
*
* This special arch_busy_wait() is necessary due to how the POSIX arch/SOC INF
* models a CPU. Conceptually it could be thought as if the MCU was running
* at an infinitely high clock, and therefore no simulated time passes while
* executing instructions(*1).
* Therefore to be able to busy wait this function does the equivalent of
* programming a dedicated timer which will raise a non-maskable interrupt,
* and halting the CPU.
*
* (*1) In reality simulated time is simply not advanced just due to the "MCU"
* running. Meaning, the SW running on the MCU is assumed to take 0 time.
*/
void arch_busy_wait(uint32_t usec_to_wait)
{
uint64_t time_end = hwm_get_time() + usec_to_wait;
while (hwm_get_time() < time_end) {
/*
* There may be wakes due to other interrupts including
* other threads calling arch_busy_wait
*/
hwtimer_wake_in_time(time_end);
posix_halt_cpu();
}
}
/**
* Will block this thread (and therefore the whole Zephyr) during usec_to_waste
*
* Very similar to arch_busy_wait(), but if an interrupt or context switch
* occurs this function will continue waiting after, ensuring that
* usec_to_waste are spent in this context, irrespectively of how much more
* time would be spent on interrupt handling or possible switched-in tasks.
*
* Can be used to emulate code execution time.
*/
void posix_cpu_hold(uint32_t usec_to_waste)
{
uint64_t time_start;
int64_t to_wait = usec_to_waste;
while (to_wait > 0) {
/*
* There may be wakes due to other interrupts or nested calls to
* cpu_hold in interrupt handlers
*/
time_start = hwm_get_time();
hwtimer_wake_in_time(time_start + to_wait);
posix_change_cpu_state_and_wait(true);
to_wait -= hwm_get_time() - time_start;
posix_irq_handler();
}
}

View file

@ -23,7 +23,7 @@ zephyr_compile_options(
zephyr_library_sources(
irq_handler.c
k_busy_wait.c
cpu_wait.c
bstests_entry.c
argparse.c
main.c

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "zephyr/types.h"
#include "fake_timer.h"
#include "time_machine.h"
#include <arch/posix/posix_soc_if.h>
#include <posix_board_if.h>
#include <posix_soc.h>
/**
* Replacement to the kernel k_busy_wait()
* Will block this thread (and therefore the whole Zephyr) during usec_to_wait
*
* Note that interrupts may be received in the meanwhile and that therefore this
* thread may lose context.
* Therefore the wait time may be considerably longer.
*
* All this function ensures is that it will return after usec_to_wait or later.
*/
void arch_busy_wait(uint32_t usec_to_wait)
{
bs_time_t time_end = tm_get_hw_time() + usec_to_wait;
while (tm_get_hw_time() < time_end) {
/*
* There may be wakes due to other interrupts or nested calls to
* k_busy_wait in interrupt handlers
*/
fake_timer_wake_in_time(time_end);
posix_halt_cpu();
}
}
/**
* Will block this thread (and therefore the whole Zephyr) during usec_to_waste
*
* Very similar to arch_busy_wait(), but if an interrupt or context switch
* occurs this function will continue waiting after, ensuring that
* usec_to_waste are spent in this context, irrespectively of how much more
* time would be spent on interrupt handling or possible switched-in tasks.
*
* Can be used to emulate code execution time.
*/
void posix_cpu_hold(uint32_t usec_to_waste)
{
bs_time_t time_start;
int64_t to_wait = usec_to_waste;
while (to_wait > 0) {
/*
* There may be wakes due to other interrupts or nested calls to
* cpu_hold in interrupt handlers
*/
time_start = tm_get_hw_time();
fake_timer_wake_in_time(time_start + to_wait);
posix_change_cpu_state_and_wait(true);
to_wait -= tm_get_hw_time() - time_start;
posix_irq_handler();
}
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2017 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "zephyr/types.h"
#include "fake_timer.h"
#include "time_machine.h"
#include <arch/posix/posix_soc_if.h>
#if defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
/**
* Replacement to the kernel k_busy_wait()
* Will block this thread (and therefore the whole zephyr) during usec_to_wait
*
* Note that interrupts may be received in the meanwhile and that therefore this
* thread may lose context
*/
void arch_busy_wait(uint32_t usec_to_wait)
{
bs_time_t time_end = tm_get_hw_time() + usec_to_wait;
while (tm_get_hw_time() < time_end) {
/*There may be wakes due to other interrupts*/
fake_timer_wake_in_time(time_end);
posix_halt_cpu();
}
}
#endif

View file

@ -115,40 +115,6 @@ uint32_t z_clock_elapsed(void)
}
#if defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT)
/**
* Replacement to the kernel k_busy_wait()
* Will block this thread (and therefore the whole zephyr) during usec_to_wait
*
* Note that interrupts may be received in the meanwhile and that therefore this
* thread may loose context
*
* This special arch_busy_wait() is necessary due to how the POSIX arch/SOC INF
* models a CPU. Conceptually it could be thought as if the MCU was running
* at an infinitely high clock, and therefore no simulated time passes while
* executing instructions(*1).
* Therefore to be able to busy wait this function does the equivalent of
* programming a dedicated timer which will raise a non-maskable interrupt,
* and halting the CPU.
*
* (*1) In reality simulated time is simply not advanced just due to the "MCU"
* running. Meaning, the SW running on the MCU is assumed to take 0 time.
*/
void arch_busy_wait(uint32_t usec_to_wait)
{
uint64_t time_end = hwm_get_time() + usec_to_wait;
while (hwm_get_time() < time_end) {
/*
* There may be wakes due to other interrupts including
* other threads calling arch_busy_wait
*/
hwtimer_wake_in_time(time_end);
posix_halt_cpu();
}
}
#endif
#if defined(CONFIG_SYSTEM_CLOCK_DISABLE)
/**
*

View file

@ -24,6 +24,7 @@ extern "C" {
void posix_irq_handler(void);
void posix_exit(int exit_code);
uint64_t posix_get_hw_cycle(void);
void posix_cpu_hold(uint32_t usec_to_waste);
#ifdef __cplusplus
}

View file

@ -16,6 +16,7 @@ extern "C" {
void posix_interrupt_raised(void);
void posix_boot_cpu(void);
int posix_is_cpu_running(void);
void posix_change_cpu_state_and_wait(bool halted);
#ifdef __cplusplus
}

View file

@ -73,7 +73,7 @@ int posix_is_cpu_running(void)
* raise a new interrupt; and how the HW models awake the CPU, and wait for it
* to complete and go to idle.
*/
static void posix_change_cpu_state_and_wait(bool halted)
void posix_change_cpu_state_and_wait(bool halted)
{
PC_SAFE_CALL(pthread_mutex_lock(&mtx_cpu));