ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_idle.c File Reference

Wait-for-interrupt idle-spin detector (see emu_exc.h). More...

#include "emu_exc.h"
#include "emu_run.h"
Include dependency graph for emu_idle.c:

Go to the source code of this file.

Functions

static RA8_INTERNAL bool internal_idle_back_edge (uc_engine *uc, uint32_t at, uint16_t hw, uint32_t aligned, bool *done)
 Does the halfword at at close a tight idle loop around aligned?
bool idle_spin_at (uc_engine *uc, uint32_t pc)
 True if pc sits in a wait-for-interrupt spin (the core is idle).

Detailed Description

Wait-for-interrupt idle-spin detector (see emu_exc.h).

Decides whether the guest PC is parked in a wait-for-interrupt spin so the run loop can cap the idle chunk's instruction budget instead of burning a full chunk on a loop that cannot make progress until an interrupt arrives. Pure Thumb instruction decoding over a read-only view of guest memory: it recognises the one-instruction forms (b ., wfi) directly and otherwise walks forward to a backward b.n that brackets the PC, requiring a wait signature inside the bracketed body so a tight busy loop is not mistaken for an idle one.

Split out of emu_exc.c: the detector shares none of the exception engine's state, and both translation units are clearer for the separation.

Since
0.1.0

Definition in file emu_idle.c.

Function Documentation

◆ idle_spin_at()

bool idle_spin_at ( uc_engine * uc,
uint32_t pc )

True if pc sits in a wait-for-interrupt spin (the core is idle).

Reports whether the core at pc is parked in a loop that can only make progress once an interrupt arrives – genuine idle, where the next thing that can happen is the periodic SysTick. Two cases are recognised:

  1. The instruction AT pc is itself a halt: b . (0xE7FE, branch-to-self) or wfi (0xBF30).
  2. pc is ENCLOSED by a wait-for-interrupt poll loop: scanning forward a few halfwords finds an unconditional backward b.n (the loop back-edge) whose target is at or before pc (so the loop wraps around pc), and the loop body holds a wfi or a cpsie i – the "re-enable interrupts and poll" idiom ThreadX's __tx_ts_wait uses (cpsid/ldr/str/cbnz/cpsie/ b .-N, spinning on execute_ptr until a tick makes a thread runnable).

The enclosing-loop test is deliberately tight: it requires the back-edge to bracket pc, so STRAIGHT-LINE code is never matched even when it sits in memory next to an idle loop (an ISR returns via bx lr, not a backward branch over itself – matching a nearby opcode would wrongly truncate it). A compute/busy loop is also excluded: it exits on a conditional branch and never re-enables interrupts mid-loop, so it carries no wfi/cpsie wait. The run loop uses this to cap the idle chunk's budget to k_idle_spin_insns instead of spinning a full k_run_chunk_insns to reach the same already-armed tick. Tick COUNT is unchanged; only idle wall-time is skipped.

Parameters
[in,out]ucUnicorn engine (instructions are read from its memory).
[in]pcProgram counter to inspect (Thumb bit ignored).
Returns
true if pc is on, or enclosed by, a wait-for-interrupt idle loop.
Precondition
uc has the code region containing pc mapped.
pc is halfword-aligned once the Thumb bit is cleared.
Postcondition
uc is unchanged (a read-only probe).
Note
Detection only; advancing time stays the run loop's job, so the tick count – and every tick-based sleep/heartbeat deadline – is preserved.
Since
0.1.0

Definition at line 120 of file emu_idle.c.

References emu_mem_read(), internal_idle_back_edge(), k_idle_scan_fwd, k_op_branch_self, k_op_wfi, and k_thumb_hw_bytes.

Referenced by internal_run_inner_budget().

◆ internal_idle_back_edge()

RA8_INTERNAL bool internal_idle_back_edge ( uc_engine * uc,
uint32_t at,
uint16_t hw,
uint32_t aligned,
bool * done )
static

Does the halfword at at close a tight idle loop around aligned?

Decodes an unconditional Thumb b.n and requires three things of it: the branch goes backwards, its target brackets aligned within k_idle_loop_max bytes, and the bracketed body contains a wait signature (cpsie i or wfi). A tight backward loop with no wait is a busy loop, not an idle one, and must not be reported as idle.

Parameters
[in]ucEngine to read instruction memory from.
[in]atAddress of the candidate branch halfword.
[in]hwThe halfword already read at at.
[in]alignedThe PC under test, halfword-aligned.
[out]doneSet true when the answer is final either way.
Returns
True when at closes an idle loop around aligned.
Precondition
uc and done are non-NULL.
hw is the halfword actually stored at at.
Postcondition
*done is true whenever the caller must stop scanning.
No judgement is returned unless the branch is unconditional b.n.
Note
Not thread-safe; the emulator is single-threaded host-side.
Return values
trueThe idle back edge condition holds or completed successfully; false otherwise.
Since
0.1.0

Definition at line 54 of file emu_idle.c.

References emu_mem_read(), k_bn_imm_sext_shl, k_bn_imm_sext_shr, k_idle_loop_max, k_op_bn_base, k_op_bn_imm, k_op_bn_mask, k_op_cpsie_i, k_op_wfi, k_thumb2_insn_bytes, and k_thumb_hw_bytes.

Referenced by idle_spin_at().