Merge pull request #1396 from stefanrueger/save

Provide terminal save command
This commit is contained in:
Stefan Rueger
2023-06-13 18:35:39 +01:00
committed by GitHub
5 changed files with 159 additions and 28 deletions

View File

@@ -46,7 +46,7 @@
.Op Fl q
.Op Fl T Ar cmd
.Op Fl t
.Op Fl U Ar memtype:op:filename:filefmt
.Op Fl U Ar memory:op:filename:filefmt
.Op Fl v
.Op Fl x Ar extended_param
.Op Fl V
@@ -710,15 +710,15 @@ Tells
to enter the interactive terminal shell before up- or downloading files
via the -U option or processing other -T terminal commands, if any. See
below for a detailed description of the terminal mode.
.It Xo Fl U Ar memtype Ns
.It Xo Fl U Ar memory Ns
.Ar \&: Ns Ar op Ns
.Ar \&: Ns Ar filename Ns
.Op \&: Ns Ar format
.Xc
Perform a memory operation as indicated. Multiple -U operations are
allowed. The
.Ar memtype
field specifies the memory type to operate on.
.Ar memory
field specifies the memory to operate on.
The available memory types are device-dependent, the actual
configuration can be viewed with the
.Cm part
@@ -962,7 +962,7 @@ The
.Ar addr
and
.Ar len
parameters of the dump, read, write and erase commands can be
parameters of the dump, read, write, save and erase commands can be
negative with the same syntax as substring computations in perl or python.
The table below details their meaning with respect to an example memory of size
sz=0x800.
@@ -1077,6 +1077,19 @@ and, if necessary, pads the remaining space by repeating the last
.Ar data
item. The fill write command does not write beyond the specified memory area
even if more data than needed were given.
.It Ar save memory {addr len} file[:format]
Save one or more memory segments to a file in a format specified by the
:format letter. The default is :r for raw binary. Each memory segment is
described by an address and length pair. In absence of any memory segments
the entire memory is saved to the file. Only Motorola S-Record (:s) and
Intel Hex (:i or :I) formats store address information with the saved
data.
.Nm Avrdude
cannot currently save ELF file formats. All the other file formats lose
the address information and concatenate the chosen memory segments into
the output file. If the file name is - then
.Nm
writes to stdout.
.It Ar erase
Perform a chip erase and discard all pending writes to EEPROM and flash.
Note that EEPROM will be preserved if the EESAVE fuse bit is set.

View File

