1 /*
2 * Copyright (c) 2017 Oticon A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * For all purposes, Zephyr threads see a CPU running at an infinitely high
9 * clock.
10 *
11 * Therefore, the code will always run until completion after each interrupt,
12 * after which arch_cpu_idle() will be called releasing the execution back to
13 * the HW models.
14 *
15 * The HW models raising an interrupt will "awake the cpu" by calling
16 * posix_interrupt_raised() which will transfer control to the irq handler,
17 * which will run inside SW/Zephyr context. After which a arch_swap() to
18 * whatever Zephyr thread may follow. Again, once Zephyr is done, control is
19 * given back to the HW models.
20 *
21 * The Zephyr OS+APP code and the HW models are gated by a mutex +
22 * condition as there is no reason to let the zephyr threads run while the
23 * HW models run or vice versa
24 *
25 */
26
27 #include <zephyr/arch/posix/posix_soc_if.h>
28 #include "posix_soc.h"
29 #include "posix_board_if.h"
30 #include "posix_core.h"
31 #include "posix_arch_internal.h"
32 #include "kernel_internal.h"
33 #include "soc.h"
34 #include "nce_if.h"
35
36 static void *nce_st;
37
posix_is_cpu_running(void)38 int posix_is_cpu_running(void)
39 {
40 return nce_is_cpu_running(nce_st);
41 }
42
43 /**
44 * Helper function which changes the status of the CPU (halted or running)
45 * and waits until somebody else changes it to the opposite
46 *
47 * Both HW and SW threads will use this function to transfer control to the
48 * other side.
49 *
50 * This is how the idle thread halts the CPU and gets halted until the HW models
51 * raise a new interrupt; and how the HW models awake the CPU, and wait for it
52 * to complete and go to idle.
53 */
posix_change_cpu_state_and_wait(bool halted)54 void posix_change_cpu_state_and_wait(bool halted)
55 {
56 if (halted) {
57 nce_halt_cpu(nce_st);
58 } else {
59 nce_wake_cpu(nce_st);
60 }
61 }
62
63 /**
64 * HW models shall call this function to "awake the CPU"
65 * when they are raising an interrupt
66 */
posix_interrupt_raised(void)67 void posix_interrupt_raised(void)
68 {
69 /* We change the CPU to running state (we awake it), and block this
70 * thread until the CPU is halted again
71 */
72 nce_wake_cpu(nce_st);
73 }
74
75
76 /**
77 * Normally called from arch_cpu_idle():
78 * the idle loop will call this function to set the CPU to "sleep".
79 * Others may also call this function with care. The CPU will be set to sleep
80 * until some interrupt awakes it.
81 * Interrupts should be enabled before calling.
82 */
posix_halt_cpu(void)83 void posix_halt_cpu(void)
84 {
85 /*
86 * We set the CPU in the halted state (this blocks this pthread
87 * until the CPU is awoken again by the HW models)
88 */
89 nce_halt_cpu(nce_st);
90
91 /* We are awoken, normally that means some interrupt has just come
92 * => let the "irq handler" check if/what interrupt was raised
93 * and call the appropriate irq handler.
94 *
95 * Note that, the interrupt handling may trigger a arch_swap() to
96 * another Zephyr thread. When posix_irq_handler() returns, the Zephyr
97 * kernel has swapped back to this thread again
98 */
99 posix_irq_handler();
100
101 /*
102 * And we go back to whatever Zephyr thread called us.
103 */
104 }
105
106
107 /**
108 * Implementation of arch_cpu_atomic_idle() for this SOC
109 */
posix_atomic_halt_cpu(unsigned int imask)110 void posix_atomic_halt_cpu(unsigned int imask)
111 {
112 posix_irq_full_unlock();
113 posix_halt_cpu();
114 posix_irq_unlock(imask);
115 }
116
117 /**
118 * The HW models will call this function to "boot" the CPU
119 * == spawn the Zephyr init thread, which will then spawn
120 * anything it wants, and run until the CPU is set back to idle again
121 */
posix_boot_cpu(void)122 void posix_boot_cpu(void)
123 {
124 nce_st = nce_init();
125 posix_arch_init();
126 nce_boot_cpu(nce_st, z_cstart);
127 }
128
129 /**
130 * Clean up all memory allocated by the SOC and POSIX core
131 *
132 * This function can be called from both HW and SW threads
133 */
posix_soc_clean_up(void)134 void posix_soc_clean_up(void)
135 {
136 nce_terminate(nce_st);
137 posix_arch_clean_up();
138 run_native_tasks(_NATIVE_ON_EXIT_LEVEL);
139 }
140