ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_run_inner.c
Go to the documentation of this file.
1
17
18#include "emu_engine.h"
19#include "emu_exc.h"
20#include "emu_mpu.h"
21#include "emu_prof.h"
22#include "emu_run_internal.h"
23#include "emu_seams.h"
24#include "emu_view.h"
25
39typedef enum : uint8_t {
43
67RA8_INTERNAL static size_t internal_run_inner_budget(uc_engine* uc, uint32_t run_pc)
68{
69 size_t busy_budget = (size_t)k_run_chunk_insns;
70 if (emu_low_power()) {
71 busy_budget = (size_t)k_run_chunk_insns / (size_t)k_low_power_div;
72 }
73 return idle_spin_at(uc, run_pc) ? (size_t)k_idle_spin_insns : busy_budget;
74}
75
98RA8_INTERNAL static bool
99internal_run_inner_check_stops(uc_engine* uc, bool* faulted, inner_action_t* act)
100{
101 (void)uc;
103 /* A --fast-sd byte-exchange returned to its caller. This consumed no
104 * modelled time, so relaunch from the returned PC without advancing the
105 * SysTick or charging a chunk (the inner-loop cap still bounds the run). */
106 *act = k_inner_continue;
107 return true;
108 }
109 if (emu_prof_stop_hit()) {
110 *act = k_inner_break; /* RA8_EMU_STOP_PC reached (prof_insn_hook) -- end the run. */
111 return true;
112 }
114 *act = k_inner_break; /* AIRCR.SYSRESETREQ -- the outer wrapper performs the reboot. */
115 return true;
116 }
117 if (emu_exc_bkpt_hit()) {
118 *faulted = true; /* firmware trapped on a BKPT -- end the run, report it. */
119 *act = k_inner_break;
120 return true;
121 }
122 return false;
123}
124
152 uint32_t vtor_base,
153 uint32_t* run_pc,
154 uc_err err,
155 bool* faulted)
156{
157 uint64_t exc_ret_pc = 0U;
158 if (emu_exc_take_exc_return(&exc_ret_pc)) {
159 exc_return(uc, (uint32_t)exc_ret_pc);
160 /* Tail-chain the next pend, but do NOT advance the SysTick: an exception
161 * return consumes no modelled time, so time advances only on a full
162 * instruction budget (below). This keeps a deferred tick from firing the
163 * instant a PendSV context switch unstacks into the new thread. */
164 (void)exc_take_pending(uc, vtor_base, false);
165 (void)uc_reg_read(uc, UC_ARM_REG_PC, run_pc);
166 return k_inner_continue;
167 }
168 if (emu_mve_nocp_take()) {
169 /* MVE NoCP fault: the seam already did the access + advanced PC; resume, synchronous. */
170 (void)uc_reg_read(uc, UC_ARM_REG_PC, run_pc);
171 return k_inner_continue;
172 }
173 if (err != UC_ERR_OK) {
174 *faulted = true;
175 return k_inner_break;
176 }
177 if (emu_mpu_fault_pending()) {
178 /* A store hit a read-only MPU region (on_mpu_ro_write stopped us).
179 * Synthesise MemManage at this boundary -- no time advances (a fault is
180 * synchronous), and the stacked PC is the faulting store. */
182 mpu_synth_memmanage(uc, vtor_base);
183 (void)uc_reg_read(uc, UC_ARM_REG_PC, run_pc);
184 return k_inner_continue;
185 }
187 /* A UDIV/SDIV by zero with CCR.DIV_0_TRP set (emulate_div0_patched stopped
188 * us). Synthesise UsageFault at this boundary -- synchronous, no time
189 * advances, and the stacked PC is the trapping divide. */
191 div0_synth_usagefault(uc, vtor_base);
192 (void)uc_reg_read(uc, UC_ARM_REG_PC, run_pc);
193 return k_inner_continue;
194 }
195 if (emu_exc_pendsv_stop()) {
196 /* Context-switch stop: a thread wrote PENDSVSET to yield. Take PendSV but do
197 * NOT advance the SysTick -- a context switch consumes no modelled time, so a
198 * thread that just suspended on a tick wait keeps waiting and the scheduler
199 * runs the highest-priority READY thread. This is what lets a low-priority
200 * worker (e.g. the NetX echo thread) run to completion before a higher-
201 * priority sleeper's tick expires. */
202 (void)exc_take_pending(uc, vtor_base, false);
203 (void)uc_reg_read(uc, UC_ARM_REG_PC, run_pc);
204 return k_inner_continue;
205 }
206 /* Full instruction budget elapsed: a tick's worth of genuine execution (or an
207 * idle spin) has passed, so advance time -- take the highest-priority pending
208 * exception (SysTick this period, and/or PendSV) and resolve it in this same
209 * chunk so a context switch does not cost a scheduling quantum. */
210 if (exc_take_pending(uc, vtor_base, true)) {
211 (void)uc_reg_read(uc, UC_ARM_REG_PC, run_pc);
212 return k_inner_continue;
213 }
214 return k_inner_break;
215}
216
217bool priv_run_inner(uc_engine* uc, uint32_t vtor_base, uint32_t* run_pc_io, uc_err* err_out)
218{
219 uint32_t run_pc = *run_pc_io;
220 uc_err err = UC_ERR_OK;
221 bool faulted = false;
222 for (uint32_t inner = 0U; inner < (uint32_t)k_run_inner_max; inner++) {
223 emu_exc_clear_pendsv_stop(); /* set by on_icsr_write iff this run ends on PENDSVSET */
224 const size_t run_budget = internal_run_inner_budget(uc, run_pc);
225 err = uc_emu_start(uc, (uint64_t)run_pc | 1U, 0, 0, run_budget);
226 (void)uc_reg_read(uc, UC_ARM_REG_PC, &run_pc);
228 if (internal_run_inner_check_stops(uc, &faulted, &act)) {
229 if (act == k_inner_break) {
230 break;
231 }
232 continue;
233 }
234 if (internal_run_inner_take_exception(uc, vtor_base, &run_pc, err, &faulted) == k_inner_break) {
235 break;
236 }
237 }
238 *run_pc_io = run_pc;
239 *err_out = err;
240 return faulted;
241}
Shared Unicorn engine access utilities for the board emulator.
bool emu_seam_take_relaunch(void)
Consume the zero-time seam-relaunch latch.
Definition emu_engine.c:58
Cortex-M exception model constants and interfaces for ra8_emulator.
bool emu_exc_take_exc_return(uint64_t *out_pc)
Consume a latched EXC_RETURN branch (read + clear).
Definition emu_exc.c:932
void emu_exc_clear_pendsv_stop(void)
Clear the PendSV context-switch stop marker (per relaunch).
Definition emu_exc.c:943
bool idle_spin_at(uc_engine *uc, uint32_t pc)
True if pc sits in a wait-for-interrupt spin (the core is idle).
Definition emu_idle.c:120
bool emu_exc_bkpt_hit(void)
Whether the firmware executed a BKPT (deliberate trap / give-up).
Definition emu_exc.c:955
bool emu_exc_pendsv_stop(void)
Whether the last engine stop was a PENDSVSET context-switch stop.
Definition emu_exc.c:949
bool emu_exc_reboot_requested(void)
Whether AIRCR.SYSRESETREQ requested a warm reboot.
void exc_return(uc_engine *uc, uint32_t exc_ret)
Perform a Cortex-M exception return for an observed EXC_RETURN branch.
Definition emu_exc.c:335
bool exc_take_pending(uc_engine *uc, uint32_t vtor_base, bool allow_systick)
Take the highest-priority pending exception, if one may activate now.
Definition emu_exc.c:530
Armv8-M MPU enforcement model for ra8_emulator.
bool emu_mpu_fault_pending(void)
Whether an RO-region write violation is latched.
Definition emu_mpu.c:320
void emu_mpu_clear_fault(void)
Clear the latched MPU violation.
Definition emu_mpu.c:326
void mpu_synth_memmanage(uc_engine *uc, uint32_t vtor_base)
Synthesise a MemManage (#4) fault for a trapped RO-region write.
Definition emu_mpu.c:281
Firmware profiler (RA8_EMU_PROFILE): sampling, hooks, reports.
bool emu_prof_stop_hit(void)
Whether the RA8_EMU_STOP_PC address was reached.
Definition emu_prof.c:967
@ k_run_inner_max
Per-chunk exception-resolve relaunch cap.
Definition emu_run.h:63
@ k_run_chunk_insns
Instructions per emulation chunk.
Definition emu_run.h:38
@ k_low_power_div
Low-power: shrink the chunk budget by.
Definition emu_run.h:39
@ k_idle_spin_insns
Budget when the core is parked on a wait-for-interrupt spin (a "b ." self-branch, wfi,...
Definition emu_run.h:47
static RA8_INTERNAL size_t internal_run_inner_budget(uc_engine *uc, uint32_t run_pc)
Compute one inner chunk's instruction budget for run_pc.
inner_action_t
Loop-control verdict a priv_run_inner boundary helper hands back.
@ k_inner_continue
Re-enter the inner loop (relaunch / tail-chain).
@ k_inner_break
End the inner loop (fault / BKPT / quiescent).
static RA8_INTERNAL inner_action_t internal_run_inner_take_exception(uc_engine *uc, uint32_t vtor_base, uint32_t *run_pc, uc_err err, bool *faulted)
Take the pending exception (or end) at the chunk boundary.
bool priv_run_inner(uc_engine *uc, uint32_t vtor_base, uint32_t *run_pc_io, uc_err *err_out)
Run one outer chunk's inner exception-resolve loop.
static RA8_INTERNAL bool internal_run_inner_check_stops(uc_engine *uc, bool *faulted, inner_action_t *act)
Resolve the zero-time relaunch and run-ending stop conditions.
Shared run-loop state + cross-TU run helpers (ra8_emulator internal).
Armv8.1-M instruction-emulation seams (M85 ops on Unicorn's M33).
bool emu_div0_fault_pending(void)
Whether a trapping divide-by-zero is latched for the run loop.
void div0_synth_usagefault(uc_engine *uc, uint32_t vtor_base)
Synthesise a UsageFault (#6) for a trapped divide-by-zero.
void emu_div0_clear_fault(void)
Clear the latched divide-by-zero fault.
bool emu_mve_nocp_take(void)
Test and clear the "NoCP fault serviced by the MVE seam" latch.
Board-view presentation: frames, composition, input routing, panels.
bool emu_low_power(void)
Whether the M33 4:1-slower low-power clock model is active.
Definition emu_view.c:645
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).