Skip to content

pit: implement the 8254 read-back command and release single-byte latches - #323

Merged
techomancer merged 1 commit into
techomancer:mainfrom
irix7:pr/pit-readback
Oct 25, 2026
Merged

techomancer merged 1 commit into
techomancer:mainfrom
irix7:pr/pit-readback

Conversation

@belikh

@belikh belikh commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

The 8254 control word 11 (read-back) was ignored: a guest asking to latch a counter's status, or status and count together, got whatever the data port read next. Several guests and diagnostics use read-back to sample a counter without disturbing it.

  • Implement read-back: D5/D4 latch count/status (active low), D3..D1 select counters, a pending latch is left alone until read, and the status byte is served before the count.
  • Release a single-byte (RW=1/2) count latch after its one read; it used to stay latched forever.
  • Compute the counter-latch value from the channel's own input frequency instead of the 1 MHz base clock (wrong for chained counters 0/1).
  • Status reports OUT (armed), RW, mode and BCD; null count is not modelled and reads clear.

Evidence: three new tests fail on upstream main before this change and pass after:

test dev::pit8254::tests::readback_latches_status_before_count ... FAILED
test dev::pit8254::tests::readback_status_only_then_count ... FAILED
test dev::pit8254::tests::single_byte_latch_releases_after_one_read ... FAILED
test result: FAILED. 0 passed; 4 failed

(The fourth failure was the serialised test mutex being poisoned by the three failures.)

Tests: cargo test -p iris --lib dev::pit8254::tests — 4 passed, 5 timed tests ignored.

…ches

The control word 11 (read-back) was ignored, so a guest could not latch a counter's status, or status and count together, and the 8254 returned whatever the data port read next. Implement it: D5/D4 select count/status, D3..D1 select counters, a pending latch is left alone until read, and the status is served before the count. Also release a single-byte (RW=1/2) count latch after its one read instead of leaving it latched forever, and compute the counter-latch value from the channel's own input frequency rather than the 1 MHz base clock.
belikh added a commit to irix7/iris that referenced this pull request Oct 25, 2026
@techomancer

Copy link
Copy Markdown
Owner

really, someone uses that. lets go!

@techomancer
techomancer merged commit b545aa0 into techomancer:main Oct 25, 2026
7 checks passed
belikh added a commit to irix7/iris that referenced this pull request Oct 25, 2026
Upstream merged the PIT read-back (techomancer#323); the fork keeps its clock-based PIT until techomancer#322. Upstream's SparseDeviceMap replaces DecodeMap only after the dirty-bitmap snapshot methods are ported.
belikh added a commit to irix7/iris that referenced this pull request Oct 26, 2026
CP0 Count is host-Instant-derived again in normal builds, and the Compare
interrupt is armed as an hptimer one-shot. This reverts the fork's
cycle-derived-by-default clock (e8b264e) and deletes the ordered guest-time
timer queue (800f785, src/cpu/guest_timer.rs) with its Machine field, PIT
registration and drain calls. The 8254 PIT returns to upstream's Instant
channels, keeping the read-back and latch-release work that landed as
upstream techomancer#323.

--features ci_clock is a real feature again: Count is derived from hot.cycles
at 10 ns per retired instruction, Compare is a count_fire_cycle threshold
checked by the per-instruction preamble and by idle parking, and the feature
is recorded in snapshot provenance so a restore into the other clock model is
refused. The CPU state description is version 3 with a `clock` field. A
version-2 fork payload migrates to the running build's clock, drops the
retired queue and gets a fresh Compare deadline from the CP0 load path.

Idle-pace (#83) is kept against the wall clock: a parked CPU advances
hot.cycles for cross-thread readers, wakes on any pending line, and paces a
masked pending interrupt. A park that wakes on a line the guest cannot take
backs off for about 20 ms: the wall clock no longer makes Count advance with
hot.cycles, so parking an instruction-counted delay loop capped boot at one
batch per slice.

The snapshot determinism validator now releases the stopped-CPU Count latch
for its inline passes, so it can tell the two clock models apart instead of
comparing two identically frozen Counts.

Measured on callisto (x86_64, 16 cores):
- determinism, bare Count-read loop, validate -n 1000000: default
  deterministic false (cp0_count A=0xe071974 B=0xe071a0e), ci_clock true.
- boot to IRIX 6.5 login, R4400: wall 45 s (three runs), ci_clock 45 s.
- guest sleep 30: wall 33 s host, ci_clock 108 s host (interface only).
- bench suite, R4400, three runs: wall 42.8 MIPS, ci_clock 41.5 MIPS,
  both 100% accuracy.
belikh added a commit to irix7/iris that referenced this pull request Oct 26, 2026
CP0 Count is host-Instant-derived again in normal builds, and the Compare
interrupt is armed as an hptimer one-shot. This reverts the fork's
cycle-derived-by-default clock (e8b264e) and deletes the ordered guest-time
timer queue (800f785, src/cpu/guest_timer.rs) with its Machine field, PIT
registration and drain calls. The 8254 PIT returns to upstream's Instant
channels, keeping the read-back and latch-release work that landed as
upstream techomancer#323.

--features ci_clock is a real feature again: Count is derived from hot.cycles
at 10 ns per retired instruction, Compare is a count_fire_cycle threshold
checked by the per-instruction preamble and by idle parking, and the feature
is recorded in snapshot provenance so a restore into the other clock model is
refused. The CPU state description is version 3 with a `clock` field. A
version-2 fork payload migrates to the running build's clock, drops the
retired queue and gets a fresh Compare deadline from the CP0 load path.

Idle-pace (#83) is kept against the wall clock: a parked CPU advances
hot.cycles for cross-thread readers, wakes on any pending line, and paces a
masked pending interrupt. A park that wakes on a line the guest cannot take
backs off for about 20 ms: the wall clock no longer makes Count advance with
hot.cycles, so parking an instruction-counted delay loop capped boot at one
batch per slice.

The snapshot determinism validator now releases the stopped-CPU Count latch
for its inline passes, so it can tell the two clock models apart instead of
comparing two identically frozen Counts.

Measured on callisto (x86_64, 16 cores):
- determinism, bare Count-read loop, validate -n 1000000: default
  deterministic false (cp0_count A=0xe071974 B=0xe071a0e), ci_clock true.
- boot to IRIX 6.5 login, R4400: wall 45 s (three runs), ci_clock 45 s.
- guest sleep 30: wall 33 s host, ci_clock 108 s host (interface only).
- bench suite, R4400, three runs: wall 42.8 MIPS, ci_clock 41.5 MIPS,
  both 100% accuracy.
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.

2 participants