C shell for EDA environments
- Read the csh and tcsh setup scripts that many EDA sites and tool vendors still ship
- Explain the difference between
setandsetenv - Extend the search path with the
patharray - Test for variables with
$?nameand read exit status from$status - Recognise csh
if,foreachandswitchblocks - Translate a csh setup script into Bash
- Import a csh environment into a Bash flow when the setup script cannot be changed
Explanation
Why you still meet the C shell
Bash is the right language for new scripts, but many design centres set up their tools with csh or tcsh. Vendor installation notes often say source /tools/<vendor>/setup.csh, team setup files are called .cshrc.project, and older run scripts start with #!/bin/csh -f. You do not need to write new csh, but you must be able to read it, change a path in it, and make its environment available to a Bash or Python flow.
tcsh is an extended csh with command-line editing. On Linux, /bin/csh is usually tcsh, and everything in this lesson works in both.
Shell variables and environment variables
| csh / tcsh | Bash | Meaning |
|---|---|---|
set top = fifo |
top=fifo |
Shell variable, not seen by child processes |
setenv PROJ_ROOT ~/proj |
export PROJ_ROOT=~/proj |
Environment variable, inherited by tools |
unset top, unsetenv PROJ_ROOT |
unset top, unset PROJ_ROOT |
Remove the variable |
echo $?PROJ_ROOT |
[[ -v PROJ_ROOT ]] |
1 if the variable is set, 0 if not |
echo $status |
echo $? |
Exit status of the last command |
set path = ( /eda/bin $path ) |
PATH=/eda/bin:$PATH |
Prepend a directory to the search path |
set uses spaces around =; setenv has no = at all. A simulator started from the shell only sees setenv variables, so a licence or tool-root variable created with set is a common reason for "tool not found" or licence errors.
csh keeps the shell variable path (a word list) and the environment variable PATH (a colon-separated string) in step: changing one updates the other. Lists use parentheses, and $path[1] is the first element.
Control flow
if ($?LD_LIBRARY_PATH) then
setenv LD_LIBRARY_PATH /eda/lib:${LD_LIBRARY_PATH}
else
setenv LD_LIBRARY_PATH /eda/lib
endif
foreach f (rtl/*.v)
echo "source: $f"
end
switch ($tool)
case iverilog:
set flags = "-g2012"
breaksw
default:
echo "unknown tool $tool"
exit 2
endsw
@ count = $count + 1
if conditions go in parentheses and blocks end with endif, end or endsw. Arithmetic uses @, with a space after it. Testing $?NAME before using $NAME matters: csh stops with Undefined variable if a script reads a variable that does not exist.
Sourcing, -f and redirection
source setup.csh runs the file in the current shell, so its setenv and set path changes stay after it finishes. Running it as csh setup.csh would change only a child shell, which then exits. The -f in #!/bin/csh -f skips the user's ~/.cshrc, so personal aliases and paths cannot change the behaviour of a shared script.
csh redirection is limited. cmd >& log sends standard output and standard error to the same file, and cmd |& tee log pipes both, but there is no direct way to send only standard error to a file. That limitation, together with weak error handling and quoting, is why new automation belongs in Bash or Python.
Environment modules
Many sites wrap their setup in Environment Modules or Lmod: module load iverilog/12.0 sets the same variables for csh, Bash and Python callers from one description. If module avail works on your machine, prefer module load in new scripts instead of sourcing a csh file.
Worked example
A shared project ships setup_eda.csh, which you are not allowed to change:
#!/bin/csh -f
# setup_eda.csh - use with: source setup_eda.csh
if (! $?PROJ_ROOT) then
setenv PROJ_ROOT $HOME/project_workspace
endif
setenv EDA_ROOT /opt/eda
set path = ( $EDA_ROOT/bin $path )
if ($?LD_LIBRARY_PATH) then
setenv LD_LIBRARY_PATH ${EDA_ROOT}/lib:${LD_LIBRARY_PATH}
else
setenv LD_LIBRARY_PATH ${EDA_ROOT}/lib
endif
setenv SIM_DEFINES "-DSIM -DNO_ASSERT_STOP"
alias simdir 'cd $PROJ_ROOT/sim'
A Bash flow cannot source this file: Bash does not understand setenv, set path = (...) or endif. Instead, run it in tcsh, print the resulting environment, and import it. Save as csh_env.sh and use it with source csh_env.sh setup_eda.csh:
# csh_env.sh - import the environment produced by a csh setup script.
# Usage (from Bash): source csh_env.sh path/to/setup.csh
csh_setup=${1:?usage: source csh_env.sh setup.csh}
[[ -f "$csh_setup" ]] || { echo "csh_env.sh: no file $csh_setup" >&2; return 2; }
csh_env=$(tcsh -f -c 'source "$1" && env -0' "$csh_setup" | base64 -w0
exit "${PIPESTATUS[0]}") \
|| { echo "csh_env.sh: $csh_setup failed" >&2; return 2; }
while IFS= read -r -d '' entry; do
name=${entry%%=*}
[[ $name =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
case $name in SHLVL|_|PWD|OLDPWD) continue ;; esac
export "$entry"
done < <(base64 -d <<<"$csh_env")
source csh_env.sh setup_eda.csh
echo "$EDA_ROOT" # /opt/eda
echo "$SIM_DEFINES" # -DSIM -DNO_ASSERT_STOP
command -v iverilog # found through the new PATH
tcsh -f -c '... "$1" ...' file passes the file name as an argument, so a path with spaces is not re-parsed. env -0 separates entries with NUL bytes, so values containing spaces or newlines survive, and base64 carries those bytes safely through a Bash variable. exit "${PIPESTATUS[0]}" returns tcsh's status rather than base64's, and capturing the output first means a failing setup script stops the import instead of half-applying it. Only valid variable names are exported, and shell bookkeeping variables are skipped. Aliases such as simdir are not part of the environment and are not imported; that is expected, because scripts should not rely on aliases.
For a script you own, translate instead of importing. The same setup in Bash:
# setup_eda.sh - use with: source setup_eda.sh
export PROJ_ROOT=${PROJ_ROOT:-$HOME/project_workspace}
export EDA_ROOT=/opt/eda
export PATH="$EDA_ROOT/bin:$PATH"
export LD_LIBRARY_PATH="$EDA_ROOT/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export SIM_DEFINES="-DSIM -DNO_ASSERT_STOP"
${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} adds the colon and old value only when the variable was already set, which replaces the whole csh if ($?LD_LIBRARY_PATH) block.
Check yourself
Why does a simulator not see a variable created with set in csh? What does $?PROJ_ROOT return, and why test it before reading $PROJ_ROOT? Why must a setup script be sourced rather than run? What does -f on the first line prevent? Why can a Bash script not source a csh file directly, and how does csh_env.sh work around that? What does >& do that > does not?
Practice: C shell for EDA environments
Write a short csh script count_rtl.csh that loops over rtl/*.v with foreach, counts the files with @, and exits with status 2 if there are none; run it with tcsh -f count_rtl.csh and print $? from Bash. Then add setenv IVERILOG_FLAGS "-g2012 -Wall" to a copy of setup_eda.csh, import it with csh_env.sh, and show that a Bash run_sim.sh can use $IVERILOG_FLAGS. Finally, translate a setup file from your own site or tool installation into Bash and check that env | sort is the same both ways for the variables it sets.
Practice work is non-certified and is not stored by ASIC Campus.