A pure-Python parser for Standard Delay Format (SDF) files — the industry-standard format used by EDA tools to back-annotate timing delays and constraints onto digital designs.
Building a Python SDF Parser for EDA Flow Automation — Closing a tooling gap that every EDA engineer eventually runs into.
- Parses
IOPATH,COND,CONDELSE,WIDTH,SETUPHOLD, andRECREM - Single file or directory of SDF files (with cross-file consistency checks)
- Single-cell extraction mode — stops early once the target cell is found
- Zero required dependencies — only the Python standard library
- Optional colored warnings via
termcolor - CLI included:
sdf-parser timing.sdf --cell INV_X1
pip install eda-sdf-parser # no extra dependencies
pip install "eda-sdf-parser[color]" # adds termcolor for colored warningsOr for development:
git clone https://github.com/rohaansch/sdf-parser
cd sdf-parser
pip install -e ".[dev]"from sdf_parser import SDFParser
parser = SDFParser()
sdf = parser.read("timing.sdf")
print(repr(sdf))
# SDF(cells=142, files=1, header=['SDFVERSION', 'DESIGN', 'TIMESCALE'])
for entry in sdf.cells["INV_X1"]:
print(entry)
# ['IOPATH', 'A', 'ZN', 2]
# ['COND', "A==1'b0", 'A', 'ZN', 1]
# ['WIDTH', None, 'posedge', 'A', 1]Each entry in sdf.cells["CELLNAME"] is a list whose first element is the
construct keyword:
| Construct | Entry format |
|---|---|
IOPATH |
[IOPATH, pin_1, pin_2, n] |
COND |
[COND, condition, pin_1, pin_2, n] |
CONDELSE |
[CONDELSE, pin_1, pin_2, n] |
WIDTH |
[WIDTH, condition, edge, port, n] |
SETUPHOLD |
[SETUPHOLD, cond_1, edge_1, port_1, cond_2, edge_2, port_2, n] |
RECREM |
[RECREM, cond_1, edge_1, port_1, cond_2, edge_2, port_2, n] |
condition is None when no COND qualifier is present.
n is the number of delay value tuples (e.g. 3 for min/typ/max).
SDF input:
(CELL
(CELLTYPE "DFFS_X2")
(INSTANCE FF1)
(TIMINGCHECK
(SETUPHOLD (posedge D) (posedge CK) (0.1:0.1:0.1) (0.05:0.05:0.05))
(RECREM (negedge SN) (posedge CK) (0.2:0.2:0.2) (0.1:0.1:0.1))
(WIDTH (posedge CK) (0.3:0.3:0.3))
)
)
Python output:
sdf.cells["DFFS_X2"]
# [
# ['SETUPHOLD', None, 'posedge', 'D', None, 'posedge', 'CK', 2],
# ['RECREM', None, 'negedge', 'SN', None, 'posedge', 'CK', 2],
# ['WIDTH', None, 'posedge', 'CK', 1],
# ]Create a parser. path sets the default file/directory used by read().
| Argument | Type | Description |
|---|---|---|
path |
str |
.sdf file or directory. Falls back to the constructor path. |
given_cell |
str |
Extract only this cell (case-insensitive). |
Raises ValueError / FileNotFoundError / IOError on bad input.
Clear internal state to reuse the parser for a different file:
sdf_fast = parser.read("fast.sdf")
parser.reset()
sdf_slow = parser.read("slow.sdf")| Attribute | Type | Description |
|---|---|---|
header_data |
dict |
Header fields (SDFVERSION, DESIGN, TIMESCALE, …) |
cells |
dict[str, list] |
Cell name → list of parsed entries |
files |
list[str] |
All files that contributed to this object |
Format a cell's entries as a human-readable multi-line string:
from sdf_parser import format_entries
print(format_entries(sdf.cells["INV_X1"]))When path is a directory every .sdf file inside it is parsed and merged.
Cells found in multiple files are compared; mismatches produce a warning:
sdf = SDFParser().read("sdf_corners/")
print(repr(sdf))
# SDF(cells=142, files=3, header=['SDFVERSION', 'DESIGN', 'TIMESCALE'])sdf-parser timing.sdf
sdf-parser timing.sdf --cell INV_X1
sdf-parser timing.sdf --cell INV_X1 --header
sdf-parser sdf_corners/ --header
sdf-parser --version
Or without installing:
python -m sdf_parser timing.sdf --cell INV_X1
- Delay values are not parsed — only the count of value tuples is stored.
- Only the constructs listed above are extracted; other SDF keywords are skipped.
- OASIS and binary SDF formats are not supported.
pip install -e ".[dev]"
pytest -v