Tcl · Module 18
The Empty Collection That Does Nothing
Every other bug in this module announces itself eventually. This one produces a clean run, a plausible report, and a result you will believe.
Intermediate17 min readtcledapart-iicollectionsvalidationdefensiveconstraints
Part II · Module 18 · Chapter 18.8 · The Empty Collection That Does Nothing
Every chapter in this module has run into this and deferred it here. 18.3 found a port pattern that matched nothing. 18.6 found *rst* on a design that spells it reset. 18.7 found a misspelled attribute returning an empty result with no error.
They are all the same failure, and it is the most expensive one in EDA scripting — not because it is hard to fix, but because nothing tells you it happened.
1. An empty collection is a normal value
set none [get_ports no_such_port]
puts " the value is : '$none'"
puts " sizeof_collection : [sizeof_collection $none]" set none [get_ports no_such_port]
the value is : ''
sizeof_collection : 0No error. The query ran, found nothing, and returned a perfectly valid collection containing zero objects.
And iterating it behaves exactly as it should:
foreach_in_collection p $none {...} runs:
-> 0 iteration(s). No error. Nothing happened.Zero iterations over zero objects. Everything is working correctly. That is the problem.
2. The full failure, start to finish
Here is what it costs. A design with a port called clk, and someone writes clock:
=== the quiet failure, start to finish ===
the design's ports are: clk reset_n {data_in[0]} {data_in[1]} {data_in[2]} {data_in[3]} {data_out[0]} {data_out[1]}
someone writes 'clock' instead of 'clk':
create_clock -name core -period 2.0 [get_ports clock]
-> the command returned without error
report_clocks:
(no clocks defined)
with the right name:
clock core period 2.0 on clkOne character. No error. A meaningless result that looks exactly like a good one.
3. Why constraint commands do not complain
It seems like the tool should object, and it is worth understanding why it does not.
Applying an operation to an empty set is mathematically fine — doing something to nothing is a no-op, not an error. And it is sometimes genuinely what you want: a script that constrains "every scan-enable port, if there are any" should not fail on a design without scan.
The tool cannot distinguish "there are legitimately none" from "you typed the name wrong", because both look identical at the point of use. Only you know which you meant, and this chapter is about saying so.
4. Where empty results come from
In rough order of frequency:
| Cause | Looks like |
|---|---|
| A typo in the name | clock for clk |
| A different convention than you assumed | rst_n vs reset_n (18.6 §3) |
Missing -hierarchical | a pin query at the top level (18.4 §2) |
| A bracket pattern | data_in[*] as a glob (18.3 §4) |
| A misspelled attribute in a filter | silent, by 18.7 §6 |
| Nothing loaded yet | every query empty, or raising |
| The design genuinely has none | the legitimate case |
The last row is why the answer cannot be "make empty results an error". It has to be a decision you make per query.
5. The guard
The registry's own sample, and the whole fix:
if {[sizeof_collection [get_ports clk]] == 0} {
error "no port matched clk"
}Three lines. Written once per query that must not be empty, it converts the most expensive failure mode in this module into an ordinary error that stops the script.
6. The guard working
Same typo, with the check in place:
proc need_ports {pattern {what "port"}} {
set c [get_ports $pattern]
if {[sizeof_collection $c] == 0} {
error "no $what matched '$pattern'" {} [list DESIGN NOMATCH $pattern]
}
return $c
}
if {[catch {create_clock -name core -period 2.0 [need_ports clock "clock port"]} e]} {
puts " STOPPED: $e"
}
create_clock -name core -period 2.0 [need_ports clk "clock port"]
puts [report_clocks]=== and the guarded version, which stops ===
guarded, with the typo:
STOPPED: no clock port matched 'clock'
guarded, with the right name:
clock core period 2.0 on clkThe typo now stops the script with a message naming the pattern that failed. The correct name works exactly as before.
Three things make that helper worth having:
- It names the pattern, so the message is actionable —
no clock port matched 'clock'tells you what to fix. - It carries a structured
-errorcode, so a caller can distinguish a missing design object from other failures, as in Chapter 14.5 §7. - It returns the collection, so it drops into an existing line with no restructuring — exactly the
need_*shape from Chapter 14.11 §5.
7. Where to put the checks
Not on every query — that is noise, and noise gets ignored. The useful rule:
Guard a query whose emptiness would make the run meaningless. Clocks, resets, the primary I/O you constrain, the cells you are about to modify. If the run is worthless without it, check it.
Count-check a query whose size you can predict. Sometimes you know roughly what to expect, and a wrong count is as informative as an empty one:
set seq [filter_collection [get_cells -hierarchical *] {is_sequential == true}]
set n [sizeof_collection $seq]
if {$n == 0} {
error "no sequential cells found" {} {DESIGN NOSEQ}
}
puts " $n sequential cells"Printing the count is almost as valuable as checking it. A log line saying 4 sequential cells on a design with forty thousand is a problem somebody will notice — which is Chapter 17.4 §5's make-it-visible argument applied to queries.
Let it be empty where empty is legitimate — but say so, so the next reader knows it was deliberate:
# A design without scan has no scan-enable ports. Empty is fine here.
set se [get_ports *scan_en*]
if {[sizeof_collection $se] > 0} { ... }8. A checklist for the module
Everything Module 18 has established, as things to check when a design query misbehaves:
- Is the size what you expect?
sizeof_collection, before anything else. - Did the loop run once? Then you used
foreachon a collection — 18.2. - Did it run zero times? Then the query was empty — this chapter.
- Is
-hierarchicalmissing? Especially for pins — 18.4 §2. - Does the name convention match? Try substrings and alternative spellings — 18.6 §3.
- Are there brackets in the pattern? 18.3 §4.
- Is the attribute name spelled right? A filter will not tell you — 18.7 §6.
- Is a design actually loaded? 17.1 §4.
9. Exercises
Exercise 1. What does a query that matches nothing return, and does it raise?
Exercise 2. A constraint is applied to an empty collection. What does the tool report?
Exercise 3. Why does the tool not treat that as an error?
Exercise 4. A loop over a collection ran zero times. What does that tell you, and how does it differ from running once?
Exercise 5. Write the three-line guard for a clock port query.
Exercise 6. Name four distinct causes of an unexpectedly empty collection.
Exercise 7. When should you deliberately allow an empty result, and what should you do about it?
Where this fits
Part of the Tcl curriculum.
