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