Tcl · Module 18
Filtering a Collection by Attribute
Selecting by name gets you close. Selecting by what something actually is gets you right — and one expression does what a loop takes six lines to do badly.
Advanced17 min readtcledapart-iicollectionsfilteringattributes
Part II · Module 18 · Chapter 18.7 · Filtering a Collection by Attribute
Chapter 18.5 §4 showed that selecting sequential cells by name found one of four. The fix was an attribute, and this chapter is about the command that uses them.
It replaces a loop you would otherwise write by hand — and it does so in a way that matters more than brevity.
1. Narrowing by a property
set all [get_cells -hierarchical *]
show {everything} $all
show {filter "is_sequential == true"} [filter_collection $all "is_sequential == true"]
show {filter "is_sequential != true"} [filter_collection $all "is_sequential != true"]
show {filter "ref_name == DFF"} [filter_collection $all "ref_name == DFF"] everything size 7 u_fifo u_alu u_fifo/u_ctrl u_fifo/u_ram u_fifo/u_decode u_alu/u_add u_alu/u_acc_reg
filter "is_sequential == true" size 4 u_fifo u_fifo/u_ctrl u_fifo/u_ram u_alu/u_acc_reg
filter "is_sequential != true" size 3 u_alu u_fifo/u_decode u_alu/u_add
filter "ref_name == DFF" size 2 u_fifo/u_ctrl u_alu/u_acc_regfilter_collection takes a collection and a predicate, and returns a new collection of the objects that satisfy it. The original is unchanged — filters produce, they do not modify.
Note the third row: 4 and 3 sum to 7, so != is a genuine complement here. Be careful assuming that in general — an attribute that is undefined on some objects will not satisfy either comparison, so the two halves can sum to less than the whole.
2. Chaining
Filters compose, and reading them as successive narrowings is usually clearer than one compound expression:
set seq [filter_collection $all "is_sequential == true"]
show { step 1: is_sequential == true} $seq
show { step 2: ref_name == DFF} [filter_collection $seq "ref_name == DFF"] chaining: sequential, then only the DFFs
step 1: is_sequential == true size 4 u_fifo u_fifo/u_ctrl u_fifo/u_ram u_alu/u_acc_reg
step 2: ref_name == DFF size 2 u_fifo/u_ctrl u_alu/u_acc_regSeven to four to two. Each step has a size you can check, which makes a chain far easier to debug than a single expression that returned nothing — you can see which step emptied it.
3. Why not a loop?
You could write it yourself, and the registry's purpose is explicit that you should not:
set seq {}
foreach_in_collection c $all {
if {[get_attribute $c is_sequential] eq "true"} { lappend seq $c }
} manual loop result : u_fifo u_fifo/u_ctrl u_fifo/u_ram u_alu/u_acc_reg
same as filter? : 1Same answer. Two reasons to prefer the filter anyway:
It is evaluated by the tool. The loop asks Tcl to iterate, call get_attribute once per object, compare in Tcl, and build a list. The filter hands one expression to the tool, which evaluates it against its own data structures. On seven cells the difference is nothing; on four million it is the difference between seconds and a coffee break.
It produces a collection, not a list. The loop above built a Tcl list, so the attributes are gone and the next step has to re-query. The filter's result is still a collection — chainable, countable, and usable anywhere a collection is.
That second reason is the one that bites in practice. A loop that converts to a list quietly ends your ability to keep filtering.
4. What the predicate can say
The mock supports == and != on an attribute. Real tools support more — numeric comparisons, pattern matches, existence tests — with the exact syntax varying.
Find yours the usual way, from the tool's reference (Chapter 17.6). What is consistent everywhere:
- the left side is an attribute name,
- the right side is a value,
- and the whole thing is a string argument, which is why it needs quoting.
5. A filter that matches nothing
a filter that matches nothing:
filter "ref_name == NOSUCHCELL" size 0Size 0, no error — the same silence as a query that matched nothing. Everything Chapter 18.8 says applies to filters too.
6. The typo that returns nothing
This one deserves its own section, because the two ways of reading an attribute disagree about mistakes.
set good [filter_collection $all "is_sequential == true"]
puts " is_sequential == true -> rc 0, size [sizeof_collection $good]"
set bad [filter_collection $all "is_sequental == true"]
puts " is_sequental == true -> rc 0, size [sizeof_collection $bad]"
set obj [index_collection $all 0]
puts " [catch {get_attribute $obj is_sequental} e] / $e" correct attribute name:
is_sequential == true -> rc 0, size 4
MISSPELLED attribute name:
is_sequental == true -> rc 0, size 0 <- no error, nothing matched
get_attribute on the same typo DOES raise:
1 / attribute 'is_sequental' not defined on 'u_fifo'A malformed expression is caught, which is worth knowing so you do not assume all filter mistakes are silent:
a malformed filter expression:
1 / cannot parse filter expression: 'is_sequential'The distinction: the tool checks the shape of the predicate, not whether the attribute exists.
7. The pattern worth keeping
A helper that makes every filter loud:
proc need_filter {coll expr {what "objects"}} {
set r [filter_collection $coll $expr]
if {[sizeof_collection $r] == 0} {
error "no $what matched '$expr'" {} [list DESIGN NOMATCH $expr]
}
return $r
}
set seq [need_filter [get_cells -hierarchical *] {is_sequential == true} "sequential cells"]One line at the call site, and a typo or a wrong assumption now stops the script with a message naming the predicate that failed. This is the same shape as need_ports from Chapter 18.3 §7, and the same Chapter 14.11 discipline.
8. Exercises
Exercise 1. What does filter_collection take and return?
Exercise 2. Give two reasons to prefer it over a hand-written loop.
Exercise 3. Why does a loop that builds a list end your ability to keep filtering?
Exercise 4. Why must the predicate be quoted or braced?
Exercise 5. A filter on a misspelled attribute name — what happens, and how does that differ from get_attribute with the same typo?
Exercise 6. A malformed predicate like "is_sequential" — error or empty result?
Exercise 7. When is an empty filter result not a bug?
Where this fits
Part of the Tcl curriculum.
