diff --git a/sweep.bash b/sweep.bash index 5704574ffb2ce071453756c181bbc4e52f9aefda..1d9df3085ae7204ee075cb8637dabc136724aa61 100755 --- a/sweep.bash +++ b/sweep.bash @@ -13,9 +13,10 @@ # Author: Mikael Henriksson # + # -# Global sweeping settings. -# ------------------------- +# Global sweeping settings begin: +# ------------------------------- # # Top level design entity name. @@ -23,18 +24,18 @@ DESIGN_NAME="my_divider" # BASH-style array with design files to be analyzed by Design Compiler. Analisys # of files are done from left (first element) to right (last element). -DESIGN_FILES=("divider.vhdl") +DESIGN_FILES=("div.vhdl") # BASH-style array with periods (in the technology time unit) to be sweept. -PERIODS=(10.00 5.00 2.50 1.25) +PERIODS=(10.0 5.0 3.333 2.5 2.0 1.666 1.428 1.25 1.111 1.0) # BASH-style array with generics to be sweept. Leave empty if no design generic # should be set. Multiple generics can be assigned for one sweep by comma # separating each generic assignment in the BASH-array element. -GENERICS=("pipeline=0" "pipeline=1" "pipeline=2") -#GENERICS=() +GENERICS=( "float_ip=0,pipe_stages=1" "float_ip=0,pipe_stages=2" + "float_ip=1,pipe_stages=1" "float_ip=1,pipe_stages=2" ) -# Design clock name to be sweept. The design must have a clock signal. +# Design clock name to be sweept. The design MUST have a clock signal. CLOCK_NAME="clk" # Enable Design Compiler adaptive retime through 'set_optimize_registers' for @@ -96,10 +97,7 @@ function sweep_to_csv() { } # Join elements of BASH array, or white space separated string, in $2 by the -# string in $1. Example usage: -#| > MY_ARRAY=('a' 'b' 'c') -#| > echo join_by '---' "${MY_ARRAY[@]}" -#| a---b---c +# string in $1. function join_by { local d=$1; shift; local f=$1; shift; printf %s "$f" "${@/#/$d}"; } @@ -128,6 +126,7 @@ function generate_synthesis_script() { # and invoke this function. Note to programmer, that means this function cannot # access any unexported items (variables, functions, etc). function run_design_compiler() { + # Change directory to design directory. local design_directory="$1" cd "$design_directory" || exit 1 @@ -166,15 +165,23 @@ function run_design_compiler() { fi } -# Export functions and variables needed by the forked instances of this script -# that run the run_design_compiler function. +# Signal SIGINT and SIGTERM interrupt handlers. Both are registered once the +# setup is complete and the actual sweep has started. +function sigint_handler() { + echo "${0}: SIGINT ignored. Send SIGTERM to terminate sweep." +} +function sigterm_handler() { + echo "${0}: SIGTERM recieved, propagating to workers and terminating." + kill -s SIGTERM -"${1}" + exit 1 +} + +# Export functions and variables needed by the forked workers. export -f run_design_compiler export DESIGN_NAME export PERIOD_DIM -# # Sanity checks for the global settings. -# if [ -z "$CLOCK_NAME" ]; then echo "Error: No clock name (\$CLOCK_NAME) provided." exit 1 @@ -236,9 +243,21 @@ want to continure? [y/N] " response fi fi +# Get absolute path of design files, which is necessary for the sweep. +DESIGN_FILES_ABS=() +for file in "${DESIGN_FILES[@]}"; do + DESIGN_FILES_ABS+=("$(realpath "$file")") +done + # Get script launch date. SCRIPT_LAUNCH_DATE=$(date +"%F_%H:%M:%S") + +# +# Setup of sweep complete. Generate the sweep top directory, launch all sweep +# workers according to the max concurreny and register signal handlers. +# + # Create sweep top directory. if [ -z "$TOP_DIR_APPEND" ]; then TOP_DIR="sweep_${DESIGN_NAME}_${SCRIPT_LAUNCH_DATE}" @@ -252,12 +271,6 @@ fi echo "Created top directory: '$TOP_DIR'" echo "" -# Get absolute path of design files, which is necessary for the sweep. -DESIGN_FILES_ABS=() -for file in "${DESIGN_FILES[@]}"; do - DESIGN_FILES_ABS+=("$(realpath "$file")") -done - # Initialize designs. We create one sub directory for each design that is # to be sweept. A 'design' is exactly one setting from ${DESIGN_GENERICS[@]}. # Further, we substitue design options into 'template.tcl' and copy the @@ -335,14 +348,35 @@ else # "${GENERICS[@]}" is empty. done fi -# Print start of process pool log. -printf "\nParallel synthesis log ($MAX_CONCURRENCY concurrent processes):\n" - -# Launch workers according to the max concurrency with xargs. -printf '%s' "${SYNTH_DIRS%?}" | xargs -L1 -d "|" --max-procs="$MAX_CONCURRENCY"\ - bash -c 'run_design_compiler $@' _ +# Display start of process pool log message. +printf "\nParallel synthesis log (max $MAX_CONCURRENCY concurrent processes):\n" + +# Trap incoming SIGINT signals, which might be accidental and can prematurely +# terminate the sweep. User should use SIGTERM to terminate sweep beyond this +# point. +#trap 'echo "$0: SIGINT ignored. Send SIGTERM to terminate sweep."' SIGINT +trap 'sigint_handler' SIGINT + +# Launch workers according to the max concurrency with XARGS. By launching XARGS +# through SETSID, all workers are launched in the same process group, and more +# importantly, in a new process group different from that of this parent sweep +# script. This shileds all worker processes from recieving SIGINT on accidental +# shell <Ctrl+C>. The process group id (PGID) of all workers is equal to the +# process id (PID) of XARGS, and can be used if a signal must be sent to all +# workers. +printf '%s' "${SYNTH_DIRS%?}" | setsid xargs -L1 -d "|" --max-procs="$MAX_CONCURRENCY"\ + bash -c 'run_design_compiler $@' _ & +XARGS_PID="$!" + +# Register SIGTERM handler. +trap "sigterm_handler ${XARGS_PID}" SIGTERM + +# Wait for all sweep workers to finish. +while kill -s 0 ${XARGS_PID} 1>/dev/null 2>&1; do + wait ${XARGS_PID} +done -# Print end of process pool log. +# Display end of process pool log message. echo "End of parallel synthesis log."