@@ -770,11 +770,11 @@ downloading files via the @code{-U} option or processing other @code{-T}
terminal commands, if any. See below for a detailed description of the
terminal mode.
@item -U @var{memtype}:@var{op}:@var{filename}[:@var{format}]
@item -U @var{memory}:@var{op}:@var{filename}[:@var{format}]
Perform a memory operation.
Multiple @option{-U} options can be specified in order to operate on
multiple memories on the same command-line invocation. The
@var{memtype} field specifies the memory type to operate on. Use
@var{memory} field specifies the memory type to operate on. Use
the @option{-v} option on the command line or the @code{part} command from
terminal mode to display all the memory types supported by a particular
device.
@@ -1959,7 +1959,7 @@ abbreviated to the shortest unambiguous form. Terminal mode provides a
command history using readline(3), so previously entered command lines can be
recalled and edited.
The @var{addr} and @var{len} parameters of the dump, read, write, and erase
The @var{addr} and @var{len} parameters of the dump, read, write, save and erase
commands can be negative with the same syntax as substring computations in
perl or python. The table below details their meaning with respect to an
example memory of size @code{sz=0x800}.
@@ -2037,28 +2037,28 @@ The following commands are implemented for all programmers:
@table @code
@item dump @var{memtype} @var{addr} @var{len}
@item dump @var{memory} @var{addr} @var{len}
Read from the specified memory interval (see above), and display in the usual hexadecimal and
ASCII form.
@item dump @var{memtype} @var{addr}
@item dump @var{memory} @var{addr}
Read from memory addr as many bytes as the most recent dump memory addr len command with this
very memory had specified (default 256 bytes), and display them.
@item dump @var{memtype}
@item dump @var{memory}
Continue dumping from the memory and location where the most recent dump command left off; if no
previous dump command has addressed a memory an error message will be shown.
@item dump @var{memtype} @var{addr} @dots{}
Start reading from @var{addr}, all the way to the last memory address (deprecated: use @code{dump @var{memtype} @var{addr} -1}).
@item dump @var{memory} @var{addr} @dots{}
Start reading from @var{addr}, all the way to the last memory address (deprecated: use @code{dump @var{memory} @var{addr} -1}).
@item dump @var{memtype} @dots{}
Read all bytes from the specified memory, and display them (deprecated: use @code{dump @var{memtype} 0 -1}).
@item dump @var{memory} @dots{}
Read all bytes from the specified memory, and display them (deprecated: use @code{dump @var{memory} 0 -1}).
@item read
Can be used as an alias for dump.
@item write @var{memtype} @var{addr} @var{data[,]} @{@var{data[,]}@}
@item write @var{memory} @var{addr} @var{data[,]} @var{@{data[,]@}}
Manually program the respective memory cells, starting at address
@var{addr}, using the data items provided. The terminal implements
reading from and writing to flash and EEPROM type memories normally
@@ -2178,16 +2178,27 @@ as it is the same as @code{0xFFFFffffFFFFffffU}.
One trailing comma at the end of data items is ignored to facilitate copy
and paste of lists.
@item write @var{memtype} @var{addr} @var{data}
@item write @var{memory} @var{addr} @var{data}
The start address @code{addr} may be omitted if the size of the memory
being written to is 1 byte in size.
@item write @var{memtype} @var{addr} @var{len} @var{data[,]} @{@var{data[,]}@} @dots{}
@item write @var{memory} @var{addr} @var{len} @var{data[,]} @var{@{data[,]@}} @dots{}
The ellipsis @dots{} form writes the data to the entire memory intervall
addressed by @var{addr len} and, if necessary, pads the remaining space by
repeating the last data item. The fill write command does not write beyond
the specified memory area even if more data than needed were given.
@item save @var{memory} @var{@{addr len@}} @var{file[:format]}
Save one or more memory segments to a file in a format specified by the
@code{:}format letter. The default is @code{:r} for raw binary. Each
memory segment is described by an address and length pair. In absence of
any memory segments the entire memory is saved to the file. Only Motorola
S-Record (@code{:s}) and Intel Hex (@code{:i} or @code{:I}) formats store
address information with the saved data. Avrdude cannot currently save
ELF file formats. All the other file formats lose the address information
and concatenate the chosen memory segments into the output file. If the
file name is - then avrdude writes to stdout.
@item erase
Perform a chip erase and discard all pending writes to EEPROM and flash.
Note that EEPROM will be preserved if the EESAVE fuse bit is set.
@@ -2869,7 +2880,7 @@ part
# parameters for bootloaders
autobaud_sync = <num> ; # autobaud detection byte, default 0x30
memory <memtype>
memory <memory>
paged = <yes/no> ; # yes/no (flash only, do not use for EEPROM)
offset = <num> ; # memory offset
size = <num> ; # bytes

View File

