Skip to content
VLSI Mentor

Tcl · Module 18

Collections vs Tcl Lists

This is the mistake every engineer makes once. The Tcl list commands do not fail on a collection — they succeed, quietly, with the wrong answer.

Intermediate17 min readtcledapart-iicollectionslistsdebugging

Part II · Module 18 · Chapter 18.2 · Collections vs Tcl Lists

You know Tcl lists well by now — Module 6 was fourteen chapters of them. A collection looks like something you could use them on. It is not.

The trouble is that the list commands do not object. They return answers. The answers are wrong, and nothing tells you.

1. The two things side by side

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set cells [get_cells -hierarchical *]
puts "  value                 : $cells"
puts "  sizeof_collection     : [sizeof_collection $cells]"
puts "  contents (for us)     : [collection_to_list $cells]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  value                 : _sel1
  sizeof_collection     : 7
  contents (for us)     : 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

Seven cells. The value held in $cells is the single word _sel1.

That is the whole explanation for everything in this chapter: to Tcl, a collection is one ordinary string. Tcl has no idea it means anything.

2. What the list commands actually do

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
puts "  llength \$cells        : [llength $cells]"
puts "  lindex \$cells 0       : [lindex $cells 0]"
puts "  lindex \$cells 1       : '[lindex $cells 1]'"

set n 0
foreach c $cells { incr n ; puts "    iteration $n: c = '$c'" }
puts "    -> $n iteration(s), for a collection of [sizeof_collection $cells]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  llength $cells        : 1
  lindex $cells 0       : _sel1
  lindex $cells 1       : ''

  foreach c $cells {...} runs:
    iteration 1: c = '_sel1'
    -> 1 iteration(s), for a collection of 7

3. One iteration, or zero?

The registry describes this as "why a loop runs zero times". You will see both, and knowing which is which saves an hour.

  • A non-empty collection is a handle — one word — so foreach runs once, with the handle as the value.
  • An empty collection is the empty string, so foreach runs zero times.

So a loop that ran zero times tells you the query matched nothing (Chapter 18.8). A loop that ran exactly once, whatever the design size, tells you that you used foreach on a collection.

"Exactly one iteration" is the signature. If a loop over cells reports processing one thing on a design with four thousand, you have this bug and not a design problem.

4. The commands that do work

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set n 0
foreach_in_collection c $cells { incr n ; puts "    iteration $n: c = $c" }
puts "    -> $n iteration(s)"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    iteration 1: c = u_fifo
    iteration 2: c = u_alu
    iteration 3: c = u_fifo/u_ctrl
    iteration 4: c = u_fifo/u_ram
    iteration 5: c = u_fifo/u_decode
    iteration 6: c = u_alu/u_add
    iteration 7: c = u_alu/u_acc_reg
    -> 7 iteration(s)

Seven iterations, each binding a real object. foreach_in_collection knows what the handle refers to; foreach cannot.

The substitutions to learn:

Instead ofUse
llength $csizeof_collection $c
foreach x $c {...}foreach_in_collection x $c {...}
lindex $c 0index_collection $c 0
concat $a $badd_to_collection $a $b
a filtering loopfilter_collection $c "expr"
llength $c == 0sizeof_collection $c == 0

5. Why not make collections be lists?

It is a fair question, and the answer is Chapter 18.1's three reasons: a list of names would have to be materialised as a string, could not distinguish same-named objects, and would carry no attributes.

The cost of those three benefits is exactly this chapter. It is a real cost, and the tools pay it because the alternative is worse on a design with millions of objects.

6. Catching it

Three habits, in increasing order of how much they save you.

Suspect "one". Any count that is 1 when it should be large is this bug. Print sizeof_collection next to llength while developing and the difference is immediate.

Name the variable for what it is. set cell_coll [get_cells ...] rather than set cells [...]. A suffix on the name makes foreach c $cell_coll look wrong on the page, which is the cheapest possible check.

Convert deliberately at the boundary. If you want Tcl-list behaviour, ask for it explicitly, and then everything from Part I genuinely does apply:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set names [collection_to_list [get_cells -hierarchical *reg*]]
puts "  [llength $names] cells"          ;# now llength is correct
foreach n [lsort $names] { ... }         ;# now foreach is correct

That conversion is also the Chapter 16.4 §6 boundary: past this line the data is plain Tcl and the logic is testable without a tool.

7. A worked diagnosis

A script reports that it constrained one cell on a design with thousands. The loop:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
set regs [get_cells -hierarchical *_reg*]
set n 0
foreach r $regs {
    incr n
    # ... do something with $r
}
puts "constrained $n cells"

Diagnosis, in order:

  1. The count is 1. Suspect the collection bug before anything about the design.
  2. Check the size properly: puts [sizeof_collection $regs]. If it is large and the loop ran once, it is confirmed.
  3. Fix the loop, not the query:
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
foreach_in_collection r $regs {
    incr n
    # ... do something with $r
}

If instead sizeof_collection reports 0, the loop was right and the query matched nothing — a different bug, and Chapter 18.8's.

Those two cases produce almost the same symptom and have opposite fixes, which is why checking the size first is the step that matters.

8. Exercises

Exercise 1. Why does llength on a collection return 1?

Exercise 2. What does foreach bind the loop variable to, and how many times does it run?

Exercise 3. You see "processed 1 cell" on a large design. What do you suspect, and what do you check first?

Exercise 4. A loop over a collection runs zero times. Is that the same bug?

Exercise 5. Give the collection equivalents of llength, foreach, lindex and concat.

Exercise 6. Why not convert every collection to a list immediately?

Exercise 7. Name a cheap habit that makes this mistake visible while reading the code.

Where this fits

Part of the Tcl curriculum.