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 <zephyr/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  */
arch_busy_wait(uint32_t usec_to_wait)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  */
posix_cpu_hold(uint32_t usec_to_waste)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 }
76