Skip to content

Read and write Cortex-M0 registers while halted. - #74

Merged
jon merged 3 commits into
mainfrom
jon/cortexm-registers
Sep 27, 2026
Merged

jon merged 3 commits into
mainfrom
jon/cortexm-registers

Conversation

@jon

@jon jon commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Add Target.ReadRegister and Target.WriteRegister for an acquired Cortex-M0. Reads cover R0–R12, SP, LR, PC, XPSR, MSP, and PSP; writes cover the same set except XPSR. An inherited halt permits register access without granting permission to resume it. Register writes persist after release.

With a borrowed MEM-AP, this reads PC and deliberately changes R4 to 42 before resuming:

core, err := cortexm.Acquire(ctx, memory)
// Retain every non-nil core, including after failed acquisition.
if err == nil {
    err = core.Halt(ctx)
}
if err == nil {
    var pc uint32
    pc, err = core.ReadRegister(ctx, cortexm.PC)
    if err == nil {
        fmt.Printf("PC=%#08x\n", pc)
    }
}
if err == nil {
    err = core.WriteRegister(ctx, cortexm.R4, 42)
}
// Do halted things.
if err == nil {
    err = core.Resume(ctx)
}
cleanupCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
cleanupErr := core.Release(cleanupCtx)
cancel()
err = errors.Join(err, cleanupErr)
// Close the memory owner only after target release succeeds.
// Retain both owners after failure; retry with a usable context.

SP selects the active stack bank; MSP and PSP name the banks explicitly. Stack writes require word alignment. PC is the debug return address and requires bit zero clear; writing it does not change Thumb state. Invalid identifiers and values are rejected before memory traffic.

Why

Halting the processor was available, but inspecting or changing its registers required callers to manage debug transfers themselves. The target now manages DCRDR data, selects the core register through DCRSR, and waits for S_REGRDY with the caller's cancellation and earlier deadline, capped at five seconds.

A selector write is never replayed after uncertain completion. Pending transfers block ordinary calls, and release must settle them before resuming or disabling debug. Reset or loss of Debug state during a pending transfer prevents automatic cleanup. A failed write may have changed the register; release does not roll back intentional register changes or restore DCRDR scratch contents.

Documentation

Update the Cortex-M guide, capability table, architecture and composition guides, and control example. The example prints PC, SP, R0, and R4 while halted. The API remains Cortex-M0 only; XPSR writes, CONTROL/PRIMASK access, reset, and stepping are outside this change.

Hardware evidence

On September 26, 2026, Nostalgia (macOS) ran two fresh CMSIS-DAP micro:bit sessions through SWD at a requested 1 MHz and AP0. Probe serial 9900360140124e4500279015000000360000000097969901; Cortex-M0 CPUID 0x410cc200. The test verified the vectors and instructions of the previously programmed counter image before acquiring the target.

OSTIOLE_CORTEXM_HIL_CONTROL=1 \
OSTIOLE_CORTEXM_HIL_REGISTERS=1 \
OSTIOLE_CORTEXM_HIL_PROGRAM=sha256:ee294cc06ab6e8228161b49506675b065c0148b26421cf1f83c8e45e35cd4e5d \
go test -tags integration ./target/cortexm -run '^TestHILCortexM0Registers$' -count=1 -v

Both sessions read all 19 registers, wrote and read back two R4 patterns plus temporary SP, MSP, PSP, and PC values, then restored and verified the full register snapshot. SP and MSP aliased as expected. The counter remained stopped across ten samples 20 milliseconds apart after restoration and advanced after resume and release. DHCSR was 0x01000000 before acquisition and after release in both sessions; initially disabled debug and running state were restored. Both target and Arm debug owners closed successfully.

The temporary PC and stack values were never used for execution. Writes to the other general registers and LR, process-stack selection, inherited halts, and failure cleanup have behavioral coverage only. State after Arm owner close was not measured.

jon added 3 commits September 26, 2026 16:08
Callers could halt a Cortex-M0 but could not inspect its registers
through the target owner. Add typed register reads while halted,
including when the halt belongs to another debugger.

Track transfers before selecting a register so cleanup cannot resume the
processor or disable debug while completion is uncertain. Release waits
for a pending transfer without replaying the selection; reset or loss of
Debug state prevents automatic cleanup.
The acquired target can now change core registers as well as read them.
Keep XPSR read-only and reject unaligned stack pointers and odd debug
return addresses before sending traffic.

A failed write may already have changed the register. Keep only cleanup
available after data staging fails or later completion is uncertain,
without replaying the selection. Writes persist after release, and
writing during an inherited halt does not grant resume ownership.
Show register reads between halt and resume in the control example. The
gated bench checks the known counter firmware, reads the selected
register set, and writes R4 patterns plus temporary stack and PC values.
It restores and verifies all registers before allowing execution again;
unconfirmed restoration retains both owners without requesting resume.

Two fresh CMSIS-DAP sessions at 1 MHz passed on the micro:bit. The CPU
counter stopped while halted and progressed after resume and release.
Both sessions restored disabled debug and closed both owners. This does
not establish execution with the temporary PC or stack values, or
cleanup after a physical transport failure.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T00:05:13.272962Z 36d1c00 Manual request
🔒 Security Review ✅ Completed 2026-09-27T00:05:27.784297Z 36d1c00 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@jon

jon commented Sep 27, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 36d1c00e74

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jon
jon merged commit 9eb90f4 into main Sep 27, 2026
9 checks passed
@jon
jon deleted the jon/cortexm-registers branch September 27, 2026 00:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant