Use when running Cadence Innovus Place & Route on a remote Linux server. Covers project setup, floorplan, CTS, routing, and signoff for TSMC 65nm designs. First copy from example template, then modify per design. Use this skill whenever the user mentions Innovus, P&R, place and route, backend, physical design, GDS generation, or wants to run post-synthesis layout.
Run Cadence Innovus P&R flow on a remote Linux server via SSH/SCP. The server has Innovus v21.16 and TSMC 65nm libraries pre-installed. This skill covers: copying example template, modifying scripts for your design, running the full flow (init → floorplan → placement → CTS → routing → signoff), and downloading results.
$SERVER, User: $USER/opt/cadence/installs/INNOVUS211/bin/innovus (NOT INNOVUSEXPORT21)$STDCELL_PATH/~/agent_digits/example/innovus_flow/../dc/gate/{top}_netlist.v, ../dc/sdc/{top}.sdcThe example template has all library paths, MMMC config, and layer maps pre-configured. Never write scripts from scratch. Copy the template, then modify only the design-specific parts.
WARNING: Innovus TCL scripts are extremely sensitive to missing options. Even a single omitted flag can cause the flow to silently produce wrong results or crash hours later. The example template has been verified through real tapeout runs — it contains hundreds of carefully tuned options for setNanoRouteMode, setSrouteMode, set_ccopt_property, setExtractRCMode, etc. that this skill only summarizes at a high level.
Therefore:
cp -r the example template as the starting point# Copy template
ssh $USER@$SERVER \
"cp -r ~/agent_digits/example/innovus_flow/ \
~/agent_digits/{project}/innovus/"
# Clean generated files from example run
ssh $USER@$SERVER \
"cd ~/agent_digits/{project}/innovus/ && \
rm -rf save/* output/* rpt/* extLogDir cdBfile *.rpt *.rpt.old logFile power.rpt \
rc_model.bin mp_data* HRAM_TOP timingReports .WeightWriting* .timing_file* *.swp .cadence/ \
innovus.cmd* innovus.log* innovus.logv* innovus_run.log model.asrt*"
{project}/innovus/
├── lib/ # LEF, GDS map, layer map (from template, DO NOT MODIFY)
│ ├── tcbn65lp_9lmT2.lef # Standard cell LEF (9 metal layer)
│ ├── innovus_gds_out.map # GDS stream out map
│ ├── quantus_extract.layermap # RC extraction layer map
│ └── tQuantus_model.bin # Generated at runtime
├── scripts/ # TCL scripts (modify per design)
│ ├── all.tcl # Master script: sources 1-6 in order
│ ├── mmmc.tcl # MMMC view definition (WC/TC/BC corners)
│ ├── 1-init.tcl # Design import + MMMC setup
│ ├── 2-fp.tcl # Floorplan + power ring + stripes + sroute
│ ├── 3-place.tcl # Placement + optimization
│ ├── 4-cts.tcl # Clock tree synthesis + decap
│ ├── 5-route.tcl # Global/detail routing + post-route opt + filler
│ ├── 6-signoff.tcl # DRC/connectivity/timing + GDS/SDF/SPEF output
│ └── fix_pwr_signoff.tcl # (optional) Power fix + re-signoff
├── pre/ # Input files (copy from DC outputs)
│ ├── {top}_netlist.v # Gate-level netlist from DC
│ └── {top}.sdc # Timing constraints from DC
├── save/ # Checkpoint saves at each stage
├── output/ # Final outputs (GDS, SDF, SPEF, netlist)
└── rpt/ # Reports (timing, DRC, connectivity, power)
| File | What to change | Typical values |
|---|---|---|
scripts/1-init.tcl | init_top_cell, init_lef_file, init_verilog, init_pwr_net, remove win | top module name, power net names |
scripts/mmmc.tcl | SDC path in create_constraint_mode | ./pre/{top}.sdc |
scripts/2-fp.tcl | Rewrite entirely — floorplan size, power nets, ring/stripes | die size, VDD/VSS |
scripts/3-place.tcl | Usually no changes needed | — |
scripts/4-cts.tcl | Usually no changes needed | — |
scripts/5-route.tcl | Fix path bug: /rpt/ → ./rpt/ (template bug) | line ~72-73 |
scripts/6-signoff.tcl | Rewrite — GDS merge list, filler cells, remove IO references | stdcell GDS only |
# Copy template (see CRITICAL section above)
# Copy DC outputs to pre/ directory
ssh $USER@$SERVER \
"cd ~/agent_digits/{project}/innovus/pre && \
rm -f *.v *.sdc *.io && \
cp ~/agent_digits/{project}/dc/gate/{top}_netlist.v ./ && \
cp ~/agent_digits/{project}/dc/sdc/{top}.sdc ./"
# Change these lines:
set init_lef_file {
./lib/tcbn65lp_9lmT2.lef # Only stdcell LEF, remove IO/analog LEFs
}
set init_top_cell {top} # Your top module name
set init_verilog ./pre/{top}_netlist.v
set init_gnd_net {VSS}
set init_pwr_net {VDD} # Single power domain, not AVDD/DVDD
# CRITICAL: Remove the "win" command — it crashes in -nowin mode
# Delete any line that is just "win"
The example template has IO pads, analog macros, multiple power domains — all of which must be removed for a pure digital core design.
Template for a simple core design:
##2-FLOORPLAN
# Die size: estimate from DC area. Target ~70% utilization.
# DC area / 0.70 = core area. Add margins (5um each side).
# Example: DC area 2200um² → core ~3140um² → side ~56 → die 66x66
floorPlan -site core -d 80 80 5.0 5.0 5.0 5.0
# Global nets — connect VDD/VSS to all std cells
clearGlobalNets
globalNetConnect VDD -type pgpin -pin VDD -inst * -all
globalNetConnect VSS -type pgpin -pin VSS -inst * -all
globalNetConnect VDD -type tiehi -inst * -all
globalNetConnect VSS -type tielo -inst * -all
# Power ring on M7 (horizontal) and M8 (vertical)
setAddRingMode -ring_target default -stacked_via_top_layer M8 -stacked_via_bottom_layer M1
addRing -nets {VDD VSS} -type core_rings -follow core \
-layer {top M7 bottom M7 left M8 right M8} \
-width {top 2 bottom 2 left 2 right 2} \
-spacing {top 1 bottom 1 left 1 right 1} \
-offset {top 0.5 bottom 0.5 left 0.5 right 0.5} -center 0
# Power stripes — MUST be added here (before routing!)
# These provide via stacks from M8 down to M1 followpins
setAddStripeMode -stacked_via_top_layer M8 -stacked_via_bottom_layer M1
addStripe -start_from left -start 20 -nets {VDD VSS} -layer M8 \
-direction vertical -width 2 -spacing 1 -number_of_sets 2 -create_pins 1
# Special route: connect followpins to ring and stripes
setSrouteMode -viaConnectToShape {ring stripe followpin}
sroute -connect {corePin floatingStripe} -nets {VDD VSS} \
-layerChangeRange {M1(1) M8(8)} \
-floatingStripeTarget {ring followpin stripe} \
-crossoverViaLayerRange {M1(1) M8(8)} \
-allowLayerChange 1 -targetViaLayerRange {M1(1) M8(8)}
fixVia -minCut
fixVia -minStep
fixVia -short
fixOpenFill -net {VDD VSS}
saveDesign ./save/fp.enc
# Find this line and change the SDC path:
create_constraint_mode -name synth -sdc_files { ./pre/{top}.sdc}
Everything else (RC corners, library sets, delay corners, analysis views) stays the same — it's pre-configured for TSMC 65nm WC/TC/BC.
The example template has absolute paths on lines ~72-73:
# WRONG (template bug):
verifyConnectivity -type all -report /rpt/postECO_v4.conn.rpt
reportGateCount -level 5 -stdCellOnly -outfile /rpt/postECO_v4.gateCount.rpt
# FIX: add "./" prefix
verifyConnectivity -type all -report ./rpt/postECO_v4.conn.rpt
reportGateCount -level 5 -stdCellOnly -outfile ./rpt/postECO_v4.gateCount.rpt
##SIGNOFF — simplified for pure core design
# Reports
verify_drc -report ./rpt/signOff.drc.rpt -limit 10000
verifyConnectivity -type all -report ./rpt/signOff.conn.rpt
reportGateCount -level 5 -stdCellOnly -outfile ./rpt/signOff.gatecount.rpt
report_power -rail_analysis_format VS -outfile ./rpt/signOff.pwr.rpt
timeDesign -postRoute -outDir ./rpt/signOffTimingReports
timeDesign -postRoute -hold -outDir ./rpt/signOffTimingReports
# SPEF output (WC and BC corners)
set timing_enable_simultaneous_setup_hold_mode false
setAnalysisMode -checkType setup
set_analysis_view -setup setup -hold setup
rcOut -spef ./output/{top}_WC.spef -rc_corner rcWC
setAnalysisMode -checkType hold
set_analysis_view -setup hold -hold hold
rcOut -spef ./output/{top}_BC.spef -rc_corner rcBC
# SDF output
set_analysis_view -setup {setup typical} -hold {hold typical}
write_sdf -version 3.0 ./output/${init_top_cell}.sdf \
-process best:typical:worst -filter -nonegchecks -celltiming all \
-target_application verilog \
-temperature -40.0:25.0:125 -voltage 1.32:1.20:1.08 \
-typ_view hold -max_view setup -min_view hold
# Netlists
saveNetlist ./output/Calibre_${init_top_cell}_wo_filler.v \
-includePowerGround -phys \
-excludeCellInst {FILL1 FILL16 FILL2 FILL32 FILL4 FILL64 FILL8}
saveNetlist ./output/Calibre_${init_top_cell}_w_filler.v -includePowerGround -phys
saveNetlist ./output/EDI_to_VCS_${init_top_cell}.v \
-excludeCellInst {FILL1 FILL16 FILL2 FILL32 FILL4 FILL64 FILL8}
# GDS — merge with stdcell GDS only (no IO, no analog)
setStreamOutMode -SEvianames false -specifyViaName default \
-supportPathType4 true -virtualConnection false -textSize 1
streamOut ./output/${init_top_cell}.gds -dieAreaAsBoundary \
-libName DesignLib -mapFile ./lib/innovus_gds_out.map \
-mode ALL -stripes 1 -units 1000 -merge {\
$TSMC_HOME/digital/Back_End/gds/tcbn65lp_200a/tcbn65lp.gds}
saveDesign ./save/signoff.enc
# IMPORTANT: Use full path to INNOVUS211, not the INNOVUSEXPORT21 that `which` resolves to
# IMPORTANT: Use -nowin flag for non-GUI mode
ssh $USER@$SERVER \
"cd ~/agent_digits/{project}/innovus && \
nohup /opt/cadence/installs/INNOVUS211/bin/innovus -nowin \
-files scripts/all.tcl > innovus_run.log 2>&1 &"
Typical runtime: 15-20 minutes for a small design (~2000 gates).
# Check log line count (grows as flow progresses)
ssh $USER@$SERVER \
"wc -l ~/agent_digits/{project}/innovus/innovus.log"
# Check latest activity
ssh $USER@$SERVER \
"tail -30 ~/agent_digits/{project}/innovus/innovus.log"
# Check if still running
ssh $USER@$SERVER \
"ps aux | grep '$USER.*innovus' | grep -v grep | wc -l"
Log milestones by line count (approximate):
# Check errors
ssh $USER@$SERVER \
"grep 'ERROR' innovus.log | grep -v 'Error Limit'"
# Check DRC
ssh $USER@$SERVER \
"cat rpt/signOff.drc.rpt"
# Check connectivity
ssh $USER@$SERVER \
"tail -10 rpt/signOff.conn.rpt"
# Check timing
ssh $USER@$SERVER \
"zcat rpt/signOffTimingReports/{top}_postRoute.summary.gz | tail -20"
ssh $USER@$SERVER \
"zcat rpt/signOffTimingReports/{top}_postRoute_hold.summary.gz | tail -20"
# Check gate count
ssh $USER@$SERVER \
"cat rpt/signOff.gatecount.rpt"
mkdir -p {local_project}/innovus_output
scp $USER@$SERVER:\
~/agent_digits/{project}/innovus/output/{top}.gds \
{local_project}/innovus_output/
scp $USER@$SERVER:\
~/agent_digits/{project}/innovus/output/{top}.sdf \
{local_project}/innovus_output/
scp $USER@$SERVER:\
~/agent_digits/{project}/innovus/output/EDI_to_VCS_{top}.v \
{local_project}/innovus_output/
scp $USER@$SERVER:\
~/agent_digits/{project}/innovus/output/{top}_WC.spef \
{local_project}/innovus_output/
scp $USER@$SERVER:\
~/agent_digits/{project}/innovus/output/{top}_BC.spef \
{local_project}/innovus_output/
+--------------------+---------+
| Setup mode | all |
+--------------------+---------+
| WNS (ns):| 0.000 | ← Worst Negative Slack. 0 = MET, negative = VIOLATED
| TNS (ns):| 0.000 | ← Total Negative Slack. 0 = all paths meet timing
| Violating Paths:| 0 | ← Must be 0
+--------------------+---------+
No DRC violations were found ← Must see this line
Begin Summary
0 Problem(s) ← Must be 0
End Summary
Common issue: "special routes with opens" on VDD/VSS means M1 followpins not fully connected to power ring. Add power stripes in floorplan to fix.
Level 0 Module {top} Gates=2032 Cells=599 Area=2194.9 um^2
| Issue | Cause | Fix |
|---|---|---|
innovus fails with "build signature file" error | which innovus resolves to INNOVUSEXPORT21 | Use full path: /opt/cadence/installs/INNOVUS211/bin/innovus |
Script stops at win command in -nowin mode | win opens GUI, incompatible with -nowin | Remove win line from 1-init.tcl |
IMPCCOPT-2215: route graph for net 'clk' not fully connected | Clock gating cells split traversal graph | Harmless — timing still passes |
IMPOPT-310: Design density exceeds 95% | Floorplan too small | Increase die size in 2-fp.tcl |
| DRC violations after adding stripes post-route | Via stacks from stripes short signal wires | Add stripes in floorplan stage, BEFORE routing |
IMPSYT-7338: session directory not found | restoreDesign uses .enc file instead of .enc.dat directory | Use restoreDesign ./save/{name}.enc.dat {top} |
| VDD/VSS "special routes with opens" | M1 followpins not connected to power ring | Add power stripes + use setSrouteMode -viaConnectToShape {ring stripe followpin} |
/rpt/ path errors in signoff | Template bug: absolute path /rpt/ instead of ./rpt/ | Fix in 5-route.tcl lines ~72-73 |
| IMPCCOPT-2215 clock graph error | Clock gating cells in traversal path | Informational only, does not affect functionality |
If you need to fix something and re-run only part of the flow (e.g., redo routing after fixing floorplan):
# Correct syntax: use .enc.dat directory, not .enc file
restoreDesign ./save/{checkpoint}.enc.dat {top}
# Example: load from placement, add stripes, then re-run CTS+route+signoff
restoreDesign ./save/place.enc.dat aer_top
# ... modify floorplan, add stripes ...
saveDesign ./save/place_modified.enc
Important checkpoints:
place.enc — after placement, before CTS/routing (best for adding stripes)cts.enc — after CTS, before routingroute4.enc — after all routing + optimization (for signoff tweaks only)Always use the full Innovus path — nohup /opt/cadence/installs/INNOVUS211/bin/innovus — because nohup doesn't load shell aliases from .bashrc, and the default innovus binary is broken.
Floorplan sizing rule of thumb: DC area × 1.5 / 0.70 = core area. Then sqrt for side length. Add 10um for margins. It's better to be too large than too small — density under 85% is comfortable.
Power stripes must go in the floorplan — they create via stacks from M8 down to M1. If added after routing, these vias will short signal wires. Use addStripe in 2-fp.tcl before any routing happens.
Monitor memory usage — Innovus can use 10-12GB for small designs. The server has enough RAM but watch for OOM on larger designs.
The IMPCCOPT-2215 error is harmless — it appears when clock gating cells are present (DC inserts them automatically when set_clock_gating_style is used). The clock tree still works correctly.
MMMC config is pre-configured — the mmmc.tcl from the example template has WC/TC/BC corners, QRC tech files, SI-aware CDB files all pointing to the right paths. Only the SDC path needs changing.
The example template (~/agent_digits/example/innovus_flow/) was originally designed for a mixed-signal design with analog macros and IO pads. The pure-digital flow above stripped those parts out. If your design includes analog modules or needs IO pads, add back the following configurations.
IMPORTANT: Always start from the example template and KEEP the analog/IO sections, only modifying design-specific names and positions. Do not try to add these from memory — the template's exact options for setSrouteMode, addIoFiller, sroute for analog/IO are tuned and verified.
set init_lef_file {
./lib/tcbn65lp_9lmT2.lef # stdcell (always needed)
/path/to/analog_macro.lef # analog macro LEF (ADD)
/path/to/tpdn65lpnv2od3_9lm.lef # IO pad LEF (ADD if using IO pads)
}
# Multiple power domains
set init_gnd_net {VSS}
set init_pwr_net {AVDD DVDD} # AVDD for analog, DVDD for digital
The floorplan becomes significantly more complex. The example template's 2-fp.tcl is the reference — it handles all of the following:
a) IO pad placement:
# Load IO assignment file (defines signal-to-pad mapping and pad order)
loadIoFile ./pre/${init_top_cell}.io
# Fill gaps between IO pads with filler pads
addIoFiller -cell PFILLER20 PFILLER10 PFILLER5 PFILLER1 PFILLER05 -prefix IOFILLER
addIoFiller -cell PFILLER0005 -prefix IOFILLER -fillAnyGap
b) Analog macro placement:
# Fix analog macro at specific coordinates
placeInstance u_ANALOG_FIXLCS 390 390 -fixed
# Add halo (keep-out zone) around all macros
addHaloToBlock -allMacro 1 1 1 1
# Block routing over the analog macro
createRouteBlk -layer all -cover -inst u_ANALOG_FIXLCS
c) Multiple power domain connections:
clearGlobalNets
globalNetConnect AVDD -type pgpin -pin AVDD -inst * # analog power
globalNetConnect DVDD -type pgpin -pin DVDD -inst * # digital power
globalNetConnect VSS -type pgpin -pin VSS -inst * # common ground
globalNetConnect DVDD -type tiehi -inst * -all
globalNetConnect VSS -type tielo -inst * -all
d) Power ring for multiple nets:
addRing -nets {DVDD AVDD VSS} -type core_rings ... # 3 nets instead of 2
e) Special routing for analog and IO power:
# Standard cell power routing
sroute -connect {corePin floatingStripe} -nets {DVDD VSS} ...
# Analog cell power routing (connect macro pins to ring)
sroute -connect {blockPin floatingStripe} -nets {DVDD AVDD VSS} \
-layerChangeRange {M1(1) AP(10)} -blockPinTarget {nearestTarget} ...
# IO pad power routing
sroute -nets {AVDD VSS DVDD} -connect {padPin} \
-padPinPortConnect {allPort oneGeom} -padPinTarget {nearestTarget} ...
streamOut ./output/${init_top_cell}.gds ... -merge {\
/path/to/tcbn65lp.gds # stdcell
/path/to/analog_macro.gds # analog macro GDS (ADD)
/path/to/tpdn65lpnv2od3.gds # IO pad GDS (ADD)
/path/to/other_macro.gds # any additional macros (ADD)
}
Also exclude IO filler cells in netlist output:
saveNetlist ./output/Calibre_${init_top_cell}_wo_filler.v \
-excludeCellInst {FILL1 FILL16 FILL2 FILL32 FILL4 FILL64 FILL8 \
PFILLER0005 PFILLER05 PFILLER1 PFILLER10 PFILLER20 PFILLER5}
{project}/innovus/
├── lib/
│ ├── tcbn65lp_9lmT2.lef # stdcell LEF
│ ├── {analog}_edited.lef # analog macro LEF (copied from template or provided)
│ ├── tpdn65lpnv2od3_9lm.lef.edited # IO pad LEF (copied from template)
│ ├── innovus_gds_out.map
│ └── quantus_extract.layermap
├── pre/
│ ├── {top}_netlist.v
│ ├── {top}.sdc
│ └── {top}.io # IO assignment file (pin-to-pad mapping)
├── scripts/
│ ├── all.tcl
│ ├── mmmc.tcl
│ ├── 1-init.tcl # includes analog + IO LEFs
│ ├── 2-fp.tcl # includes IO + analog placement
│ ├── 3-place.tcl # same as digital
│ ├── 4-cts.tcl # same as digital
│ ├── 5-route.tcl # same as digital (routing blockage protects analog)
│ └── 6-signoff.tcl # multi-GDS merge + IO filler exclusion
The .io file defines which signal maps to which IO pad and the pad order around the die perimeter. This is design-specific and must be created for each project. The example template has pre/MIX_TOP.io as a reference for the format. Key points:
pad_instance_name pad_cell_type signal_name side offset