Skip to content
VLSI Mentor

Linux & Bash · Part II · Module 25 · EDA Environment and Tool Access

What a Tool Needs Before It Will Start

An EDA tool that will not launch is almost never broken. It is missing one of five things, and each of them produces a different message from a different source. This chapter turns that into a checklist you can run — measured against a real tool, built for the purpose, that fails in all five ways on demand.

Intermediate21 min readLinuxPart IIEDAEnvironmentDiagnosis

Part II · Module 25 · Chapter 25.1 · What a Tool Needs Before It Will Start

"The tool does not work" is almost always "the tool did not start", and those are different problems. A tool that does not start has not read your design, so nothing about your design can be the cause.

1. No Commercial Tool Is Installed Here, So One Was Built

The honest position first:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ for t in vcs xrun simv vsim lmstat; do command -v $t > /dev/null || echo "$t absent"; done
vcs absent
xrun absent
simv absent
vsim absent
lmstat absent

So rather than invent transcripts, this module uses a tool written for it: a small C program called simtool, linked against a real shared library, which checks a licence file, looks for a feature line in it, and verifies that its temporary directory is writable — exiting with a different status for each failure. It is not a simulator, and it does not need to be: the subject of this module is everything that happens before a simulator reads a single line of Verilog, and that part is faithfully reproduced.

Where a statement needs a commercial tool, it is attributed rather than shown — Chapter 22.1 §7's rule.

2. The Five Preconditions

Five things a tool needs, and who complains when each is missing

Five preconditions for a tool to start. An executable found on PATH, reported by the shell as command not found. Runtime libraries resolved by the dynamic loader, reported as library not loaded. A licence, checked by the tool. Writable temporary and home areas, checked by the tool. And the tool's own configuration. The first two fail before the tool's code runs.An executable on PATHthe shell resolves itRuntime librariesthe dynamic loader resolvesthemA licence it can readthe tool checkscommand not foundstatus 127, from the shellLibrary not loadedstatus 134, from the loaderA FATAL line it wroteitselfa status the tool chosethenthen12
Reading left to right is the order the system checks them in, and the order you should diagnose in. The first two are resolved before the tool's own code runs at all — which is why their messages come from the shell and the dynamic loader rather than from the tool, and why they look nothing like a tool error. The last three are the tool's own checks, so they are the only ones whose wording you can expect to be helpful.
#PreconditionWho reports the failureTypical status
1An executable on PATHthe shell127
2Its runtime librariesthe dynamic loader134 here
3A licence it can readthe tooltool-specific
4Writable temp and home areasthe tool, or a crashtool-specific
5Project variables it requiresthe tool, or wrong resultstool-specific

The first two are resolved before the tool's own code runs, which is why their messages do not look like tool errors and why searching a vendor's documentation for them finds nothing.

3. It Works When Everything Is Present

The baseline, which you need before any of the failures mean anything:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ printf 'FEATURE simtool 2024.0901 permanent\n' > licenses/simtool.lic
$ SIMTOOL_LICENSE_FILE=$PWD/licenses/simtool.lic ./tools/simtool-2024.09/bin/simtool chip_top
simtool 2024.09 starting (simlib 1.4.2)
simlib: elaborating chip_top
$ echo $?
0

Two lines of output and status 0. Note that the second line comes from the shared library, not from the tool — which will matter in §5.

4. The Three Failures the Tool Reports Itself

No licence variable at all:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ ./tools/simtool-2024.09/bin/simtool chip_top
simtool: FATAL: SIMTOOL_LICENSE_FILE is not set
$ echo $?
3

The variable is set but the file is not there:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ SIMTOOL_LICENSE_FILE=/nope.lic ./tools/simtool-2024.09/bin/simtool chip_top
simtool: FATAL: cannot read licence file: /nope.lic
$ echo $?
4

The file exists and does not grant this tool:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ printf 'FEATURE othertool 1.0 permanent\n' > licenses/wrong.lic
$ SIMTOOL_LICENSE_FILE=$PWD/licenses/wrong.lic ./tools/simtool-2024.09/bin/simtool chip_top
simtool: FATAL: no simtool feature in /home/engineer/eda/licenses/wrong.lic
$ echo $?
5

Three distinct statuses — 3, 4, 5 — for three distinct causes, and each message names the thing to fix. That is what a well-behaved tool does, and it is worth noticing because it is the standard against which you should judge the real ones. Chapter 21.4 argued for exactly this; here it is from the caller's side.

And a fourth, which is the one people least expect:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ mkdir -p /tmp/ro25 && chmod 500 /tmp/ro25
$ SIMTOOL_LICENSE_FILE=$LIC TMPDIR=/tmp/ro25 ./tools/simtool-2024.09/bin/simtool chip_top
simtool: FATAL: TMPDIR is not writable: /tmp/ro25
$ echo $?
6

A tool needs somewhere to write. Not just its output directory — a scratch area for intermediate files, which it takes from TMPDIR and which on a compute node may be a local disk that is full or a path that does not exist on that machine. Chapter 22.4 is where that lives; the symptom here is a tool that refuses to start for a reason unrelated to anything you did.

