In short
Every commercial VLSI tool - Design Compiler, Genus, Innovus, PrimeTime, Vivado, Quartus, Calibre, Virtuoso - is driven by a text command language, and that language is almost always Tcl (Virtuoso is the exception - its scripting language is SKILL, with OCEAN for analog simulation). A chip is never built by clicking: the RTL-to-GDSII flow is a set of Tcl scripts that read design files, apply constraints, run synthesis or place-and-route, and write reports, while Perl or Python generates those scripts, parses the megabyte-long logs and builds the summary tables a lead reviews. Scripting is examined in university VLSI/EDA-lab courses and in "VLSI design flow" or "physical design" electives; GATE EC/VLSI tests the flow and constraint concepts rather than syntax. In Indian hiring (Intel, Qualcomm, Nvidia, AMD, Samsung, Texas Instruments, Synopsys, Cadence, Siemens EDA, Bosch, Einfochips, Sasken, Tessolve, Wipro/HCL design services), a CAD or physical-design interview reliably includes "write a Tcl proc", "parse this report with a regex" and "what does this SDC line mean".
Explained
Why scripting runs the VLSI flow
- A single SoC block is synthesised, placed and timed hundreds of times; only a scripted flow is repeatable, version-controllable and reviewable.
- Batch mode: tools are launched non-interactively -
dc_shell -f syn.tcl | tee syn.log,innovus -no_gui -files pnr.tcl,vivado -mode batch -source build.tcl,pt_shell -f sta.tcl. GUI use is for debug only. - Three layers of a real flow: (1) shell/Makefile or job scheduler (LSF/SGE) launches runs; (2) Tcl drives the tool; (3) Perl/Python generates scripts and extracts numbers from logs and reports into CSV/HTML dashboards.
- Why Tcl and not Python inside tools: Tcl is tiny, embeddable in C/C++ and has a uniform "everything is a string" command syntax, so every EDA vendor embedded it in the 1990s. Vendors now expose Python too (Synopsys and Cadence Python shells, Vivado has no Python shell but ships a Tcl-callable
XSCT/Python flow, OpenROAD/OpenLane use Tcl plus Python), but Tcl remains the lingua franca; tool-command questions are asked in Tcl. - Open-source flow used in Indian academic/startup work: Yosys + OpenROAD under OpenLane (the C-DAC/IIT-supported SCL 180 nm and SkyWater 130 nm shuttles), all Tcl/Python driven.
Tcl essentials for EDA
- Syntax rule: a command is
name arg arg ...; the first word is the command, words are separated by spaces, and a newline or;ends it. There are no operators at statement level -exprdoes arithmetic. - Substitution (the whole language):
$varvariable substitution,[cmd]command substitution,\escape. **Braces{}suppress substitution; double quotes""allow it** - the single most asked Tcl question. - Core commands:
set,expr,puts,if/else,for,foreach,while,switch,proc,return,lindex/llength/lappend/lsort/lsearch/lrange,string length/first/range/map/trim/toupper,regexp,regsub,format,scan,open/gets/read/close/puts $fh,file exists/dirname/tail,glob,catch,error,source,exec,array set/get/names,dict(Tcl 8.5+),info exists,incr,unset,upvar,global. - Lists vs strings: a Tcl list is a string with space-separated elements;
llength {a b c}is 3.lindexis 0-based. - Associative arrays and dicts:
set a(cell1) 0.42,array names a; ordict set d cell1 0.42,dict get d cell1. - Procedures and scope: variables inside a
procare local; useglobal/upvaror pass arguments. Default argsproc p {a {b 10}} {...}, variable args withargs. - Error handling:
if {[catch {risky_cmd} msg]} { puts "failed: $msg" }- essential so a 6-hour P&R run does not die on one missing file. - Tool-specific extensions (collections): Synopsys tools add
get_cells,get_nets,get_pins,get_ports,get_clocks,all_registers,all_inputs,foreach_in_collection,sizeof_collection,get_attribute. A collection is not a Tcl list - you must useforeach_in_collection/get_object_name, a classic interview trap. - Common EDA Tcl commands:
read_verilog,analyze/elaborate,link,current_design,set_db/set_app_var,compile_ultra,syn_generic/syn_map(Genus),place_opt,clock_opt,route_opt,report_timing,report_qor,report_area,report_power,report_constraint -all_violators,write_sdc,write_sdf,write -format verilog.
SDC - constraints are a Tcl script
- SDC (Synopsys Design Constraints) is an industry-standard Tcl-syntax file read by synthesis, P&R and STA tools; Xilinx's XDC is the same idea with Vivado commands.
- Clocks:
create_clock -name clk -period 2.5 [get_ports clk];create_generated_clock;set_clock_uncertainty 0.05 [get_clocks clk];set_clock_transition;set_clock_groups -asynchronous -group {clk_a} -group {clk_b}. - I/O timing:
set_input_delay/set_output_delay -clock clk -max/-min;set_driving_cell,set_load,set_max_fanout,set_max_transition,set_max_capacitance. - Exceptions (timing constraints the tool must relax):
set_false_path(no functional path, e.g. across async domains),set_multicycle_path(path allowed more than one clock cycle),set_max_delay/set_min_delay(point-to-point),set_case_analysis(constant on a mode pin),set_disable_timing. - Environment:
set_operating_conditions,set_wire_load_model(pre-layout),set_clock_latency -source(andset_clock_latencyfor network latency) for PLL insertion delay,set_dont_touch,set_dont_use,set_size_only. - Rule of thumb examined often: a constraint error, not an RTL error, is the usual cause of "synthesis passed but silicon failed"; always close with
report_constraint -all_violatorsandcheck_timing.
Perl for VLSI: regex, reports and legacy flows
- Perl dominated EDA scripting from the 1990s; large Indian service companies still maintain Perl flow infrastructure, so interviews ask it.
- Variables:
$scalar,@array,%hash;myfor lexical scope;use strict; use warnings;mandatory in reviews. - Idioms:
while (<FH>) { ... }line loop with$_;chomp;split /\s+/, $_;join ",", @f;push/pop/shift/unshift;sort { $a <=> $b };exists $h{$k};keys/values;sprintf "%.3f";open(my $fh, "<", $file) or die "$!";. - Regex (the reason Perl survives):
if (/slack\s+\(VIOLATED\)\s+(-?\d+\.\d+)/) { $slack = $1; };s///gsubstitute,tr///transliterate; modifiersiignore case,gglobal,mmultiline (^ $match at每 line - no, at each line),sdot matches newline,xextended; greedy.*vs lazy.*?; capture groups(), anchors^ $, classes\d \w \s, quantifiers* + ? {n,m}. - Typical jobs: extract WNS/TNS from a timing report, build a cell-count table from an area report, convert a spreadsheet of pin locations into an I/O constraint file, generate repetitive RTL or test vectors, diff two netlists' instance lists.
- Why teams migrated to Python: readability, objects, standard library (
csv,json,re,argparse,os,subprocess,pandas,matplotlib), and far easier maintenance of 5,000-line flow scripts.
Python in the modern VLSI toolbox
- Flow control and data:
refor the same regex work as Perl;pandasto tabulate QoR across hundreds of runs;matplotlibfor congestion/slack plots;openpyxl/xlsxwriterfor the Excel reports managers actually read;subprocessto launchdc_shell;jinja2to template Tcl and SDC files per corner. - Verification and modelling: cocotb writes testbenches in Python driving a Verilog DUT through VPI/VHPI on Icarus/Verilator/VCS/Questa; PyVerilog parses and transforms Verilog ASTs; MyHDL/Amaranth generate synthesisable RTL from Python;
numpy/scipyfor DSP golden models compared against RTL output. - Tool APIs: Synopsys, Cadence and Siemens EDA now ship Python interfaces; OpenROAD exposes a Python API; Vivado is driven from Python only indirectly (generate a Tcl script, or use the XSDB/
vivado -mode batchroute, or Xilinx'spyXSCT-style wrappers) - a point candidates get wrong. - Regression harness pattern: Python reads a YAML/JSON run matrix (corners, clock periods, floorplan options) → renders Tcl per run → submits jobs → waits → parses logs → writes
summary.csv+ HTML → flags runs with WNS < 0 or DRC > 0. - When to use which: Tcl because the tool forces it; Python for everything around the tool; Perl only to maintain what already exists. Shell/Makefile for dependency-driven stages;
makenaturally reruns only what changed.
Flow, environment and engineering practice
- Scripted RTL-to-GDSII stages: lint/CDC → simulation → synthesis (DC/Genus) → formal equivalence (Formality/Conformal) → floorplan → placement → CTS → routing → signoff STA (PrimeTime), physical verification (Calibre DRC/LVS), power (PrimePower/Voltus), DFT/ATPG (TestMAX/Modus). Each stage is a Tcl script taking the previous stage's database.
- Reusable structure:
setup.tcl(libraries, paths, variables) →constraints.sdc→ stage scripts →reports/. Never hardcode a path or a library name; keep them in one setup file and use relative paths and environment variables ($env(PROJ_ROOT)). - Version control and reproducibility: scripts, SDC and setup in Git; log every tool version; tag the run directory with date, tool version and Git SHA.
- Compute environment: Linux (RHEL/CentOS/Rocky), licence servers, LSF/SLURM queues, 100 GB+ run directories; Indian design-service centres (Bengaluru, Hyderabad, Noida, Pune) run exactly this.
- Legal and commercial context: EDA licences are expensive node-locked/floating licences governed by the Copyright Act, 1957 (software) and contract; US export-control rules apply to advanced-node EDA. Layout topographies are protected under the Semiconductor Integrated Circuits Layout-Design Act, 2000 (SICLD), administered by MeitY, not by patent or copyright. GST on software/SaaS licences is at the 18% slab (slabs are 5%/18%/40% since 22 September 2025). India's Semicon India programme (ISM, MeitY) funds fabs, ATMP/OSAT units and the Chips-to-Startup/DLI schemes that give colleges free EDA-tool and SCL 180 nm/SkyWater PDK access - the usual route to a student tapeout.
Key points
- Tcl - Tool Command Language; the command interpreter embedded in nearly every EDA tool, so the tool's commands are Tcl commands.
- Batch mode - running a tool from a script with no GUI (
dc_shell -f,innovus -files,vivado -mode batch -source); the only auditable way to build a chip. - Substitution rules -
$xvariable,[cmd]command,{}blocks substitution,""permits it; the top Tcl interview question. - expr - Tcl's arithmetic command;
set a [expr {$b*2}], and bracing the expression is both faster and safer. - proc - defines a procedure; its variables are local unless declared
globalor linked withupvar. - catch - runs a command and traps the error so a long run does not abort:
catch {cmd} msg. - Collection - a Synopsys/Cadence object handle; iterate with
foreach_in_collection, size withsizeof_collection- it is *not* a Tcl list. - SDC - standard Tcl-syntax constraint file (
create_clock,set_input_delay,set_false_path); XDC is the Vivado equivalent. - False path vs multicycle path - false path is never timed; a multicycle path is timed but given N cycles.
- report_timing / report_qor - the commands whose output scripts parse for WNS, TNS, area, power, utilisation.
- WNS / TNS - worst and total negative slack; the headline numbers of any QoR table.
- Perl regex -
/pattern/matching with captures$1, substitutions///, modifiersi g m x; the classic log-parsing tool. - Python ecosystem -
re,pandas,subprocess,jinja2,argparsefor flow automation;cocotb,PyVerilog,MyHDL/Amaranthfor verification and RTL generation. - Jinja2/templating - generate per-corner Tcl and SDC instead of copying scripts, eliminating divergence between runs.
- Makefile / LSF - dependency-based stage rerun and farm job submission around the Tcl scripts.
- No hardcoding - paths, library names, clock periods live in a setup file or input data, never inside flow logic.
- OpenLane - open Tcl/Python RTL-to-GDSII flow (Yosys + OpenROAD + Magic/KLayout) used for SkyWater 130 nm and SCL 180 nm student tapeouts.
- SICLD Act, 2000 - the Indian law protecting IC layout-designs, administered by MeitY.
Formulas and examples
WNS = min(slack over all paths); TNS = Σ(negative slacks only); both in ns
Maximum clock frequency f_max = 1 / (T_clk − WNS) with T_clk the constrained period (s), f in Hz
Multicycle setup path required time = N × T_clk − T_setup, where N = cycles set by
set_multicycle_path N -setupUtilisation = (Σ standard-cell area + macro area) / core area × 100 %
Runtime of a swept regression ≈ N_corners × N_configs × t_run / N_parallel_licences
Example 1 - a complete, minimal synthesis script in Tcl
# setup
set DESIGN top
set RTL_DIR ./rtl
set LIB_DIR $env(STD_CELL_LIB)
set_app_var target_library "$LIB_DIR/sc_tt_1v8_25c.db"
set_app_var link_library "* $target_library"
set_app_var search_path [list . $RTL_DIR $LIB_DIR]
# read RTL (abort cleanly if a file is bad)
foreach f [glob -nocomplain $RTL_DIR/*.v] {
if {[catch {read_verilog $f} msg]} { puts "ERROR reading $f: $msg"; exit 1 }
}
current_design $DESIGN
link
# constraints
create_clock -name clk -period 2.5 [get_ports clk]
set_clock_uncertainty 0.05 [get_clocks clk]
set_input_delay 0.6 -clock clk [remove_from_collection [all_inputs] [get_ports clk]]
set_output_delay 0.5 -clock clk [all_outputs]
set_false_path -from [get_ports rst_n]
set_max_transition 0.3 [current_design]
compile_ultra -no_autoungroup
report_timing -max_paths 10 > reports/$DESIGN.timing.rpt
report_area > reports/$DESIGN.area.rpt
report_qor > reports/$DESIGN.qor.rpt
write -format verilog -hierarchy -output netlist/$DESIGN.v
write_sdc constraints/$DESIGN.sdc
Example 2 - Tcl proc and collection iteration (classic interview exercise) Count flip-flops and list cells whose area exceeds a limit.
proc big_cells {limit} {
set n 0
foreach_in_collection c [get_cells -hier *] {
set a [get_attribute $c area]
if {$a > $limit} {
puts [format "%-40s %8.3f" [get_object_name $c] $a]
incr n
}
}
return $n
}
puts "FF count : [sizeof_collection [all_registers -cells]]"
puts "Big cells : [big_cells 5.0]"
- Note
[expr {$a > $limit}]is implicit insideif; andget_object_nameis needed because$cprints as an opaque handle.
Example 3 - slack, f_max and a multicycle path (numbers) A path is constrained at create_clock -period 2.5 (400 MHz). The report gives T_cq = 0.12 ns, T_comb = 2.31 ns, T_net = 0.18 ns, T_setup = 0.09 ns, skew 0.05 ns, uncertainty 0.05 ns.
- Required = 2.5 − 0.09 − 0.05 − 0.05 = 2.31 ns
- Arrival = 0.12 + 2.31 + 0.18 = 2.61 ns
- Slack = 2.31 − 2.61 = −0.30 ns (violating)
- f_max = 1/(2.5 + 0.30) = 1/2.80 ns = 357.1 MHz (using T_clk − WNS with WNS = −0.30)
- If the path is genuinely allowed two cycles,
set_multicycle_path 2 -setup -to [get_pins reg_b/D]makes Required = 2×2.5 − 0.09 − 0.05 − 0.05 = 4.81 ns, so slack = 4.81 − 2.61 = +2.20 ns - met without touching the RTL. (The matching-hold 1must also be set, else a hold violation appears.)
Example 4 - Perl one-file report parser Extract every violating endpoint and its slack, then print WNS and TNS.
#!/usr/bin/perl
use strict; use warnings;
my ($wns, $tns, %slack) = (0, 0);
open(my $fh, "<", $ARGV[0]) or die "cannot open $ARGV[0]: $!";
my $ep = "";
while (<$fh>) {
$ep = $1 if /^\s*Endpoint:\s+(\S+)/;
if (/^\s*slack\s+\(VIOLATED\)\s+(-?\d+\.\d+)/) {
$slack{$ep} = $1;
$tns += $1; # already negative
$wns = $1 if $1 < $wns;
}
}
close $fh;
printf "WNS = %.3f ns TNS = %.3f ns violating paths = %d\n",
$wns, $tns, scalar keys %slack;
for my $k (sort { $slack{$a} <=> $slack{$b} } keys %slack) {
printf " %-45s %7.3f\n", $k, $slack{$k};
}
If the file holds slacks −0.300, −0.120 and −0.045 on three endpoints: WNS = −0.300 ns, TNS = −0.465 ns, count = 3.
Example 5 - Python regression harness with utilisation check
import re, subprocess, pandas as pd
from jinja2 import Template
tcl = Template(open("syn.tcl.j2").read())
rows = []
for period in [2.0, 2.5, 3.0]:
open("run.tcl", "w").write(tcl.render(period=period, design="top"))
subprocess.run(["dc_shell", "-f", "run.tcl"], stdout=open(f"log_{period}.txt", "w"))
log = open(f"log_{period}.txt").read()
wns = float(re.search(r"slack \(\w+\)\s+(-?\d+\.\d+)", log).group(1))
area = float(re.search(r"Total cell area:\s+([\d.]+)", log).group(1))
rows.append({"period_ns": period, "fmax_MHz": round(1000/(period - wns), 1),
"wns_ns": wns, "area_um2": area, "met": wns >= 0})
pd.DataFrame(rows).to_csv("summary.csv", index=False)
With WNS = −0.30 ns at 2.0 ns, +0.10 ns at 2.5 ns and +0.55 ns at 3.0 ns:
| period (ns) | WNS (ns) | f_max (MHz) | timing met |
|---|---|---|---|
| 2.00 | −0.30 | 1000/2.30 = 434.8 | no |
| 2.50 | +0.10 | 1000/2.40 = 416.7 | yes |
| 3.00 | +0.55 | 1000/2.45 = 408.2 | yes |
Utilisation check: core area 400 µm × 400 µm = 160,000 µm²; cells total 96,000 µm², one macro 12,000 µm² → utilisation = 108,000/160,000 = 67.5 % (healthy; above about 80 % routing congestion usually appears).
| Tcl | Perl | Python | |
|---|---|---|---|
| Role in flow | Inside the tool - drives it | Legacy log parsing, text generation | Flow glue, data analysis, verification |
| Runs inside dc_shell / Innovus / Vivado | Yes, natively | No | Only via vendor Python shells |
| Regex strength | regexp/regsub, adequate | Best-in-class, built into syntax | re module, very good |
| Data structures | Lists, arrays, dicts | Arrays, hashes, references | Rich: list/dict/set/objects, pandas |
| Readability / maintenance | Verbose, quoting-sensitive | Terse, can get cryptic | Highest |
| Typical interview ask | Write a proc, explain {} vs "" | Write a regex to pull slack | Parse report into CSV, cocotb basics |
Where it is used
- Physical design and STA teams (Intel Bengaluru, Qualcomm Hyderabad/Bengaluru, Nvidia Pune, AMD, Samsung SSIR Noida/Bengaluru, Micron, Texas Instruments): daily Tcl for Innovus/ICC2/PrimeTime, Python for QoR dashboards.
- CAD/flow engineer roles (Synopsys Bengaluru/Noida, Cadence Noida/Pune, Siemens EDA, Arm, Marvell): build and maintain the Tcl/Python/Makefile flow, licence and farm automation - a career path that is almost pure scripting.
- Design-services and VLSI training companies (Einfochips, Tessolve, Sasken, L&T Technology Services, Mirafra, HCLTech, Wipro, Mindgrove, InCore): turnkey RTL-to-GDSII and DFT projects built on customer-specific script flows.
- FPGA and product engineering (ISRO/URSC, DRDO labs, BEL, Tata Elxsi, Bosch, Continental, automotive and defence FPGA work): Vivado/Quartus Tcl scripts for repeatable, auditable builds with XDC constraints under DO-254/ISO 26262-style documentation.
- Verification teams: UVM regressions launched by Python/shell, cocotb for Python testbenches, Perl/Python coverage-report merging and triage.
- Academic and ISM-funded tapeouts: OpenLane/Yosys/OpenROAD flows on SCL 180 nm and SkyWater 130 nm through C-DAC/MeitY Chips-to-Startup and DLI schemes; IIT and NIT VLSI labs run these as graded assignments.
Interview questions
- Q: What is the difference between
{}and""in Tcl? A: Double quotes allow$variableand[command]substitution before the command runs; braces pass the text through literally, which is whyif,forandexprbodies are braced. - Q: Why is
set x [expr {$a + $b}]preferred overset x [expr $a + $b]? A: Bracing stops double substitution, so the expression compiles once (faster) and values containing operators or spaces cannot be re-evaluated - it is also safer against injection. - Q: What is a collection and why can't you use
foreachon it? A: A collection is an opaque handle to tool database objects, not a Tcl list; it must be traversed withforeach_in_collectionand its elements named withget_object_name. - Q: What does
create_clock -period 2.5 [get_ports clk]do, and what else must you constrain? A: It defines a 400 MHz clock at that port; you still need input/output delays, clock uncertainty and transition, driving cell and load, false/multicycle paths, and max transition/fanout/capacitance. - Q: Difference between a false path and a multicycle path? A: A false path is removed from timing analysis because it is not functional (e.g. a static config bit or an async crossing); a multicycle path is still analysed but allowed N clock cycles.
- Q: How would you find the worst 10 violating paths and their endpoints from a script? A:
report_timing -max_paths 10 -slack_lesser_than 0 -nosplit > rpt, orreport_constraint -all_violators; then parse theslacklines withregexpin Tcl orrein Python. - Q: Write a Perl regex to capture a negative slack value from a timing report line. A:
if (/slack\s+\(VIOLATED\)\s+(-?\d+\.\d+)/) { $slack = $1; }--?allows the sign, the capture group gives the number in$1. - Q: Greedy vs lazy regex - why does it matter in log parsing? A:
.*takes the longest match and can swallow later fields on the same line;.*?takes the shortest, so use the lazy form (or a negated class like[^\s]+) when fields are adjacent. - Q: How do you make a long P&R script survive one failing step? A: Wrap risky commands in
catch, log the message, and either continue with a sane default or exit with a non-zero status so the farm job is marked failed rather than silently "passing". - Q: Why is Tcl still used when Python exists? A: It is embedded in the tool binaries and defines the tool command set; vendors' Python shells are newer wrappers, and millions of lines of production SDC and flow scripts are Tcl.
- Q: What is cocotb and when would you use it over SystemVerilog UVM? A: cocotb drives a Verilog/VHDL DUT from Python coroutines via VPI; it is attractive for quick block-level tests, algorithmic golden models and when the team's expertise is Python - large constrained-random SoC verification still uses UVM.
- Q: Your synthesis met timing but P&R shows large violations. What would you check in the scripts first? A: Constraint mismatch - wire-load model versus real parasitics, missing clock uncertainty/latency, SDC not propagated to P&R, false paths wrongly applied, and whether both stages read the same SDC and corner libraries.
- Q: How do you keep 200 corner/configuration runs organised? A: Template the Tcl/SDC with Jinja2 or a Tcl proc, one run directory per configuration tagged with tool version and Git SHA, submit via LSF/SLURM, and parse all logs into one CSV/HTML QoR table flagging WNS < 0 or DRC > 0.
Common mistakes
- Treating Tcl like C: writing
if (a > b)orx = 5instead ofif {$a > $b} {...}andset x 5, and forgetting thatexpris needed for arithmetic. - Using
""where{}is required, so$and[are substituted too early - the commonest source of "works for one net, breaks for all". - Applying
foreach,llengthorlindexto a collection instead offoreach_in_collection/sizeof_collection. - Hardcoding absolute paths, library names and clock periods inside flow scripts instead of a single setup/data file - the flow then breaks on another machine or node.
- Over-constraining or under-constraining: blanket
set_false_pathor a missingset_clock_uncertainty/set_input_delay, then trusting a clean report; always runcheck_timingandreport_constraint -all_violators. - Writing a greedy regex (
.*) that silently captures the wrong field, or parsing a report whose format changes between tool versions instead of using the tool's own machine-readable output. - Ignoring exit status and errors: no
catch, no check of the log forError:/Warning:, so a failed stage is reported as a successful run. - Setting a multicycle setup without the matching hold adjustment, converting a setup fix into a hold violation.
Related topics
Static timing analysis and constraints · ASIC physical design: floorplan, placement, CTS and routing · Logic synthesis and RTL coding guidelines · Programmable logic: PLDs, CPLDs and FPGAs · Verification: SystemVerilog, UVM and assertions · Design for testability: scan and ATPG.