This is the mail archive of the
gdb@sourceware.org
mailing list for the GDB project.
GDB support for flash: implementation
- From: Jim Blandy <jimb at codesourcery dot com>
- To: gdb at sourceware dot org
- Date: Mon, 05 Jun 2006 11:34:12 -0700
- Subject: GDB support for flash: implementation
Thanks very much to the folks who contributed to the thread the other
week about GDB's flash programming support! Based on the feedback,
I've made the following adjustments to our user interface sketch/spec:
* Drop the requirement that 'load' preserve the contents of those
portions of the flash that the binary being loaded doesn't touch.
Since erase operations affect entire sectors, these semantics would
require a read-erase-write cycle covering, in the worst case, a
complete flash sector. When programming flash directly over a JTAG
interface, it's not unusual for it to take tens of seconds to write
a sector; this is an unacceptable cost when the user knows the old
contents aren't worth preserving.
* Note that we could extend GDB's memory region facilities to
establish different settings for different regions of flash, or for
different flash devices. However, we'll leave developing this idea
for later.
But with those changes, people seemed to feel that the facilities
proposed would cover their daily use cases.
So I think now we can start talking about the implementation. Here's
an edit of the original document that goes further and describes an
implementation that seemed reasonable to us; search down for
"Implementation".
So, what do people think? Would this proposal work for you?
----
Background
Flash memory is a popular form of non-volatile memory. When reading,
it is byte-addressable, like traditional RAM or ROM. An entire block
(many kilobytes, typically) of flash can be erased with a single
command. Individual bytes can be written only if they have been
erased, but not subsequently written. Therefore, to write a single
byte, without changing other bytes in the same block, one must read
the block, erase the block, and then re-write the block using the
previously read data, as modified by the intended write.
Flash is typically used for program storage. On some systems, flash is
in fact the only place to store programs. For example, some systems
have have relatively large amounts of flash, but very small amounts of
RAM.
If the flash memory controller has a JTAG interface (most do) then the
flash memory can typically be programmed using the same ICE/BDM unit
that is used for debugging.
Because GDB already communicates with these ICE units (typically via a
GDB stub), and because GDB already supports loading programs onto
embedded systems (via the load command), it is natural that GDB
support loading programs into flash memory as well. This document
proposes a GDB user interface for loading programs into flash memory.
In what follows, the term "GDB" refers to the user's view of GDB,
which includes GDB, any applicable stub, the ICE units, etc. Thus
statements like "GDB must do X" are not meant to imply that GDB proper
must do X, but rather that GDB must cause X to occur.
Rationale paragraphs appear in (parenthesis).
Program Images
If the program image loaded by the load command will result in any
portion of the program image being placed in flash memory, then GDB is
responsible for programming those portions of the flash
accordingly. GDB may also arbitrarily modify bytes in flash that fall
outside of the program image.
If the target hardware requires any other modifications to special
memory addresses (such as placing the initial value of the program
counter at a specified address), then it is the responsibility of the
programmer to ensure that the program image contains appropriate
values at those addresses; GDB's responsibility is simply to
accurately copy the program image to the target.
(The rationale for using load (rather than an alternative flash
command) is that, on a system in which programs are located in flash,
loading a program implies placing it in flash. Furthermore, GUIs,
scripts, etc., that use load (or the MI equivalent thereof) will not
require change to work with flash systems.)
(The proposal originally required the load command to leave portions
of flash that fell outside the program image unchanged. While this
rule would minimize side effects, it may be impractical to follow on
some devices. Since erase operations affect entire sectors, preserving
bytes outside the image would require a read-erase-write cycle
covering, in the worst case, a complete flash sector. When programming
flash directly over a JTAG interface, it's not unusual for it to take
tens of seconds to write a sector; this is an unacceptable cost when
the user knows the old contents aren't worth preserving. However, it
might be valuable to provide the original behavior as an option.)
Variables and Data
By default, variables and other data located in flash may not be
modified by the user, other than by use of the load command. For
example, if i is located in flash, then:
set var i = 10
will result in an error message. (As a consequence, GDB must be able
to determine which addresses lie in flash; otherwise, GDB, using a
stub with a 100% transparent interface to flash, would not be able to
issue an error.)
(The rationale for forbidding modification of variables is that most
such attempted modifications probably represent user
error. Furthermore, because setting a single variable requires erasing
and writing an entire flash sector, there might be some noticable
delay in implementing this request.)
Customization
GDB will have a new write-flash variable. This variable will have
three possible states:
* on
All writes to flash, including those to variables/data, are
permitted. This mode may be used to permit explicit "poke"
operations.
* load
The load command may write to flash; no other commands may write
to flash. This mode is the default.
* off
No commands may write to flash. This mode may be used for safety
when debugging a production system.
(In the future, we may want to adapt GDB's memory regions to allow
different parts of the address space to carry different settings.)
Implementation
There are many different flash devices in general use, from many
different manufacturers. The algorithms required to load new data into
flash vary from one chip to the next. Some devices support more than
one programming method; the best choice may depend on what resources
are available elsewhere in the system (RAM for buffering, and so on).
To contain this complexity, code specific to particular flash devices
is placed in the GDB stub, and GDB itself uses flash-specific but
device-independent remote protocol requests and replies to program the
flash.
The GDB load command initially tries to place the program image in
memory using ordinary memory write requests --- remote protocol M or X
requests. However, when the stub detects that GDB has attempted to
write to flash memory, it returns an error code of the form:
Eflash:addr;length
In this reply, addr and length are the start address and length of the
block of flash memory the write request attempted to modify. That is,
a write that overlaps any portion of a region of flash memory elicits
an Eflash response giving the location and size of the entire flash
memory. If the write request overlaps two areas of flash, the Eflash
request reports the lowest-addressed area. When the stub returns an
Eflash result, it should discard the entire write request; if portions
of the request cover non-flash memory, the contents of that memory
should be unchanged.
GDB's reaction to an Eflash response depends on the current
write-flash setting. If write-flash is off, or if it is load and the
write was not part of an image load, GDB displays an error message to
the user, and gives up on the command. Otherwise, GDB follows this
procedure::
* First, it sends zero or more packets of the following form to
the stub:
vFlashErase:addr;length
This directs the stub to erase all flash sectors that overlap
with the region from addr to length. The region need not be
aligned on flash sector boundaries, and may cover any number of
sectors; it is the stub's responsibility to expand the region to
the smallest set of sectors that completely contains the given
region.
* Then, it sends the data to be loaded into flash using packets of
the following form:
vFlashWrite:addr,length:data
where addr, length, and data all have the same form as they do
in the X binary download packet. The set of vFlashWrite packets
preceding a vFlashDone packet (see below) must not overlap, and
must appear in order of increasing address.
Writes may fall outside the regions given by the previously
transmitted vFlashErase packets, but the results are
unpredictable if a given area of flash is rewritten without
being erased.
* Finally, it indicates that all the data to be programmed has
been sent, with a packet of the form:
vFlashDone
Until the vFlashDone packet is received, the effects of prior
vFlashErase and vFlashWrite packets may be unpredictable.
Receipt of an Eflash response to a memory write indicates that the
stub supports the vFlashErase, vFlashWrite, and vFlashDone requests.
(Rationale:
* The Eflash response allows the stub to screen writes to
flash. It would certainly be possible for GDB itself to screen
writes by address, and it could be useful for GDB to have
detailed knowledge of the target's address space for other
reasons as well. However, stub-based screening is also an
important case to support, because it allows detailed knowledge
of the device to be held in software more tightly coupled to the
device than GDB is. And it's simple: on targets with a single
flash device, the stub need only make a simple address overlap
comparison and return a fixed-string Eflash response.
* By itself, the existing X packet is not suitable for flash
programming. To allow multiple X packets to load data into the
same flash sector, X would have to follow a save-erase-combine
procedure, which can be extremely slow on some devices. So a
separate erasure packet, at least, is necessary.
* It is possible to use the X packet for both flash programming
and ordinary memory writes, instead of introducing a new
vFlashWrite packet. However, the procedure for programming flash
can be very different from the procedure for writing ordinary
memory --- for example, many devices require some handshaking to
allow them time to complete the write --- so the code handling
the X packet would need to dispatch to different routines
depending on the address anyway. It seems simpler to restrict X
to writing ordinary memory, and introduce a new packet for flash
programming.
Also, using separate packets for memory writes (X, M) and flash
writes (vFlashWrite) makes it easy for the stub to tell whether
to respond to a write request with an Eflash result, or whether
to call the flash-programming code. Certainly, the stub could
keep track of whether the write occurs between a vFlashErase and
a vFlashDone, but that would make the protocol stateful, and
thus a bit more fragile.
Finally, using separate packets makes it unnecessary for the
stub to handle writes overlapping different kinds of memory,
removing some complexity.
* We allow writes without preceding erases in order to support
cases where the user understands the mechanics of their flash
device, and knows that a given combination of writes will work
as intended. For example, a flash-without-erase command could
allow a careful user to combine several program images on a
single flash sector.
* Some devices allow faster programming in blocks. Requiring
vFlashWrite packets to appear in order of increasing address and
requiring GDB to send a vFlashDone packet when all the data has
been transmitted allow the stub to buffer data into blocks, and
then write them when the buffer is full, or when all data has
been transmitted.)