-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Run multiple threads of nextpnr-ice40 to find a seed that gives a layout that meets timing.
- Loading branch information
1 parent
f666174
commit 6a38c8b
Showing
1 changed file
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
#!/bin/bash | ||
|
||
usage() { | ||
echo "Usage: $(basename $0) [OPTION]" | ||
echo "Run multiple Place-N-Route threads with nextpnr-ice40" | ||
echo "" | ||
echo " -d Debugging mode, output all data from nextpnr-ice40" | ||
echo " -f <freq> Set target frequency for design in MHz (might be overridden from value in synthesis file)" | ||
echo " -l <log_file> Set log output filename (default: $LOG_FILE)" | ||
echo " -n <num_threads> Set number of threads to run (default: $NUM_THREADS, max: $(nproc))" | ||
echo " -o <out_file> Set Place-N-Route output filename (default: $OUT_FILE)" | ||
echo " -p <pin_file> Set pin file to use (default: $PIN_FILE)" | ||
echo " -s <synth_file> Set synthesis file to use (default: $SYNTH_FILE)" | ||
echo " -t '<time>[SUFFIX]' Set time to run before script is killed" | ||
echo " SUFFIX can be 's' for seconds, 'm' for minutes, 'h' for hours." | ||
echo " Given two or more arguments, time is sum of the values." | ||
} | ||
|
||
export TOP_DIR=$(pwd) | ||
export LOCKFILE=$(mktemp -u -p $TOP_DIR) | ||
export LOCKDIR=$(mktemp -u -p $TOP_DIR) | ||
|
||
export OUT_FILE=application_fpga_par.json | ||
export LOG_FILE=application_fpga_par.txt | ||
|
||
export SYNTH_FILE=synth.json | ||
export SYNTH_FILE_SET=0 | ||
|
||
export PIN_FILE=data/application_fpga_tk1.pcf | ||
export PIN_FILE_SET=0 | ||
|
||
export PACKAGE=sg48 | ||
|
||
export PNR_DEBUG=/dev/null | ||
|
||
# Get number of available CPU threads | ||
NUM_THREADS=$(nproc --ignore=1) | ||
|
||
# Array to store process IDs | ||
pids=() | ||
|
||
# Trap ctrl-c and call ctrl_c() | ||
trap 'ctrl_c' SIGINT | ||
|
||
cleanup() { | ||
echo "Running cleanup..." | ||
# Kill all child processes of the script | ||
pstree -p "$$" | grep -o '([0-9]\+)' | grep -o '[0-9]\+' | grep -v "$$" | xargs kill -9 >& /dev/null | ||
# Do directory cleanup | ||
rm -rf $TOP_DIR/tmp.* | ||
echo "Cleanup finished." | ||
} | ||
|
||
ctrl_c() { | ||
echo "" | ||
cleanup | ||
} | ||
|
||
terminate_script() { | ||
echo -n "Time limit reached!" | ||
kill -SIGINT $$ | ||
} | ||
|
||
start_thread() { | ||
OUT_DIR="$(mktemp -d -p $TOP_DIR)" || { echo "Failed to create tempdir"; exit 1; } | ||
cd $OUT_DIR | ||
echo "Starting nextpnr run on PID = $BASHPID, tempdir = $OUT_DIR" | ||
while true; do | ||
nextpnr-ice40 \ | ||
-l $LOG_FILE \ | ||
--randomize-seed \ | ||
--freq $FREQ \ | ||
--ignore-loops \ | ||
--up5k \ | ||
--package $PACKAGE \ | ||
--json $SYNTH_FILE \ | ||
--pcf $PIN_FILE \ | ||
--write $OUT_FILE >& $PNR_DEBUG | ||
exit_code=$? | ||
|
||
if [ $exit_code -eq 0 ]; then | ||
mkdir $LOCKDIR 2>/dev/null | ||
if [ $? -ne 0 ]; then | ||
return 1; | ||
fi | ||
|
||
( | ||
flock -x 200 | ||
|
||
SEED=$(grep "Generated random seed" $LOG_FILE | awk '{print $5}' | awk --bignum '{printf "%20d\n", $0}') | ||
MAX_CLK_PLACE=$(grep "Max frequency for clock" $LOG_FILE | awk 'NR==1{print $7}') # First occurence | ||
MAX_CLK_ROUTE=$(grep "Max frequency for clock" $LOG_FILE | awk 'NR==2{print $7}') # Second occurence | ||
TARGET_CLK=$(grep "Max frequency for clock" $LOG_FILE | awk 'NR==1{print $11}') # First occurence | ||
|
||
echo -n "PASS! Seed = $SEED, Max frequency for clock: place = $MAX_CLK_PLACE MHz, " | ||
echo "route = $MAX_CLK_ROUTE MHz, target = $TARGET_CLK MHz" | ||
cp -v $OUT_FILE $TOP_DIR/ | ||
cp -v $LOG_FILE $TOP_DIR/ | ||
|
||
) 200>$LOCKFILE | ||
|
||
return 0 | ||
else | ||
|
||
( | ||
flock -x 200 | ||
|
||
SEED=$(grep "Generated random seed" $LOG_FILE | awk '{print $5}' | awk --bignum '{printf "%20d\n", $0}') | ||
MAX_CLK_PLACE=$(grep "Max frequency for clock" $LOG_FILE | awk 'NR==1{print $7}') # First occurence | ||
MAX_CLK_ROUTE=$(grep "Max frequency for clock" $LOG_FILE | awk 'NR==2{print $7}') # Second occurence | ||
TARGET_CLK=$(grep "Max frequency for clock" $LOG_FILE | awk 'NR==1{print $11}') # First occurence | ||
|
||
echo -n "FAIL! Seed = $SEED, Max frequency for clock: place = $MAX_CLK_PLACE MHz, " | ||
echo "route = $MAX_CLK_ROUTE MHz, target = $TARGET_CLK MHz, restarting..." | ||
rm -rf $OUT_DIR/* | ||
|
||
) 200>$LOCKFILE | ||
fi | ||
done | ||
} | ||
|
||
# Check flags | ||
while getopts "df:l:n:o:p:s:t:h" arg; do | ||
case $arg in | ||
d) | ||
PNR_DEBUG=1 | ||
;; | ||
f) | ||
FREQ=${OPTARG} | ||
;; | ||
l) | ||
LOG_FILE=${OPTARG} | ||
;; | ||
n) | ||
NUM_THREADS=${OPTARG} | ||
;; | ||
o) | ||
OUT_FILE=${OPTARG} | ||
;; | ||
p) | ||
PIN_FILE=${OPTARG} | ||
PIN_FILE_SET=1 | ||
;; | ||
s) | ||
SYNTH_FILE=${OPTARG} | ||
SYNTH_FILE_SET=1 | ||
;; | ||
t) | ||
TIMEOUT=${OPTARG} | ||
;; | ||
h) | ||
usage | ||
exit 0 | ||
;; | ||
*) | ||
#echo "Invalid argument: ${OPTARG}" | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
# Function to handle failure of the sleep command | ||
handle_sleep_failure() { | ||
exit 1 | ||
} | ||
|
||
export SYNTH_FILE=$(realpath $SYNTH_FILE) | ||
export PIN_FILE=$(realpath $PIN_FILE) | ||
export OUT_FILE=$(basename $OUT_FILE) | ||
export LOG_FILE=$(basename $LOG_FILE) | ||
|
||
if [ ! -f $SYNTH_FILE ]; then | ||
echo "No $SYNTH_FILE file found. Did you run Yosys?" | ||
exit 1 | ||
fi | ||
|
||
if [ -f $OUT_FILE ]; then | ||
echo "Place-N-Route output file $OUT_FILE found. Remove before starting again!" | ||
exit 1 | ||
fi | ||
|
||
if [ -f $LOG_FILE ]; then | ||
echo "Log file $LOG_FILE found. Remove before starting again!" | ||
exit 1 | ||
fi | ||
|
||
if [ -z "$FREQ" ]; then | ||
echo "No target frequency set!" | ||
exit 1 | ||
else | ||
export FREQ | ||
fi | ||
|
||
if [ -n "$TIMEOUT" ]; then | ||
echo "Timeout: $TIMEOUT" | ||
# Start background timer | ||
( sleep ${TIMEOUT[@]} && terminate_script ) & | ||
if ! ps -p $! > /dev/null; then | ||
exit 1 | ||
fi | ||
fi | ||
|
||
echo "Threads: $NUM_THREADS" | ||
echo "Target frequency: $FREQ MHz (might be overridden from value in synthesis file)" | ||
echo "Synthesis file: $SYNTH_FILE" | ||
echo "Pin file: $PIN_FILE" | ||
echo "Package: $PACKAGE" | ||
echo "Out file: $OUT_FILE" | ||
echo "Log file: $LOG_FILE" | ||
echo "" | ||
echo "Lock file: $LOCKFILE" | ||
echo "Lock dir: $LOCKDIR" | ||
echo "" | ||
# Start threads equal to the number of CPU cores | ||
for i in $(seq 1 $NUM_THREADS); do | ||
# Start the thread in the background | ||
start_thread & | ||
# Collect the PID | ||
pids+=($!) | ||
done | ||
|
||
wait_for_success() { | ||
while true; do | ||
# Check for exit 0 status of all collected PID processes | ||
if wait -n ${pids[@]}; then | ||
cleanup | ||
echo "Exiting..." | ||
exit 0 | ||
else | ||
echo "Exiting..." | ||
exit 1 | ||
fi | ||
done | ||
} | ||
|
||
# Wait for all threads | ||
wait_for_success |