|
@@ -5,6 +5,8 @@ use integer;
|
|
|
use PerlIO::gzip;
|
|
|
use File::Temp;
|
|
|
use File::Spec;
|
|
|
+use Time::HiRes qw(usleep tv_interval);
|
|
|
+use Digest::CRC qw(crc32);
|
|
|
|
|
|
my $esptool = ($^O eq 'MSWin32') ? 'esptool.exe' : 'esptool.py';
|
|
|
$esptool = $ENV{'ESPTOOL'} || $esptool;
|
|
@@ -77,11 +79,40 @@ sub unquote_cmd($) {
|
|
|
}
|
|
|
|
|
|
my @args = @ARGV;
|
|
|
-my $file = shift(@args);
|
|
|
+my $esponly = 0;
|
|
|
+my $file;
|
|
|
+while (1) {
|
|
|
+ $file = shift(@args);
|
|
|
+ last if ($file !~ /^\-/);
|
|
|
+
|
|
|
+ if ($file eq '--esponly') {
|
|
|
+ $esponly = 1;
|
|
|
+ } elsif ($file eq '--') {
|
|
|
+ $file = shift(@args);
|
|
|
+ last;
|
|
|
+ } else {
|
|
|
+ undef $file; # Invalid argument, print usage
|
|
|
+ last;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+my $port = shift(@args);
|
|
|
+if (!defined($port)) {
|
|
|
+ die "Usage: $0 file.fw port [esptool options...]\n";
|
|
|
+}
|
|
|
|
|
|
-if (!defined($file)) {
|
|
|
- die "Usage: $0 fwfile [esptool options...]\n";
|
|
|
+if (!File::Spec->file_name_is_absolute($port)) {
|
|
|
+ if (-c "/dev/$port") {
|
|
|
+ $port = "/dev/$port";
|
|
|
+ } elsif (-c "/dev/tty$port") {
|
|
|
+ $port = "/dev/tty$port";
|
|
|
+ } elsif ($^O eq 'MSWin32') {
|
|
|
+ $port = "\\\\.\\$port";
|
|
|
+ } else {
|
|
|
+ die "$0: no such serial port: $port\n";
|
|
|
+ }
|
|
|
}
|
|
|
+print STDERR "Using serial port device $port\n";
|
|
|
|
|
|
open(my $fw, '<:gzip', $file)
|
|
|
or die "$0: $file: $!\n";
|
|
@@ -103,7 +134,6 @@ while (read($fw, $hdr, 16) == 16) {
|
|
|
}
|
|
|
|
|
|
my $t = $type[$c->{'type'}];
|
|
|
- last if ($t eq 'end'); # End of stream
|
|
|
|
|
|
my $d;
|
|
|
if (read($fw, $d, $c->{'len'}) != $c->{'len'}) {
|
|
@@ -112,8 +142,9 @@ while (read($fw, $hdr, 16) == 16) {
|
|
|
last;
|
|
|
}
|
|
|
$c->{'data'} = $d;
|
|
|
-
|
|
|
push(@chunks, $c);
|
|
|
+
|
|
|
+ last if ($t eq 'end'); # End of stream
|
|
|
}
|
|
|
|
|
|
close($fw);
|
|
@@ -127,6 +158,10 @@ my %espopt = ('before' => 'default_reset', 'after' => 'hard_reset',
|
|
|
'flash_mode' => undef, 'flash_freq' => undef,
|
|
|
'flash_size' => undef);
|
|
|
|
|
|
+# Create a compressed data buffer without the ESP32 chunks
|
|
|
+my $fpgadata;
|
|
|
+open(my $fpgafh, '>:gzip', \$fpgadata) or die;
|
|
|
+
|
|
|
my $nc = 0;
|
|
|
foreach my $c ( @chunks ) {
|
|
|
my $t = $type[$c->{'type'}];
|
|
@@ -144,10 +179,15 @@ foreach my $c ( @chunks ) {
|
|
|
print $tf $c->{'data'};
|
|
|
close($tf);
|
|
|
push(@espfiles, '0x'.$addr, $tff);
|
|
|
+ } else {
|
|
|
+ print $fpgafh $c->{'hdr'};
|
|
|
+ print $fpgafh $c->{'data'};
|
|
|
}
|
|
|
$nc++;
|
|
|
}
|
|
|
|
|
|
+close($fpgafh);
|
|
|
+
|
|
|
foreach my $e (keys %espopt) {
|
|
|
my $ev = $ENV{"ESP\U$e"};
|
|
|
if (defined($ev)) {
|
|
@@ -155,28 +195,195 @@ foreach my $e (keys %espopt) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+$espopt{'port'} = $port;
|
|
|
+
|
|
|
my @espcmd = unquote_cmd($esptool);
|
|
|
foreach my $o (sort grep (!/^flash_/, keys %espopt)) {
|
|
|
if (defined($espopt{$o})) {
|
|
|
push(@espcmd, "--$o", $espopt{$o});
|
|
|
}
|
|
|
}
|
|
|
-push(@espcmd, @args);
|
|
|
push(@espcmd, 'write_flash', '-z');
|
|
|
foreach my $o (sort grep (/^flash_/, keys %espopt)) {
|
|
|
if (defined($espopt{$o})) {
|
|
|
push(@espcmd, "--$o", $espopt{$o});
|
|
|
}
|
|
|
}
|
|
|
-push(@espcmd, @espfiles);
|
|
|
|
|
|
-if (defined($ENV{'VERBOSE'})) {
|
|
|
- print STDERR join(' ', @espcmd), "\n";
|
|
|
-}
|
|
|
+push(@espcmd, @espfiles);
|
|
|
|
|
|
+print STDERR join(' ', @espcmd), "\n";
|
|
|
$err = system(@espcmd);
|
|
|
if ($err == -1) {
|
|
|
print STDERR "$0: $espcmd[0]: $!\n";
|
|
|
+} elsif ($err) {
|
|
|
+ print STDERR "$0: $espcmd[0]: exit $err\n";
|
|
|
+}
|
|
|
+exit $err if ($err || $esponly);
|
|
|
+
|
|
|
+my $SerialPort = eval {
|
|
|
+ require Device::SerialPort;
|
|
|
+ Device::SerialPort->import(qw(:PARAM));
|
|
|
+ 'Device::SerialPort';
|
|
|
+} || eval {
|
|
|
+ require Win32::SerialPort;
|
|
|
+ Win32::SerialPort->import(qw(:STAT));
|
|
|
+ 'Win32::SerialPort';
|
|
|
+} || die "$0: need Device::SerialPort or Win32::SerialPort\n";
|
|
|
+
|
|
|
+print STDERR "Waiting for reinit...\n";
|
|
|
+usleep(4000000);
|
|
|
+
|
|
|
+my $tty = $SerialPort->new($port);
|
|
|
+die "$0: $port: $!\n" if (!$tty);
|
|
|
+
|
|
|
+$tty->buffers($tty->buffer_max);
|
|
|
+$tty->reset_error;
|
|
|
+
|
|
|
+$tty->user_msg(1);
|
|
|
+$tty->error_msg(1);
|
|
|
+
|
|
|
+$tty->handshake('none');
|
|
|
+$tty->databits(8);
|
|
|
+$tty->baudrate(115200);
|
|
|
+$tty->parity('none');
|
|
|
+$tty->stopbits(1);
|
|
|
+$tty->datatype('raw');
|
|
|
+$tty->stty_ignbrk(0);
|
|
|
+#$tty->stty_brkint(0);
|
|
|
+$tty->stty_parmrk(0);
|
|
|
+$tty->stty_istrip(0);
|
|
|
+$tty->stty_inlcr(0);
|
|
|
+$tty->stty_igncr(0);
|
|
|
+$tty->stty_icrnl(0);
|
|
|
+$tty->stty_opost(0);
|
|
|
+$tty->stty_echo(0);
|
|
|
+$tty->stty_echonl(0);
|
|
|
+$tty->stty_icanon(0);
|
|
|
+$tty->stty_isig(0);
|
|
|
+#$tty->stty_iexten(0);
|
|
|
+$tty->read_char_time(0);
|
|
|
+$tty->write_settings;
|
|
|
+
|
|
|
+# In case DTR and/or RTS was asserted on the physical serial port.
|
|
|
+# Note that DTR needs to be deasserted before RTS!
|
|
|
+# However, deasserting DTR on the ACM port prevents it from working,
|
|
|
+# so only do this if we don't see CTS (which is always enabled on ACM)...
|
|
|
+if ($tty->can_modemlines && !($tty->modemlines & $tty->MS_CTS_ON)) {
|
|
|
+ usleep(100000);
|
|
|
+ $tty->dtr_active(0);
|
|
|
+ usleep(100000);
|
|
|
+ $tty->rts_active(0);
|
|
|
+ usleep(100000);
|
|
|
+}
|
|
|
+
|
|
|
+sub tty_read {
|
|
|
+ my($tty,$bufref,$timeout) = @_;
|
|
|
+ my $d = $$bufref;
|
|
|
+
|
|
|
+ if ($d eq '') {
|
|
|
+ my $c;
|
|
|
+ $tty->read_const_time($timeout);
|
|
|
+ ($c,$d) = $tty->read(256);
|
|
|
+ return '' if (!$c);
|
|
|
+ }
|
|
|
+
|
|
|
+ my $r = substr($d,0,1);
|
|
|
+ $$bufref = substr($d,1);
|
|
|
+
|
|
|
+ return $r;
|
|
|
}
|
|
|
+my $ttybuf = '';
|
|
|
+
|
|
|
+sub tty_write($$) {
|
|
|
+ my($tty,$data) = @_;
|
|
|
+ my $bytes = length($data);
|
|
|
+ my $offs = 0;
|
|
|
+
|
|
|
+ while ($bytes) {
|
|
|
+ my $cnt = $tty->write(substr($data,$offs,$bytes));
|
|
|
+ usleep(10000) unless ($cnt);
|
|
|
+ $offs += $cnt;
|
|
|
+ $bytes -= $cnt;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $offs;
|
|
|
+}
|
|
|
+
|
|
|
+my $found = 0;
|
|
|
+my $tt = time();
|
|
|
+my $start_enq = $tt;
|
|
|
+while (($tt = time()) - $start_enq < 30) {
|
|
|
+ my $d = tty_read($tty, \$ttybuf, 1000);
|
|
|
+ if ($d eq '') {
|
|
|
+ tty_write($tty, "\005"); # ENQ
|
|
|
+ } else {
|
|
|
+ my $ix = index("\026\004\027", $d);
|
|
|
+ if ($ix < 0) {
|
|
|
+ print STDERR $d;
|
|
|
+ next;
|
|
|
+ }
|
|
|
+ $found = $found == $ix ? $found+1 : 0;
|
|
|
+ last if ($found == 3);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+if ($found < 3) {
|
|
|
+ die "$0: $port: no MAX80 ESP32 detected\n";
|
|
|
+}
|
|
|
+
|
|
|
+$start_enq = $tt;
|
|
|
+my $winspc;
|
|
|
+while (!defined($winspc)) {
|
|
|
+ $tt = time();
|
|
|
+ if ($tt - $start_enq >= 10) {
|
|
|
+ die "$0: $port: failed to start FPGA firmware upload\n";
|
|
|
+ }
|
|
|
+ tty_write($tty, "\034\001: /// MAX80 FW UPLOAD \~\@\~ \$\r\n\035");
|
|
|
+ my $d;
|
|
|
+ do {
|
|
|
+ $d = tty_read($tty, \$ttybuf, 1000);
|
|
|
+ if ($d eq "\036") {
|
|
|
+ $winspc = 0;
|
|
|
+ last;
|
|
|
+ } else {
|
|
|
+ print $d;
|
|
|
+ }
|
|
|
+ } while ($d ne '');
|
|
|
+}
|
|
|
+
|
|
|
+my $bytes = length($fpgadata);
|
|
|
+my $offset = 0;
|
|
|
+while ($offset < $bytes) {
|
|
|
+ my $chunk = $bytes - $offset;
|
|
|
+ $chunk = 64 if ($chunk > 64);
|
|
|
+ $chunk = $winspc if ($chunk > $winspc);
|
|
|
+
|
|
|
+ my $d = tty_read($tty, \$ttybuf, $chunk ? undef : 1000);
|
|
|
+ if ($d ne '') {
|
|
|
+ if ($d eq "\022") {
|
|
|
+ $winspc = 0;
|
|
|
+ } elsif ($d eq "\024") {
|
|
|
+ $winspc += 256;
|
|
|
+ } elsif ($d eq "\027" || $d eq "\030" || $d eq "\025") {
|
|
|
+ die "$0: $port: upload terminated by MAX80\n";
|
|
|
+ } elsif ($d ne "\006") {
|
|
|
+ print STDERR $d;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ next unless($chunk);
|
|
|
+
|
|
|
+ my $data = substr($fpgadata, $offset, $chunk);
|
|
|
+ my $hdr = pack("CCvVV", 2, $chunk-1, 0, $offset, crc32($data));
|
|
|
+
|
|
|
+ tty_write($tty, $hdr.$data);
|
|
|
+
|
|
|
+ $offset += $chunk;
|
|
|
+ $winspc -= $chunk;
|
|
|
+}
|
|
|
+
|
|
|
+tty_write($tty, "\004"); # EOT
|
|
|
+$tty->close;
|
|
|
|
|
|
-exit !!$err;
|
|
|
+exit 0;
|