Linux & Bash · Part II · Module 26 · Compile and Simulation File Lists
What a Filelist Is and Why Flows Use One
Module 25 got the tool running. This module is about what you hand it: one file naming every source, in order, with the options that apply to them. It is the most boring artifact in a flow and the one that causes the most unexplained compile failures.
Foundation20 min readLinuxPart IIFilelistsCompileEDA
Part II · Module 26 · Chapter 26.1 · What a Filelist Is and Why Flows Use One
Chapter 23.3 established the filelist as the authority on which of three identically named files is compiled. This module is about the filelist itself.
1. What It Is
A text file, one entry per line, naming every source the tool should read:
$ cat filelists/chip.f
rtl/core/chip_top.sv
rtl/axi/axi_master.sv
rtl/uart/uart_tx.sv
tb/tests/tb_chip.sv
+incdir+rtl/includeFour source files and one option. The tool is pointed at it with a flag — -f for Icarus and Synopsys, -F for a variant, -file for some, +incdir+-style arguments for others — and every tool in this industry accepts some form of it, which is the first reason flows use one.
$ iverilog -g2012 -o work/sim.vvp -f filelists/chip.f
$ echo $?
0One list, four tools, one answer
2. Why Not Just a Command Line
Four reasons, and the fourth is the one that matters most in practice.
Length. A real design has two to twenty thousand sources, and a command line has a hard maximum. This is worth measuring rather than asserting, because the failure is not the one people expect.
Forty thousand files, with paths averaging fifty characters:
$ find /tmp/many -name '*.sv' | wc -l
40000
$ find /tmp/many -name '*.sv' | wc -c
2000000
$ getconf ARG_MAX
1048576Two megabytes of paths against a one-megabyte limit. On the command line:
$ iverilog -g2012 -o /tmp/many.vvp /tmp/many/*.sv
bash: /opt/homebrew/bin/iverilog: Argument list too long
$ echo $?
126Status 126 — "found but could not be executed" — because the exec itself failed before iverilog existed as a process. And the same limit defeats the obvious way of building a list:
$ ls /tmp/many/*.sv > /tmp/many.f
bash: /bin/ls: Argument list too long
$ echo $?
126find does not place the paths on a command line at all, so it works:
$ find /tmp/many -name '*.sv' | sort > /tmp/many.f
$ wc -l < /tmp/many.f
40000And the compile reads them from the file:
$ iverilog -g2012 -o /tmp/many.vvp -f /tmp/many.f
$ echo $?
0
$ wc -c < /tmp/many.vvp
5189711Forty thousand sources compiled, via a two-megabyte list that could never have been an argument list. Chapter 22.1 §9 introduced ARG_MAX; this is the reason the filelist exists at all rather than being a convenience.
At six thousand files — 300 KB of paths, comfortably under the limit — the glob compiles fine, which is why the problem is invisible on a small design and arrives all at once.
Reuse. Lint, simulation, synthesis and formal all need the same sources. One list read by four tools cannot diverge; four command lines will.
Review. A filelist is a file in the repository, so adding a source is a commit with an author and a date. A command line edited in a wrapper script is neither.
And order. A command line preserves order too — but a filelist makes the order visible and reviewable, which matters because the order is load-bearing.
3. Order Is Load-Bearing, and the Proof Is One Swap
A package and a module that imports it:
// pkg.sv
package p; parameter int W = 8; endpackage// use.sv
module use_pkg; import p::*; initial $display("W=%0d", W); endmoduleCompiled with the user first:
$ cat wrong.f
/tmp/ord/use.sv
/tmp/ord/pkg.sv
$ iverilog -g2012 -o w.vvp -f wrong.f
/tmp/ord/use.sv:1: syntax error
/tmp/ord/use.sv:1: error: Invalid module item.
$ echo $?
2
$ ls w.vvp
ls: w.vvp: No such file or directoryExit 2 and no artifact. And with the two lines swapped:
$ cat right.f
/tmp/ord/pkg.sv
/tmp/ord/use.sv
$ iverilog -g2012 -o r.vvp -f right.f
$ echo $?
0
$ vvp r.vvp < /dev/null
W=8Same two files, same tool, same flags. The only difference is the order of two lines in a text file, and one of the two orders does not compile.
Three things follow:
A filelist is a sequence, not a set. Any tooling that sorts it, deduplicates it or regenerates it has to preserve the dependency order — which is Chapter 26.4's whole problem.
The error names the consumer, not the cause. use.sv:1: syntax error points at the file that was fine; the problem is that pkg.sv had not been read yet. That is the single most misleading error in this module, and recognising it saves an hour of staring at a correct file.
And "syntax error" is what a missing package looks like. The parser reaches import p::*, does not know p, and cannot make sense of the rest. A reviewer who has not seen this assumes the file is broken.
4. Entries Are Not All Paths
The +incdir+rtl/include line in §1 is not a file. A real filelist mixes four kinds of line:
| Kind | Example | Meaning |
|---|---|---|
| A source path | rtl/axi/axi_master.sv | compile this |
| An option | +incdir+rtl/include, +define+FAST, -sv | applies to what follows, or to everything |
| A nested list | -f filelists/axi.f | read that list here |
| A comment or blank | # the AXI block, `` | ignored |
Any tool that reads a filelist must handle all four, and every home-grown filelist script that treats every line as a path produces a confident wrong answer — Chapter 23.3 §4 measured exactly that. The skeleton is:
while IFS= read -r line || [ -n "$line" ]; do
line=${line%%#*} # strip comments
line=${line%$'\r'} # survive a Windows edit
[ -n "${line//[[:space:]]/}" ] || continue
case $line in
(-[fF]*) : ;; # a nested list
(-*|+*) continue ;; # an option: not a file
(*) : ;; # a source path
esac
done < "$fl"Four details, all measured elsewhere in Part II: the || [ -n "$line" ] so a final line with no newline is not dropped (Chapter 17.5 §4), the \r strip, the blank test using parameter expansion rather than a process, and the parenthesised case patterns because this frequently sits inside $( ) under Bash 3.2 (Chapter 25.1).
5. Relative to What?
The question Chapter 23.3 §4 measured, and it belongs here too because it is the filelist's defining ambiguity.
$ iverilog -g2012 -o work/sim.vvp -f filelists/chip.f # from the repo root
$ echo $?
0
$ cd filelists && iverilog -g2012 -o /tmp/s.vvp -f chip.f
rtl/core/chip_top.sv: No such file or directory
No top level modules, and no -s option.
$ echo $?
1Entries resolve against the current working directory, not against the filelist's own location. So a filelist is only usable from the directory it was written for, and that directory is part of its meaning even though nothing in the file records it.
Three ways projects deal with this, in increasing order of robustness:
A convention: "all filelists are relative to $PROJECT_ROOT, and the flow cds there first." Cheap, undocumented, and the commonest.
Absolute paths, usually generated: unambiguous, unreadable, and they make the filelist machine-specific — which is fine for a generated artifact and wrong for a committed one.
And a variable prefix: $PROJECT_ROOT/rtl/axi/axi_master.sv, which most tools expand from the environment. Readable and portable, at the cost that any textual processing of the filelist must expand it too — Chapter 26.5 is where that is checked rather than hoped.
6. The One-Screen Summary
filelists/
├── chip.f the simulation list: what tb_chip needs
├── rtl.f just the design, for lint and synthesis
└── axi.f one block, included by the others| Question | Answered by |
|---|---|
| What is compiled? | the filelist, and nothing else |
| In what order? | the order of its lines |
| With which include paths and defines? | its option lines (Chapter 26.2) |
| Where did the list come from? | generated, or committed (Chapter 26.3) |
| Is it deterministic? | only if sorted deliberately (Chapter 26.4) |
| Does every path exist? | only if checked (Chapter 26.5) |
| Do lint and simulation agree? | only if they read the same lists (Chapter 26.6) |
Every row is a question somebody asks during a debug, and every one has a one-line answer if the filelist is right.
Practice
- Find the filelist your flow actually uses — the build-script search from Chapter 24.5 §4.
- Count its lines, and classify each as a path, an option, a nested list or a comment.
- Reproduce §3: a package and a module that imports it, compiled both ways. Record both statuses.
- Read the wrong-order error. Which file does it name, and which file is the problem?
- Find a
`timescalein your repository. Which files does it apply to, and would reordering change that? - Count the sources in your design and multiply by the average path length. Compare with
getconf ARG_MAX. - Run your flow's compile from a different directory. Does it still work?
- Check whether your filelists use relative paths, absolute paths or a variable prefix.
- If relative: find where the flow
cds, and what happens if it does not. - Write the four-kinds-of-line skeleton and run it over a real filelist. Does it classify every line?
- Remove the
|| [ -n "$line" ]and strip the final newline from a filelist. Which entry disappears?
Check Your Understanding
- What is a filelist, and what flag reads it?
- What are the four reasons to use one rather than a command line?
- What happened when a package was listed after the module importing it?
- Why is that error misleading?
- Why do plain modules not need ordering, and what does that lead people to assume?
- What does a reordered
`timescalechange, and why is it the worst entry on the list? - What are the four kinds of line in a filelist?
- What are filelist entries relative to?
- What are the three ways projects resolve that, and what does each cost?
7. Remember
- A filelist is the single statement of what gets compiled. Every tool in the industry reads some form of one.
- It exists because a command line cannot hold the list. Measured: 40,000 files is 2 MB of paths against a 1 MB
ARG_MAX, and the glob compile died withArgument list too long, status 126. - The same limit breaks
ls *.sv > list.f. Usefind, which never puts the paths on a command line. - At 6,000 files the glob still works, which is why the problem is invisible until it is total.
- It is a sequence, not a set. Measured: swapping two lines turned a working compile into
rc 2with no artifact. - Packages, interfaces, compiled headers and
`timescalemust come first. Plain modules need not, which is why filelists look order-insensitive until they are not. - The wrong-order error names the consumer, not the cause, and looks like a syntax error in a correct file.
- A reordered
`timescalechanges timing with no diagnostic at all. - Four kinds of line: a path, an option, a nested list, a comment or blank. Any tool that treats them all as paths is wrong.
- Entries resolve against the current directory, not the filelist's own — so a filelist implies a directory it does not record.
- Handle the missing final newline,
\r, and parenthesisedcasepatterns in anything that reads one.
8. What Is Next
The same argument — the set of things to act on belongs in a file, not inside a script — reappears for tests rather than sources in Chapter 31.2. Chapter 26.2 takes the option lines seriously: putting include paths and defines inside the list so that every tool reading it compiles the same thing — measured, including a demonstration that the options inside a filelist really do take effect.
Where this fits
Part of the Linux & Bash curriculum.
