Skip to content
VLSI Mentor

Tcl · Module 28

Make Decides What Re-Runs

Make answers one question: what is stale. Getting a useful answer out of it is entirely about the graph you write, and the obvious graph gives the wrong one.

Intermediate20 min readtcledapart-iimakedependenciesautomation

Part II · Module 28 · Chapter 28.2 · Make Decides What Re-Runs

Chapter 28.1 launches a stage. Running every stage every time is correct and wasteful: a flow that takes six hours is run once a night, and a one-line constraint change should not cost six hours.

Make answers exactly one question — what is out of date — and it is very good at it.

1. Targets, prerequisites, recipes

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$(NETLIST): $(RTL) $(SYNTH_SDC) flow/stage.tcl
	@mkdir -p $(dir $@)
	@echo "  [synth]  $(DESIGN)"
	@$(TCLSH) flow/stage.tcl $(DESIGN) synth > $(WORK)/synth/run.log 2>&1
	@touch $@

A target is a file that should exist. Its prerequisites are the files it depends on. The recipe is how to produce it, and Make runs it only when a prerequisite is newer than the target.

Three details in that rule are doing real work.

flow/stage.tcl is a prerequisite. The flow script is an input to the result exactly as the RTL is, so editing the flow invalidates the netlist. Leaving it out is the single most common omission, and it produces a netlist that silently predates the script that supposedly made it.

$(dir $@) and touch $@. $@ is the target. The recipe creates the directory and then touches the target so its timestamp is newer than the prerequisites — necessary here because the mock does not write the file itself, and in a real flow a reasonable safety net for a tool whose output timestamp you do not control.

@ suppresses the echo of the command, so the output is the flow's rather than Make's. Useful for readability and worth removing the moment you are debugging a rule.

2. The brief's claim is a property of the graph

The registry asks for a flow where "a changed constraint re-runs timing and nothing else". My first Makefile had one SDC file, with the netlist depending on it. Touching it re-ran everything.

3. The four states, measured

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== a cold build: everything runs ===
    [synth]  core_top
    [timing] core_top
    [place]  core_top

=== nothing changed ===
    make: Nothing to be done for `all'.

=== touch the SIGN-OFF constraints ===
    [timing] core_top

=== touch the SYNTHESIS constraints ===
    [synth]  core_top
    [timing] core_top
    [place]  core_top

[timing] alone for a sign-off constraint change — the brief's claim, now true by construction. And all three for a synthesis constraint change, which is also correct, because the netlist really does depend on it.

Note the second state: Nothing to be done is the output you want most often and the one people distrust. A flow that reports nothing when nothing changed is working; the reflex to add clean to every invocation throws away the entire benefit.

4. Three Make behaviours that will bite you

Measured on GNU Make 3.81, the version shipped with macOS.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== 1. tab versus spaces in a recipe ===
    spaces : mk/Makefile.spaces:2: *** missing separator.  Stop.
    tab    : indented with a tab

Recipes must be indented with a tab. Spaces give missing separator, which names a symptom rather than the cause and is the single most common first encounter with Make. An editor configured to expand tabs will do this to you silently.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== 2. does a cd persist to the next recipe line? ===
    two recipe lines : /private/tmp/m28
    one recipe line  : /tmp

Each recipe line runs in its own shell. cd /tmp followed by pwd on the next line prints the original directory, because the cd happened in a shell that has already exited. Chain them with && on one line, or use a subshell — and the practical consequence is that a recipe cannot build up shell state across lines at all.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== 3. a target whose name is an existing file ===
    without .PHONY : []  <- nothing ran
    with .PHONY    : [isphony ran]

A target whose name matches an existing file is considered up to date. A clean target does nothing at all if a file called clean exists in the directory. .PHONY declares that the target is an action rather than a file, and the failure without it is silent — make clean simply succeeds, having cleaned nothing.

5. What Make is good at, and what to keep out of it

Good at: deciding what is stale, expressing a dependency graph declaratively, and running independent branches in parallel with -j. Placement and a power report that both depend only on the netlist are genuinely independent, and Make will run them at once with no further work.

Keep out of it:

Flow logic. A recipe should be one or two lines that invoke something. Multi-line shell inside a recipe is shell logic in the worst possible place — each line in a separate shell, with Make's variable syntax ($$ for a shell variable) layered on top.

Iteration over corners. A foreach in Make is a text-expansion loop evaluated when the Makefile is read, not a runtime loop. Module 26's scenario list belongs in Tcl, where it is data with reasons attached. Generating 80 Make targets from a corner list is possible and makes the graph unreadable.

Anything needing arithmetic. Make has no arithmetic (Chapter 28.4 §3).

State that is not a file. Make's entire model is file timestamps. "Has the licence server been reachable since the last run" is not a file, and encoding it as a stamp file is how a Makefile becomes a database with no schema.

6. Calling the flow, not reimplementing it

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$(TIMING): $(NETLIST) $(SIGNOFF_SDC)
	@mkdir -p $(dir $@)
	@echo "  [timing] $(DESIGN)"
	@$(TCLSH) flow/stage.tcl $(DESIGN) route > $@ 2>&1

One line of real work, and it hands off to Tcl. The division is:

QuestionAnswered by
Is the netlist out of date?Make
What does synthesis consist of?Tcl
Where does the log go?the wrapper, or the Tcl (Chapter 27.1)
Did it work?the exit status, which Make checks

And the recipe invokes the same entry point a human would. A Makefile whose recipes contain a different invocation from the one documented is a Makefile that works until somebody debugs a stage by hand and gets a different result — so in practice the recipe should call Chapter 28.1's wrapper rather than tclsh directly, which is one fewer path to keep consistent.

7. Exercises

Exercise 1. What single question does Make answer?

Exercise 2. Why is flow/stage.tcl listed as a prerequisite, and what happens if it is omitted?

Exercise 3. Touching the single SDC re-ran everything. Why was that correct?

Exercise 4. What makes "a changed constraint re-runs timing only" true, and where does that distinction come from?

Exercise 5. Why is Nothing to be done the output you want, and what reflex destroys the benefit?

Exercise 6. What does a recipe indented with spaces produce?

Exercise 7. cd /tmp then pwd on the next recipe line printed the original directory. Why?

Exercise 8. What does make clean do if a file called clean exists?

Exercise 9. How does a wrapper that exits 0 on failure break Make's staleness logic?

Exercise 10. A tool rewrites its output file on every run. What does that do to Make, and what is the fix?

Exercise 11. Name three things to keep out of a Makefile.

Where this fits

Part of the Tcl curriculum.