Skip to content

Feat/run stepback edit#79

Merged
Gaok1 merged 13 commits into
mainfrom
feat/run-stepback-edit
Jul 15, 2026
Merged

Feat/run stepback edit#79
Gaok1 merged 13 commits into
mainfrom
feat/run-stepback-edit

Conversation

@Gaok1

@Gaok1 Gaok1 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

No description provided.

Gaok1 and others added 13 commits June 1, 2026 17:04
- Rewrite EN virtual-memory.md to full parity with the PT guide (21 sections)
- PT: add the four VM modes (Off/Sv32/Custom/Manual) + Custom parametric
  scheme; rewrite the tab section to the real structure
  (Status/Tree/Settings/TLB; TLB Stats/Entries/Settings); fix stale UI refs
- READMEs (en+pt): add the Virtual Memory/TLB tab to the feature list and
  link virtual-memory.md; fix tab numbering
- cache-config.md (en+pt): document the tlb.* block (.fcache) and the
  vm_mode/vm_offset_bits/vm_level_bits fields (.rcfg)
- platform doc: add VM/TLB subsection + doc-index entry
- in-app: drop the .rs citation from the TLB tutorial; add the TLB/VM block
  to the in-app .fcache reference
Introduce src/falcon/machine/ as the single sanctioned owner of mutable
simulator state, with a reversible step journal. Standalone in this phase
(#[allow(dead_code)]); wired into RunState in Phase 2.

- types/parse: typed edit targets (RegId/FRegId/MemWidth/RegTarget) and
  width-bounded parsing that rejects overflow (never truncates); accepts
  `_` digit separators.
- journal: bounded stack of ChangeSets with a Rewind enum
  (CpuOnly / Delta / Full).
- RAM capture at the real chokepoint Ram::store8 (write_log pre-images),
  correct under write-back caches; CacheSnapshot clones the cache
  subsystem (sans RAM). mem_mut_unjournaled preserves a Full checkpoint.
- 11 unit tests: step/edit round-trip, overflow rejection, ring bound.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace RunState's `pub(crate) cpu` / `pub(crate) mem` fields with a single
`machine: Machine`, routing the ~262 read sites through thin `run.cpu()` /
`run.mem()` accessors and every mutation through the journaling gateway's
`*_unjournaled()` escape hatches. No behavior change: the journal stays empty
until Phase 3 wires execution through `step_interpreted`, so the unjournaled
clears are no-ops here.

- RunState.machine is pub(crate) (not private): the type-safety that matters is
  Machine's own private cpu/mem fields (mutation only via named methods). A
  private field would break the disjoint `self.run.machine` vs `self.run.backend`
  borrows used in GO / background-hart stepping.
- Add Machine::cpu_mem_mut_unjournaled() -> (&mut Cpu, &mut CacheController) for
  the four sites needing both &mut at once (single-step ExecCtx, pipeline_tick,
  and the two sync_mmu_to_cpu calls) — two separate &mut self accessors can't
  stack.
- Reads renamed across view/input/app; reset/load/config mutations go through
  cpu_mut_unjournaled()/mem_mut_unjournaled(); test support helpers migrated too.

cargo build (default + --features jit) + 357 tests + clippy --all-targets green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Route the non-GO branch of `single_step_selected_sequential` through the
journaling `Machine::step_interpreted` instead of building a manual
`ExecCtx` + a throwaway `InterpreterBackend::default()`. The step now
records a reversible change-set (consumed by the Phase 4 stepback button);
nothing observes the journal yet, so behavior is unchanged.

- GO keeps the JIT burst path: `cpu_mem_mut_unjournaled()` + `self.run.backend`
  (GO writes RAM directly and bypasses the byte-level log).
- New `Machine::account_step_cycles(cpi)` folds the per-instruction
  cycle/stats accounting (`add_instruction_cycles` + `snapshot_stats`) into
  the journaled step *without* clearing the journal — the step's pre-image
  snapshot already covers it, so stepback reverts it too. GO still accounts
  via `mem_mut_unjournaled()`.

step_interpreted reuses the Machine's persistent InterpreterBackend (only its
JIT hot-profile accumulates; sim results are identical to default() per step).

cargo build (default + --features jit), 357 tests, cargo clippy --all-targets:
all green, no new warnings.
Wire the journaled step history to the Run tab: a `step-back` control and a
`b` shortcut undo the most recent instruction, edit, or whole GO burst.