5. The Failure That Is Not the Tool's

Move the shared library away and the tool never runs at all:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ mv tools/simtool-2024.09/lib/libsim.dylib /tmp/
$ SIMTOOL_LICENSE_FILE=$LIC ./tools/simtool-2024.09/bin/simtool chip_top
dyld[20842]: Library not loaded: @rpath/libsim.dylib
  Referenced from: <…> /home/engineer/eda/tools/simtool-2024.09/bin/simtool
  Reason: tried: '/home/engineer/eda/tools/simtool-2024.09/lib/libsim.dylib' (no such file), …
$ echo $?
134

Four things in that output are different in kind from §4's:

The prefix is dyld[20842], not simtool. The dynamic loader is speaking, and the tool's own code has not executed a single instruction. No amount of tool configuration will change this message.

The status is 134, which is 128 + 6 — a process killed by signal 6, SIGABRT. Chapter 7.6 established the 128 + N rule; this is it appearing where you would not expect a signal at all.

It lists every path it tried, which is the most useful loader diagnostic there is and the reason to read the whole message rather than the first line.

And the licence was present and irrelevant. Precondition 2 fails before precondition 3 is examined, which is why the diagnosis order in §2 matters: checking the licence first would have told you nothing.

Chapter 25.3 is this failure in full, including why the variable that fixes it behaves differently on macOS than on Linux.

6. The Checklist as a Script

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
#!/usr/bin/env bash
# toolcheck — why will this tool not start?
# usage: toolcheck <tool> [licence-variable]
# exit:  0 all five preconditions look satisfied · 1 a problem was found · 2 usage
set -o nounset

