Physical_Synthesis_Engine is a compact C++17 EDA-style timing engine inspired
by physical synthesis and static timing analysis workflows used in tools such as
Cadence Innovus. It parses a simple gate-level netlist, builds a directed timing
graph, computes arrival and required arrival times, reports slack and critical
paths, and runs a timing-driven gate-sizing optimization pass.
The project is intentionally dependency-light and suitable for Linux/CMake builds. It is designed as a resume-quality systems project rather than a toy hello-world example.
- Gate-level netlist model with gates, nets, pins, fanout, primary inputs, and primary outputs.
- Supported gate types:
INV,NAND2,NOR2,AND2,OR2,BUF,DFF. - Hash maps for name lookup, vectors for compact storage, adjacency lists for graph traversal, and queues for topological sorting.
- Text netlist parser with deterministic example circuits.
- Static timing analysis:
- topological sort,
- arrival time propagation,
- required arrival time back-propagation,
- per-gate slack,
- WNS/TNS,
- critical path reporting.
- Timing-driven gate sizing with
X1,X2, andX4drive strengths. - Optional multithreaded arrival-time recomputation by independent timing levels using C++ threads.
- Unit-style tests for arrival/slack math, threaded determinism, and optimizer improvement.
Comments start with #.
input A B C
output Y
gate U1 NAND2 N1 A B delay=0.32 area=1.20 drive=1
gate U2 INV N2 N1 delay=0.22 area=0.80 drive=1
gate U3 OR2 Y N2 C delay=0.28 area=1.10 drive=1
Gate syntax:
gate <instance> <type> <output_net> <input_net...> delay=<ns> area=<units> drive=<1|2|4>
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build buildctest --test-dir build --output-on-failureYou can also run the test binary directly:
./build/timing_testsBasic STA:
./build/timing_engine examples/simple_path.net --clock 0.75STA with timing-driven optimization and four worker threads:
./build/timing_engine examples/simple_path.net --clock 0.75 --optimize --threads 4More examples:
./build/timing_engine examples/fanout_reconvergent.net --clock 1.20 --threads 4
./build/timing_engine examples/large_generated.net --clock 1.40 --optimize --threads 4Loaded netlist: examples/simple_path.net
=== Timing-Driven Gate Sizing Summary ===
Iterations: 3
Upsized gates: 3
Before WNS/TNS/Area: -0.110 / -0.110 / 3.100
After WNS/TNS/Area: 0.139 / 0.000 / 6.820
=== Static Timing Report ===
Clock period: 0.750 ns
Threads: 4 (levelized parallel arrival)
Gates: 3, Nets: 7
Estimated area: 6.820
WNS: 0.139 ns
TNS: 0.000 ns
Critical path:
1. U1 (NAND2, arrival=0.181 ns, slack=0.139 ns)
2. U2 (INV, arrival=0.307 ns, slack=0.139 ns)
3. U3 (OR2, arrival=0.611 ns, slack=0.139 ns)
The timing graph is built from net drivers to gate loads. A Kahn topological sort creates deterministic evaluation order and also provides timing levels. Arrival time is propagated forward through the graph. Required time is propagated backward from primary-output endpoints using the target clock period.
The multithreaded mode evaluates gates in the same timing level concurrently. Levels are processed in order, so each worker only reads timing values from already-computed predecessor levels. This keeps output deterministic while still showing realistic parallel STA structure.
The optimizer is a simple physical-synthesis-style pass. It finds the current critical path, upsizes the gate with the largest effective delay when possible, and reruns timing. Larger drive strengths reduce delay but increase area, mirroring the timing/area tradeoff of real gate sizing.