psx_residency_sim/lib.rs
1// SPDX-License-Identifier: GPL-2.0-or-later
2//! Host-side drive model and route simulator for `psx-residency`.
3//!
4//! The simulator runs the real [`psx_residency::Residency`] against a model of the
5//! console's drive, so a route through a region graph can be checked for
6//! deadline slack and misses long before the transport exists on the console.
7//! Nothing here is part of a guest build: games depend on `psx-residency`
8//! only, and this crate is `no_std` plus `alloc` so it still compiles for the
9//! console target in the SDK lint.
10//!
11//! # What is measured and what is assumed
12//!
13//! The drive model's numbers come from the streaming survey and are labelled
14//! as the survey labels them:
15//!
16//! * Read: 6.49 ms per sector at double speed and 13.20 ms at single speed,
17//! measured on silicon, within 1% of the specification.
18//! * Seek: 11, 79, 137 and 310 ms for 1, 16, 128 and 512 sectors, measured on
19//! silicon, with a run-to-run swing of about 2x (rotational phase dominates).
20//!
21//! Everything else is an assumption, and the model says so in its types:
22//! seeks between measured points are interpolated linearly, seeks beyond 512
23//! sectors are clamped to the 512-sector value unless the caller supplies one
24//! ([`DriveModel::with_far_seek_millis`]), a cancelled read stops
25//! instantly unless the caller supplies an abort time, and install work costs
26//! nothing unless the caller supplies ticks per region (the design lists the
27//! install cost as unmeasured). A simulation result is only as good as these.
28//!
29//! # Use
30//!
31//! ```
32//! use psx_residency_sim::{simulate, RegionGraph, Route, SimConfig};
33//!
34//! // Four regions of 16 sectors, 400 units apart, laid out in route order.
35//! let mut graph = RegionGraph::chain(4, 16, 400);
36//! graph.layout_in_order(1_000, 0);
37//! graph.needs_with_neighbors();
38//! // Sprint along them at 375 units per second.
39//! let route = Route::walk(&graph, &[0, 1, 2, 3], 375);
40//! let config = SimConfig::new(96, 900);
41//! let report = simulate(&graph, &route, &config);
42//! assert_eq!(report.misses, 0);
43//! assert!(report.min_slack_millis > 0);
44//! ```
45
46#![no_std]
47
48extern crate alloc;
49#[cfg(test)]
50extern crate std;
51
52pub mod churn;
53pub mod drive;
54pub mod run;
55pub mod world;
56
57pub use churn::{churn_report, ChurnConfig, ChurnReport};
58pub use drive::{
59 DriveModel, Variance, MEASURED_SEEK_MILLIS, READ_MICROS_PER_SECTOR_DOUBLE,
60 READ_MICROS_PER_SECTOR_SINGLE,
61};
62pub use run::{simulate, SimConfig, SimReport, WorstMiss};
63pub use world::{RegionGraph, Route, RouteSample, SimRegion, TICKS_PER_SECOND};