Machine / journal:
- `stepback()` now returns the `StepbackKind` it undid (Step | Edit |
  Checkpoint) so the UI refreshes the matching bookkeeping; `ChangeSet` gains
  a `kind` field, threaded through `record`.
- New `Machine::sync_mmu()` — a journal-preserving MMU satp/priv sync. The old
  per-step `sync_mmu_to_cpu` went through `cpu_mem_mut_unjournaled`, which
  cleared the journal on *every* sequential step and silently truncated the
  history to one entry. The sync only writes MMU metadata (captured by the
  next step's snapshot), so it never needed to clear. Fixes multi-step undo.

App:
- `App::stepback_one()` pops one change-set and refreshes reg/float ages,
  prev_x/pc, the exec-trace row + run-count (Step only), fault/lifecycle, and
  the imem scroll. `can_stepback_now()` gates on `!is_running && can_stepback()`
  — a non-empty journal already implies the last activity was a reversible
  sequential step / GO burst (pipeline & background harts clear it).
- GO bursts take a one-shot full checkpoint at the burst's first tick
  (`run.go_checkpointed`), so step-back rewinds to just before the burst.
- `restart_simulation` drops the journal (the timeline belongs to the program
  being replaced).

UI — new reusable `Toolbar` component (view/components/toolbar.rs):
- Models a dense row of label/value + action cells as the single source of
  truth for both rendering and mouse hit-testing. The run-controls bar used to
  compute its layout twice (spans in the view, parallel column arithmetic in
  `run_status_hit`) and drift on every change; both now derive from one
  `build_run_toolbar(app)`. `run_status_hit` shrinks from ~115 lines to a
  one-line delegation, and the local span helpers in status.rs are gone.
- `RunButton::Stepback` added; rendered dimmed + unclickable when unavailable.

Tests: `StepbackKind` reporting (machine), step-back availability in the bar
(mouse), and a full multi-step undo round-trip reverting regs/PC/trace (app).
cargo build (default + --features jit), 360 tests, clippy --all-targets: green,
no new warnings.
Make Machine<P: JournaledPipeline = NoPipeline> own and journal a pipeline
snapshot alongside CPU/memory. Every change-set now carries the pipelines
reversible exec snapshot (() for NoPipeline), captured by the same journaling
methods, and stepback restores it. Adds step_pipeline (the sole journaling
tick path) and pipeline()/pipeline_mut() accessors. Default NoPipeline keeps
all existing callers and the 13 machine tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…l it per cycle

The pipeline is microarchitectural CPU state, so it now lives *inside* Machine
together with cpu/mem instead of as a free field on App. Each clock cycle runs
through Machine::step_pipeline, which snapshots cpu + pipeline + cache and the
RAM pre-images before the tick and records one change-set — so a single
step-back rewinds the whole cycle (stages, functional units, branch predictor,
hazard read-outs, Gantt history, fetch PC, counters) atomically with the CPU
and memory. Forgetting to journal a cycle is no longer expressible: the
pipeline field is private and the only execution path is step_pipeline.

This is the fix for "stepback não funciona": the default mode is the pipeline
(enabled, or sequential/await), whose ticks used to go through the unjournaled
hatch — and the per-cycle MMU re-sync went through it too, clearing the journal
every cycle — so can_stepback_now() was never true outside the pipeline-off
path. step_pipeline re-syncs the MMU journal-preservingly and fills the journal.

Mechanics:
- Machine is generic over P: JournaledPipeline (default NoPipeline) so falcon
  stays ui-agnostic; ui instantiates Machine<PipelineSimState>.
- PipelineExecSnapshot captures only the reversible exec slice (not view/config).
- New StepbackKind::Cycle marks a stall/bubble cycle (state rewinds, exec-trace
  and run counts untouched); a committed cycle is Step. stepback_one decrements
  the run count by the popped trace rows PC (the retired instruction is stages
  behind the rewound fetch PC in the pipeline).
- account_pipeline_commit replaces the post-commit mem_mut_unjournaled accounting
  that would otherwise erase the just-recorded cycle.
- ~250 pipeline access sites rewired through run.pipeline()/pipeline_mut().

Test: stepback_reverts_pipeline_cycle_state drives the pipeline and proves a full
cycle-by-cycle round-trip to the pre-run CPU+pipeline state. 344 tests green,
clippy clean (no new warnings), default + --features jit build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…re config

Drop the max_cores=1 pin from stepback_reverts_pipeline_cycle_state so it runs
with the shipped default (4 cores, AllHarts scope, only hart 0 running) — the
exact configuration a user hits — proving background harts stay idle and do not
clear the journal, so pipeline step-back is live by default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hine

Add inline editing to the Run tab for integer registers x1–x31, the PC,
float registers f0–f31, and RAM cells (width follows mem_view_bytes). Every
edit funnels through the journaling Machine mutators (write_reg / write_freg
/ write_mem), so a manual edit is undone by step-back exactly like an
executed instruction. Out-of-range input is rejected (editor stays open with
a message), never silently truncated; x0 is immutable.

State: RunEditTarget {Reg|FReg|Mem} + run_edit/run_edit_buf/run_edit_error in
RunState. App gains begin/cancel/commit_run_edit plus cell_format,
run_edit_seed and refresh_after_edit helpers.

Keyboard: an open editor claims keystrokes at the top of handle_execution_key
(Esc cancels, Enter commits, Backspace trims, format-valid chars extend); the
plain-char global shortcuts ([ ] ? h) are guarded while editing so they can be
typed into a string cell.

Mouse: a value-column click opens the editor (integer rows keep label-column
pin/unpin; float rows; new handle_memory_click for RAM cells). The memory
row→address math moved to RunState::visible_memory_base_addr so the renderer
and the click hit-test share one source of truth.

Render: cursor overlays ({buf}█) for the edited register/float/memory cell in
sidebar.rs, plus a status line carrying the commit/cancel prompt or the
rejection message. tick() closes any open editor when a run becomes active.

Tests: 8 new run_edit tests (write reg/PC/float/memory, x0 + overflow
rejection, edit↔stepback round-trip, no-op while running). 369 tests green,
clippy clean (no new warnings), build default and --features jit.

Instruction-word editing (RunEditTarget::Instr) is deferred to a follow-up:
the imem click already redirects the PC, so it needs a dedicated UX decision.
Editing the PC wrote cpu.pc but left the pipeline's fetch_pc stale, so with
the pipeline enabled (the default) the next step kept fetching the old
address — the PC "didn't advance". commit_run_edit now redirects the pipeline
to the new PC on a successful PC edit, mirroring the imem PC-redirect click.
The redirect is reversible state captured by the change-set's pipeline
snapshot, so step-back undoes it together with the PC write.

New test commit_pc_redirects_pipeline_fetch; run_edit suite 9 green.
…ditor

Two additions to the Run tab's value handling.

Binary view: FormatMode gains a Bin variant, cycled by the Format button
(hex → dec → bin → str). Values render as 0b… (32/16/8 bits by cell width) and
the editor parses base-2 input (optional 0b prefix, `_` separators), rejecting
anything wider than the cell — the same overflow guard as the other formats.

Clipboard: Ctrl+C copies the edit buffer, Ctrl+V (and a bracketed terminal
paste) appends clipboard text, filtered to characters legal for the active
format so a trailing newline or stray spaces are dropped. This moved the
editor's whole key path into handle_post_find_intercepts, where key modifiers
are visible and it pre-empts every Run shortcut — replacing the earlier split
between handle_execution_key and a global-shortcut guard. edit_char_allowed is
now the shared rule for both typing and paste.

Tests: parse_cell_binary (base-2 + overflow + non-binary-digit), and
commit_writes_register_in_binary. 372 tests green, clippy clean, build default
and --features jit.
…imem panel

Right-click (or double left-click) on an imem row opens the inline editor
on the 32-bit instruction word; a plain click keeps its PC-redirect
meaning and the marker column keeps the breakpoint toggle. While typing,
the row shows a live decode preview of the partial value.

Commit goes through the journaling Machine::write_mem (so step-back
undoes the edit), then invalidates the JIT range and, with the pipeline
enabled, refetches from the current PC so a stale latched word never
executes.
… details panel

Adds a details panel that stays pinned to a selected instruction and lets
individual fields (registers, immediate, funct bits, binary view, or the
mnemonic as assembly) be double-clicked and edited directly, sharing the
journaled write path with the existing word-level editor so step-back still
undoes field edits. Also folds in pipeline control-style rework and toolbar
component reuse touched by this pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Gaok1
Gaok1 merged this pull request into main Jul 15, 2026
10 checks passed
Gaok1 added a commit that referenced this pull request Jul 15, 2026
Gaok1 added a commit that referenced this pull request Jul 18, 2026
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