@@ -1203,13 +1203,13 @@ static int b2num(const char *filename, FILE *f, const AVRMEM *mem, const Segment
break;
}
for (int i = segp->addr; i < segp->addr + segp->len; i++) {
for(int seen = 0, i = segp->addr; i < segp->addr + segp->len; i++) {
char cbuf[81];
if (i > 0) {
if (putc(',', f) == EOF)
if(seen++)
if(putc(',', f) == EOF)
goto writeerr;
}
unsigned num = mem->buf[i];
/*
* For a base of 8 and a value < 8 to convert, don't write the
@@ -1440,7 +1440,7 @@ int fileio(int op, const char *filename, FILEFMT format,
return -1;
}
if(size < 0 || op == FIO_READ || FIO_READ_FOR_VERIFY)
if(size < 0 || op == FIO_READ || op == FIO_READ_FOR_VERIFY)
size = mem->size;
const Segment_t seg = {0, size};
@@ -1449,7 +1449,7 @@ int fileio(int op, const char *filename, FILEFMT format,
// Normalise segment address and length to be non-negative
int segmemt_normalise(const AVRMEM *mem, Segment_t *segp) {
int segment_normalise(const AVRMEM *mem, Segment_t *segp) {
int addr = segp->addr, len = segp->len, maxsize = mem->size;
int digits = maxsize > 0x10000? 5: 4;
@@ -1493,7 +1493,7 @@ static int fileio_segments_normalise(int oprwv, const char *filename, FILEFMT fo
return -1;
for(int i=0; i<n; i++)
if(segmemt_normalise(mem, seglist+i) < 0)
if(segment_normalise(mem, seglist+i) < 0)
return -1;
using_stdio = 0;
@@ -1639,4 +1639,3 @@ int fileio_segments(int oprwv, const char *filename, FILEFMT format,
return ret;
}

View File

@@ -1036,7 +1036,7 @@ int fileio_fmt_autodetect(const char *fname);
int fileio(int oprwv, const char *filename, FILEFMT format,
const AVRPART *p, const char *memtype, int size);
int segmemt_normalise(const AVRMEM *mem, Segment_t *segp);
int segment_normalise(const AVRMEM *mem, Segment_t *segp);
int fileio_segments(int oprwv, const char *filename, FILEFMT format,
const AVRPART *p, const AVRMEM *mem, int n, const Segment_t *seglist);

View File

@@ -67,6 +67,7 @@ struct command {
static int cmd_dump (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]);
static int cmd_write (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]);
static int cmd_save (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]);
static int cmd_flush (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]);
static int cmd_abort (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]);
static int cmd_erase (const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]);
@@ -93,6 +94,7 @@ struct command cmd[] = {
{ "dump", cmd_dump, _fo(read_byte_cached), "display a memory section as hex dump" },
{ "read", cmd_dump, _fo(read_byte_cached), "alias for dump" },
{ "write", cmd_write, _fo(write_byte_cached), "write data to memory; flash and EEPROM are cached" },
{ "save", cmd_save, _fo(write_byte_cached), "save memory data to file" },
{ "flush", cmd_flush, _fo(flush_cache), "synchronise flash and EEPROM cache with the device" },
{ "abort", cmd_abort, _fo(reset_cache), "abort flash and EEPROM writes, ie, reset the r/w cache" },
{ "erase", cmd_erase, _fo(chip_erase_cached), "perform a chip or memory erase" },
@@ -634,6 +636,112 @@ static int cmd_write(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *ar
return 0;
}
static int cmd_save(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]) {
if(argc < 3 || (argc > 1 && str_eq(argv[1], "-?"))) {
msg_error(
"Syntax: save <mem> {<addr> <len>} <file>[:<format>]\n"
"Function: save memory segments to file (default format :r raw binary)\n"
);
return -1;
}
AVRMEM *mem, *omem;
if(!(omem = avr_locate_mem(p, argv[1]))) {
pmsg_error("(save) %s memory type not defined for part %s\n", argv[1], p->desc);
return -1;
}
if(argc > 3 && !(argc&1)) {
pmsg_error("(save) need pairs <addr> <len> to describe memory segments\n");
return -1;
}
// Last char of filename is format if the penultimate char is a colon
FILEFMT format = FMT_RBIN;
char *fn = argv[argc-1];
size_t len = strlen(fn);
if(len > 2 && fn[len-2] == ':') { // Assume format specified
format = fileio_format(fn[len-1]);
if(format == FMT_ERROR) {
pmsg_error("(save) invalid file format :%c; known formats are\n", fn[len-1]);
for(int f, c, i=0; i<62; i++) {
c = i<10? '0'+i: (i&1? 'A': 'a') + (i-10)/2;
f = fileio_format(c);
if(f != FMT_ERROR)
msg_error(" :%c %s\n", c, fileio_fmtstr(f));
}
return -1;
}
len -= 2;
}
char *filename = memcpy(cfg_malloc(__func__, len+1), fn, len);
mem = avr_dup_mem(omem);
int n = argc > 3? (argc-3)/2: 1;
Segment_t *seglist = cfg_malloc(__func__, n*sizeof*seglist);
int ret = -1;
// Build memory segment list
seglist[0].addr = 0; // Defaults to entire memory
seglist[0].len = mem->size;
if(argc > 3) {
for(int cc = 2, i = 0; i < n; i++, cc+=2) {
const char *errstr;
seglist[i].addr = str_int(argv[cc], STR_INT32, &errstr);
if(errstr) {
pmsg_error("(save) address %s: %s\n", argv[cc], errstr);
goto done;
}
seglist[i].len = str_int(argv[cc+1], STR_INT32, &errstr);
if(errstr) {
pmsg_error("(save) length %s: %s\n", argv[cc], errstr);
goto done;
}
}
}
int nbytes = 0; // Total number of bytes to save
for(int i=0; i<n; i++) { // Ensure addr and lengths are non-negative
if(segment_normalise(mem, seglist+i) < 0)
goto done;
nbytes += seglist[i].len;
}
if(nbytes <= 0 && !str_eq(filename, "-"))
pmsg_warning("(save) no file written owing to empty memory segment%s\n",
str_plural(n));
if(nbytes <= 0) {
ret = 0;
goto done;
}
// Read memory from device/cache
report_progress(0, 1, "Reading");
for(int i = 0; i < n; i++) {
for(int j = seglist[i].addr; j < seglist[i].addr + seglist[i].len; j++) {
int rc = pgm->read_byte_cached(pgm, p, mem, j, mem->buf+j);
if(rc < 0) {
report_progress(1, -1, NULL);
pmsg_error("(save) error reading %s address 0x%0*x of part %s\n",
mem->desc, j<16? 1: j<256? 2: j<65536? 4: 5, j, p->desc);
return -1;
}
report_progress(j, nbytes, NULL);
}
}
report_progress(1, 1, NULL);
ret = fileio_segments(FIO_WRITE, filename, format, p, mem, n, seglist);
done:
avr_free_mem(mem);
free(seglist);
free(filename);
return ret < 0? ret: 0;
}
static int cmd_flush(const PROGRAMMER *pgm, const AVRPART *p, int argc, char *argv[]) {
if(argc > 1) {