Much EDA scripting is reading text that a tool wrote: a log full of messages, a report with a slack number three lines down, a list of names with brackets in them. This lesson covers the string command for simple jobs, format and scan for numbers, and regexp and regsub for patterns.
Reading time: about 8 minutes.
5.1 The string command
string is a family of subcommands. The ones you will use most:
set s "report_timing -late"
puts [string length $s]
puts [string range $s 0 12]
puts [string first "_" $s]
puts [string last "_" $s]
puts [string index $s 0]
puts [string toupper $s]
puts [string trim " x "]
puts [string trimleft "00042" 0]
puts [string repeat "-" 12]
puts [string map {_ -} $s]
puts [string equal abc abc]
puts [string compare abc abd]
puts [string is integer -strict 42]
puts [string is double -strict abc]
This prints:
19
report_timing
6
6
r
REPORT_TIMING -LATE
x
42
------------
report-timing -late
1
-1
1
0
string first and string last return an index, or -1 if the text is not found. string map replaces several substrings in one pass. string is tests what kind of text a value is: integer, double, alpha, digit and others. Use -strict so that an empty string is not reported as a number.
5.2 Matching with glob patterns
string match compares text against a glob pattern: * matches any run of characters, ? one character, and [abc] one of a set.
puts [string match {ff_*} ff_a]
puts [string match {u[0-9]} u7]
puts [string match -nocase {FF*} ff_a]
This prints three lines of 1. Glob patterns are the same style tools use for names, so they are the natural choice for selecting cells and ports. For anything more structured, use a regular expression.
5.3 format and scan
format builds text from values, in the style of C's printf.
puts [format "%-8s|%6d|%8.3f" cell 42 -0.1234]
puts [format "%05d" 42]
puts [format "%x" 255]
puts [format "%s has %d regs" acc8 9]
This prints:
cell | 42| -0.123
00042
ff
acc8 has 9 regs
%-8s pads a string to eight characters on the left, %6d pads an integer to six characters, and %8.3f prints a float with three decimals in a field of eight. Use these to line up the columns of a summary table.
scan is the reverse: it reads values out of text according to a format.
scan "WNS -0.123 ns" "WNS %f %s" v unit
puts "$v $unit"
This prints -0.123 ns. scan is handy for fixed layouts. For lines whose layout varies, regular expressions are more robust.
5.4 Regular expressions with regexp
regexp tests whether text matches a pattern and can capture parts of it. The result is 1 for a match and 0 for none. Put the pattern in braces so that Tcl does not process the backslashes first.
Let us consider a log line. The message ids in the following example are ones that the Innovus reference lists, and the line itself is synthetic.
set line "**ERROR: (ENCTS-423): no library found for cell u1"
puts [regexp {\(([A-Z]+)-(\d+)\)} $line whole pkg num]
puts "$whole | $pkg | $num"
puts [regexp {nomatch} $line]
puts [regexp -inline {\(([A-Z]+-\d+)\)} $line]
puts [regexp -inline -all {[0-9]+} "a1 b22 c333"]
puts [regexp -all {l} "hello world"]
puts [regexp -nocase {error} $line]
puts [regexp {^\*\*(ERROR|WARN)} $line m sev]
puts $sev
This prints:
1
(ENCTS-423) | ENCTS | 423
0
(ENCTS-423) ENCTS-423
1 22 333
3
1
1
ERROR
The pattern \(([A-Z]+)-(\d+)\) looks for a literal bracket, capital letters, a hyphen, digits and a closing bracket. Each pair of parentheses without a backslash is a capture group. After the pattern, the variables receive the whole match first and then one value per group. With -inline, the matches are returned as a list instead. With -all, every match is found, and the return value is the number of matches.
The commonly used pieces:
.matches any character,*zero or more,+one or more,?zero or one.\dis a digit,\swhite space,\wa word character.[a-z]is a range, and[^a-z]is anything except a lower-case letter.^and$anchor to the start and end of the text.(a|b)means either one.
5.5 Why the pattern goes in braces
A backslash in double quotes is consumed by Tcl before the regular expression engine sees it.
puts [regexp "\\d+" "abc123"]
puts [regexp {\d+} "abc123"]
puts [regexp "\d+" "abc123"]
This prints:
1
1
0
The third form failed, because Tcl turned \d in quotes into a plain d. Using braces avoids the problem and is easier to read.
5.6 Greedy and lazy
By default * and + match as much as possible. Add ? after them to match as little as possible.
puts [regexp -inline {<(.*)>} "<a> <b>"]
puts [regexp -inline {<(.*?)>} "<a> <b>"]
This prints:
{<a> <b>} {a> <b}
<a> a
The greedy pattern ran to the last closing bracket. The lazy one stopped at the first. When a pattern captures more than you expected, a greedy match is usually the reason.
5.7 Substitution with regsub
regsub replaces matches. Use \1, \2 for captured groups, and -all to replace every match.
puts [regsub {u(\d+)} "u1 u22" {inst_\1}]
puts [regsub -all {u(\d+)} "u1 u22" {inst_\1}]
set n [regsub -all {\s+} "a b c" " " clean]
puts "$n: $clean"
This prints:
inst_1 u22
inst_1 inst_22
2: a b c
Without a variable name, regsub returns the new text. With a variable name at the end, it stores the text there and returns the number of replacements.
5.8 Names with brackets
Bus bits such as top/u1[3] contain square brackets, which are special for Tcl, for glob patterns and for regular expressions. Each of them needs care.
puts [regsub -all {[^a-z0-9_]} {top/u1[3]} _]
catch {regsub -all {[^a-z0-9_]} "top/u1[3]" _} err
puts $err
This prints:
top_u1_3_
invalid command name "3"
In the braced form, Tcl left the text alone. In the quoted form, Tcl treated [3] as a command to run and failed. The same applies to a name held in a variable: keep it in braces when you write it, and escape the brackets in a glob pattern with a backslash, as in string match {ff_sum_reg\[0\]} $name.
5.9 Parsing a value out of a report
set row "Slack Time -0.234"
if {[regexp {Slack Time\s+(-?[0-9.]+)} $row -> slack]} {
puts "slack=$slack"
}
This prints slack=-0.234. The pattern allows any amount of white space and an optional minus sign. The variable -> is only a name. Many scripts use it to show that the whole match is being ignored. The skeleton of this example, a regexp inside an if, is the basis of every report parser.
5.10 A small Innovus example
The Legacy getOptMode command prints each parameter as a line in the form -opt_add_insts true # bool, default=true, followed by the bare value. The redirect command can send command output to a Tcl variable with its -variable option. To be run at the Innovus prompt. Check the option order with redirect -help.
Legacy UI.
redirect optmodes {getOptMode -opt_add_insts} -variable
if {[regexp {(-opt_add_insts)\s+(\S+)\s+#} $optmodes -> name value]} {
puts "$name is $value"
}
The first line captures the printed text in the variable optmodes, and the second pulls out the parameter name and value. For a single value, getOptMode ... -quiet is simpler, as Lesson 2 showed. Capturing and parsing becomes valuable when a command prints a larger block of text that has no -quiet form.
5.11 Why an interviewer asks about this
Typical questions: "how do you extract a number from a report line", "what does greedy mean in a regular expression", and "why is the pattern in braces". Answers should show a regexp with a capture group, name the lazy ? modifier, and explain that braces stop Tcl from consuming backslashes. A practical question may hand you a few log lines and ask for a count of errors by message id, and Lesson 9 builds that tool.
Key takeaways
stringhandles cutting, searching and testing,string matchuses glob patterns, andformatandscanconvert between text and numbers.regexptests and captures, and-inlineand-allreturn values instead of a count.- Put patterns in braces so that Tcl does not consume the backslashes.
- Names with brackets need braces or escapes everywhere, and
regsub -allis the tool for cleaning them.