ADR 0005: Shared SDC layer, bounded Tcl subset, and CONSTR v1 binary sections¶
- Status: accepted
- Date: 2026-10-17; amended 2026-10-24 (
set_propagated_clockandunset_propagated_clockare interpreted typed commands kept inCONSTR v1, SDC-REQ-0070; the readers ofrusteda-formatsalso usethiserror,serde,flate2,rayon,rustc-hashandsmallvec)
Context¶
Timing constraints arrive as Synopsys Design Constraints (SDC) files ([@sdc2016]) written in a
subset of Tcl ([@tcl86]). Embedding a third-party C Tcl interpreter would pull in an unsafe runtime
with filesystem, process, and socket access, breaking the pure-Rust, deterministic, dependency-free
contract of rusteda-formats (std + rusteda-core only). At the same time, real synthesis and
place-and-route constraint scripts use Tcl variables, environment dictionary reads and writes,
arithmetic expressions, conditionals with info exists, list manipulation, plain-list foreach
loops, and relative source includes alongside the 26 Phase 2a timing and environment commands,
6 electrical DRC commands, and 7 synthesis constraint commands. Furthermore, imported constraints
need a zero-copy-friendly binary section layout (CONSTR v1) inside the workspace container so
engines can load normalized canonical constraints without re-parsing Tcl.
Decision¶
- The module
sdcofrusteda-formats(crates/formats/rusteda-formats/src/sdc.rsandsrc/sdc/) implements the complete Phase 2a SDC reader, declarative command schema, canonical writer, andCONSTR v1binary codec (SDC-REQ-0001toSDC-REQ-0055, specified in docs/specs/sdc/sdc.md) with zero external dependencies beyondstdandrusteda-core. - A bounded, deterministic Tcl subset interpreter (
src/sdc/eval.rs,src/sdc/expr.rs) evaluates SDC scripts without a general Tcl runtime (SDC-REQ-0007toSDC-REQ-0015): - Variables (
set,unset,$v,${v},::global qualifier,{*}argument expansion, undefined variableD0007) and seeded$::env(VAR)/$env(VAR)array access (Envtable, never reading process environment variables implicitly). - Typed
[expr {...}]arithmetic (i64,f64, float-promoting/, string comparisons, logical/ternary operators, and math functionsabs,min,max,int,double,round). if/elseif/elsewith short-circuiting[info exists var]guards,foreachover plain Tcl lists (rejecting object collections withD0006),putscaptured asD0040, list and string commands (list,concat,llength,lindex,lappend,split,join,string,lsearch+lreplace), collection helpers (add_to_collection,remove_from_collection), and relativesourceresolution (rejecting absolute paths and..traversal withD0006).- Explicit step (
Limits::max_steps = 100_000) and recursion depth (Limits::max_depth = 32) budgets enforced withD0008, and explicitD0006diagnostics for out-of-subset Tcl constructs (proc,while,for,switch,eval,uplevel,upvar,namespace,regexp,regsub,open,exec,socket,glob,array,catch). - Every SDC command is validated against a declarative
CmdSchematable (src/sdc/schema.rs) that checks unknown commands (D0010), unknown options (D0003), missing/extra/duplicate/mutually exclusive options (D0004), argument types (D0005), and Phase 2b commands/options (D0010/D0011) before converting numeric arguments into canonical internal units (ns,pF,kOhm,V,mA,nW) according to the activeUnitsstate (set_units,D0030). - DRC commands (
set_max_transition,set_max_capacitance,set_min_capacitance,set_max_fanout,set_fanout_load,set_port_fanout_number) emitD0021(Severity::Warning) and synthesis commands (set_dont_use,set_dont_touch,set_dont_touch_network,set_size_only,set_max_area,set_max_dynamic_power,set_max_leakage_power) emitD0020(Severity::Note), while preserving typedCmdAST records for round-trip writing and binary storage. sdc::write(src/sdc/write.rs) emits canonical, variable-free SDC text whose re-parse is idempotent (read_str(&write(&s)).0 == s,SDC-REQ-0003).Sdc::to_sectionsandSdc::from_sections(src/sdc/constr.rs) encode and decode the 13 little-endianCONSTR v1sections (STR_BYTES,STR_OFFS,HEADER,SOURCES,ENV,CMDS,ARGS,F64,OBJS,OBJ_CHILD,PATS,CANON,DIAGS), andrusteda import --sdc(crates/apps/rusteda-cli/src/import_sdc.rs) writes them to the workspace tableconstraints(constraints.rbin, kind"CONSTR", version1).
Consequences¶
rusteda-formatsparses, validates, canonicalizes, and binary-serializes Phase 2a SDC files deterministically in safe Rust without external crates.- All numeric quantities in
SdcandCONSTR v1are scaled on ingestion to canonical units (ns,pF,kOhm,V,mA,nW), whileUnitspreserves the active unit flags for canonical output and bit-exact binary round tripping (Sdc::exact_eq). - Phase 2b features (
create_generated_clock,set_propagated_clock,unset_propagated_clock,set_timing_derate,set_min_pulse_width,set_ideal_network,-through, and-reference_pin) fail fast with structuredD0010/D0011diagnostics carrying exact source locations rather than being silently ignored.