[ $# -ge 1 ] || { printf 'usage: toolcheck <tool> [licence-variable]\n' >&2; exit 2; }
tool=$1
licvar=${2:-}
bad=0
note() { printf '%-4s %s\n' "$1" "$2"; }

# 1. is it on PATH, and which one did we get?
path=$(command -v -- "$tool" 2>/dev/null) || {
  note FAIL "$tool is not on PATH"
  printf '     PATH entries:\n'; printf '%s\n' "$PATH" | tr ':' '\n' | sed 's/^/       /'
  exit 1; }
note ok "found: $path"
case $path in
  /*) : ;;
  *)  note WARN "command -v returned a bare name — $tool is a function or builtin, not a program" ;;
esac

# a second one further along PATH is worth knowing about
others=$(command which -a -- "$tool" 2>/dev/null | sed '1d') || others=
[ -z "$others" ] || { note WARN "other copies on PATH:"; printf '%s\n' "$others" | sed 's/^/       /'; }

# 2. are its runtime libraries resolvable?
if command -v otool > /dev/null 2>&1; then
  missing=$(otool -L -- "$path" 2>/dev/null | sed '1d' | awk '{print $1}' |
            while IFS= read -r l; do
              case $l in (@*) continue ;; esac         # rpath-relative: resolved at run time
              [ -e "$l" ] || printf '%s\n' "$l"
            done)
elif command -v ldd > /dev/null 2>&1; then
  missing=$(ldd -- "$path" 2>/dev/null | awk '/not found/ {print $1}')
else
  missing=''; note WARN "neither otool nor ldd is available; skipping the library check"
fi
if [ -n "${missing:-}" ]; then
  note FAIL "unresolved libraries:"; printf '%s\n' "$missing" | sed 's/^/       /'; bad=1
else
  note ok "runtime libraries resolve"
fi

# 3. does the no-licence path work?
"$path" -version > /dev/null 2>&1
vrc=$?
if [ "$vrc" -eq 0 ]; then
  note ok "-version runs, so the executable and its libraries are fine"
elif [ "$vrc" -ge 128 ]; then
  note FAIL "-version died on signal $(( vrc - 128 )) (status $vrc) — run it directly and read the loader's message"
  bad=1
else
  note WARN "-version exited $vrc; the tool may use a different flag"
fi

# 4. the licence variable, if one was named
if [ -n "$licvar" ]; then
  eval "licval=\${$licvar:-}"
  if [ -z "${licval:-}" ]; then
    note FAIL "$licvar is not set"; bad=1
  else
    note ok "$licvar=$licval"
    IFS=: read -r -a parts <<< "$licval"
    for p in "${parts[@]}"; do
      case $p in
        *@*) note ok  "  $p looks like port@host (not checked from here)" ;;
        *)   [ -r "$p" ] && note ok "  $p is readable" || { note FAIL "  $p is not readable"; bad=1; } ;;
      esac
    done
  fi
fi

# 5. writable scratch and home
for d in "${TMPDIR:-/tmp}" "$HOME"; do
  [ -w "$d" ] && note ok "writable: $d" || { note FAIL "not writable: $d"; bad=1; }
done

[ "$bad" -eq 0 ]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ export PATH=$PWD/tools/simtool-2024.09/bin:$PATH
$ SIMTOOL_LICENSE_FILE=$PWD/licenses/simtool.lic toolcheck simtool SIMTOOL_LICENSE_FILE
ok   found: /home/engineer/eda/tools/simtool-2024.09/bin/simtool
ok   runtime libraries resolve
ok   -version runs, so the executable and its libraries are fine
ok   SIMTOOL_LICENSE_FILE=/home/engineer/eda/licenses/simtool.lic
ok     /home/engineer/eda/licenses/simtool.lic is readable
ok   writable: /var/folders/…/T/
ok   writable: /home/engineer
$ echo $?
0

Eight decisions, each with a reason you can check:

command -v -- "$tool" and then a test for a leading /, because a bare name means a shell function or builtin rather than a program — Chapter 22.6 §2's discriminator, and a real cause of "the tool behaves differently in a script".

command which -a rather than bare which, because which may itself be a shell function or alias; command bypasses that. The sed '1d' drops the copy we already reported.

@*) continue when walking otool -L output, because an @rpath/@loader_path entry is resolved at run time against the executable's search paths and testing it as a filesystem path would report every such library as missing. Getting this wrong would make the check cry wolf on every correctly built tool.

Both otool and ldd branches, with a WARN if neither exists. ldd is absent on macOS and otool on Linux, so a script that assumes either is wrong half the time — Chapter 21.6 §1's feature test.

-version is tried and a failure only warns. The flag is not standard, which Chapter 25.7 measures across four tools — so treating its absence as an error would reject working installations.

eval "licval=\${$licvar:-}" to read a variable whose name is in a variable. This is the one place in the course where eval is the right answer in Bash 3.2, because ${!var} indirect expansion exists but ${!var:-} with a default does not compose cleanly; the value is a variable name the user supplied, and it is worth noting that an untrusted name here would be a shell-injection hole.

A -version that dies on a signal is a FAIL, not a WARN, per the callout above — and it is the only part of the script that catches a missing rpath library.

And a port@host licence entry is reported, not tested. Checking it needs a network query and a vendor tool, neither of which this script can assume — Chapter 25.4 is where that is taken apart. Saying "not checked from here" is better than a check that silently passes.

Practice

  1. Run command -v for a tool you use. Is the answer a path or a bare name?
  2. Run which -a for it. Is there more than one?
  3. Run the tool's -version (or -ID, or --version). Does it work with no licence set?
  4. Unset your licence variable in a subshell and run a real command. What is the message and the status?
  5. Point the licence variable at a file that does not exist. Does the message distinguish that from the variable being unset?
  6. Find your tool's TMPDIR requirement. Set TMPDIR to a read-only directory and see what happens.
  7. Run otool -L (macOS) or ldd (Linux) on a tool's executable. How many libraries does it need?
  8. Identify which of those entries are @rpath-relative and explain why a file test on them would be wrong.
  9. Write toolcheck and run it against a tool that works. Then break one precondition at a time and confirm each is reported.
  10. Point it at a shell function with the same name as a tool and confirm the WARN.
  11. Give it a licence variable holding port@host and check the message.
  12. For each of the five preconditions, write down who reports the failure on your system.

Check Your Understanding

  1. Why is "the tool does not work" a different problem from "the tool did not start"?
  2. What are the five preconditions, in checking order?
  3. Which two fail before the tool's own code runs, and how can you tell?
  4. What did statuses 3, 4, 5 and 6 distinguish?
  5. Why is -version the first thing to run?
  6. What does status 134 mean, and who produced the message?
  7. Why does the library check skip entries beginning with @?
  8. Why command which -a rather than which -a?
  9. Why is a port@host licence entry reported rather than tested?
  10. Why does a failing -version only warn?

7. Remember

  • "Did not start" is not "does not work." A tool that did not start never read your design.
  • Five preconditions: executable on PATH, runtime libraries, licence, writable temp and home, project variables.
  • The first two fail before the tool's code runs, so their messages come from the shell and the loader and are absent from vendor documentation.
  • Measured: statuses 3, 4, 5 and 6 for an unset licence variable, an unreadable file, a missing feature, and an unwritable TMPDIR.
  • -version needs no licence — measured — so it is the first command to run and splits the diagnosis in half.
  • Status 134 is 128 + 6, SIGABRT, and the message is dyld's. It lists every path it tried; read all of it.
  • A tool needs somewhere to write. TMPDIR on a compute node is not the one on your workstation.
  • command -v returning a bare name means a function or builtin, not a program.
  • Use command which -a, since which itself may be shadowed.
  • Skip @rpath entries when testing libraries as files, or the check cries wolf on every working tool.
  • ldd is Linux, otool -L is macOS. Feature-test for both.
  • Report what you did not check — a port@host licence needs a network query.

8. What Is Next

Chapter 25.2 takes precondition one seriously: putting a specific tool version on PATH, and proving which executable a command actually resolved to — measured with two versions of the same tool installed side by side.

Where this fits

Part of the Linux & Bash curriculum.