Binary timing result store (sta.rbin / STA v2)¶
The binary timing result store (sta.rbin, artifact kind "STA", format version 2) persists the
complete output of a static timing run in the shared RBIN v1 container
(crates/core/rusteda-workspace/src/rbin.rs, specified in
docs/specs/workspace/README.md). It satisfies the binary store,
incremental staging, and mapped query requirements (STA-REQ-0302, STA-REQ-0305, STA-REQ-0312
to STA-REQ-0315, STA-REQ-0401, STA-REQ-0405, WS-REQ-0030 to WS-REQ-0038, WS-REQ-0045).
Design rationale and layout trade-offs are recorded in
ADR 0007.
1. Container and opening contract¶
sta.rbin is a little-endian, 8-byte-aligned RBIN v1 file with kind = "STA" and
format_version = 2. The engine reader (StaStore) opens version 2 only and rejects an older
table with a corrupt-format error, so a revision analyzed by an older ruststa is analyzed again
(ruststa run). The shared workspace reader (rusteda_workspace::sta) reads versions 1 and 2
(docs/specs/workspace/sta.md). Every fixed-stride section uses a #[repr(C)] Pod + Zeroable record type
so bytemuck::try_cast_slice succeeds directly on an mmap'd file (Revision::open_rbin) or
aligned buffer (Rbin::read) without copying or heap allocation.
Two open paths are provided on StaStore:
- Mapped query open (
StaStore::from_rbin,StaStore::read_path,StaStore::open_rev,StaStore::open_head): validates the 64-byteRBIN v1header, the 24-entry section directory (exact section IDs1..=24, element sizes, 8-byte alignments, and in-file bounds), and the 64-byteStaMetastructure invariants (store_mode,precision,num_corners,num_nodes,NODE_REClength,EP_SLACKlength,EP_ORDERlength, andPATH_CHAIN_PTRlength) inO(1)time without hashing the file payload (WS-REQ-0036). - Full payload verification (
StaStore::verify_hash,StaStore::open_verified_path,StaStore::open_for_mutation_input): recomputesxxh3-64over[header_len .. EOF)and verifies that it matches the stored checksum (WS-REQ-0035,WS-REQ-0036). Commands that consumesta.rbinas a mutation input (ruststa check) or verify a revision (rusteda verify) call this path.
2. Section directory (1..=24)¶
Every STA v2 file contains the 24 sections below in ascending section ID order:
| ID | Name | elem_size |
Description |
|---|---|---|---|
| 1 | STR_BYTES |
1 | Concatenated UTF-8 string bytes (string 0 is ""). |
| 2 | STR_OFFS |
4 | u32 byte offsets into STR_BYTES (N_strings + 1 sentinel entries). |
| 3 | META |
64 | Single StaMeta record: counts, store_mode, precision, num_top_k_paths, backend_str. |
| 4 | CORNERS |
16 | CornerRec per operating corner (name_str, lib_count, flags). |
| 5 | CLOCKS |
48 | ClockRec per constraint clock (period_ns, waveform edges, setup/hold uncertainty). |
| 6 | TAGS |
16 | TagRec per launch tag (clock_id, launch_edge, exc_state, launch_source_node, path_group_str). |
| 7 | DISABLED_ARCS |
16 | DisabledArcRec per disabled timing arc (arc_idx, src_node, dst_node, reason). |
| 8 | NODE_REC |
152 | Per-(corner, node) NodeRec indexed by corner_idx * num_nodes + node_id (Full mode; empty in Summary mode). |
| 9 | TAG_EXTRA_PTR |
4 | CSR row pointer (num_corners * num_nodes + 1) * u32 into TAG_EXTRA (Full mode; empty in Summary mode). |
| 10 | TAG_EXTRA |
56 | TagExtraRec for active launch tags beyond inline primary tag (Full mode; empty in Summary mode). |
| 11 | ENDPOINTS |
36 | EndpointRec per checked timing endpoint (data_node, clock_node, capture_clock, path_group_str, check_arc_idx, kind_flags, worst_setup_rank, worst_hold_rank, name_bucket_plus1). |
| 12 | EP_SLACK |
16 | EpSlackRec (setup_slack, hold_slack) indexed by corner_idx * num_endpoints + ep_idx. |
| 13 | EP_ORDER |
4 | Pre-sorted u32 endpoint indices per (corner_idx, is_hold) bucket (ascending slack, ties by endpoint_idx). |
| 14 | PATH_RANK |
12 | PathRankRec (slack_ns: f32, endpoint_idx: u32, corner_idx: u16, flags: u8, pad: u8) for up to path_limit worst paths. |
| 15 | PATH_CHAIN_PTR |
8 | CSR row pointer (num_ranked_paths + 1) * u64 into PATH_CHAIN. |
| 16 | PATH_CHAIN |
4 | u32 words storing 4 header words (required, check_value) followed by three words [node, step_word, name_bucket] per step. |
| 17 | PATHS |
56 | PathHeaderRec for the top-K materialized worst paths (K = num_top_k_paths, default 100). |
| 18 | PATH_POINTS |
32 | PathPointRec slices referenced by PathHeaderRec.{point_begin, point_end}. |
| 19 | NAME_HASH |
4 | Open-addressed xxh3_64 hash table (4 * u32 per bucket) mapping port names and "inst/pin" names to (NodeId, prefix_str, leaf_str). |
| 20 | INST_HASH |
4 | Open-addressed xxh3_64 hash table (4 * u32 per bucket) mapping instance names to (inst_str, cell_str, pin_node_begin, pin_count). |
| 21 | SUMMARY |
80 | SummaryRec per (corner, path_group) with setup_wns, setup_tns, setup_violating, hold_wns, hold_tns, hold_violating, the corner's max_transition and max_capacitance violations (the same on every row of the corner, STA-REQ-0807), and endpoint counts. |
| 22 | UNCONSTRAINED |
12 | UnconstrainedRec per unconstrained endpoint (node, reason, reserved name bucket), sorted by (node, reason). |
| 23 | NOTES |
16 | NoteRec (code_str, message_str, severity, count) for aggregated diagnostics and notes. |
| 24 | SCOPE |
1 | Canonical JSON bytes of ScopeReport (top module, corners, wire/clock/CPPR models, backend, precision, store_mode, exceptions, CompletenessReport, and limits: the counts of the pin limit checks of every corner, absent in a table whose analysis did not check them, STA-REQ-0807). |
3. Record layouts and canonicalization¶
NodeRec(152 bytes):slew([[f64; 2]; 2]for[Rise/Fall][Early/Late], 32B),required([[f64; 2]; 2], 32B), andslack([[f64; 2]; 2], 32B).- Primary tag
tag(u32, 4B),pad(u32storing the node'sNAME_HASHbucket index forO(1)reverse name resolution),arrival([[f64; 2]; 2], 32B), andpred([[u32; 2]; 2], 16B). TagExtraRec(56 bytes): storestag(u32),pad(u32),arrival([[f64; 2]; 2]), andpred([[u32; 2]; 2]) for each active launch tag beyond the inline primary tag.- Sentinels and
-0.0canonicalization (STA-REQ-0302,STA-REQ-0405): - Every floating-point field (
f64andf32) passes throughcanon_f64/canon_f32, converting-0.0(-0.0f64.to_bits() == 0x8000_0000_0000_0000) to+0.0(0x0000_0000_0000_0000) and canonicalizing quietNaNpayloads. - Absent early arrivals use
+inf(f64::INFINITY), absent late arrivals use-inf(f64::NEG_INFINITY), absent required times use-inf(early) and+inf(late), and absent slacks use+inf(orNaNinEpSlackRec). -
Determinism: parallel construction (
rayon) sorts all candidate paths, endpoints, disabled arcs, and unconstrained endpoints by canonical deterministic keys, producing byte-for-byte identicalsta.rbinimages across--threads 1and--threads 32. -
EndpointRec(36 bytes):kind_flagsbits0..8hold theEndpointKindTagand bits8..10the capture clock edge (0 = Rise,1 = Fall,2 = None); bits10..32are zero.name_bucket_plus1is theNAME_HASHbucket ofdata_nodeplus one, or0when the node has no bucket. PathRankRec(12 bytes):flagsbit0is the hold flag, bit1the endpoint transition (0 = Rise,1 = Fall), bits2..4the check kind (0setup,1hold,2recovery,3removal), and bit7marks a top-Kpath.PATH_CHAINstep (3 words):nodeis the fullu32node id;step_wordbit0is the transition, bits1..3thePathPointKindand bits3..32the tag slot (0= primaryNodeRec.tag,k=TAG_EXTRAoffsetk - 1);name_bucketis the node'sNAME_HASHbucket.
4. Storage modes (Full vs Summary)¶
StoreMode::Full(--store-mode full, default,WS-REQ-0034): populates all 24 sections, includingNODE_REC,TAG_EXTRA_PTR, andTAG_EXTRA. Supports all mapped queries, arbitrary-Npath reconstruction up topath_limit,inspect_pin,inspect_instance, and incremental seeding (STA-REQ-0315).StoreMode::Summary(--store-mode summary,WS-REQ-0035): omitsNODE_REC,TAG_EXTRA_PTR, andTAG_EXTRA(stored as 0-length sections) while retainingSUMMARY,ENDPOINTS,EP_SLACK,EP_ORDER,PATH_RANK,PATH_CHAIN_PTR,PATH_CHAIN, top-KPATHSandPATH_POINTS,NAME_HASH,INST_HASH,UNCONSTRAINED,NOTES, andSCOPE. Callingrequire_full_mode_for_incremental(),inspect_instance(), orinspect_pin()on a non-endpoint node in aSummarystore returnsStaStoreError::SummaryModeUnavailable(INPUT_REJECTED).
5. Zero-copy mapped queries¶
StaStore serves ruststa query and rusteda inspect directly from mapped sections (WS-REQ-0030
to WS-REQ-0038):
query_summary()/query_scope(): readsSUMMARY(80B records),META,CORNERS, andSCOPEwithout touchingNODE_REC,PATHS, orPATH_POINTS.endpoint_table(corner_idx)/query_endpoints(corner_idx, check_kind, limit): returns the compact<= 22-byteCompactEndpointEntrytable in ascendingNodeIdorder (STA-REQ-0313) or slicesEP_ORDERinO(limit).query_path_handle(rank_idx)/reconstruct_path_from_chain(rank_idx):- For
rank_idx < num_top_k_paths, readsPATHS[rank_idx]and its slice inPATH_POINTS. - For
num_top_k_paths <= rank_idx < num_ranked_pathsinFullmode, walksPATH_CHAINin two passes: Pass 1 batch-touches each step'sNodeRecto prefetch cache lines; Pass 2 reads arrivals and slews fromNODE_REC/TAG_EXTRAand computes step delays (STA-REQ-0314). inspect_pin(pin_name, corner_idx)/inspect_port(port_name, corner_idx): probesNAME_HASH(xxh3_64over"inst/pin"on a 256-byte stack buffer without heap allocation) and readsNODE_REC+TAG_EXTRAinO(1)(STA-REQ-0315).inspect_instance(inst_name, corner_idx): probesINST_HASHand reads the contiguous slice ofNODE_RECfor all pins of the instance inO(pins_on_inst).
6. Capacity limits (T0022)¶
Every index of the table is stored at its full width, and an analysis that a field cannot hold is
rejected before the table is built, with error T0022 (INPUT_REJECTED), never packed truncated
(AGENTS rule 9.2):
| Limit | Field |
|---|---|
num_endpoints <= 4_294_967_294 |
PathRankRec.endpoint_idx, EP_ORDER (u32, u32::MAX is NO_INDEX). |
num_corners <= 16 |
PathRankRec.corner_idx and the per-corner sections. |
string bytes <= 4_294_967_295 and strings < 4_294_967_295 |
STR_OFFS (u32) and every *_str id. |
cell-name string id <= 1_048_575 |
INST_HASH bucket word 2, bits 0..20. |
pins per instance <= 4_095 |
INST_HASH bucket word 2, bits 20..32. |
tag slot <= 536_870_911 |
PATH_CHAIN step_word bits 3..32 (the tag table holds at most 4096 tags). |
The string bound counts every name the builder can add (ports, instances, cells, cell pins,
corners, clocks, path groups and diagnostics), so a table that passes the check cannot overflow.
PATH_CHAIN_PTR is u64, so the path chains have no 4-GiB word limit. Format version 1 packed
endpoint indices into 20 bits, node ids into 24 bits, endpoint name buckets into 22 bits and chain
pointers into u32; a design with more than 1_048_575 endpoints (OpenC910 has 4_444_505) could
not be stored.
7. Workspace revision staging¶
StaStoreData::stage_to_txn(txn, upstream) writes sta.rbin into a workspace transaction (Txn)
alongside upstream artifact hashes and populates summary.sta (setup_wns_ns, setup_tns_ns,
setup_violating_endpoints, hold_wns_ns, hold_tns_ns, hold_violating_endpoints,
max_trans_violating, max_cap_violating, max_fanout_violating, drv_status,
min_pulse_width_violating, min_period_violating, pulse_width_status, total_endpoints,
unconstrained_endpoints, corners) and status.sta (timing = "met" | "violated", drv,
pulse_width) (WS-REQ-0038, WS-REQ-0045, STA-REQ-0807).
8. The limits table¶
The pin limit checks of the same analysis are a separate table, limits.rbin (rbin kind LIMITS,
version 1), committed with sta.rbin with the same upstream tables, so that the 24 sections of
STA v2 and their shared readers are unchanged (STA-REQ-0806). Its sections are STR_BYTES (1),
STR_OFFS (2), META (3, 16 bytes: corners, rows, row budget), CORNERS (4, 24 bytes: name and
the unclocked and conditional pulse counts), KINDS (5, 56 bytes per corner and kind: checked,
violating, unlimited and kept rows, the first kept row, the worst slack or NaN, and the checks
that were not evaluated) and ROWS (6,
48 bytes: value, limit, slack, node, pin, net and clock strings, corner, kind and flags with the
transition in bits 0 and 1 and the source in bits 2 and 3), the rows of each corner and kind
contiguous and in slack order. A reader checks every count, range and string index before use.