Files
avrdude/tools/test-avrdude
2024-01-30 23:15:06 +13:00

406 lines
16 KiB
Bash
Executable File

#!/usr/bin/env bash
# published under GNU General Public License, version 3 (GPL-3.0)
# authors Hans Eirik Bull and Stefan Rueger, 2024
progname=$(basename "$0")
tfiles=$(dirname "$0")/test_files
tfiles=$(printf "%q" "$tfiles") # Quote directory string in case there are spaces etc
avrdude_conf='' # Configuration for every run, eg, '-C path_to_avrdude_conf'
delay=4 # Some programmers need a delay between AVRDUDE calls
avrdude_bin=avrdude # Executable
list_only=0 # Normal run
declare -a pgm_and_target=() # Array with test option strings, eg, "-c dryrun -p m328p"
skip_eeprom=0 # Do not skip EEPROM tests for bootloaders by default
verbose=0 # Do not show AVRDUDE errors and warnings by default
Usage() {
cat <<END
Syntax: $progname {<opts>}
Function: test AVRDUDE for certain programmer and part combinations
Options:
-c <configuration spec> additional configuration options used for all runs
-d <sec> delay between test commands (default $delay seconds)
-e <avrdude path> set path of AVRDUDE executable (default $avrdude_bin)
-l list test commands but do not execute them
-p <programmer/part specs> can be used multiple times, overrides default tests
-s skip EEPROM tests for bootloaders
-v verbose: show AVRDUDE error and warning messages
-? or -h show this help text
Example:
\$ $progname -d 0 -p "-c dryrun -p t13" -p "-c dryrun -p m4809"
END
}
while getopts ":\?hc:d:e:lp:sv" opt; do
case ${opt} in
c) avrdude_conf="$OPTARG"
;;
d) delay="$OPTARG"
;;
e) avrdude_bin="$OPTARG"
;;
l) list_only=1
;;
p) pgm_and_target+=("$OPTARG")
;;
s) skip_eeprom=1
;;
v) verbose=1
;;
--) shift;
break
;;
[h?])
Usage; exit 0
;;
\?) echo "Invalid option: -$OPTARG" 1>&2
Usage; exit 1
;;
: ) echo "Invalid option: -$OPTARG requires an argument" 1>&2
Usage; exit 1
;;
esac
done
shift $((OPTIND -1))
if [[ ${#pgm_and_target[@]} -eq 0 ]]; then
# Default tests in absence of -p
pgm_and_target+=(
"-cdryrun -pm2560"
"-cdryrun -px64a1"
"-cpkobn_updi -B1 -patmega3208"
"-cpkobn_updi -B1 -patmega3209"
"-cpkobn_updi -B1 -patmega4808"
"-cjtag2updi -patmega4809 -Pusb:2341:0058 -r"
"-cpkobn_updi -B1 -pattiny3217"
"-cpkobn_updi -B1 -pavr128da48"
"-cpkobn_updi -B1 -pavr128db48"
"-cpkobn_updi -B1 -pavr64dd32"
"-cpkobn_updi -B1 -pavr64ea48"
"-cpkobn_updi -B1 -pavr16eb32"
"-cxplainedmini_isp -patmega328pb"
"-cxplainedmini_updi -pattiny1616"
"-cxplainedmini_updi -pattiny3217"
"-cxplainedpro_updi -B1 -pattiny817"
"-cxplainedpro_pdi -B0.5 -patxmega128a1u"
"-cxplainedpro -B4MHz -patmega256rfr2"
)
fi
arraylength=${#pgm_and_target[@]}
echo -n "Testing $avrdude_bin"
$avrdude_bin -v 2>&1 | grep Version | cut -f2- -d: | sed s/Version/version/
tmp=/dev/shm # Temporary RAM directory
[[ -d $tmp && -w $tmp ]] || tmp=/tmp # Fall back to /tmp if /dev/shm unusable
status=$(mktemp "$tmp/$progname.status.XXXXXX")
logfile=$(mktemp "$tmp/$progname.log.XXXXXX")
outfile=$(mktemp "$tmp/$progname.out.XXXXXX")
trap "rm -f $status $logfile $outfile" EXIT
TIMEFORMAT=%R # time built-in only returns elapsed wall-clock time
elapsed=0 # Global variable holding time of last execute command in seconds
command=(sleep 0.1) # Array with AVRDUDE command
# Execute args as command, set $elapsed and return exit value of command; don't call in a subshell
execute () {
if [[ $list_only -eq 1 ]]; then
echo "\$ ${command[@]}" | tr -s " "
return 0;
fi
sleep "$delay"
# These shenanigans keep stdout, stderr and the exit code of the command
{ read elapsed < <({ time { eval "$@"; echo $? >"$status"; } 2>&4 4>&-; } 4>&2 2>&1 >&3 3>&-); } 3>&1
return $(cat "$status")
}
# Evaluate condition, print execution time and success or failure
result () {
local ret;
eval "$@" && ret=0 || ret=1
if [[ $list_only -eq 0 ]]; then
if [[ $ret -eq 0 ]]; then
echo"$(printf '%7.3f s' $elapsed): $specify"
else
echo"$(printf '%7.3f s' $elapsed): $specify (failed command below)"
echo "\$ ${command[@]}" | tr -s " "
FAIL=true
sleep 4 # Let the hw settle down before the next test
fi
[[ $verbose -eq 1 ]] && { touch $logfile $outfile; cat $logfile $outfile; }
else
touch $outfile; cat $outfile
fi
rm -f $logfile $outfile; elapsed=-999; specify="unknown"; command=(sleep 0.1)
return $ret
}
nofusetest=(-pattiny11/ -pt11/ -pattiny12/ -pt12/ -pattiny15/ -pt15/ -pat89s51/ -p89s51/
-pat89s52/ -p89s52/ -pat90s1200/ -p1200/ -pat90s4414/ -p4414/ -pat90s2313/ -p2313/ -pat90s2333/
-p2333/ -pat90s2343/ -p2343/ -pat90s2323/ -p2323/ -pattiny22/ -pt22/ -pat90s4433/ -p4433/
-pat90s8515/ -p8515/ -pat90s8535/ -p8535/ -pat90s4434/ -p4434/ -patmega163/ -pm163/
-patmega161/ -pm161/ -pattiny28/ -pt28/ -patxmega64a4/ -px64a4/ -patxmega128a4/ -px128a4/
-patxmega192a1/ -px192a1/ -patxmega256a1/ -px256a1/ -pat32uc3a0512/ -puc3a0512/ -pavr8ea28/
-pavr8ea28/ -pavr8ea32/ -pavr8ea32/ -plgt8f88p/ -plgt8f88p/ -plgt8f168p/ -plgt8f168p/
-plgt8f328p/ -plgt8f328p/)
for (( p=0; p<$arraylength; p++ )); do
if [[ $list_only -eq 1 ]]; then
[[ p -ne 0 ]] && echo
echo "# ${pgm_and_target[$p]}"
key=''
else
echo "Prepare \"${pgm_and_target[$p]}\" and press 'enter' or 'space' to continue. Press any other key to skip"
read -n1 -s -r -p $'' key
sleep 0.25
fi
if [ "$key" == '' ]; then
FAIL=false
avrdude=($avrdude_bin $avrdude_conf -qq ${pgm_and_target[$p]} -l $logfile)
# Get flash and EEPROM size in bytes and make sure the numbers are in dec form
FLASH_SIZE=$(${avrdude[@]} -cdryrun -T 'part -m' 2>/dev/null | grep flash | awk '{print $2}')
EE_SIZE=$(${avrdude[@]} -cdryrun -T 'part -m' 2>/dev/null | grep eeprom | awk '{print $2}')
if [[ -z "$FLASH_SIZE" ]]; then
echo "Cannot detect flash; check that \"${pgm_and_target[$p]}\" are valid avrdude options; skipping this test"
continue
fi
# Memories that may or may not be present
USERSIG_SIZE=$(${avrdude[@]} -cdryrun -T 'part -m' 2>/dev/null | grep usersig | awk '{print $2}') # R/W
# Is the to be tested programmer for a bootloader?
is_bootloader=0
# Isolate programmer (assumes either -c prog or -cprog but not sth more tricky such as -qc prog)
programmer=$(echo ${pgm_and_target[$p]} | sed 's/ *\([^-]\)/\1/g' | tr \ \\n | grep ^-c)
if [ -n "$programmer" ]; then
($avrdude_bin $avrdude_conf "$programmer"/At 2>/dev/null | grep -q prog_modes.PM_SPM) && is_bootloader=1
fi
# Should we test fuses?
fusetest=1
# Isolate part (assumes either -p part or -ppart)
part=$(echo ${pgm_and_target[$p]} | sed 's/ *\([^-]\)/\1/g' | tr \ \\n | grep ^-p | tr A-Z a-z)
if [ -n "$part" ]; then
[[ "${nofusetest[@]}" =~ "$part/" ]] && fusetest=0
fi
# Should EEPROM test be carried out?
check_eeprom=1
[[ -z "$EE_SIZE" ]] && check_eeprom=0
[[ $is_bootloader -eq 1 && $skip_eeprom -eq 1 ]] && check_eeprom=0
#####
# Dryrun tests for high-level progrmmer-independent tests (only for -m2560 or similar)
#
if [[ "$programmer" == -cdryrun && $FLASH_SIZE -eq 262144 ]]; then
# Raw test
specify="flash raw format -T/-U write/verify cola-vending-machine.raw"
command=(${avrdude[@]}
-T '"erase flash; write flash -512 0xc0cac01a 0xcafe \"secret Coca .bin recipe\""'
-U flash:w:$tfiles/cola-vending-machine.raw
-T '"write flash -1024 \"Hello World\""')
execute "${command[@]}" > $outfile
result [[ ! -s $outfile '&&' ! -s $logfile ]]
specify="flash extended address and hole test"
command=(${avrdude[@]} -U flash:w:$tfiles/blink-mega2560+lext-test.hex)
execute "${command[@]}"
result [ $? == 0 ]
# Test binary, octal, decimal, hexadecimal and R number lists for I/O
declare -A numsys=([b]=binary [o]=octal [d]=decimal [h]=hexadecimal [R]=R)
for fmt in b o d h R; do
tmpfile="$tmp/app+data-m2560-$$.$fmt-txt"
resfile="$tmp/app+data-m2560-$$.raw"
specify="flash writing ${numsys[$fmt]} numbers"
command=(${avrdude[@]}
-U $tfiles/urboot_m2560_1s_x16m0_115k2_uart0_rxe0_txe1_led+b7_pr_ee_ce.hex
-T '"write flash 0x3fd00 0xc0cac01a 0xcafe \"secret Coca Cola recipe\""'
-U flash:w:$tfiles/cola-vending-machine.raw
-U flash:r:$tmpfile:$fmt)
execute "${command[@]}"
result [ $? == 0 ]
specify="flash reading and verifying ${numsys[$fmt]} numbers"
command=(${avrdude[@]}
-U flash:w:$tmpfile:$fmt
-U flash:r:$resfile:r)
execute "${command[@]}"
touch $tmpfile $resfile
result cmp -s $resfile $tfiles/expected-flash-m2560.raw
rm -f $tmpfile $resfile
done
specify="flash writing srec format"
tmpfile="$tmp/app+data-m2560-$$.srec"
command=(${avrdude[@]}
-U $tfiles/urboot_m2560_1s_x16m0_115k2_uart0_rxe0_txe1_led+b7_pr_ee_ce.hex
-T '"write flash 0x3fd00 0xc0cac01a 0xcafe \"secret Coca Cola recipe\""'
-U flash:w:$tfiles/cola-vending-machine.raw
-U flash:r:$tmpfile:s)
execute "${command[@]}"
result [ $? == 0 ]
specify="flash reading and verifying srec format file"
command=(${avrdude[@]}
-U flash:w:$tmpfile:s
-U flash:v:$tfiles/expected-flash-m2560.raw:r)
execute "${command[@]}"
result [ $? == 0 ]
rm -f $tmpfile
fi
#####
# Fuse test (bootloaders usually cannot set fuses)
#
if [[ $is_bootloader -ne 1 && $fusetest -eq 1 ]]; then
if [ -n "$EE_SIZE" ]; then
specify="fuse access: clear, set and read eesave fuse bit"
command=(${avrdude[@]} -T '"config eesave=0; config eesave=1; config eesave"')
else
specify="fuse access: clear, set and read wdton fuse bit"
command=(${avrdude[@]} -T '"config wdton=0; config wdton=1; config wdton"')
fi
execute "${command[@]}" > $outfile
fusebit=$(grep ^config $outfile | awk '{print $4}')
sed -i -e/^config/d -e/"> "/d $outfile
result [[ '"$fusebit"' == 1 '&&' ! -s $outfile '&&' ! -s $logfile ]]
if [ -n "$EE_SIZE" ]; then
specify="fuse access: set eesave fusebit to delete EEPROM on chip erase"
command=(${avrdude[@]} -T '"config eesave=ee*erased"')
execute "${command[@]}" > $outfile
sed -i -e/^config/d -e/"> "/d $outfile
result [[ ! -s $outfile '&&' ! -s $logfile ]]
fi
fi
######
# Chip erase for defined initial state
#
specify="chip erase"
command=(${avrdude[@]} -e)
execute "${command[@]}"
result [ $? == 0 ]
#####
# Flash test: a relatively difficult file with two code blocks and one data block with holes
#
specify="flash -U write/verify holes_rjmp_loops_${FLASH_SIZE}B.hex"
command=(${avrdude[@]} -Uflash:w:$tfiles/holes_rjmp_loops_${FLASH_SIZE}B.hex)
execute "${command[@]}"
result [ $? == 0 ]
if [ $? != 0 ]; then # Not working? try a file without holes
specify="flash -U write/verify rjmp_loops_for_bootloaders_${FLASH_SIZE}B.hex"
command=(${avrdude[@]} -Uflash:w:$tfiles/rjmp_loops_for_bootloaders_${FLASH_SIZE}B.hex)
execute "${command[@]}"
result [ $? == 0 ]
fi
specify="flash -T write/verify holes_rjmp_loops_${FLASH_SIZE}B.hex"
command=(${avrdude[@]} -T '"write flash '$tfiles/holes_rjmp_loops_${FLASH_SIZE}B.hex:a'"')
execute "${command[@]}" > $outfile
result [[ ! -s $outfile '&&' ! -s $logfile ]]
if [ $? != 0 ]; then # Not working? try a file without holes
specify="flash -T write/verify rjmp_loops_for_bootloaders_${FLASH_SIZE}B.hex"
command=(${avrdude[@]} -T '"write flash '$tfiles/rjmp_loops_for_bootloaders_${FLASH_SIZE}B.hex:a'"')
execute "${command[@]}" > $outfile
result [[ ! -s $outfile '&&' ! -s $logfile ]]
fi
######
# EEPROM tests
#
if [ $check_eeprom -eq 1 ]; then
# -U cannot cope with EEPROMs that are unable to set cleared bits but
# the terminal can if the eesave fuse makes chip erase erase EEPROM
specify="eeprom check whether programmer can flip 0s to 1s"
command=(${avrdude[@]} -Ueeprom:w:0x55:m -Ueeprom:w:0xaa:m)
execute "${command[@]}"
result [ $? == 0 ]
if [ $? == 0 ]; then # OK, try a file with holes
specify="eeprom -U write/verify holes_pack_my_box_${EE_SIZE}B.hex"
command=(${avrdude[@]} -Ueeprom:w:$tfiles/holes_pack_my_box_${EE_SIZE}B.hex)
execute "${command[@]}"
result [ $? == 0 ]
if [ $? != 0 ]; then # Not working? try a file without holes
specify="eeprom -U write/verify the_quick_brown_fox_${EE_SIZE}B.hex"
command=(${avrdude[@]} -Ueeprom:w:$tfiles/the_quick_brown_fox_${EE_SIZE}B.hex)
execute "${command[@]}"
result [ $? == 0 ]
fi
else
echo "# ... the next test may therefore take longer"
fi
specify="eeprom -T write/verify holes_{the_five_boxing_wizards,pack_my_box}_${EE_SIZE}B.hex"
command=(${avrdude[@]}
-T '"write eeprom '$tfiles/holes_the_five_boxing_wizards_${EE_SIZE}B.hex:a'"'
-T flush
-T '"write eeprom '$tfiles/holes_pack_my_box_${EE_SIZE}B.hex:a'"')
execute "${command[@]}" > $outfile
result [[ ! -s $outfile '&&' ! -s $logfile ]]
if [ $? != 0 ]; then # Not working? try a file without holes
specify="eeprom -T write/verify lorem_ipsum_${EE_SIZE}B.srec"
command=(${avrdude[@]} -T '"write eeprom '$tfiles/lorem_ipsum_${EE_SIZE}B.srec:a'"')
execute "${command[@]}" > $outfile
result [[ ! -s $outfile '&&' ! -s $logfile ]]
fi
fi
######
# Chip erase and verify
#
specify="chip erase and spot check flash is actually erased"
command=(${avrdude[@]} -e -Uflash:v:$tfiles/holes_flash_0xff_${FLASH_SIZE}B.hex)
execute "${command[@]}"
result [ $? == 0 ]
if [[ $? == 0 && $check_eeprom -eq 1 && $is_bootloader -eq 0 ]]; then
specify="spot check eeprom is erased, too"
command=(${avrdude[@]} -Ueeprom:v:$tfiles/holes_eeprom_0xff_${EE_SIZE}B.hex)
execute "${command[@]}"
result [ $? == 0 ]
fi
######
# Write and verify random data to usersig if present
#
if [[ -n "$USERSIG_SIZE" && $is_bootloader -ne 1 ]]; then
tmpfile="$tmp/usersig_dump$$_${USERSIG_SIZE}B.bin"
specify="usersig -T/-U write/read random_data_${USERSIG_SIZE}B.bin"
command=(${avrdude[@]}
-T '"erase usersig; write usersig '$tfiles/random_data_${USERSIG_SIZE}B.bin'"'
-T flush
-U usersig:r:$tmpfile:r
-U usersig:v:$tmpfile:r
-T '"erase usersig"'
-T flush
-U usersig:v:$tfiles/0xff_${USERSIG_SIZE}B.hex:i)
execute "${command[@]}" >$outfile
touch "$tmpfile"
result [[ ! -s $outfile '&&' ! -s $logfile ]] '&&' cmp -s "$tfiles/random_data_${USERSIG_SIZE}B.bin" "$tmpfile"
rm -f "$tmpfile"
fi
if [ $FAIL == true ]; then
echo ''
read -rep "One or more AVRDUDE \"${pgm_and_target[$p]}\" tests failed. Do you want to retry this particular test? (y/n): " choice
case "$choice" in
[yY])
p=$p-1; # Re-run the same for-loop iterator
;;
*)
# Continue with the next hardware setup in the list
;;
esac
fi
fi #key
done #for