diff --git a/.gitattributes b/.gitattributes index 234192dbe9eb8afce76299392036747cbdae2adb..78c73e7851e79f41baf484dbebb76f18fb8ad3aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -185,6 +185,9 @@ CEP/BB/SourceDB/bootstrap -text Firmware/tools/bootstrap -text Firmware/tools/src/flash_images.sh -text Firmware/tools/src/restart_images.sh -text +Firmware/tools/src/rsuctl3 -text +Firmware/tools/src/rsuctl3.pl -text +Firmware/tools/src/rsuctl3_reset -text Firmware/tools/src/view_images.sh -text JAVA/CEP/jParmFacade/nbproject/.cvsignore -text JAVA/GUI/MAC/jRSP/.cvsignore -text diff --git a/Firmware/tools/src/Makefile.am b/Firmware/tools/src/Makefile.am index eb1bdc9f50d7b1d9f9c75a8755c9095c33c88063..c3541d178b0fff637713c5bae56a7de33da4c3a1 100644 --- a/Firmware/tools/src/Makefile.am +++ b/Firmware/tools/src/Makefile.am @@ -1,7 +1,11 @@ sbin_SCRIPTS = \ flash_images.sh \ restart_images.sh \ - view_images.sh + view_images.sh \ + rsuctl\ + rsuctl3 \ + rsuctl3_reset \ + README.rsuctl dist_sbin_SCRIPTS = $(sbin_SCRIPTS) diff --git a/Firmware/tools/src/README.rsuctl b/Firmware/tools/src/README.rsuctl new file mode 100644 index 0000000000000000000000000000000000000000..b53dfd06aa17c952342bf225bf70fd42b8b7551d --- /dev/null +++ b/Firmware/tools/src/README.rsuctl @@ -0,0 +1,293 @@ + +rsuctl manual +------------- + +This directory contains the rsuctl utility used to update firmware on the RSP boards. +The help information + +Usage +----- + +# ./rsuctl -h + +rsuctl (Revision: 1.15 ) # Remote System Update - program firmware in FPGA flash + +Usage: rsuctl [options] command + + options + [-i ifname] # Ethernet interface name on which to communicate (default is eth1) + [-m mac-address] # IP-address of the target board + # (default is broadcast address ff:ff:ff:ff:ff:ff) + + command (specify at least one of the following) + [-l] # List the images currently stored in all pages of the flash + + [-b -f in.ttf -o out.bin] # Convert a ttf file to a binary file + [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) + [-x -p page # Start (load & reset) new firmware from the specified page + [-w -p page -f image[.ttf] [-F]] # Write img.bin into specified page (-F forces write to page 0) + [-v -p page -f image[.ttf]] # Compare flash page with img.bin + [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin + + [-r] # Reset all FPGA's (load factory image) + [-c] # Clear all FPGA's (restart current image) + [-s] # Send SYNC pulse to all FPGA's + + [-P] # Ping the board (find board when using broadcast address) + [-V] # Read RSU status and RSR versions + [-h] # This help text + + +Introduction +------------- + +The flash is divided into 16 pages. Page 0 will be write protected in +the final system and contains the factory image for the BP FPGA. Pages +1 to 7 may contain application images for the BP FPGA. + +Page 8 contains the factory image for the AP FPGA's. Pages 9 to 15 +may contain application images for the AP FPGA's. + +Listing the contents of the flash +--------------------------------- + +To list the images that are available in the flash use the -l argument: + +# ./rsuctl -l +=== connected to RSP board(s) on eth1:FF:FF:FF:FF:FF:FF + +BP Factory 0: +BP Image 1: 8740783 Fri Apr 7 16:02:37 2006 bp_20060407.ttf +AP Factory 8: +AP Image 9: 13251099 Fri Apr 7 16:01:08 2006 ap_20060407.ttf +AP Image 15: 32366 Thu Mar 16 16:21:53 2006 lofar.jpg + +=== OK + +When an image is written to a flash page using the rsuctl utility, +information about the file is written to the last block of the +destination page. This acts as a directory to the flash. This +directory is read by the "./rsuctl -l" command. The directory block +for the BP Factory image is empty because that image was not written +using rsuctl and therefor the directory entry for it is missing. + +Writing an image to a flash page +-------------------------------- + +To write a new image to the flash you can use the -w argument, specify +the destination page (-p) and the file to write. + +# ./rsuctl -w -p1 -f bp.ttf +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== Reading 'bp.ttf' +=== Converting 'bp.ttf' to binary +=== |--------------------------------| +=== ................................ +=== erasing page 01 +=== |--------------------------------| +=== ................................ +=== writing and verifying page 01 +=== |--------------------------------| +=== ................................ +=== OK + +Because the file extension is .ttf, this ttf text file is first +converted to binary before being written to flash. After this +conversion, the destination flash page is erase. As the new image is +written each block is verified after it has been written. + +You can write arbitrary data to the flash, only when the file +extension is ttf is the file converted from ttf to binary format. The +following command writes the banner jpg from www.lofar.org to page +15. + +# ./rsuctl -w -p15 -f lofar.jpg +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== Reading 'lofar.jpg' +=== erasing page 15 +=== |--------------------------------| +=== ................................ +=== writing and verifying page 15 +=== |--------------------------------| +=== ................................ +=== OK + +Verifying an image in the flash +------------------------------- + +To verify that an image in the flash corresponds to a file on disk use +the -v option. + +# ./rsuctl -v -p1 -f bp.ttf +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== Reading 'bp.ttf' +=== Converting 'bp.ttf' to binary +=== |--------------------------------| +=== ................................ +=== verifying page 01 +=== |--------------------------------| +=== ................................ +=== OK + +Supplying the wrong reference file might result in the following +output. + +# ./rsuctl -v -p1 -f bp41.ttf +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== Reading 'bp41.ttf' +=== Converting 'bp41.ttf' to binary +=== |--------------------------------| +=== ................................ +=== verifying page 01 +=== |--------------------------------| +=== XXXXXXXXXXXXXXXXXX.............. +=== FAILED + +Each '.' represents on sector on the flash (128 blocks). When an error +is found in any of the blocks within a sector a 'X' is shown. Note +that the sectors at the end appear to be correct. This is because the +page is zero padded to the end and the actual image only occupies just +over half of the flash page. + +Dumping a flash page to file +---------------------------- +The binary data stored in flash can be dumped to file with the -d +command. + +# ./rsuctl -d -p15 my.jpg +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== Dumping to file 'dump.jpg' +=== dumping page 15 +=== |--------------------------------| +=== ................................ +=== OK + +Note that the complete page (excluding the directory block) is dumped, +irrespective of the orginal size of the image file. This results in a +file of 4193280 bytes which is (32*128 - 1 ) * 1024). The file is +zeropadded from the orginal image size upto the page size (minus the one +directory block). + +Converting a .ttf file to a binary file +--------------------------------------- +To prevent repeated conversion from .ttf to bin or to compare a binary +file with the result of a dumped image you can use the -b command. + +# ./rsuctl -b -f bp.ttf -o bp.bin +=== Reading 'bp.ttf' +=== Converting 'bp.ttf' to binary +=== |--------------------------------| +=== ................................ +=== OK + +Erasing pages +------------- + +To erase a page of the flash use the -e command. The factory image in +page 0 is protected. To force erasing page 0 add the -F flag. Adding +the -F flag will only disable the software protection in the rsuctl +program, not the hardware protection on the board. The hardware +protection of page 0 in the flash can only be disabled by setting the +right dip switches. + +# ./rsuctl -e -p5 +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== erasing page 05 +=== |--------------------------------| +=== ................................ +=== OK + +# ./rsuctl -e -p0 +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== ERR erasing factory image (page 0) prohibited, use -F to force erase of factory image at ./rsuctl line 778. + +# ./rsuctl -e -p0 -F +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== erasing page 00 +=== |--------------------------------| +=== ................................ +=== OK + +Resetting All FPGA's +-------------------- + +To return to the factory default images on all FPGA's use the -r +command. + +# ./rsuctl -r +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== waiting 3 seconds after reset +=== reconfiguring AP's with factory image (page 8) +=== waiting 1 second after AP reconfig +=== waiting 3 seconds after clear + +RSU Status: + rdy : Configuration done + err : Configuration successfull + fpga : BP was reconfigured + image: Factory image + trig : User reconfig + +RSP version: 2 +BP version: 4.1 +AP[0] version: 4.1 +AP[1] version: 4.1 +AP[2] version: 4.1 +AP[3] version: 4.1 +=== OK + +Clearing FPGA's +--------------- + +Clearing FPGA's will help resyncing the internal datapaths if needed. + +# ./rsuctl -c +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== waiting 3 seconds after clear + +RSU Status: + rdy : Configuration done + err : Configuration successfull + fpga : BP was reconfigured + image: Factory image + trig : User reconfig + +RSP version: 2 +BP version: 4.1 +AP[0] version: 4.1 +AP[1] version: 4.1 +AP[2] version: 4.1 +AP[3] version: 4.1 +=== OK + +Send SYNC to all FPGA's +----------------------- + +# ./rsuctl -s +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff +=== OK + +Read RSU status and versions +---------------------------- + +# ./rsuctl -V +=== connected to RSP board(s) on eth1:ff:ff:ff:ff:ff:ff + +RSU Status: + rdy : Configuration done + err : Configuration successfull + fpga : BP was reconfigured + image: Factory image + trig : User reconfig + +RSP version: 2 +BP version: 4.1 +AP[0] version: 4.1 +AP[1] version: 4.1 +AP[2] version: 4.1 +AP[3] version: 4.1 +=== OK + + +END OF DOCUMENT + diff --git a/Firmware/tools/src/rsuctl b/Firmware/tools/src/rsuctl new file mode 100755 index 0000000000000000000000000000000000000000..09a3849589aff36954466de15d570f0d780fed13 --- /dev/null +++ b/Firmware/tools/src/rsuctl @@ -0,0 +1,1002 @@ +#!/usr/bin/perl +$VERSION = q$Revision$; +# +# rsuctl # Remote System Update - program firmware in FPGA flash +# +# Usage: rsuctl [options] command +# +# options +# [-i ifname] # Ethernet interface name on which to communicate (default is eth1) +# -m mac-address # MAC-address of the target board (Mandatory) +# +# command (specify at least one of the following) +# [-l] # List the images currently stored in all pages of the flash +# +# [-b -f in.ttf -o out.bin] # Convert a ttf file to a binary file +# [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) +# [-x -p page # Start (load & reset) new firmware from the specified page +# [-w -p page -f image[.ttf] [-F]] # Write img.bin into specified page (-F forces write to page 0) +# [-v -p page -f image[.ttf]] # Compare flash page with img.bin +# [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin +# +# [-r] # Reset all FPGA's (load factory image) +# [-c] # Clear all FPGA's (restart current image) +# [-s] # Send SYNC pulse to all FPGA's +# +# [-q] # Prevent checking for running RSPDriver (prevents pause at start) +# [-P] # Ping the board (find board when using broadcast address) +# [-V] # Read RSU status and RSR versions +# [-h] # This help text +# +# + +use Net::RawIP; +use Getopt::Std; +use File::stat; +use Switch; +use POSIX; # for geteuid() + +$g_debug = 0; # set to 1 to enable debug output +$g_packet = ''; # global packet string +$g_error = 0; # global error status + +# +# Definitions +# +$PID_RSR = 0x01; +$PID_RSU = 0x02; + +$REG_STATUS = 0x00; +$REG_STATUS_SIZE = 200; +$REG_VERSION = 0x01; +$REG_VERSION_SIZE = 2; + +$REG_RW = 0x01; +$REG_ERASE = 0x02; +$REG_RECONFIG = 0x03; +$REG_CLEAR = 0x04; + +$CMD_ERASE = 0x01; + +$CMD_SYNC = 0x01; +$CMD_CLEAR = 0x02; +$CMD_RESET = 0x04; + +$MEPHDRSIZE = 16; + +$TYPE_READ = 0x01; +$TYPE_WRITE = 0x02; +$TYPE_READACK = 0x03; +$TYPE_WRITEACK = 0x04; + +$ADDR_BLP_NONE = 0x00; +$ADDR_RSP = 0x01; + +$FLASHSIZE = 64 * 1024 * 1024; # 64MB +$BLOCKSIZE = 1024; # block = 1024 bytes +$BLOCKS_PER_SECTOR = 128; # sector = 128 blocks +$SECTORS_PER_PAGE = 32; # page = 32 sectors +$BLOCKS_PER_PAGE = $BLOCKS_PER_SECTOR * $SECTORS_PER_PAGE; +$PAGES = 16; +$PROGRESS_SIZE = 32; + +$RESPONSETIMEOUT = 2; +$TO = 5; + +# +# pack format for the MEP message +# +# C 0x10 Ethernet Type byte 0 +# C 0xFA Ethernet Type byte 1 +# C TYPE +# C STATUS +# S FRAMELENGTH +# C BLPID +# C RSP +# C PID +# C REGID +# S OFFSET +# S PAYLOAD_LENGTH +# S SEQ_NR +# S RESERVED +# +$MEPSENDFORMAT="CCCCSCCCCSSSS"; +$MEPRECVFORMAT="H28CCSCCCCSSSS"; + +# ETHLEN = max length of Ethernet payload +$ETHLEN = 1500; + +# +# Error code +# +%STATUS = ( 0 => "=== OK", + 1 => "=== ERR unknown MEP message type", + 2 => "=== ERR illegal BLP address", + 3 => "=== ERR invalid PID", + 4 => "=== ERR register does not exist", + 5 => "=== ERR offset too large", + 6 => "=== ERR message is too large", + 7 => "=== ERR message corruption during RSP processing", + 8 => "=== ERR message lost during RSP processing" + ); + +# +# usage +# +sub usage +{ + print STDERR <<END; + +# Remote System Update - program firmware in FPGA flash + +Usage: rsuctl [options] command + + standalone command (doesn't need an RSP MAC address to run) + [-b -f in.ttf -o out.bin] # Convert a ttf file to a binary file + + options + [-i ifname] # Ethernet interface name on which to communicate (default is eth1) + -m mac-address # MAC-address of the target board (Mandatory) + + command (specify at least one of the following, need -m option at least) + [-l] # List the images currently stored in all pages of the flash + + [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) + [-x -p page # Start (load & reset) new firmware from the specified page + [-w -p page -f image[.ttf] [-F]] # Write img.bin into specified page (-F forces write to page 0) + [-v -p page -f image[.ttf]] # Compare flash page with img.bin + [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin + + [-r] # Reset all FPGA's (load factory image) + [-c] # Clear all FPGA's (restart current image) + [-s] # Send SYNC pulse to all FPGA's + + [-q] # Prevent checking for running RSPDriver (prevents pause at start) + [-P] # Ping the board (find board when using broadcast address) + [-V] # Read RSU status and RSR versions + [-h] # This help text + +END + + exit; +} + +# +# pack_rsu($type, $regid, $size, $offset) +# +# Pack an RSU message +# +sub pack_rsu +{ + my ($type, $regid, $size, $offset) = @_; + + return pack($MEPSENDFORMAT, + 0x10, 0xFA, $type, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSU, $regid, $offset, $size, 0, 0); +} + +# +# pcapcallback, save packet in $g_packet +# +# savepacket($handle, $hdr, $packet) +# +sub savepacket +{ + my ($handle, $hdr, $packet) = @_; + $g_packet = $packet; +} + +# +# ($payload,$error) = readresponse($pcap) +# +sub readresponse +{ + my ($pcap) = @_; + my ($retval) = 0; + + do { # keep going when we receive invalid messages + $retval = 0; # assume success + + # wait for message to arrive + eval { + use POSIX qw(SIGALRM); + POSIX::sigaction(SIGALRM, + POSIX::SigAction->new(sub { die })) + || die "=== ERR error setting SIGALRM handler: $!\n"; + + alarm $RESPONSETIMEOUT; + (0 == loop ($pcap, 1, \&savepacket, '')) + || die "=== ERR Failed to receive response"; + alarm 0; + }; + if ($@) { + $retval = 1; # fatal (timeout) + return ('', $retval, ''); + } + + ($ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved) = + unpack($MEPRECVFORMAT, $g_packet); + + if ($TYPE_WRITEACK != $type && $TYPE_READACK != $type) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid TYPE\n" if ($g_debug); + } + if ($PID_RSU != $pid && $PID_RSR != $pid) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid PID\n" if ($g_debug); + } + + printf STDERR "0x%s 0x%02x 0x%02x %5d 0x%02x 0x%02x 0x%02x 0x%02x %5d %5d %5d %5d\n", + $ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved if ($g_debug); + + if (0 == $retval && 0 != $status) { + $retval = 1; # fatal + print STDERR "=== FATAL protocol error: status=", $STATUS{$status}, "\n"; + print STDERR "=== Are you using the correct Ethernet interface? Use -i argument to change, e.g. -i eth1\n"; + exit; + } + } until ($retval != 2); + + return ('', $retval, '') if ($retval); + + return (substr($g_packet, 14 + $MEPHDRSIZE), $retval, $ethdr); +} + +# +# flash($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) +# +sub flash +{ + my ($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) = @_; + + printf STDERR "=== %s page %02d\n", $msg, $page; + print STDERR "=== |" . ("-" x $SECTORS_PER_PAGE) . "|\n=== "; + + my ($success) = 1; + # leave last block for directory entry + my ($block); + for ($block = 0; $block < $BLOCKS_PER_PAGE - 1; $block += $blockinc) { + + if ($writecb) { + $packet = pack_rsu($TYPE_WRITE, $cmd, $size, + ($page * $BLOCKS_PER_PAGE) + $block); + $packet .= &$writecb($page, $block, $image); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + + return $error if ($error); + } + + if ($readcb) { + $packet = pack_rsu($TYPE_READ, $cmd, $size, + ($page * $BLOCKS_PER_PAGE) + $block); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + return $error if ($error); + + $success = &$readcb($page, $block, $payload, $image) && $success; + } + + if (0 == $block % $BLOCKS_PER_SECTOR) { + if ($success) { print STDERR "."; } + else { print STDERR "X"; } + $success = 1; + } + } + print STDERR "\n"; + + return 0; +} + +# +# writedir($sock, $pcap, $page, $entry) +# Write the entry to the last block of page $page +# +sub writedir +{ + my ($sock, $pcap, $page, $entry) = @_; + + die "=== ERR internal error: size of direntry > $BLOCKSIZE" if (length($entry) > $BLOCKSIZE); + + print STDERR "writing dir @", (($page + 1) * $BLOCKS_PER_PAGE) - 1, "\n" if ($g_debug); + + # add zero padding if needed + $entry .= pack("C", 0) x ($BLOCKSIZE - length($entry)) if (length($entry) < $BLOCKSIZE); + + # write last block with dir entry + $packet = pack_rsu($TYPE_WRITE, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) - 1); + $packet .= $entry; + + my $error = 2; + $sock->send_eth_frame($packet); + + ($result, $error) = readresponse($pcap); + + return $error; +} + +# +# readdir($sock, $pcap, $page) +# Read the directory entry from the specified page +# +sub readdirentry +{ + my ($sock, $pcap, $page) = @_; + + my ($packet) = pack_rsu($TYPE_READ, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) -1); + + $sock->send_eth_frame($packet); + + my $error = 0; + ($entry, $error) = readresponse($pcap); + return ('', $error) if $error; + + $entry = '' if (substr($entry,0,1) eq pack("C", 0xff)); + + return ($entry, $error); +} + +# +# erasecb($page, $block, $image) +# +sub erasecb +{ + my ($page, $block, $image) = @_; + + print STDERR "erasecb: $page, $block\n" if ($g_debug); + + return pack("C", $CMD_ERASE); +} + +# +# erase($sock, $pcap, $page) +# page = -1 means all pages +# +sub erase +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, $BLOCKS_PER_SECTOR, + "erasing", $REG_ERASE, 1, + \&erasecb, 0, ''); +} + +# +# Verify the data +# +sub verifycb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "verifycb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# verify($sock, $pcap, $page, $image) +# +sub verify +{ + my ($sock, $pcap, $page, $image) = @_; + + return flash($sock, $pcap, $page, 1, + "verifying", $REG_RW, $BLOCKSIZE, + 0, \&verifycb, $image); +} + +# +# Dump the data to file +# +sub dumpcb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "dumpcb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + print DUMPFILE $payload; + + return 1; +} + +# +# dump($sock, $pcap, $page) +# +sub dump($$$) +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, 1, + "dumping", $REG_RW, $BLOCKSIZE, + 0, \&dumpcb, ''); +} + +# +# Write the data +# +sub writepagecb +{ + my ($page, $block, $image) = @_; + + print STDERR "writepagecb: page=$page, block=$block\n" if ($g_debug); + + my ($data) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $data .= pack("C", 0) x ($BLOCKSIZE - length($data)); + + return $data; +} + +# +# Read and verify the data +# +sub readpagecb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "readpagecb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# Create a direntry string based on filename +# direntry($filename) +# +sub formatentry +{ + my ($filename) = @_; + + $stat = stat($filename) || die "=== ERR failed to stat file '$filename'"; + $datetime = ctime($stat->mtime); + chop($datetime); + + # return 'size YYYY mm dd hh:mm filename' + return sprintf("%10d %s %s", $stat->size, $datetime, $filename); +} + +# +# writepage($sock, $pcap, $page, $filename, $image) +# +sub writepage +{ + my ($sock, $pcap, $page, $filename, $image) = @_; + + $error = flash($sock, $pcap, $page, 1, + "writing and verifying", $REG_RW, $BLOCKSIZE, + \&writepagecb, \&readpagecb, $image); + + if (!$error) { + $error = writedir($sock, $pcap, $page, formatentry($filename)); + } else { + # clear entry, an error has occured + writedir($sock, $pcap, $page, ''); + } + + return $error; +} + +# +# Read entire ttffile into a string +# convert it to a binary string and +# return than string +# +# ttfread($filename) +# +sub fread +{ + my ($filename) = @_; + local $/; + + # open input and output files + open(TTFFILE, $filename) || die "=== ERR failed to open file '$filename'"; + $ttf = <TTFFILE>; + close(TTFFILE); + + # remove \r's + # replace \n, by spaces + $ttf =~ tr/\r//; + $ttf =~ tr/\n/ /; + + @bytes = split(/,/, $ttf); + $result = ''; + foreach $byte (@bytes) { + $result .= pack("C", $byte); + } + + # return the binary string + return $result; +} + +# +# Read image from ttf or bin file. If filename ends in .ttf +# then the file is converted from ttf to bin format first. +# +# readimage($filename) +# +sub readimage +{ + my ($filename) = @_; + local $/ = undef; # read entire file + + # open input and output files + die "=== ERR no image file specified" if (!defined($filename)); + open(IMAGEFILE, $filename) || die "=== ERR failed to open file '$filename'"; + print STDERR "=== Reading '$filename'\n"; + $data = <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + @parts = split(/\./, $filename); + if (@parts[-1] eq "ttf") { + # replace \r\n, by spaces + $data =~ tr/\r\n/ /; + + @bytes = split(/,/, $data); + + print STDERR "=== Converting '$filename' to binary\n"; + print STDERR "=== |" . ("-" x $PROGRESS_SIZE) . "|\n=== "; + + $i = 0; + $interval = $#bytes / $PROGRESS_SIZE; + $interval = 1 if ($interval < .5); + + $bindata = ''; + foreach $byte (@bytes) { + $bindata .= pack("C", $byte); + + $i++; + print STDERR "." if (0 == $i % $interval); + } + + print STDERR "\n"; + return $bindata; + } + + return $data; +} + +# +# Read RSU status from RSR status register +# readrsustatus($sock, $pcap) +# +sub readrsustatus +{ + my ($sock, $pcap) = @_; + + $size = $REG_STATUS_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_STATUS, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + $rsustatus = unpack("C", substr($result, 164, 1)); + + print STDERR sprintf("\nRSU Status:\n rdy : %s\n err : %s\n fpga : %s\n image: %s\n trig : %s\n\n", + ('Configuration ongoing', 'Configuration done')[$rsustatus & 1], + ('Configuration successfull', 'Configuration error')[($rsustatus & 2) >> 1], + ('BP was reconfigured', 'AP was reconfigured')[($rsustatus & 4) >> 2], + ('Factory image', 'Application image')[($rsustatus & 8) >> 3], + ('Board reset', 'User reconfig', 'User reset', 'Watchdog')[($rsustatus >> 4) & 7]); + + return $error; +} + +# +# Read versions of all FPGA's (handy after reconfig, reset, clear, etc) +# readversions($sock, $pcap) +# +sub readversions +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "RSP version: $id\nBP version: $maj.$min\n"; + + # read AP versions + for ($ap = 0; $ap < 4; $ap++) { + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + 1 << $ap, 0, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "AP[$id] version: $maj.$min\n"; + } + + return 0; +} + +# +# Find MAC addresses of active boards +# ping($sock, $pcap) +# +sub ping +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + + while (!$error) { + ($result, $error, $ethdr) = readresponse($pcap); + $mac = substr($ethdr, 12, 12); + $mac =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/; + print STDERR "Found RSP board on MAC: ", $mac, "\n" if (!$error); + } + + return 0; +} + +# +# Reconfigure. Load new firmware from flash into FPGA's. After loading +# has finished, the FPGA will be reset and restart with new firmware. +# +# reconfig($sock, $pcap, $page) +# +sub reconfig +{ + my ($sock, $pcap, $page) = @_; + + $subpage = $page & 7; + $fpga = 1 - ($page >> 3); + + $cmd = ($fpga << 7) | $subpage; + + $packet = pack_rsu($TYPE_WRITE, $REG_RECONFIG, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + + # there will be no response + # ($response, $error) = readresponse($pcap); + + return 0; +} + +# +# Reset control for all FPGA's +# +# clearctl($sock, $pcap, $cmd) +# valid values for $cmd: $CMD_SYNC, $CMD_CLEAR, $CMD_RESET +# +sub clearctl +{ + my ($sock, $pcap, $cmd) = @_; + + $packet = pack_rsu($TYPE_WRITE, $REG_CLEAR, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + ($response, $error) = readresponse($pcap); + + return $error; +} + +# +# Report result +# report($error, $report_ok = false) +# +sub report +{ + my ($error, $report_ok) = @_; + + if ($error || $g_error) { + print STDERR "=== FAILED\n"; + exit 1; + } + else { + print STDERR "=== OK\n" if ($report_ok); + } +} + +# +# Change range of the page parameter +# chkpage($page) +# +sub chkpage +{ + my ($page) = @_; + + die "=== ERR no flash page specified, use -p" if (!defined($page)); + if ($page < 0 || $page >= $PAGES) { + die "=== ERR invalid page index $page, should be >= 0 and < $PAGES"; + } +} + +# +# Get rsu status and versions +# version_cmd($sock, $pcap) +# +sub version_cmd +{ + my ($sock, $pcap) = @_; + + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + return $error; +} + +sub main +{ + my $sock = 0; + + print STDERR "=== rsuctl ($VERSION)\n"; + + Getopt::Std::getopts("i:m:lbf:o:exwvdp:FrcsPVhq"); + + # check that at least one argument is present + usage() if ($opt_h); + + if (defined($opt_b)) { + + # CONVERT TO BINARY + # + # readimage + + die "=== ERR no input file specified" if (!defined($opt_f) || ($opt_f eq "")); + die "=== ERR no output file specified" if (!defined($opt_o) || ($opt_o eq "")); + $image = readimage($opt_f); + + open(DUMPFILE, ">$opt_o") || die "=== ERR failed to open file '$opt_o' for writing\n"; + print DUMPFILE $image; + close(DUMPFILE); + report($error, true); + + return; + } + + if (!defined($opt_m)) { + print STDERR "=== ERR Must specify MAC address using -m option"; + usage(); + } + usage() unless ($opt_l || $opt_b || $opt_e || $opt_x || $opt_w || $opt_v || + $opt_d || $opt_r || $opt_c || $opt_s || $opt_P || $opt_V || $opt_h); + + # assign default values + $opt_i = 'eth1' if !defined($opt_i); + + # Create socket to the boards + if (!defined($opt_b)) { + + die "=== ERR this program needs to run as root to send/recv raw Ethernet frames" if (0 != geteuid()); + + $sock = new Net::RawIP; + $sock->ethnew($opt_i); + $sock->ethset(dest => $opt_m); + + $TIMEOUT=5; + $pcap = $sock->pcapinit($opt_i, "not ether dst $opt_m", $ETHLEN, $TIMEOUT); + + die "=== ERR could not open raw socket to RSP board(s) on $opt_i:$opt_m: $!\n" unless $sock; + + print STDERR "=== connected to RSP board(s) on $opt_i:$opt_m\n"; + + if (!defined($opt_q)) { + print STDERR "=== checking for running RSPDriver\n"; + ($payload, $error) = readresponse($pcap); + die "=== RSPDriver appears to be running, stop it first\n" if ($payload ne ''); + } + } + + if (defined($opt_l)) { + + # LIST IMAGES IN FLASH + # + # readdirentry for all pages + + print STDERR "=== listing flash contents\n\n"; + for ($page = 0; $page < $PAGES; $page++) { + ($entry, $error) = readdirentry($sock, $pcap, $page); + break if $error; + print STDERR sprintf("%s %s %2d: %s\n", ($page < 8 ?'BP':'AP'), + (0 == $page % 8 ? 'Factory':'Image '), + $page, $entry) if ((0 == $page % 8) || $entry ne ''); + } + print STDERR "\n"; + report($error, true); + + return; + } + + if (defined($opt_e)) { + + # ERASE + # + # erase flash page + + die "=== ERR erasing factory image (page 0) prohibited, use -F to force erase of factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + report(erase($sock, $pcap, $opt_p), true); + + return; + } + if (defined($opt_x)) { + + # RECONFIG + # + # reconfig BP or AP + # if BP wait $TO seconds + # clear + # wait $TO seconds + # read rsustatus & version + + chkpage($opt_p); + report(reconfig($sock, $pcap, $opt_p)); + print STDERR "=== waiting $TO seconds after reconfig\n"; sleep $TO; + report(clearctl($sock, $pcap, $CMD_CLEAR)); + report($error) if $error; + print STDERR "=== waiting $TO seconds after clear\n"; sleep $TO; + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + report($error, true); + + return; + } + if (defined($opt_w)) { + + # WRITE + # + # readimage + # erase page + # write & verify page + + die "=== ERR writing to factory image (page 0) prohibited, use -F to force write to factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + $image = readimage($opt_f); + report(erase($sock, $pcap, $opt_p)); + report(writepage($sock, $pcap, $opt_p, $opt_f, $image), true); + + return; + } + if (defined($opt_v)) { + + # VERIFY + # + # readimage reference + # verify flash page against reference + + chkpage($opt_p); + $image = readimage($opt_f); + report(verify($sock, $pcap, $opt_p, $image), true); + + return; + } + if (defined($opt_d)) { + + # DUMP + # + # dump flash page to file + + chkpage($opt_p); + die "=== ERR no image file specified" if (!defined($opt_f)); + + open(DUMPFILE, ">$opt_f") || die "=== ERR failed to open file '$opt_f' for writing\n"; + print STDERR "=== Dumping to file '$opt_f'\n"; + $error = dump($sock, $pcap, $opt_p); + close(DUMPFILE); + report($error, true); + + return; + } + if (defined($opt_r)) { + + # RESET + # + # send reset (factory image (page 0) will start on BP) + # wait $DTO seconds + # reconfigure AP's with page 8 (factory default for AP's) + # wait $TO second + # read rsustatus and versions + + $DTO = $TO * 2; + + report(clearctl($sock, $pcap, $CMD_RESET)); + print STDERR "=== waiting $DTO seconds after reset\n"; sleep $DTO; + print STDERR "=== reconfiguring AP's with factory image (page 8)\n"; + $error = reconfig($sock, $pcap, 8); + + # don't check the error return code because we know + # it indicates failure while the reconfig is actually in progress + + print STDERR "=== waiting $TO second after AP reconfig\n"; sleep $TO; + report(version_cmd($sock, $pcap) && $error, true); + + return; + } + if (defined($opt_c)) { + + # CLEAR + # + # send clear + # wait $TO seconds + # read rsustatus and versions + + report(clearctl($sock, $pcap, $CMD_CLEAR)); + print STDERR "=== waiting $TO seconds after clear\n"; sleep $TO; + report(version_cmd($sock, $pcap), true); + + return; + } + if (defined($opt_s)) { + + # SYNC + # + # send sync + + report(clearctl($sock, $pcap, $CMD_SYNC), true); + + return; + } + if (defined($opt_P)) { + + report(ping($sock, $pcap), true); + return; + + } + if (defined($opt_V)) { + + # VERSIONS + # + # read rsustatus and versions + + report(version_cmd($sock, $pcap), true); + + return; + } + +} + +main(); diff --git a/Firmware/tools/src/rsuctl3 b/Firmware/tools/src/rsuctl3 new file mode 100755 index 0000000000000000000000000000000000000000..fad33dfeef95ed185dfe034975dacad9eb52523e --- /dev/null +++ b/Firmware/tools/src/rsuctl3 @@ -0,0 +1,969 @@ +#!/usr/bin/perl +$VERSION = q$Revision: 3.00$; + +# +# rsuctl # Remote System Update - program firmware in FPGA flash +# +# Usage: rsuctl [options] command +# +# options +# [-i ifname] # Ethernet interface name on which to communicate (default is eth1) +# -m mac-address # MAC-address of the target board (Mandatory) +# +# command (specify at least one of the following) +# [-l] # List the images currently stored in all pages of the flash +# +# [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) +# [-x -p page # Start (load & reset) new firmware from the specified page +# [-w -p page -b bp_img[.hex] -a ap_img[.hex] [-F]] # Write bp and ap image into specified page (-F forces write to page 0) +# [-v -p page -b bp_img[.hex] -a ap_img[.hex]] # Compare flash page with bp and ap image +# [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin +# +# [-r] # Reset all FPGA's (load factory image) +# [-c] # Clear all FPGA's (restart current image) +# [-s] # Send SYNC pulse to all FPGA's +# +# [-q] # Prevent checking for running RSPDriver (prevents pause at start) +# [-P] # Ping the board (find board when using broadcast address) +# [-V] # Read RSU status and RSR versions +# [-h] # This help text +# +# + +#use Socket; +use Net::RawIP; +use Getopt::Std; +use File::stat; +use Switch; +use POSIX; # for geteuid() + +$g_debug = 0; # set to 1 to enable debug output +$g_packet = ''; # global packet string +$g_error = 0; # global error status + +# +# Definitions +# +$PID_RSR = 0x01; +$PID_RSU = 0x02; + +$REG_STATUS = 0x00; +$REG_STATUS_SIZE = 200; +$REG_VERSION = 0x01; +$REG_VERSION_SIZE = 2; + +$REG_RW = 0x01; +$REG_ERASE = 0x02; +$REG_RECONFIG = 0x03; +$REG_CLEAR = 0x04; + +$CMD_ERASE = 0x01; + +$CMD_SYNC = 0x01; +$CMD_CLEAR = 0x02; +$CMD_RESET = 0x04; + +$MEPHDRSIZE = 16; + +$TYPE_READ = 0x01; +$TYPE_WRITE = 0x02; +$TYPE_READACK = 0x03; +$TYPE_WRITEACK = 0x04; + +$ADDR_BLP_NONE = 0x00; +$ADDR_RSP = 0x01; + +$FLASHSIZE = 64 * 1024 * 1024; # 64MB +$BLOCKSIZE = 1024; # block = 1024 bytes +$BLOCKS_PER_SECTOR = 128; # sector = 128 blocks +$SECTORS_PER_PAGE = 32; # page = 32 sectors +$BLOCKS_PER_PAGE = $BLOCKS_PER_SECTOR * $SECTORS_PER_PAGE; # 4096 blocks +$PAGESIZE = $SECTORS_PER_PAGE * $BLOCKS_PER_SECTOR * $BLOCKSIZE; # 4MB +$PAGES = 16; + +$PROGRESS_SIZE = 32; + +$RESPONSETIMEOUT = 2; +$TO = 5; + +# +# pack format for the MEP message +# +# C 0x10 Ethernet Type byte 0 +# C 0xFA Ethernet Type byte 1 +# C TYPE +# C STATUS +# S FRAMELENGTH +# C BLPID +# C RSP +# C PID +# C REGID +# S OFFSET +# S PAYLOAD_LENGTH +# S SEQ_NR +# S RESERVED +# +$MEPSENDFORMAT="CCCCSCCCCSSSS"; +$MEPRECVFORMAT="H28CCSCCCCSSSS"; + +# ETHLEN = max length of Ethernet payload +$ETHLEN = 1500; + +# +# Error code +# +%STATUS = ( 0 => "=== OK", + 1 => "=== ERR unknown MEP message type", + 2 => "=== ERR illegal BLP address", + 3 => "=== ERR invalid PID", + 4 => "=== ERR register does not exist", + 5 => "=== ERR offset too large", + 6 => "=== ERR message is too large", + 7 => "=== ERR message corruption during RSP processing", + 8 => "=== ERR message lost during RSP processing" +); + +# +# usage +# +sub usage +{ + print STDERR <<END; + + # Remote System Update - program firmware in FPGA flash + + Usage: rsuctl [options] command + + options + [-i ifname] # Ethernet interface name on which to communicate (default is eth1) + -m mac-address # MAC-address of the target board (Mandatory) + + command (specify at least one of the following, need -m option at least) + [-l] # List the images currently stored in all pages of the flash + + [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) + [-x -p page # Start (load & reset) new firmware from the specified page + [-w -p page -b bp_image.hex -a ap_image.hex [-F]] # Write bp and ap image into specified page (-F forces write to page 0) + [-v -p page -b bp_image.hex -a ap_image.hex] # Compare flash page with bp and ap image + [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin + + [-r] # Reset all FPGA's (load factory image) + [-c] # Clear all FPGA's (restart current image) + [-s] # Send SYNC pulse to all FPGA's + + [-q] # Prevent checking for running RSPDriver (prevents pause at start) + [-P] # Ping the board (find board when using broadcast address) + [-V] # Read RSU status and RSR versions + [-h] # This help text + +END + + exit; +} + +# +# pack_rsu($type, $regid, $size, $offset) +# +# Pack an RSU message +# +sub pack_rsu +{ + my ($type, $regid, $size, $offset) = @_; + + return pack($MEPSENDFORMAT, + 0x10, 0xFA, $type, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSU, $regid, $offset, $size, 0, 0);; +} + +# +# pcapcallback, save packet in $g_packet +# +# savepacket($handle, $hdr, $packet) +# +sub savepacket +{ + my ($handle, $hdr, $packet) = @_; + $g_packet = $packet; +} + +# +# ($payload,$error) = readresponse($pcap) +# +sub readresponse +{ + my ($pcap) = @_; + my ($retval) = 0; + + do { # keep going when we receive invalid messages + $retval = 0; # assume success + + # wait for message to arrive + eval { + use POSIX qw(SIGALRM); + POSIX::sigaction(SIGALRM, + POSIX::SigAction->new(sub { die })) + || die "=== ERR error setting SIGALRM handler: $!\n"; + + alarm $RESPONSETIMEOUT; + (0 == loop ($pcap, 1, \&savepacket, '')) + || die "=== ERR Failed to receive response"; + alarm 0; + }; + if ($@) { + $retval = 1; # fatal (timeout) + return ('', $retval, ''); + } + + ($ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved) = + unpack($MEPRECVFORMAT, $g_packet); + + if ($TYPE_WRITEACK != $type && $TYPE_READACK != $type) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid TYPE\n" if ($g_debug); + } + if ($PID_RSU != $pid && $PID_RSR != $pid) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid PID\n" if ($g_debug); + } + + printf STDERR "0x%s 0x%02x 0x%02x %5d 0x%02x 0x%02x 0x%02x 0x%02x %5d %5d %5d %5d\n", + $ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved if ($g_debug); + + if (0 == $retval && 0 != $status) { + $retval = 1; # fatal + print STDERR "=== FATAL protocol error: status=", $STATUS{$status}, "\n"; + print STDERR "=== Are you using the correct Ethernet interface? Use -i argument to change, e.g. -i eth1\n"; + exit; + } + } until ($retval != 2); + + return ('', $retval, '') if ($retval); + + return (substr($g_packet, 14 + $MEPHDRSIZE), $retval, $ethdr); +} + +# +# flash($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) +# +sub flash +{ + my ($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) = @_; + + printf STDERR "=== %s page %02d\n", $msg, $page; + print STDERR "=== |" . ("-" x $SECTORS_PER_PAGE) . "|\n=== "; + #print STDERR "Image: $image \n"; + + my ($success) = 1; + # leave last block for directory entry + my ($block); + for ($block = 0; $block < $BLOCKS_PER_PAGE - 1; $block += $blockinc) { + if ($writecb) { + $packet = pack_rsu($TYPE_WRITE, $cmd, $size, ($page * $BLOCKS_PER_PAGE) + $block); + + $packet .= &$writecb($page, $block, $image); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + + return $error if ($error); + } + + if ($readcb) { + $packet = pack_rsu($TYPE_READ, $cmd, $size, + ($page * $BLOCKS_PER_PAGE) + $block); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + return $error if ($error); + + $success = &$readcb($page, $block, $payload, $image) && $success; + } + + if (0 == $block % $BLOCKS_PER_SECTOR) { + if ($success) { print STDERR "."; } + else { print STDERR "X"; } + $success = 1; + } + } + print STDERR "\n"; + + return 0; +} + +# +# writedir($sock, $pcap, $page, $entry) +# Write the entry to the last block of page $page +# +sub writedir +{ + my ($sock, $pcap, $page, $entry) = @_; + + die "=== ERR internal error: size of direntry > $BLOCKSIZE" if (length($entry) > $BLOCKSIZE); + + print STDERR "writing dir @", (($page + 1) * $BLOCKS_PER_PAGE) - 1, "\n" if ($g_debug); + + # add zero padding if needed + $entry .= pack("C", 0) x ($BLOCKSIZE - length($entry)) if (length($entry) < $BLOCKSIZE); + + # write last block with dir entry + $packet = pack_rsu($TYPE_WRITE, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) - 1); + $packet .= $entry; + + my $error = 2; + $sock->send_eth_frame($packet); + + ($result, $error) = readresponse($pcap); + + return $error; +} + +# +# readdir($sock, $pcap, $page) +# Read the directory entry from the specified page +# +sub readdirentry +{ + my ($sock, $pcap, $page) = @_; + + my ($packet) = pack_rsu($TYPE_READ, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) -1); + + $sock->send_eth_frame($packet); + + my $error = 0; + ($entry, $error) = readresponse($pcap); + return ('', $error) if $error; + + $entry = '' if (substr($entry,0,1) eq pack("C", 0xff)); + + return ($entry, $error); +} + +# +# erasecb($page, $block, $image) +# +sub erasecb +{ + my ($page, $block, $image) = @_; + + print STDERR "erasecb: $page, $block\n" if ($g_debug); + + return pack("C", $CMD_ERASE); +} + +# +# erase($sock, $pcap, $page) +# page = -1 means all pages +# +sub erase +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, $BLOCKS_PER_SECTOR, + "erasing", $REG_ERASE, 1, + \&erasecb, 0, ''); +} + +# +# Verify the data +# +sub verifycb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "verifycb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# verify($sock, $pcap, $page, $image) +# +sub verify +{ + my ($sock, $pcap, $page, $image) = @_; + + return flash($sock, $pcap, $page, 1, + "verifying", $REG_RW, $BLOCKSIZE, + 0, \&verifycb, $image); +} + +# +# Dump the data to file +# +sub dumpcb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "dumpcb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + print DUMPFILE $payload; + + return 1; +} + +# +# dump($sock, $pcap, $page) +# +sub dump($$$) +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, 1, + "dumping", $REG_RW, $BLOCKSIZE, + 0, \&dumpcb, ''); +} + +# +# Write the data +# +sub writepagecb +{ + my ($page, $block, $image) = @_; + + print STDERR "writepagecb: page=$page, block=$block\n" if ($g_debug); + + my ($data) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $data .= pack("C", 0) x ($BLOCKSIZE - length($data)); + + return $data; +} + +# +# Read and verify the data +# +sub readpagecb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "readpagecb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# Create a direntry string based on filename +# direntry($filename) +# +sub formatentry +{ + my ($bp_filename, $ap_filename) = @_; + + $stat = stat($bp_filename) || die "=== ERR failed to stat file '$bp_filename'"; + $bp_datetime = ctime($stat->mtime); + chop($bp_datetime); + + $stat = stat($ap_filename) || die "=== ERR failed to stat file '$ap_filename'"; + $ap_datetime = ctime($stat->mtime); + chop($ap_datetime); + + # return 'size YYYY mm dd hh:mm bp_filename ; size YYYY mm dd hh:mm ap_filename' + return sprintf("%s( %s ) ; %s( %s )", $bp_filename, $bp_datetime, $ap_filename, $ap_datetime); +} + +# +# writepage($sock, $pcap, $page, $bp_filename, $ap_filename, $image) +# +sub writepage +{ + my ($sock, $pcap, $page, $bp_filename, $ap_filename, $image) = @_; + + $error = flash($sock, $pcap, $page, 1, + "writing and verifying", $REG_RW, $BLOCKSIZE, + \&writepagecb, \&readpagecb, $image); + + if (!$error) { + $error = writedir($sock, $pcap, $page, formatentry($bp_filename, $ap_filename)); + } else { + # clear entry, an error has occured + writedir($sock, $pcap, $page, ''); + } + return $error; +} + + +# +# Read images from the hex files. +# then the files are converted from hex to bin format. +# +# readhexfiles($filename_bp, &filename_ap ) +# +sub readhexfiles +{ + my ($filename_bp, $filename_ap) = @_; + local $/ = undef; # read entire file + + # open input and output files + die "=== ERR no image file specified" if ((!defined($filename_bp)) or (!defined($filename_ap))); + + open(IMAGEFILE, $filename_bp) || die "=== ERR failed to open file '$filename_bp'"; + print STDERR "=== Reading '$filename_bp'\n"; + $data = <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + open(IMAGEFILE, $filename_ap) || die "=== ERR failed to open file '$filename_ap'"; + print STDERR "=== Reading '$filename_ap'\n"; + $data .= <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + print STDERR "=== Converting '$filename_bp' and '$filename_ap' to binary\n"; + print STDERR "=== |" . ("-" x $PROGRESS_SIZE) . "|\n=== "; + + $i = 0; + $interval = length($data) / ($PROGRESS_SIZE * 2); + $interval = 1 if ($interval < .5); + + $bindata = ''; + + for ($byte = 0; $byte < length($data); $byte += 2) { + $bytestr = substr($data, $byte, 2); + + $bindata .= pack("C", hex($bytestr)); + + # print progress bar + $i++; + print STDERR "." if (0 == $i % $interval); + } + + # append FF's to fill buffer to $IMAGESIZE - 1 + $bindata .= pack("C", 255) x (($PAGESIZE - $BLOCKSIZE) - length($bindata)); + + print STDERR "\n"; + + return $bindata; +} + +# +# Read RSU status from RSR status register +# readrsustatus($sock, $pcap) +# +sub readrsustatus +{ + my ($sock, $pcap) = @_; + + $size = $REG_STATUS_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_STATUS, 0, $size, 0, 0); + + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + $rsustatus = unpack("C", substr($result, 164, 1)); + + print STDERR sprintf("\nRSU Status:\n Rdy : %s\n FpgaType : %s\n ImageType: %s\n Trig : %s\n\n", + ('Configuration ongoing', 'Configuration done')[$rsustatus & 1], + ('loaded from flash', 'loaded via JTAG')[($rsustatus >> 2) & 1], + ('Factory image is running', 'User image is running')[($rsustatus >> 3) & 1], + ('Reconfiguration due to button reset' + , 'Unknown' + , 'Reconfiguration due to over temperature' + , 'Unknown' + , 'Reconfiguration due to user reset' + , 'Unknown' + , 'Reconfiguration due to watchdog reset' )[($rsustatus >> 4) & 7]); + + return $error; +} + +# +# Read versions of all FPGA's (handy after reconfig, reset, clear, etc) +# readversions($sock, $pcap) +# +sub readversions +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "RSP version: $id\nBP version: $maj.$min\n"; + + # read AP versions + for ($ap = 0; $ap < 4; $ap++) { + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + 1 << $ap, 0, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "AP[$id] version: $maj.$min\n"; + } + + return 0; +} + +# +# Find MAC addresses of active boards +# ping($sock, $pcap) +# +sub ping +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + + while (!$error) { + ($result, $error, $ethdr) = readresponse($pcap); + $mac = substr($ethdr, 12, 12); + $mac =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/; + print STDERR "Found RSP board on MAC: ", $mac, "\n" if (!$error); + } + + return 0; +} + +# +# Reconfigure. Load new firmware from flash into FPGA's. After loading +# has finished, the FPGA will be reset and restart with new firmware. +# +# reconfig($sock, $pcap, $page) +# +sub reconfig +{ + my ($sock, $pcap, $page) = @_; + + $cmd = $page & 15; # use bit 0..3 + $cmd = $cmd | 128; + $packet = pack_rsu($TYPE_WRITE, $REG_RECONFIG, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + + # there will be no response + # ($response, $error) = readresponse($pcap); + + return 0; +} + +# +# Reset control for all FPGA's +# +# clearctl($sock, $pcap, $cmd) +# valid values for $cmd: $CMD_SYNC, $CMD_CLEAR, $CMD_RESET +# +sub clearctl +{ + my ($sock, $pcap, $cmd) = @_; + + $packet = pack_rsu($TYPE_WRITE, $REG_CLEAR, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + ($response, $error) = readresponse($pcap); + + return $error; +} + +# +# Report result +# report($error, $report_ok = false) +# +sub report +{ + my ($error, $report_ok) = @_; + + if ($error || $g_error) { + print STDERR "=== FAILED\n"; + exit 1; + } + else { + print STDERR "=== OK\n" if ($report_ok); + } +} + +# +# Change range of the page parameter +# chkpage($page) +# +sub chkpage +{ + my ($page) = @_; + + die "=== ERR no flash page specified, use -p" if (!defined($page)); + if ($page < 0 || $page >= $PAGES) { + die "=== ERR invalid page index $page, should be >= 0 and < $PAGES"; + } +} + +# +# Get rsu status and versions +# version_cmd($sock, $pcap) +# +sub version_cmd +{ + my ($sock, $pcap) = @_; + + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + return $error; +} + +sub main +{ + my $sock = 0; + + print STDERR "=== rsuctl ($VERSION), for RSP3 boards only\n"; + + Getopt::Std::getopts("i:m:lexwvb:a:dp:f:FrcsPVhq"); + + # check that at least one argument is present + usage() if ($opt_h); + + if (!defined($opt_m)) { + print STDERR "=== ERR Must specify MAC address using -m option"; + usage(); + } + + usage() unless ($opt_l || $opt_e || $opt_x || $opt_w || $opt_v || + $opt_d || $opt_r || $opt_c || $opt_s || $opt_P || $opt_V || $opt_h); + + # assign default values + $opt_i = 'eth1' if !defined($opt_i); + + # Create socket to the boards + die "=== ERR this program needs to run as root to send/recv raw Ethernet frames" if (0 != geteuid()); + + $sock = new Net::RawIP; + $sock->ethnew($opt_i); + $sock->ethset(dest => $opt_m); + + $TIMEOUT=5; + $pcap = $sock->pcapinit($opt_i, "not ether dst $opt_m", $ETHLEN, $TIMEOUT); + + die "=== ERR could not open raw socket to RSP board(s) on $opt_i:$opt_m: $!\n" unless $sock; + + print STDERR "=== connected to RSP board(s) on $opt_i:$opt_m\n"; + + if (!defined($opt_q)) { + print STDERR "=== checking for running RSPDriver\n"; + ($payload, $error) = readresponse($pcap); + die "=== RSPDriver appears to be running, stop it first\n" if ($payload ne ''); + } + + if (defined($opt_l)) { + + # LIST IMAGES IN FLASH + # + # readdirentry for all pages + + print STDERR "=== listing flash contents\n\n"; + for ($page = 0; $page < $PAGES; $page++) { + ($entry, $error) = readdirentry($sock, $pcap, $page); + break if $error; + print STDERR sprintf("%s %2d: %s\n", + (0 == $page ? 'Factory':'Image '), $page, $entry) if ((0 == $page) || $entry ne ''); + } + print STDERR "\n"; + report($error, true); + + return; + } + + if (defined($opt_e)) { + + # ERASE + # + # erase flash page + + die "=== ERR erasing factory image (page 0) prohibited, use -F to force erase of factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + report(erase($sock, $pcap, $opt_p), true); + + return; + } + + if (defined($opt_x)) { + + # RECONFIG + # + # reconfig BP and AP + # wait $TO seconds + # clear + # wait $TO seconds + # read rsustatus & version + + chkpage($opt_p); + report(reconfig($sock, $pcap, $opt_p)); + report(clearctl($sock, $pcap, $CMD_RESET)); + report($error) if $error; + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + report($error, true); + + return; + } + + if (defined($opt_w)) { + + # WRITE + # + # readimage + # erase page + # write & verify page + + die "=== ERR writing to factory image (page 0) prohibited, use -F to force write to factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + $image = readhexfiles($opt_b, $opt_a); + report(erase($sock, $pcap, $opt_p)); + report(writepage($sock, $pcap, $opt_p, $opt_b, $opt_a, $image), true); + + return; + } + + if (defined($opt_v)) { + + # VERIFY + # + # readimage reference + # verify flash page against reference + + chkpage($opt_p); + $image = readhexfiles($opt_b, $opt_a); + report(verify($sock, $pcap, $opt_p, $image), true); + + return; + } + + if (defined($opt_d)) { + + # DUMP + # + # dump flash page to file + + chkpage($opt_p); + die "=== ERR no image file specified" if (!defined($opt_f)); + + open(DUMPFILE, ">$opt_f") || die "=== ERR failed to open file '$opt_f' for writing\n"; + print STDERR "=== Dumping to file '$opt_f'\n"; + $error = dump($sock, $pcap, $opt_p); + close(DUMPFILE); + report($error, true); + + return; + } + + if (defined($opt_r)) { + + # RESET + # + # send reset (factory image (page 0) will start on BP) + # wait $DTO seconds + # reconfigure AP's with page 8 (factory default for AP's) + # wait $TO second + # read rsustatus and versions + + report(reconfig($sock, $pcap, 0)); # set image 0 (factory image) + report(clearctl($sock, $pcap, $CMD_RESET)); + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + print STDERR "=== reconfiguring BP and AP's with factory image\n"; + + # don't check the error return code because we know + # it indicates failure while the reconfig is actually in progress + + report(version_cmd($sock, $pcap) && $error, true); + + return; + } + + if (defined($opt_c)) { + + # CLEAR + # + # send clear + # wait $TO seconds + # read rsustatus and versions + + report(clearctl($sock, $pcap, $CMD_CLEAR)); + print STDERR "=== waiting $TO seconds after clear\n"; sleep $TO; + report(version_cmd($sock, $pcap), true); + + return; + } + + if (defined($opt_s)) { + + # SYNC + # + # send sync + + report(clearctl($sock, $pcap, $CMD_SYNC), true); + + return; + } + + if (defined($opt_P)) { + report(ping($sock, $pcap), true); + return; + } + + if (defined($opt_V)) { + # versions + # + # read rsustatus and versions + + report(version_cmd($sock, $pcap), true); + + return; + } +} + +main(); \ No newline at end of file diff --git a/Firmware/tools/src/rsuctl3.pl b/Firmware/tools/src/rsuctl3.pl new file mode 100644 index 0000000000000000000000000000000000000000..fad33dfeef95ed185dfe034975dacad9eb52523e --- /dev/null +++ b/Firmware/tools/src/rsuctl3.pl @@ -0,0 +1,969 @@ +#!/usr/bin/perl +$VERSION = q$Revision: 3.00$; + +# +# rsuctl # Remote System Update - program firmware in FPGA flash +# +# Usage: rsuctl [options] command +# +# options +# [-i ifname] # Ethernet interface name on which to communicate (default is eth1) +# -m mac-address # MAC-address of the target board (Mandatory) +# +# command (specify at least one of the following) +# [-l] # List the images currently stored in all pages of the flash +# +# [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) +# [-x -p page # Start (load & reset) new firmware from the specified page +# [-w -p page -b bp_img[.hex] -a ap_img[.hex] [-F]] # Write bp and ap image into specified page (-F forces write to page 0) +# [-v -p page -b bp_img[.hex] -a ap_img[.hex]] # Compare flash page with bp and ap image +# [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin +# +# [-r] # Reset all FPGA's (load factory image) +# [-c] # Clear all FPGA's (restart current image) +# [-s] # Send SYNC pulse to all FPGA's +# +# [-q] # Prevent checking for running RSPDriver (prevents pause at start) +# [-P] # Ping the board (find board when using broadcast address) +# [-V] # Read RSU status and RSR versions +# [-h] # This help text +# +# + +#use Socket; +use Net::RawIP; +use Getopt::Std; +use File::stat; +use Switch; +use POSIX; # for geteuid() + +$g_debug = 0; # set to 1 to enable debug output +$g_packet = ''; # global packet string +$g_error = 0; # global error status + +# +# Definitions +# +$PID_RSR = 0x01; +$PID_RSU = 0x02; + +$REG_STATUS = 0x00; +$REG_STATUS_SIZE = 200; +$REG_VERSION = 0x01; +$REG_VERSION_SIZE = 2; + +$REG_RW = 0x01; +$REG_ERASE = 0x02; +$REG_RECONFIG = 0x03; +$REG_CLEAR = 0x04; + +$CMD_ERASE = 0x01; + +$CMD_SYNC = 0x01; +$CMD_CLEAR = 0x02; +$CMD_RESET = 0x04; + +$MEPHDRSIZE = 16; + +$TYPE_READ = 0x01; +$TYPE_WRITE = 0x02; +$TYPE_READACK = 0x03; +$TYPE_WRITEACK = 0x04; + +$ADDR_BLP_NONE = 0x00; +$ADDR_RSP = 0x01; + +$FLASHSIZE = 64 * 1024 * 1024; # 64MB +$BLOCKSIZE = 1024; # block = 1024 bytes +$BLOCKS_PER_SECTOR = 128; # sector = 128 blocks +$SECTORS_PER_PAGE = 32; # page = 32 sectors +$BLOCKS_PER_PAGE = $BLOCKS_PER_SECTOR * $SECTORS_PER_PAGE; # 4096 blocks +$PAGESIZE = $SECTORS_PER_PAGE * $BLOCKS_PER_SECTOR * $BLOCKSIZE; # 4MB +$PAGES = 16; + +$PROGRESS_SIZE = 32; + +$RESPONSETIMEOUT = 2; +$TO = 5; + +# +# pack format for the MEP message +# +# C 0x10 Ethernet Type byte 0 +# C 0xFA Ethernet Type byte 1 +# C TYPE +# C STATUS +# S FRAMELENGTH +# C BLPID +# C RSP +# C PID +# C REGID +# S OFFSET +# S PAYLOAD_LENGTH +# S SEQ_NR +# S RESERVED +# +$MEPSENDFORMAT="CCCCSCCCCSSSS"; +$MEPRECVFORMAT="H28CCSCCCCSSSS"; + +# ETHLEN = max length of Ethernet payload +$ETHLEN = 1500; + +# +# Error code +# +%STATUS = ( 0 => "=== OK", + 1 => "=== ERR unknown MEP message type", + 2 => "=== ERR illegal BLP address", + 3 => "=== ERR invalid PID", + 4 => "=== ERR register does not exist", + 5 => "=== ERR offset too large", + 6 => "=== ERR message is too large", + 7 => "=== ERR message corruption during RSP processing", + 8 => "=== ERR message lost during RSP processing" +); + +# +# usage +# +sub usage +{ + print STDERR <<END; + + # Remote System Update - program firmware in FPGA flash + + Usage: rsuctl [options] command + + options + [-i ifname] # Ethernet interface name on which to communicate (default is eth1) + -m mac-address # MAC-address of the target board (Mandatory) + + command (specify at least one of the following, need -m option at least) + [-l] # List the images currently stored in all pages of the flash + + [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) + [-x -p page # Start (load & reset) new firmware from the specified page + [-w -p page -b bp_image.hex -a ap_image.hex [-F]] # Write bp and ap image into specified page (-F forces write to page 0) + [-v -p page -b bp_image.hex -a ap_image.hex] # Compare flash page with bp and ap image + [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin + + [-r] # Reset all FPGA's (load factory image) + [-c] # Clear all FPGA's (restart current image) + [-s] # Send SYNC pulse to all FPGA's + + [-q] # Prevent checking for running RSPDriver (prevents pause at start) + [-P] # Ping the board (find board when using broadcast address) + [-V] # Read RSU status and RSR versions + [-h] # This help text + +END + + exit; +} + +# +# pack_rsu($type, $regid, $size, $offset) +# +# Pack an RSU message +# +sub pack_rsu +{ + my ($type, $regid, $size, $offset) = @_; + + return pack($MEPSENDFORMAT, + 0x10, 0xFA, $type, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSU, $regid, $offset, $size, 0, 0);; +} + +# +# pcapcallback, save packet in $g_packet +# +# savepacket($handle, $hdr, $packet) +# +sub savepacket +{ + my ($handle, $hdr, $packet) = @_; + $g_packet = $packet; +} + +# +# ($payload,$error) = readresponse($pcap) +# +sub readresponse +{ + my ($pcap) = @_; + my ($retval) = 0; + + do { # keep going when we receive invalid messages + $retval = 0; # assume success + + # wait for message to arrive + eval { + use POSIX qw(SIGALRM); + POSIX::sigaction(SIGALRM, + POSIX::SigAction->new(sub { die })) + || die "=== ERR error setting SIGALRM handler: $!\n"; + + alarm $RESPONSETIMEOUT; + (0 == loop ($pcap, 1, \&savepacket, '')) + || die "=== ERR Failed to receive response"; + alarm 0; + }; + if ($@) { + $retval = 1; # fatal (timeout) + return ('', $retval, ''); + } + + ($ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved) = + unpack($MEPRECVFORMAT, $g_packet); + + if ($TYPE_WRITEACK != $type && $TYPE_READACK != $type) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid TYPE\n" if ($g_debug); + } + if ($PID_RSU != $pid && $PID_RSR != $pid) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid PID\n" if ($g_debug); + } + + printf STDERR "0x%s 0x%02x 0x%02x %5d 0x%02x 0x%02x 0x%02x 0x%02x %5d %5d %5d %5d\n", + $ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved if ($g_debug); + + if (0 == $retval && 0 != $status) { + $retval = 1; # fatal + print STDERR "=== FATAL protocol error: status=", $STATUS{$status}, "\n"; + print STDERR "=== Are you using the correct Ethernet interface? Use -i argument to change, e.g. -i eth1\n"; + exit; + } + } until ($retval != 2); + + return ('', $retval, '') if ($retval); + + return (substr($g_packet, 14 + $MEPHDRSIZE), $retval, $ethdr); +} + +# +# flash($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) +# +sub flash +{ + my ($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) = @_; + + printf STDERR "=== %s page %02d\n", $msg, $page; + print STDERR "=== |" . ("-" x $SECTORS_PER_PAGE) . "|\n=== "; + #print STDERR "Image: $image \n"; + + my ($success) = 1; + # leave last block for directory entry + my ($block); + for ($block = 0; $block < $BLOCKS_PER_PAGE - 1; $block += $blockinc) { + if ($writecb) { + $packet = pack_rsu($TYPE_WRITE, $cmd, $size, ($page * $BLOCKS_PER_PAGE) + $block); + + $packet .= &$writecb($page, $block, $image); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + + return $error if ($error); + } + + if ($readcb) { + $packet = pack_rsu($TYPE_READ, $cmd, $size, + ($page * $BLOCKS_PER_PAGE) + $block); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + return $error if ($error); + + $success = &$readcb($page, $block, $payload, $image) && $success; + } + + if (0 == $block % $BLOCKS_PER_SECTOR) { + if ($success) { print STDERR "."; } + else { print STDERR "X"; } + $success = 1; + } + } + print STDERR "\n"; + + return 0; +} + +# +# writedir($sock, $pcap, $page, $entry) +# Write the entry to the last block of page $page +# +sub writedir +{ + my ($sock, $pcap, $page, $entry) = @_; + + die "=== ERR internal error: size of direntry > $BLOCKSIZE" if (length($entry) > $BLOCKSIZE); + + print STDERR "writing dir @", (($page + 1) * $BLOCKS_PER_PAGE) - 1, "\n" if ($g_debug); + + # add zero padding if needed + $entry .= pack("C", 0) x ($BLOCKSIZE - length($entry)) if (length($entry) < $BLOCKSIZE); + + # write last block with dir entry + $packet = pack_rsu($TYPE_WRITE, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) - 1); + $packet .= $entry; + + my $error = 2; + $sock->send_eth_frame($packet); + + ($result, $error) = readresponse($pcap); + + return $error; +} + +# +# readdir($sock, $pcap, $page) +# Read the directory entry from the specified page +# +sub readdirentry +{ + my ($sock, $pcap, $page) = @_; + + my ($packet) = pack_rsu($TYPE_READ, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) -1); + + $sock->send_eth_frame($packet); + + my $error = 0; + ($entry, $error) = readresponse($pcap); + return ('', $error) if $error; + + $entry = '' if (substr($entry,0,1) eq pack("C", 0xff)); + + return ($entry, $error); +} + +# +# erasecb($page, $block, $image) +# +sub erasecb +{ + my ($page, $block, $image) = @_; + + print STDERR "erasecb: $page, $block\n" if ($g_debug); + + return pack("C", $CMD_ERASE); +} + +# +# erase($sock, $pcap, $page) +# page = -1 means all pages +# +sub erase +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, $BLOCKS_PER_SECTOR, + "erasing", $REG_ERASE, 1, + \&erasecb, 0, ''); +} + +# +# Verify the data +# +sub verifycb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "verifycb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# verify($sock, $pcap, $page, $image) +# +sub verify +{ + my ($sock, $pcap, $page, $image) = @_; + + return flash($sock, $pcap, $page, 1, + "verifying", $REG_RW, $BLOCKSIZE, + 0, \&verifycb, $image); +} + +# +# Dump the data to file +# +sub dumpcb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "dumpcb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + print DUMPFILE $payload; + + return 1; +} + +# +# dump($sock, $pcap, $page) +# +sub dump($$$) +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, 1, + "dumping", $REG_RW, $BLOCKSIZE, + 0, \&dumpcb, ''); +} + +# +# Write the data +# +sub writepagecb +{ + my ($page, $block, $image) = @_; + + print STDERR "writepagecb: page=$page, block=$block\n" if ($g_debug); + + my ($data) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $data .= pack("C", 0) x ($BLOCKSIZE - length($data)); + + return $data; +} + +# +# Read and verify the data +# +sub readpagecb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "readpagecb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# Create a direntry string based on filename +# direntry($filename) +# +sub formatentry +{ + my ($bp_filename, $ap_filename) = @_; + + $stat = stat($bp_filename) || die "=== ERR failed to stat file '$bp_filename'"; + $bp_datetime = ctime($stat->mtime); + chop($bp_datetime); + + $stat = stat($ap_filename) || die "=== ERR failed to stat file '$ap_filename'"; + $ap_datetime = ctime($stat->mtime); + chop($ap_datetime); + + # return 'size YYYY mm dd hh:mm bp_filename ; size YYYY mm dd hh:mm ap_filename' + return sprintf("%s( %s ) ; %s( %s )", $bp_filename, $bp_datetime, $ap_filename, $ap_datetime); +} + +# +# writepage($sock, $pcap, $page, $bp_filename, $ap_filename, $image) +# +sub writepage +{ + my ($sock, $pcap, $page, $bp_filename, $ap_filename, $image) = @_; + + $error = flash($sock, $pcap, $page, 1, + "writing and verifying", $REG_RW, $BLOCKSIZE, + \&writepagecb, \&readpagecb, $image); + + if (!$error) { + $error = writedir($sock, $pcap, $page, formatentry($bp_filename, $ap_filename)); + } else { + # clear entry, an error has occured + writedir($sock, $pcap, $page, ''); + } + return $error; +} + + +# +# Read images from the hex files. +# then the files are converted from hex to bin format. +# +# readhexfiles($filename_bp, &filename_ap ) +# +sub readhexfiles +{ + my ($filename_bp, $filename_ap) = @_; + local $/ = undef; # read entire file + + # open input and output files + die "=== ERR no image file specified" if ((!defined($filename_bp)) or (!defined($filename_ap))); + + open(IMAGEFILE, $filename_bp) || die "=== ERR failed to open file '$filename_bp'"; + print STDERR "=== Reading '$filename_bp'\n"; + $data = <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + open(IMAGEFILE, $filename_ap) || die "=== ERR failed to open file '$filename_ap'"; + print STDERR "=== Reading '$filename_ap'\n"; + $data .= <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + print STDERR "=== Converting '$filename_bp' and '$filename_ap' to binary\n"; + print STDERR "=== |" . ("-" x $PROGRESS_SIZE) . "|\n=== "; + + $i = 0; + $interval = length($data) / ($PROGRESS_SIZE * 2); + $interval = 1 if ($interval < .5); + + $bindata = ''; + + for ($byte = 0; $byte < length($data); $byte += 2) { + $bytestr = substr($data, $byte, 2); + + $bindata .= pack("C", hex($bytestr)); + + # print progress bar + $i++; + print STDERR "." if (0 == $i % $interval); + } + + # append FF's to fill buffer to $IMAGESIZE - 1 + $bindata .= pack("C", 255) x (($PAGESIZE - $BLOCKSIZE) - length($bindata)); + + print STDERR "\n"; + + return $bindata; +} + +# +# Read RSU status from RSR status register +# readrsustatus($sock, $pcap) +# +sub readrsustatus +{ + my ($sock, $pcap) = @_; + + $size = $REG_STATUS_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_STATUS, 0, $size, 0, 0); + + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + $rsustatus = unpack("C", substr($result, 164, 1)); + + print STDERR sprintf("\nRSU Status:\n Rdy : %s\n FpgaType : %s\n ImageType: %s\n Trig : %s\n\n", + ('Configuration ongoing', 'Configuration done')[$rsustatus & 1], + ('loaded from flash', 'loaded via JTAG')[($rsustatus >> 2) & 1], + ('Factory image is running', 'User image is running')[($rsustatus >> 3) & 1], + ('Reconfiguration due to button reset' + , 'Unknown' + , 'Reconfiguration due to over temperature' + , 'Unknown' + , 'Reconfiguration due to user reset' + , 'Unknown' + , 'Reconfiguration due to watchdog reset' )[($rsustatus >> 4) & 7]); + + return $error; +} + +# +# Read versions of all FPGA's (handy after reconfig, reset, clear, etc) +# readversions($sock, $pcap) +# +sub readversions +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "RSP version: $id\nBP version: $maj.$min\n"; + + # read AP versions + for ($ap = 0; $ap < 4; $ap++) { + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + 1 << $ap, 0, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "AP[$id] version: $maj.$min\n"; + } + + return 0; +} + +# +# Find MAC addresses of active boards +# ping($sock, $pcap) +# +sub ping +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + + while (!$error) { + ($result, $error, $ethdr) = readresponse($pcap); + $mac = substr($ethdr, 12, 12); + $mac =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/; + print STDERR "Found RSP board on MAC: ", $mac, "\n" if (!$error); + } + + return 0; +} + +# +# Reconfigure. Load new firmware from flash into FPGA's. After loading +# has finished, the FPGA will be reset and restart with new firmware. +# +# reconfig($sock, $pcap, $page) +# +sub reconfig +{ + my ($sock, $pcap, $page) = @_; + + $cmd = $page & 15; # use bit 0..3 + $cmd = $cmd | 128; + $packet = pack_rsu($TYPE_WRITE, $REG_RECONFIG, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + + # there will be no response + # ($response, $error) = readresponse($pcap); + + return 0; +} + +# +# Reset control for all FPGA's +# +# clearctl($sock, $pcap, $cmd) +# valid values for $cmd: $CMD_SYNC, $CMD_CLEAR, $CMD_RESET +# +sub clearctl +{ + my ($sock, $pcap, $cmd) = @_; + + $packet = pack_rsu($TYPE_WRITE, $REG_CLEAR, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + ($response, $error) = readresponse($pcap); + + return $error; +} + +# +# Report result +# report($error, $report_ok = false) +# +sub report +{ + my ($error, $report_ok) = @_; + + if ($error || $g_error) { + print STDERR "=== FAILED\n"; + exit 1; + } + else { + print STDERR "=== OK\n" if ($report_ok); + } +} + +# +# Change range of the page parameter +# chkpage($page) +# +sub chkpage +{ + my ($page) = @_; + + die "=== ERR no flash page specified, use -p" if (!defined($page)); + if ($page < 0 || $page >= $PAGES) { + die "=== ERR invalid page index $page, should be >= 0 and < $PAGES"; + } +} + +# +# Get rsu status and versions +# version_cmd($sock, $pcap) +# +sub version_cmd +{ + my ($sock, $pcap) = @_; + + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + return $error; +} + +sub main +{ + my $sock = 0; + + print STDERR "=== rsuctl ($VERSION), for RSP3 boards only\n"; + + Getopt::Std::getopts("i:m:lexwvb:a:dp:f:FrcsPVhq"); + + # check that at least one argument is present + usage() if ($opt_h); + + if (!defined($opt_m)) { + print STDERR "=== ERR Must specify MAC address using -m option"; + usage(); + } + + usage() unless ($opt_l || $opt_e || $opt_x || $opt_w || $opt_v || + $opt_d || $opt_r || $opt_c || $opt_s || $opt_P || $opt_V || $opt_h); + + # assign default values + $opt_i = 'eth1' if !defined($opt_i); + + # Create socket to the boards + die "=== ERR this program needs to run as root to send/recv raw Ethernet frames" if (0 != geteuid()); + + $sock = new Net::RawIP; + $sock->ethnew($opt_i); + $sock->ethset(dest => $opt_m); + + $TIMEOUT=5; + $pcap = $sock->pcapinit($opt_i, "not ether dst $opt_m", $ETHLEN, $TIMEOUT); + + die "=== ERR could not open raw socket to RSP board(s) on $opt_i:$opt_m: $!\n" unless $sock; + + print STDERR "=== connected to RSP board(s) on $opt_i:$opt_m\n"; + + if (!defined($opt_q)) { + print STDERR "=== checking for running RSPDriver\n"; + ($payload, $error) = readresponse($pcap); + die "=== RSPDriver appears to be running, stop it first\n" if ($payload ne ''); + } + + if (defined($opt_l)) { + + # LIST IMAGES IN FLASH + # + # readdirentry for all pages + + print STDERR "=== listing flash contents\n\n"; + for ($page = 0; $page < $PAGES; $page++) { + ($entry, $error) = readdirentry($sock, $pcap, $page); + break if $error; + print STDERR sprintf("%s %2d: %s\n", + (0 == $page ? 'Factory':'Image '), $page, $entry) if ((0 == $page) || $entry ne ''); + } + print STDERR "\n"; + report($error, true); + + return; + } + + if (defined($opt_e)) { + + # ERASE + # + # erase flash page + + die "=== ERR erasing factory image (page 0) prohibited, use -F to force erase of factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + report(erase($sock, $pcap, $opt_p), true); + + return; + } + + if (defined($opt_x)) { + + # RECONFIG + # + # reconfig BP and AP + # wait $TO seconds + # clear + # wait $TO seconds + # read rsustatus & version + + chkpage($opt_p); + report(reconfig($sock, $pcap, $opt_p)); + report(clearctl($sock, $pcap, $CMD_RESET)); + report($error) if $error; + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + report($error, true); + + return; + } + + if (defined($opt_w)) { + + # WRITE + # + # readimage + # erase page + # write & verify page + + die "=== ERR writing to factory image (page 0) prohibited, use -F to force write to factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + $image = readhexfiles($opt_b, $opt_a); + report(erase($sock, $pcap, $opt_p)); + report(writepage($sock, $pcap, $opt_p, $opt_b, $opt_a, $image), true); + + return; + } + + if (defined($opt_v)) { + + # VERIFY + # + # readimage reference + # verify flash page against reference + + chkpage($opt_p); + $image = readhexfiles($opt_b, $opt_a); + report(verify($sock, $pcap, $opt_p, $image), true); + + return; + } + + if (defined($opt_d)) { + + # DUMP + # + # dump flash page to file + + chkpage($opt_p); + die "=== ERR no image file specified" if (!defined($opt_f)); + + open(DUMPFILE, ">$opt_f") || die "=== ERR failed to open file '$opt_f' for writing\n"; + print STDERR "=== Dumping to file '$opt_f'\n"; + $error = dump($sock, $pcap, $opt_p); + close(DUMPFILE); + report($error, true); + + return; + } + + if (defined($opt_r)) { + + # RESET + # + # send reset (factory image (page 0) will start on BP) + # wait $DTO seconds + # reconfigure AP's with page 8 (factory default for AP's) + # wait $TO second + # read rsustatus and versions + + report(reconfig($sock, $pcap, 0)); # set image 0 (factory image) + report(clearctl($sock, $pcap, $CMD_RESET)); + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + print STDERR "=== reconfiguring BP and AP's with factory image\n"; + + # don't check the error return code because we know + # it indicates failure while the reconfig is actually in progress + + report(version_cmd($sock, $pcap) && $error, true); + + return; + } + + if (defined($opt_c)) { + + # CLEAR + # + # send clear + # wait $TO seconds + # read rsustatus and versions + + report(clearctl($sock, $pcap, $CMD_CLEAR)); + print STDERR "=== waiting $TO seconds after clear\n"; sleep $TO; + report(version_cmd($sock, $pcap), true); + + return; + } + + if (defined($opt_s)) { + + # SYNC + # + # send sync + + report(clearctl($sock, $pcap, $CMD_SYNC), true); + + return; + } + + if (defined($opt_P)) { + report(ping($sock, $pcap), true); + return; + } + + if (defined($opt_V)) { + # versions + # + # read rsustatus and versions + + report(version_cmd($sock, $pcap), true); + + return; + } +} + +main(); \ No newline at end of file diff --git a/Firmware/tools/src/rsuctl3_reset b/Firmware/tools/src/rsuctl3_reset new file mode 100755 index 0000000000000000000000000000000000000000..cd65665acd130682a9cd069863ac35764fd336f1 --- /dev/null +++ b/Firmware/tools/src/rsuctl3_reset @@ -0,0 +1,970 @@ +#!/usr/bin/perl +$VERSION = q$Revision: 3.00$; + +# +# rsuctl # Remote System Update - program firmware in FPGA flash +# +# Usage: rsuctl [options] command +# +# options +# [-i ifname] # Ethernet interface name on which to communicate (default is eth1) +# -m mac-address # MAC-address of the target board (Mandatory) +# +# command (specify at least one of the following) +# [-l] # List the images currently stored in all pages of the flash +# +# [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) +# [-x -p page # Start (load & reset) new firmware from the specified page +# [-w -p page -b bp_img[.hex] -a ap_img[.hex] [-F]] # Write bp and ap image into specified page (-F forces write to page 0) +# [-v -p page -b bp_img[.hex] -a ap_img[.hex]] # Compare flash page with bp and ap image +# [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin +# +# [-r] # Reset all FPGA's (load factory image) +# [-c] # Clear all FPGA's (restart current image) +# [-s] # Send SYNC pulse to all FPGA's +# +# [-q] # Prevent checking for running RSPDriver (prevents pause at start) +# [-P] # Ping the board (find board when using broadcast address) +# [-V] # Read RSU status and RSR versions +# [-h] # This help text +# +# + +#use Socket; +use Net::RawIP; +use Getopt::Std; +use File::stat; +use Switch; +use POSIX; # for geteuid() + +$g_debug = 0; # set to 1 to enable debug output +$g_packet = ''; # global packet string +$g_error = 0; # global error status + +# +# Definitions +# +$PID_RSR = 0x01; +$PID_RSU = 0x02; + +$REG_STATUS = 0x00; +$REG_STATUS_SIZE = 200; +$REG_VERSION = 0x01; +$REG_VERSION_SIZE = 2; + +$REG_RW = 0x01; +$REG_ERASE = 0x02; +$REG_RECONFIG = 0x03; +$REG_CLEAR = 0x04; + +$CMD_ERASE = 0x01; + +$CMD_SYNC = 0x01; +$CMD_CLEAR = 0x02; +$CMD_RESET = 0x04; + +$MEPHDRSIZE = 16; + +$TYPE_READ = 0x01; +$TYPE_WRITE = 0x02; +$TYPE_READACK = 0x03; +$TYPE_WRITEACK = 0x04; + +$ADDR_BLP_NONE = 0x00; +$ADDR_RSP = 0x01; + +$FLASHSIZE = 64 * 1024 * 1024; # 64MB +$BLOCKSIZE = 1024; # block = 1024 bytes +$BLOCKS_PER_SECTOR = 128; # sector = 128 blocks +$SECTORS_PER_PAGE = 32; # page = 32 sectors +$BLOCKS_PER_PAGE = $BLOCKS_PER_SECTOR * $SECTORS_PER_PAGE; # 4096 blocks +$PAGESIZE = $SECTORS_PER_PAGE * $BLOCKS_PER_SECTOR * $BLOCKSIZE; # 4MB +$PAGES = 16; + +$PROGRESS_SIZE = 32; + +$RESPONSETIMEOUT = 2; +$TO = 5; + +# +# pack format for the MEP message +# +# C 0x10 Ethernet Type byte 0 +# C 0xFA Ethernet Type byte 1 +# C TYPE +# C STATUS +# S FRAMELENGTH +# C BLPID +# C RSP +# C PID +# C REGID +# S OFFSET +# S PAYLOAD_LENGTH +# S SEQ_NR +# S RESERVED +# +$MEPSENDFORMAT="CCCCSCCCCSSSS"; +$MEPRECVFORMAT="H28CCSCCCCSSSS"; + +# ETHLEN = max length of Ethernet payload +$ETHLEN = 1500; + +# +# Error code +# +%STATUS = ( 0 => "=== OK", + 1 => "=== ERR unknown MEP message type", + 2 => "=== ERR illegal BLP address", + 3 => "=== ERR invalid PID", + 4 => "=== ERR register does not exist", + 5 => "=== ERR offset too large", + 6 => "=== ERR message is too large", + 7 => "=== ERR message corruption during RSP processing", + 8 => "=== ERR message lost during RSP processing" +); + +# +# usage +# +sub usage +{ + print STDERR <<END; + + # Remote System Update - program firmware in FPGA flash + + Usage: rsuctl [options] command + + options + [-i ifname] # Ethernet interface name on which to communicate (default is eth1) + -m mac-address # MAC-address of the target board (Mandatory) + + command (specify at least one of the following, need -m option at least) + [-l] # List the images currently stored in all pages of the flash + + [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) + [-x -p page # Start (load & reset) new firmware from the specified page + [-w -p page -b bp_image.hex -a ap_image.hex [-F]] # Write bp and ap image into specified page (-F forces write to page 0) + [-v -p page -b bp_image.hex -a ap_image.hex] # Compare flash page with bp and ap image + [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin + + [-r] # Reset all FPGA's (load factory image) + [-c] # Clear all FPGA's (restart current image) + [-s] # Send SYNC pulse to all FPGA's + + [-q] # Prevent checking for running RSPDriver (prevents pause at start) + [-P] # Ping the board (find board when using broadcast address) + [-V] # Read RSU status and RSR versions + [-h] # This help text + +END + + exit; +} + +# +# pack_rsu($type, $regid, $size, $offset) +# +# Pack an RSU message +# +sub pack_rsu +{ + my ($type, $regid, $size, $offset) = @_; + + return pack($MEPSENDFORMAT, + 0x10, 0xFA, $type, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSU, $regid, $offset, $size, 0, 0);; +} + +# +# pcapcallback, save packet in $g_packet +# +# savepacket($handle, $hdr, $packet) +# +sub savepacket +{ + my ($handle, $hdr, $packet) = @_; + $g_packet = $packet; +} + +# +# ($payload,$error) = readresponse($pcap) +# +sub readresponse +{ + my ($pcap) = @_; + my ($retval) = 0; + + do { # keep going when we receive invalid messages + $retval = 0; # assume success + + # wait for message to arrive + eval { + use POSIX qw(SIGALRM); + POSIX::sigaction(SIGALRM, + POSIX::SigAction->new(sub { die })) + || die "=== ERR error setting SIGALRM handler: $!\n"; + + alarm $RESPONSETIMEOUT; + (0 == loop ($pcap, 1, \&savepacket, '')) + || die "=== ERR Failed to receive response"; + alarm 0; + }; + if ($@) { + $retval = 1; # fatal (timeout) + return ('', $retval, ''); + } + + ($ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved) = + unpack($MEPRECVFORMAT, $g_packet); + + if ($TYPE_WRITEACK != $type && $TYPE_READACK != $type) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid TYPE\n" if ($g_debug); + } + if ($PID_RSU != $pid && $PID_RSR != $pid) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid PID\n" if ($g_debug); + } + + printf STDERR "0x%s 0x%02x 0x%02x %5d 0x%02x 0x%02x 0x%02x 0x%02x %5d %5d %5d %5d\n", + $ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved if ($g_debug); + + if (0 == $retval && 0 != $status) { + $retval = 1; # fatal + print STDERR "=== FATAL protocol error: status=", $STATUS{$status}, "\n"; + print STDERR "=== Are you using the correct Ethernet interface? Use -i argument to change, e.g. -i eth1\n"; + exit; + } + } until ($retval != 2); + + return ('', $retval, '') if ($retval); + + return (substr($g_packet, 14 + $MEPHDRSIZE), $retval, $ethdr); +} + +# +# flash($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) +# +sub flash +{ + my ($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) = @_; + + printf STDERR "=== %s page %02d\n", $msg, $page; + print STDERR "=== |" . ("-" x $SECTORS_PER_PAGE) . "|\n=== "; + #print STDERR "Image: $image \n"; + + my ($success) = 1; + # leave last block for directory entry + my ($block); + for ($block = 0; $block < $BLOCKS_PER_PAGE - 1; $block += $blockinc) { + if ($writecb) { + $packet = pack_rsu($TYPE_WRITE, $cmd, $size, ($page * $BLOCKS_PER_PAGE) + $block); + + $packet .= &$writecb($page, $block, $image); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + + return $error if ($error); + } + + if ($readcb) { + $packet = pack_rsu($TYPE_READ, $cmd, $size, + ($page * $BLOCKS_PER_PAGE) + $block); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + return $error if ($error); + + $success = &$readcb($page, $block, $payload, $image) && $success; + } + + if (0 == $block % $BLOCKS_PER_SECTOR) { + if ($success) { print STDERR "."; } + else { print STDERR "X"; } + $success = 1; + } + } + print STDERR "\n"; + + return 0; +} + +# +# writedir($sock, $pcap, $page, $entry) +# Write the entry to the last block of page $page +# +sub writedir +{ + my ($sock, $pcap, $page, $entry) = @_; + + die "=== ERR internal error: size of direntry > $BLOCKSIZE" if (length($entry) > $BLOCKSIZE); + + print STDERR "writing dir @", (($page + 1) * $BLOCKS_PER_PAGE) - 1, "\n" if ($g_debug); + + # add zero padding if needed + $entry .= pack("C", 0) x ($BLOCKSIZE - length($entry)) if (length($entry) < $BLOCKSIZE); + + # write last block with dir entry + $packet = pack_rsu($TYPE_WRITE, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) - 1); + $packet .= $entry; + + my $error = 2; + $sock->send_eth_frame($packet); + + ($result, $error) = readresponse($pcap); + + return $error; +} + +# +# readdir($sock, $pcap, $page) +# Read the directory entry from the specified page +# +sub readdirentry +{ + my ($sock, $pcap, $page) = @_; + + my ($packet) = pack_rsu($TYPE_READ, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) -1); + + $sock->send_eth_frame($packet); + + my $error = 0; + ($entry, $error) = readresponse($pcap); + return ('', $error) if $error; + + $entry = '' if (substr($entry,0,1) eq pack("C", 0xff)); + + return ($entry, $error); +} + +# +# erasecb($page, $block, $image) +# +sub erasecb +{ + my ($page, $block, $image) = @_; + + print STDERR "erasecb: $page, $block\n" if ($g_debug); + + return pack("C", $CMD_ERASE); +} + +# +# erase($sock, $pcap, $page) +# page = -1 means all pages +# +sub erase +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, $BLOCKS_PER_SECTOR, + "erasing", $REG_ERASE, 1, + \&erasecb, 0, ''); +} + +# +# Verify the data +# +sub verifycb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "verifycb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# verify($sock, $pcap, $page, $image) +# +sub verify +{ + my ($sock, $pcap, $page, $image) = @_; + + return flash($sock, $pcap, $page, 1, + "verifying", $REG_RW, $BLOCKSIZE, + 0, \&verifycb, $image); +} + +# +# Dump the data to file +# +sub dumpcb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "dumpcb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + print DUMPFILE $payload; + + return 1; +} + +# +# dump($sock, $pcap, $page) +# +sub dump($$$) +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, 1, + "dumping", $REG_RW, $BLOCKSIZE, + 0, \&dumpcb, ''); +} + +# +# Write the data +# +sub writepagecb +{ + my ($page, $block, $image) = @_; + + print STDERR "writepagecb: page=$page, block=$block\n" if ($g_debug); + + my ($data) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $data .= pack("C", 0) x ($BLOCKSIZE - length($data)); + + return $data; +} + +# +# Read and verify the data +# +sub readpagecb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "readpagecb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# Create a direntry string based on filename +# direntry($filename) +# +sub formatentry +{ + my ($bp_filename, $ap_filename) = @_; + + $stat = stat($bp_filename) || die "=== ERR failed to stat file '$bp_filename'"; + $bp_datetime = ctime($stat->mtime); + chop($bp_datetime); + + $stat = stat($ap_filename) || die "=== ERR failed to stat file '$ap_filename'"; + $ap_datetime = ctime($stat->mtime); + chop($ap_datetime); + + # return 'size YYYY mm dd hh:mm bp_filename ; size YYYY mm dd hh:mm ap_filename' + return sprintf("%s( %s ) ; %s( %s )", $bp_filename, $bp_datetime, $ap_filename, $ap_datetime); +} + +# +# writepage($sock, $pcap, $page, $bp_filename, $ap_filename, $image) +# +sub writepage +{ + my ($sock, $pcap, $page, $bp_filename, $ap_filename, $image) = @_; + + $error = flash($sock, $pcap, $page, 1, + "writing and verifying", $REG_RW, $BLOCKSIZE, + \&writepagecb, \&readpagecb, $image); + + if (!$error) { + $error = writedir($sock, $pcap, $page, formatentry($bp_filename, $ap_filename)); + } else { + # clear entry, an error has occured + writedir($sock, $pcap, $page, ''); + } + return $error; +} + + +# +# Read images from the hex files. +# then the files are converted from hex to bin format. +# +# readhexfiles($filename_bp, &filename_ap ) +# +sub readhexfiles +{ + my ($filename_bp, $filename_ap) = @_; + local $/ = undef; # read entire file + + # open input and output files + die "=== ERR no image file specified" if ((!defined($filename_bp)) or (!defined($filename_ap))); + + open(IMAGEFILE, $filename_bp) || die "=== ERR failed to open file '$filename_bp'"; + print STDERR "=== Reading '$filename_bp'\n"; + $data = <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + open(IMAGEFILE, $filename_ap) || die "=== ERR failed to open file '$filename_ap'"; + print STDERR "=== Reading '$filename_ap'\n"; + $data .= <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + print STDERR "=== Converting '$filename_bp' and '$filename_ap' to binary\n"; + print STDERR "=== |" . ("-" x $PROGRESS_SIZE) . "|\n=== "; + + $i = 0; + $interval = length($data) / ($PROGRESS_SIZE * 2); + $interval = 1 if ($interval < .5); + + $bindata = ''; + + for ($byte = 0; $byte < length($data); $byte += 2) { + $bytestr = substr($data, $byte, 2); + + $bindata .= pack("C", hex($bytestr)); + + # print progress bar + $i++; + print STDERR "." if (0 == $i % $interval); + } + + # append FF's to fill buffer to $IMAGESIZE - 1 + $bindata .= pack("C", 255) x (($PAGESIZE - $BLOCKSIZE) - length($bindata)); + + print STDERR "\n"; + + return $bindata; +} + +# +# Read RSU status from RSR status register +# readrsustatus($sock, $pcap) +# +sub readrsustatus +{ + my ($sock, $pcap) = @_; + + $size = $REG_STATUS_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_STATUS, 0, $size, 0, 0); + + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + $rsustatus = unpack("C", substr($result, 164, 1)); + + print STDERR sprintf("\nRSU Status:\nRdy : %s\nFpgaType : %s\nImageType : %s\nTrig : %s\n\n", + ('Configuration ongoing', 'Configuration done')[$rsustatus & 1], + ('loaded from flash', 'loaded via JTAG')[($rsustatus >> 2) & 1], + ('Factory image is running', 'User image is running')[($rsustatus >> 3) & 1], + ('Reconfiguration due to button reset' + , 'Reconfiguration due to over temperature' + , 'Reconfiguration due to user reset' + , 'Unknown' + , 'Reconfiguration due to watchdog reset' + , 'Unknown' + , 'Unknown' + , 'Unknown' )[($rsustatus >> 4) & 7]); + + return $error; +} + +# +# Read versions of all FPGA's (handy after reconfig, reset, clear, etc) +# readversions($sock, $pcap) +# +sub readversions +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "RSP version: $id\nBP version: $maj.$min\n"; + + # read AP versions + for ($ap = 0; $ap < 4; $ap++) { + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + 1 << $ap, 0, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "AP[$id] version: $maj.$min\n"; + } + + return 0; +} + +# +# Find MAC addresses of active boards +# ping($sock, $pcap) +# +sub ping +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + + while (!$error) { + ($result, $error, $ethdr) = readresponse($pcap); + $mac = substr($ethdr, 12, 12); + $mac =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/; + print STDERR "Found RSP board on MAC: ", $mac, "\n" if (!$error); + } + + return 0; +} + +# +# Reconfigure. Load new firmware from flash into FPGA's. After loading +# has finished, the FPGA will be reset and restart with new firmware. +# +# reconfig($sock, $pcap, $page) +# +sub reconfig +{ + my ($sock, $pcap, $page) = @_; + + $cmd = $page & 15; # use bit 0..3 + $cmd = $cmd | 128; + $packet = pack_rsu($TYPE_WRITE, $REG_RECONFIG, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + + # there will be no response + # ($response, $error) = readresponse($pcap); + + return 0; +} + +# +# Reset control for all FPGA's +# +# clearctl($sock, $pcap, $cmd) +# valid values for $cmd: $CMD_SYNC, $CMD_CLEAR, $CMD_RESET +# +sub clearctl +{ + my ($sock, $pcap, $cmd) = @_; + + $packet = pack_rsu($TYPE_WRITE, $REG_CLEAR, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + ($response, $error) = readresponse($pcap); + + return $error; +} + +# +# Report result +# report($error, $report_ok = false) +# +sub report +{ + my ($error, $report_ok) = @_; + + if ($error || $g_error) { + print STDERR "=== FAILED\n"; + exit 1; + } + else { + print STDERR "=== OK\n" if ($report_ok); + } +} + +# +# Change range of the page parameter +# chkpage($page) +# +sub chkpage +{ + my ($page) = @_; + + die "=== ERR no flash page specified, use -p" if (!defined($page)); + if ($page < 0 || $page >= $PAGES) { + die "=== ERR invalid page index $page, should be >= 0 and < $PAGES"; + } +} + +# +# Get rsu status and versions +# version_cmd($sock, $pcap) +# +sub version_cmd +{ + my ($sock, $pcap) = @_; + + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + return $error; +} + +sub main +{ + my $sock = 0; + + print STDERR "=== rsuctl ($VERSION), for RSP3 boards only\n"; + + Getopt::Std::getopts("i:m:lexwvb:a:dp:f:FrcsPVhq"); + + # check that at least one argument is present + usage() if ($opt_h); + + if (!defined($opt_m)) { + print STDERR "=== ERR Must specify MAC address using -m option"; + usage(); + } + + usage() unless ($opt_l || $opt_e || $opt_x || $opt_w || $opt_v || + $opt_d || $opt_r || $opt_c || $opt_s || $opt_P || $opt_V || $opt_h); + + # assign default values + $opt_i = 'eth1' if !defined($opt_i); + + # Create socket to the boards + die "=== ERR this program needs to run as root to send/recv raw Ethernet frames" if (0 != geteuid()); + + $sock = new Net::RawIP; + $sock->ethnew($opt_i); + $sock->ethset(dest => $opt_m); + + $TIMEOUT=5; + $pcap = $sock->pcapinit($opt_i, "not ether dst $opt_m", $ETHLEN, $TIMEOUT); + + die "=== ERR could not open raw socket to RSP board(s) on $opt_i:$opt_m: $!\n" unless $sock; + + print STDERR "=== connected to RSP board(s) on $opt_i:$opt_m\n"; + + if (!defined($opt_q)) { + print STDERR "=== checking for running RSPDriver\n"; + ($payload, $error) = readresponse($pcap); + die "=== RSPDriver appears to be running, stop it first\n" if ($payload ne ''); + } + + if (defined($opt_l)) { + + # LIST IMAGES IN FLASH + # + # readdirentry for all pages + + print STDERR "=== listing flash contents\n\n"; + for ($page = 0; $page < $PAGES; $page++) { + ($entry, $error) = readdirentry($sock, $pcap, $page); + break if $error; + print STDERR sprintf("%s %2d: %s\n", + (0 == $page ? 'Factory':'Image '), $page, $entry) if ((0 == $page) || $entry ne ''); + } + print STDERR "\n"; + report($error, true); + + return; + } + + if (defined($opt_e)) { + + # ERASE + # + # erase flash page + + die "=== ERR erasing factory image (page 0) prohibited, use -F to force erase of factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + report(erase($sock, $pcap, $opt_p), true); + + return; + } + + if (defined($opt_x)) { + + # RECONFIG + # + # reconfig BP and AP + # wait $TO seconds + # clear + # wait $TO seconds + # read rsustatus & version + + chkpage($opt_p); + report(reconfig($sock, $pcap, $opt_p)); + report(clearctl($sock, $pcap, $CMD_RESET)); + report($error) if $error; + #print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + #$error = readrsustatus($sock, $pcap); + #$error = readversions($sock, $pcap) && $error; + #report($error, true); + + return; + } + + if (defined($opt_w)) { + + # WRITE + # + # readimage + # erase page + # write & verify page + + die "=== ERR writing to factory image (page 0) prohibited, use -F to force write to factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + $image = readhexfiles($opt_b, $opt_a); + report(erase($sock, $pcap, $opt_p)); + report(writepage($sock, $pcap, $opt_p, $opt_b, $opt_a, $image), true); + + return; + } + + if (defined($opt_v)) { + + # VERIFY + # + # readimage reference + # verify flash page against reference + + chkpage($opt_p); + $image = readhexfiles($opt_b, $opt_a); + report(verify($sock, $pcap, $opt_p, $image), true); + + return; + } + + if (defined($opt_d)) { + + # DUMP + # + # dump flash page to file + + chkpage($opt_p); + die "=== ERR no image file specified" if (!defined($opt_f)); + + open(DUMPFILE, ">$opt_f") || die "=== ERR failed to open file '$opt_f' for writing\n"; + print STDERR "=== Dumping to file '$opt_f'\n"; + $error = dump($sock, $pcap, $opt_p); + close(DUMPFILE); + report($error, true); + + return; + } + + if (defined($opt_r)) { + + # RESET + # + # send reset (factory image (page 0) will start on BP) + # wait $DTO seconds + # reconfigure AP's with page 8 (factory default for AP's) + # wait $TO second + # read rsustatus and versions + + report(reconfig($sock, $pcap, 0)); # set image 0 (factory image) + report(clearctl($sock, $pcap, $CMD_RESET)); + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + print STDERR "=== reconfiguring BP and AP's with factory image\n"; + + # don't check the error return code because we know + # it indicates failure while the reconfig is actually in progress + + report(version_cmd($sock, $pcap) && $error, true); + + return; + } + + if (defined($opt_c)) { + + # CLEAR + # + # send clear + # wait $TO seconds + # read rsustatus and versions + + report(clearctl($sock, $pcap, $CMD_CLEAR)); + print STDERR "=== waiting $TO seconds after clear\n"; sleep $TO; + report(version_cmd($sock, $pcap), true); + + return; + } + + if (defined($opt_s)) { + + # SYNC + # + # send sync + + report(clearctl($sock, $pcap, $CMD_SYNC), true); + + return; + } + + if (defined($opt_P)) { + report(ping($sock, $pcap), true); + return; + } + + if (defined($opt_V)) { + # versions + # + # read rsustatus and versions + + report(version_cmd($sock, $pcap), true); + + return; + } +} + +main();