| title | OpenROAD and ORFS |
|---|---|
| nav_order | 20 |
This is the single reference document for everything ORFS- and OpenROAD-related in this repository. It covers installing the Dockerised flow, running an example end-to-end, porting a new design, building OpenROAD from source, and driving the OpenROAD database directly from Python or Tcl.
The supported install path runs ORFS inside a Docker container that Ships the pinned OpenROAD, Yosys, OpenSTA, KLayout, and other tool binaries. You need Docker installed and nothing else.
git clone https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts.git
cd OpenROAD-flow-scriptsPlace the following script in the root of the cloned repo as
runorfs.sh:
#!/bin/bash
# Use first argument as tag if provided
if [ -n "$1" ]; then
tag="$1"
echo "Using user-specified tag: $tag"
else
tag=$(git describe --tags 2>/dev/null)
if [ -n "$tag" ]; then
echo "Using Git tag: $tag"
else
echo "Warning: No tag specified and commit is not on a tag. Defaulting to 'latest'."
tag="latest"
fi
fi
echo "Running OpenROAD flow with tag: ${tag}"
docker run --rm -it \
-u $(id -u ${USER}):$(id -g ${USER}) \
-v $(pwd)/flow:/OpenROAD-flow-scripts/flow \
-v $(pwd)/..:/OpenROAD-flow-scripts/UCSC_ML_suite \
-e DISPLAY=${DISPLAY} \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v ${HOME}/.Xauthority:/.Xauthority \
--network host \
--security-opt seccomp=unconfined \
openroad/orfs:${tag}Make it executable:
chmod +x runorfs.shThe script exports your X display so GUI applications work inside the
container and mounts flow/ from your workspace into the image, so
designs, scripts, and results are editable from both sides.
Run the version matching your ORFS checkout, or pass an explicit tag:
# version matching your ORFS checkout
./runorfs.sh
# specific tag
git checkout v3.0-3141-gb6d79b23
./runorfs.sh v3.0-3141-gb6d79b23
# or a commit ID
git checkout 7fcc19
./runorfs.sh 7fcc19Beware — your cloned version of ORFS should match the version of the
Docker image that you run. If it doesn't, command interfaces may have
diverged. runorfs.sh handles this automatically via git describe --tags, but only if you cloned ORFS with its full history. A
git clone --depth 1 will have no tags and silently fall back to
openroad/orfs:latest, which may not match the scripts you just cloned.
Prefer a full clone, or pass an explicit tag to ./runorfs.sh.
Recent openroad/orfs:latest images are built with AVX-512
instructions and will crash partway through the flow with "child killed:
illegal instruction" on CPUs that do not support it (this includes most
pre-Ice-Lake Intel desktops and every Zen / Zen 2 / Zen 3 AMD part). If
you see that error, use make LEC_CHECK=0 to skip the resizer's logical
equivalence check that requires AVX-512.
You can safely ignore the message groups: cannot find name for group ID 1000 when the Docker image starts.
Inside the container:
I have no name!@diode:/OpenROAD-flow-scripts$ cd flow
I have no name!@diode:/OpenROAD-flow-scripts/flow$ makeThis runs the default GCD (Greatest Common Divisor) design through the Nangate 45 nm platform end to end. You should see a lot of output and a final summary table (see Walkthrough for the full breakdown).
Note — a few WARNING lines are expected; any Error is not.
To view the final result graphically:
I have no name!@diode:/OpenROAD-flow-scripts/flow$ make gui_finalwhich should open the GCD design like this:
If GUI applications inside the container fail with:
Authorization required, but no authorization protocol specified
Could not load the Qt platform plugin "xcb" in "" even though it was found.
run:
xhost +local:on the host before you start the Docker image. This lets the container talk to your X server.
This section assumes the Installation smoke test works.
The documentation is at OpenROAD ReadTheDocs.
The general flow steps ORFS runs are:
A successful run ends with a table like:
Log Elapsed/s Peak Memory/MB sha1sum .odb [0:20)
1_1_yosys_canonicalize 0 38 45af71d069285cb71fba
1_2_yosys 0 37 8f4f1609d45714b16838
1_synth 0 102 2c0cb35f152969a57a97
2_1_floorplan 0 121 4c0c21a703c4514a4f9e
2_2_floorplan_macro 0 98 4c0c21a703c4514a4f9e
2_3_floorplan_tapcell 0 98 41d55f307cb9baec622e
2_4_floorplan_pdn 0 101 4b02fdf1131f9ecfb828
3_1_place_gp_skip_io 18 102 c60b97e4377b3a96d8c4
3_2_place_iop 0 100 e10d623fff656d759c03
3_3_place_gp 17 212 611a676ae84b63e3582b
3_4_place_resized 0 119 611a676ae84b63e3582b
3_5_place_dp 0 106 6f409421b824c2937041
4_1_cts 2 128 3ae290878676cd808502
5_1_grt 21 218 cc66d1cd20f7651b10e1
5_2_route 25 2369 cfb6ef9f4d29c356f88b
5_3_fillcell 0 101 e40b0e5835f90ee733e0
6_1_fill 0 99 e40b0e5835f90ee733e0
6_1_merge 2 424
6_report 2 157
Total 87 2369
What each step does:
- 1_1_yosys_canonicalize — normalises the input Verilog.
- 1_2_yosys — logic synthesis with Yosys.
- 1_synth — post-processes the synthesised netlist for the flow.
- 2_1_floorplan — determines floorplan area and aspect ratio.
- 2_2_floorplan_macro — places fixed-size macro blocks (e.g. SRAMs).
- 2_3_floorplan_tapcell — inserts tap cells.
- 2_4_floorplan_pdn — builds the power ring and/or power straps.
- 3_1_place_gp_skip_io — global placement excluding I/O.
- 3_2_place_iop — places I/O cells around the perimeter.
- 3_3_place_gp — global placement of the gates.
- 3_4_place_resized — global-placement timing optimisation (resizing, buffering).
- 3_5_place_dp — detailed placement of the gates.
- 4_1_cts — clock tree synthesis (CTS).
- 5_1_grt — global routing.
- 5_2_route — detailed routing.
- 5_3_fillcell — adds fill cells to row positions without cells.
- 6_1_fill — adds routing fill to the design.
- 6_1_merge — merges the GDS of library cells into the design.
- 6_report — final design reporting (timing, area, power).
make alone runs the default design. To pick another, pass the
design's config.mk via DESIGN_CONFIG:
make DESIGN_CONFIG=./designs/nangate45/gcd/config.mk
Same design on ASAP7:
make DESIGN_CONFIG=./designs/asap7/gcd/config.mk
The general form:
make DESIGN_CONFIG=designs/<PLATFORM>/<DESIGN>/config.mk
where PLATFORM is the technology (nangate45, asap7, sky130hd,
etc.) and DESIGN is the design name (gcd, etc.).
make DESIGN_CONFIG=./designs/nangate45/gcd/config.mk gui_finalThere are targets to clean individual steps or the whole flow:
clean_synthclean_floorplanclean_placeclean_ctsclean_routeclean_finishclean_all
For example:
make DESIGN_CONFIG=./designs/asap7/gcd/config.mk clean_allmake DESIGN_CONFIG=designs/<PLATFORM>/<DESIGN>/config.mk <STEP>where STEP is synth, floorplan, place, cts, route, or
finish. Useful for iterating on one phase — e.g. to debug global
routing, repeat:
make DESIGN_CONFIG=designs/<PLATFORM>/<DESIGN>/config.mk clean_route
make DESIGN_CONFIG=designs/<PLATFORM>/<DESIGN>/config.mk routeAs long as you don't touch features that affect earlier stages, this is
safe. If you modify something that changes the floorplan, clean from
clean_floorplan onward.
To run openroad interactively outside the Makefile, first set up your
PATH by sourcing the environment script (the Makefile does this
automatically):
source env.shVerify with:
which openroadwhich should point to:
OpenROAD-flow-scripts/tools/install/OpenROAD/bin/openroad
For an interactive Tcl console pre-configured for a specific design:
make DESIGN_CONFIG=designs/<PLATFORM>/<DESIGN>/config.mk bash
source env.sh
openroad <tcl file>make bash sets SCRIPTS_DIR, LEF_FILES, LIB_FILES, etc. so the
ORFS scripts under flow/scripts work. For example, to read a design
after CTS:
source $::env(SCRIPTS_DIR)/load.tcl
load_design 4_cts.odb 4_cts.sdcBecause the config file specifies design and technology, you only name the ODB and SDC.
Outputs land in:
| Path | Contents |
|---|---|
results/<PLATFORM>/<DESIGN>/base/ |
Final files (GDS, DEF, ODB, SDC, SPEF, …) |
logs/<PLATFORM>/<DESIGN>/base/ |
One log per flow step |
reports/<PLATFORM>/<DESIGN>/base/ |
Per-step reports (timing, area, power, …) |
A config file parameterises one design in one technology. The key variables:
# Specifies the technology subdirectory.
export PLATFORM = nangate45
# Input file list (a single file or a list of files).
export VERILOG_FILES = $(DESIGN_HOME)/src/$(DESIGN_NAME)/gcd.v
# The timing constraint file.
export SDC_FILE = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NAME)/constraint.sdc
# Pick a floorplan size so the logic cells use 55% of the area.
export CORE_UTILIZATION ?= 55
The full list of overridable variables is at
Flow Variables.
Many have reasonable defaults; your config only specifies what differs.
make bash sources the defaults followed by your config.
There is an ORFS tutorial upstream.
OpenROAD exposes man pages at three levels:
- Level 1 — top-level openroad command (
man openroad) - Level 2 — individual module / Tcl commands (
man clock_tree_synthesis) - Level 3 — info / error / warning codes (
man CTS-0001)
Documentation:
- OpenROAD: https://openroad.readthedocs.io/en/latest/
- ORFS: https://openroad-flow-scripts.readthedocs.io/en/latest/
Porting a design to ORFS is three steps:
- Add your Verilog source to
OpenROAD-flow-scripts/flow/designs/src/<your-design>. - Create a design folder and
*.sdc/*.mkfor each technology you target. - Update
OpenROAD-flow-scripts/flow/Makefileto include a target for your design.
sha256 is a cryptographic hash function that can be implemented as a hardware accelerator. The RTL lives in this directory.
Add source. Copy the RTL Verilog files to
OpenROAD-flow-scripts/flow/designs/src/sha256.
Create a design config. Assume we target sky130hd. Create
OpenROAD-flow-scripts/flow/designs/sha256/. At a minimum this folder
needs a config.mk (target platform, source files, flow knobs),
constraint.sdc (timing constraints), and a rules-base.json (DRC
metric checks).
The config file requires DESIGN_NAME, PLATFORM, VERILOG_FILES,
and SDC_FILE:
export DESIGN_NAME = sha256 # Module name of top-level instance
export PLATFORM = sky130hd # Intended platform
export VERILOG_FILES = $(sort $(wildcard $(DESIGN_HOME)/src/$(DESIGN_NICKNAME)/*.v))
export SDC_FILE = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/constraint.sdc
Note: the design name sha256 is not arbitrary. The top-level
sha256.v instantiates a module named sha256. Note the clock name as
well — it must match constraint.sdc.
You can add knobs like CORE_UTILIZATION (fraction of core area used)
or PLACE_DENSITY (cell density during placement). The full list is in
the
Flow Variables.
A more complete sha256 config:
export DESIGN_NICKNAME = sha256
export DESIGN_NAME = sha256
export PLATFORM = sky130hd
export VERILOG_FILES = $(sort $(wildcard $(DESIGN_HOME)/src/$(DESIGN_NICKNAME)/*.v))
export SDC_FILE = $(DESIGN_HOME)/$(PLATFORM)/$(DESIGN_NICKNAME)/constraint.sdc
export CORE_UTILIZATION = 40
export TNS_END_PERCENT = 100
export CTS_CLUSTER_SIZE = 25
export CTS_CLUSTER_DIAMETER = 45
You can tune global routing per design. fastroute.tcl lets you pin
routing layers (and separate clock vs signal):
set_global_routing_layer_adjustment $::env(MIN_ROUTING_LAYER)-$::env(MAX_ROUTING_LAYER) 0.4
set_routing_layers -clock $::env(MIN_CLK_ROUTING_LAYER)-$::env(MAX_ROUTING_LAYER)
set_routing_layers -signal $::env(MIN_ROUTING_LAYER)-$::env(MAX_ROUTING_LAYER)The SDC file carries clock speed and I/O timing. See SDC constraints for the syntax.
Ensure the clock name matches the top-level Verilog.
current_design sha256
set clk_name clk
set clk_port_name clk
set clk_period 6.5
set clk_io_pct 0.25
set clk_port [get_ports $clk_port_name]
create_clock -name $clk_name -period $clk_period $clk_port
set non_clock_inputs [lsearch -inline -all -not -exact [all_inputs] $clk_port]
set_input_delay [expr $clk_period * $clk_io_pct] -clock $clk_name $non_clock_inputs
set_output_delay [expr $clk_period * $clk_io_pct] -clock $clk_name [all_outputs]Append DESIGN_CONFIG=./designs/sky130hd/sha256/config.mk to the
Makefile, uncomment it, and run make to launch the flow.
- Defining
sha256/rules-base.json. - Replacing a ported design's RAM with
fakeRAM. - Porting designs written in SystemVerilog using
SYNTH_HDL_FRONTEND(Slang).
Most users will not need to build OpenROAD from source. Prefer the prebuilt image in Installation unless you need to modify the source. You can build either inside Docker or locally. On WSL, use the local method.
BEWARE: if you have local copies of Yosys, OpenSTA, or OpenROAD on
your PATH, they take priority over what's in the Docker image. If
you've taken CSE 125 / 225, you may already have Yosys installed
locally.
git clone https://github.com/The-OpenROAD-Project/OpenROAD-flow-scripts.git
cd OpenROAD-flow-scriptsRun all subsequent commands from the repo root unless noted otherwise.
The dependencies live inside the build image, so there's nothing to install beyond Docker.
./build_openroad.shDocker is the default build method, so no flag is needed.
To add debug symbols when building in Docker, edit
tools/OpenROAD/docker/Dockerfile.builder and append CMake flags to the
build command:
RUN ./etc/Build.sh -compiler=${compiler} -threads=${numThreads} -deps-prefixes-file=${depsPrefixFile} -cmake="-DCMAKE_BUILD_TYPE=DEBUG"Note: the --openroad-args argument to ./build_openroad.sh is not
forwarded to the Docker build scripts, so you can't enable debug the
same way as the local build.
Very similar to the ORFS Docker image used in the Walkthrough, except you reference your locally built image and tag (shown at the end of the build output):
#25 naming to docker.io/openroad/flow-ubuntu22.04-builder:6cd62b
A modified runorfs.sh (call it runbuilder.sh) pointing at that
image:
#!/bin/bash
TAG="${1:-latest}"
echo "Running OpenROAD flow with tag: ${TAG}"
docker run --rm -it \
-u $(id -u ${USER}):$(id -g ${USER}) \
-v $(pwd)/flow:/OpenROAD-flow-scripts/flow \
-e DISPLAY=${DISPLAY} \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v ${HOME}/.Xauthority:/.Xauthority \
--network host \
--security-opt seccomp=unconfined \
docker.io/openroad/flow-ubuntu22.04-builder:${TAG}Run a specific tag:
./runbuilder.sh 6cd62bsetup.sh installs the dependencies. You can run it wholesale if you
have root, but the three steps break down into root / non-root cleanly:
-
(NO ROOT) Recursively pull OpenROAD's submodules:
git submodule update --init --recursive
(You could also have cloned with
--recursive— this is a good way to ensure submodules are up to date.) -
(ROOT) Install system dependencies:
sudo ./etc/DependencyInstaller.sh -base
This is an ORFS-provided script. It installs the dependencies assuming a supported OS.
-
(NO ROOT) Install the other common dependencies:
./etc/DependencyInstaller.sh -common -prefix="./dependencies"Builds specific versions of SWIG, cmake, and so on into
dependencies/.
The commonly-used invocation:
source dev_env.sh
./build_openroad.sh --no_init --openroad-args "-DCMAKE_BUILD_TYPE=DEBUG" --localsource dev_env.sh— use the tools under./dependencies.--local— build directly on the host, not in Docker.--openroad-args— forward CMake flags (e.g. debug build).--no_init— skip submodule re-init. Pairs well with--or_repo <REPO> --or_branch <BRANCH>when you want to compile against a specific OpenROAD fork/branch (default is the one ORFS tracks).
If you just want a vanilla local build:
./build_openroad.sh --localwhich will update submodules to the versions this commit needs.
Set your PATH:
source env.shVerify:
which openroadShould point to:
OpenROAD-flow-scripts/tools/install/OpenROAD/bin/openroad
Run all OpenROAD regressions (slow):
cd tools/OpenROAD/tests
./regression.shOne test:
./regression gcd_nangate45Or just:
openroad gcd_nangate45.tclRun regressions for one submodule:
cd tools/OpenROAD/src/rsz/tests
# All tests, 10 threads
./regression -j 10
# Tests matching a regex
./regression -R repair_setup
# A single regression TCL directly
openroad repair_setup1.tclGolden outputs are stored with extensions .ok (log), .vok
(Verilog), .defok (DEF). Regressions diff the run output against the
golden; simple diff is used unless equivalence checking is enabled.
Test outputs land under results/. The log is <TEST>-tcl.log, the
diff is <TEST>-tcl.diff, and the Verilog / DEF outputs (if any) are
<TEST>_out-tcl.v / .def.
gdb --args openroad [tcl file]You will typically want a debug build (see above) to get useful stack
traces. The default Docker image does not include gdb, so you'd need
to add it (more to come).
You can drive OpenROAD's database (ORDB) and timing engine from Python. The ASP-DAC 24 tutorial has many good examples.
For the full API:
openroad -pythonThe OpenDB Python tests are a good reference: https://github.com/The-OpenROAD-Project/OpenDB/tree/master/tests/python.
An example design is provided in ordb/final.tar.gz:
tar zxvf ordb/final.tar.gzimport openroad
from openroad import Design, Tech, Timing
import rcx
import os
import odb
openroad.openroad_version()
odb_file = "final/odb/spm.odb"
def_file = "final/def/spm.def"
lef_files = [
"/home/mrg/.ciel/sky130A/libs.ref/sky130_fd_sc_hd/techlef/sky130_fd_sc_hd__nom.tlef",
"/home/mrg/.ciel/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef",
]
lib_files = [
"/home/mrg/.ciel/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib",
]
tech = Tech()
for lef_file in lef_files:
tech.readLef(lef_file)
for lib_file in lib_files:
tech.readLiberty(lib_file)
design = Design(tech)Either DEF or ODB works for placement/routing:
design.readDef(def_file)
design.readDb(odb_file)You can extract from either detailed routing or global routing. The Tcl form that selects between them:
estimate_parasitics
-placement|-global_routing
[-spef_file spef_file]
Detailed-routing extraction from Python:
rcx.define_process_corner(ext_model_index=0, file="X")
ext_rules = "~/.ciel/sky130A/libs.tech/openlane/rules.openrcx.sky130A.nom.spef_extractor"
# NOTE: position-dependent
rcx.extract(
ext_model_file=ext_rules,
corner_cnt=1,
max_res=50.0,
coupling_threshold=0.1,
cc_model=12,
context_depth=5,
debug_net_id="",
lef_res=False,
no_merge_via_res=False,
)Adjust extracted RC:
rcx.adjust_rc(res_factor, cc_factor, gndc_factor)Diff SPEF (Tcl):
diff_spef -file 31-user_project_wrapper.spef
Or the Python equivalent:
rcx.diff_spef(
file=spef_file,
r_conn=False,
r_res=False,
r_cap=False,
r_cc_cap=False,
)Over nets:
for net in design.getBlock().getNets():
print("**** ", net.getName())Over instances (skipping filler / tap / decap):
for inst in design.getBlock().getInsts():
if "FILLER" in inst.getName():
continue
if "TAP" in inst.getName():
continue
if "decap" in inst.getMaster().getName():
continue
print(
inst.getName(),
inst.getMaster().getName(),
design.isSequential(inst.getMaster()),
design.isInClock(inst),
design.isBuffer(inst.getMaster()),
design.isInverter(inst.getMaster()),
)Over pins on an instance:
for outTerm in inst.getTerms():
if timing.isEndpoint(outTerm):
pass
if design.isInSupply(outTerm):
pass
if outTerm.isOutputSignal():
pass
if outTerm.isInputSignal():
passOver library cells (masters):
for lib in tech.getDB().getLibs():
for master in lib.getMasters():
print(master.getName())
for mterm in master.getMTerms():
print(" ", mterm.getName())Read SDC and build a Timing object:
design.evalTclString("read_sdc {}".format("final/spm/sdc/spm.sdc"))
timing = Timing(design)Query arrival / slew / slack / capacitance:
timing.getPinArrival(inTerm, Timing.Rise)
timing.getPinSlew(inTerm, Timing.Rise)
timing.getPinSlack(inTerm, Timing.Rise, Timing.Max)
timing.getNetCap(net, corner, Timing.Max)
timing.getNetCap(net, corner, Timing.Min)Per-corner power:
for corner in timing.getCorners():
print(
timing.staticPower(inst, corner),
timing.dynamicPower(inst, corner),
)Cell timing arcs:
for lib in tech.getDB().getLibs():
for master in lib.getMasters():
print(master.getName())
for mterm in master.getMTerms():
print(" ", mterm.getName())
for m in timing.getTimingFanoutFrom(mterm):
print(" -> ", m.getName())The most reliable way to find additional methods is a runtime
breakpoint() + autocomplete. Set a breakpoint:
for lib in tech.getDB().getLibs():
for master in lib.getMasters():
print(master.getName())
for mterm in master.getMTerms():
print(" ", mterm.getName())
breakpoint()Run your script:
$ openroad -python load.py
...
(Pdb)
At the Pdb prompt, p mterm.get<TAB><TAB> autocompletes visible
methods:
(Pdb) p mterm.get
mterm.getBBox mterm.getDiffArea mterm.getMPins mterm.getName mterm.getSigType
mterm.getConstName mterm.getIndex mterm.getMTerm mterm.getOxide2AntennaModel mterm.getTargets
mterm.getDefaultAntennaModel mterm.getIoType mterm.getMaster mterm.getShape
Note that you need the p prefix — autocomplete does not fire on a
bare mterm.get.
OpenROAD's native scripting language is Tcl. This section covers the basics of scripting ORFS stages and driving the database interactively.
You need OpenROAD installed. Once running, drop into the Tcl shell:
$ openroad
OpenROAD v2.0-22053-g4e2370113b
Features included (+) or not (-): +GPU +GUI +Python : DEBUG
This program is licensed under the BSD-3 license. See the LICENSE file for details.
Components of this program may be licensed under more restrictive licenses which must be honored.
openroad>
Tcl is the scripting language used by most commercial and open-source EDA tools. The basics you need to know:
set— assign a value to a variable.puts— print a string.expr— evaluate an arithmetic expression.proc— define a procedure.foreach— iterate over a list.
set a 10
set b 20
puts "The sum is: [expr $a + $b]"Lists:
set my_list [list 1 2 3 4 5]
foreach item $my_list {
puts "Item: $item"
}Procedures:
proc greet {name greeting} {
set msg "$greeting, $name!"
return $msg
}
set message [greet "OpenROAD" "Hello"]
puts "Returned message: $message"Embed Tcl directly in scripts. The shell offers commands for design entry, synthesis, routing, checking, etc.:
read_lef my_design.lef
read_def my_design.def
read_liberty my_design.lib
check_timingAn example design ships in ordb/final.tar.gz:
tar xvf ordb/final.tar.gzLoad it:
set odb_file "final/odb/spm.odb"
set def_file "final/def/spm.def"
set lef_files {"/home/mrg/.ciel/sky130A/libs.ref/sky130_fd_sc_hd/techlef/sky130_fd_sc_hd__nom.tlef"
"/home/mrg/.ciel/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef"}
set lib_files {"/home/mrg/.ciel/sky130A/libs.ref/sky130_fd_sc_hd/lib/sky130_fd_sc_hd__tt_025C_1v80.lib"}
foreach lef_file $lef_files {
read_lef $lef_file
}
foreach lib_file $lib_files {
read_liberty $lib_file
}
read_def $def_fileFor larger setups you can lean on the ORFS
make bash helpers instead of loading
everything manually.
Iterate over cells:
set cells [get_cells]
foreach cell $cells {
set cell_name [get_property $cell full_name]
puts "Cell: $cell_name"
}Iterate over nets:
set nets [get_nets]
foreach net $nets {
set net_name [get_property $net full_name]
puts "Net: $net_name"
}Query timing:
set path [lindex [find_timing_paths -sort_by_slack -group_count 1] 0]
set slack [get_property $path slack]
puts "Critical Path Slack: $slack"General property access:
set cell [get_cells -name my_cell]
set cell_type [get_property $cell type]
puts "Cell Type: $cell_type"The ORFS flow scripts live in flow/scripts/. They are a good source
of idiomatic Tcl. For example, flow/scripts/io_placement.tcl:
source $::env(SCRIPTS_DIR)/load.tcl
erase_non_stage_variables place
if {![env_var_exists_and_non_empty FLOORPLAN_DEF] && \
![env_var_exists_and_non_empty FOOTPRINT] && \
![env_var_exists_and_non_empty FOOTPRINT_TCL]} {
load_design 3_1_place_gp_skip_io.odb 2_floorplan.sdc
log_cmd place_pins \
-hor_layers $::env(IO_PLACER_H) \
-ver_layers $::env(IO_PLACER_V) \
{*}$::env(PLACE_PINS_ARGS)
write_db $::env(RESULTS_DIR)/3_2_place_iop.odb
write_pin_placement $::env(RESULTS_DIR)/3_2_place_iop.tcl
} else {
log_cmd exec cp $::env(RESULTS_DIR)/3_1_place_gp_skip_io.odb $::env(RESULTS_DIR)/3_2_place_iop.odb
}This script sources load.tcl, then — after checking that no
pre-built floorplan or footprint is in effect — runs load_design,
place_pins, write_db, and write_pin_placement. If one of those
variables is set, it just copies the input ODB through. log_cmd is a
Tcl procedure that wraps a command so its stdout lands in the step log.
Other useful step scripts to skim: global_place, resize (timing
optimiser), detail_place, cts, global_route, detailed_route.
- Timing-specific Tcl commands: STA Tutorial.
- OpenROAD reference: https://openroad.readthedocs.io/en/latest/.

