3.1 Const-in-program-memory Feature
For some devices, the MPLAB XC8 compiler can employ a const-in-progmem feature that automatically
places any const
-qualified objects with static storage duration into
program memory rather than into RAM. When enabled, this feature affects where such
objects are placed, how they are accessed, and the size and format of pointers that
might indirectly access them.
Description
- Devices that map their entire Flash memory into the data space (such as those in the tinyAVR® or AVR XMEGA® 3 families),
- Devices with configurable Flash mapping, which can map part of their Flash memory into the data space (such as those in the AVRDA family), and
- Devices that do not map any part of Flash memory into the data space.
__AVR_PROGMEM_IN_DATA_ADDRESS_SPACE__
is defined by the
compiler for devices in group (1) or (2), provided the
-mno-const-data-in-progmem
has not been used. The macro
__AVR_CONST_DATA_IN_CONFIG_MAPPED_PROGMEM__
is defined for
devices in group (2), provided the
-mno-const-data-in-config-mapped-progmem
option has not been
used. To see macros defined by the compiler, use the -E
and
-dM
options with your project and check the definitions printed
in the standard output stream.Group (1) Devices
The const-in-progmem feature is not required and not used with any device from
group (1), which map their program memory into the data space. Programs built
for such devices have const
-qualified objects with static
storage duration placed into the mapped Flash region where they can be read from
data memory. For these devices, this compiler feature is not necessary, and the
placement and access of const
-qualified objects is similar in
both compilers.
Group (2) Devices
When building for devices in group (2), which map a block of program memory into
the data space, the const-in-config-mapped-progmem feature can be used to
control where const
-qualified objects are placed and accessed.
This feature is enabled by the
-mconst-data-in-config-mapped-progmem
option, or by default
if no option is specified. When enabled, devices in group (2) are treated
similarly to devices in group (1). All const
-qualified objects
with static storage duration are placed into a Flash region that is mapped by
the runtime startup code, and which can be read from data memory. The size of
this mapped Flash region, however, is limited to a single 32 KB block that is
chosen by the linker.
This behavior can be disabled using the
-mno-const-data-in-config-mapped-progmem
option, so that
const
-qualified objects are stored in and read from
non-mapped program memory, and the device is then treated similarly to those in
group (3). And as is the case for other devices in group (3), the
const-in-progmem feature can be disabled entirely using the
-mno-const-data-in-progmem
option, forcing the compiler to
copy const
-qualified data to RAM, where it can be read. In this
case, the behavior is compatible with AVR GCC.
Group (3) Devices
When building for devices in group (3), which do not map program memory in the
data space, the const-in-progmem feature can be used to control where
const
-qualified objects are placed and accessed. This
feature is enabled by the -mconst-data-in-progmem
option, or by
default if no option is specified. It can be disabled by using the
-mno-const-data-in-progmem
option.
const
-qualified objects are placed in program memory but are
copied to a data memory location by the runtime startup code where they are
accessed at runtime. The same is true for string literals. Additionally, data
pointers not using any non-standard specifiers will indirectly access RAM
objects, with any use of the const
qualifier indicating only
that the target is read-only. To be able to read program memory indirectly, the
pointer must use a non-standard specifier, such as __memx
or
__flashn
. For
example:const char * cp1; // I can read objects in RAM
char * cp2; // I can read and write objects in RAM
const __memx char * cp3; // I can read objects in RAM or program memory
One
consequence of this is that any function that takes a pointer argument (either
const
or non-const
qualified target), for
example, can only be passed the address of objects in the data memory space.
This is particularly relevant for functions in the standard C library. An
alternate function must be supplied and called if the action has to be performed
using the address of an object in program memory.When the const-in-progmem feature is enabled (the default action if no
option has been specified), const
-qualified objects are instead
placed in and read from program memory. String literals are handled similarly.
Such objects are read using alternate instruction sequences that are typically
longer than the equivalent sequences that read from data memory, but less data
memory is used by the program.
In addition, pointers to const
-qualified types can indirectly
read objects in either data or program memory, and a bit encoded into addresses
assigned to such pointers determines which space is to be accessed. Essentially,
these pointers act like their type had been specified with
__memx
in addition to the const
qualifier.
Code to dereference the pointer is larger, but because they can access either
memory space, no duplication of pointers or functions is required to handle
objects in each space.
Migration
- Disable the const-in-progmem feature and ensure legacy library routines that read from program memory are linked in with the project code.
- Leave the const-in-progmem
feature enabled and ensure that the project source code is not making
incorrect assumptions about the location of
const
-qualified objects.
Disable the const-in-progmem Feature
To follow the first migration approach, disable this feature by deselecting the
"Enables access of const variables directly from flash" checkbox in the “XC8
Global option” category in the MPLAB X IDE Project Properties dialog. Disabling
this feature will ensure that const
-qualified objects with
static storage duration are copied to and accessed from data memory. Any pointer
to a const
-qualified type will read only from data memory.
AVR-GCC provides program-memory alternatives to some standard C library routines
that take pointer arguments. These alternatives use a _P
suffix
and are written so that they will read from program memory, for example
strlen_P()
, specified in
<avr/pgmspace.h>
, which can be called to find the
length of a string in program memory. These functions are not supplied with
MPLAB XC8; however, you can download the AVR-libc to obtain the source files for
these functions.
This Online Microchip Help web page documents the AVR-libc. It also contains a link to the AVR Libc web page where you can find a link to the download area. Download the required AVR-libc archive and unzip it to a local directory. You can include source files into your projects directly from this local directory or copy the required source files into your project directory and then add them to your project from that location. Note that some functions are written in assembly code.
Your program will need declarations from the
<avr/pgmspace.h>
header, which can be found in the
downloaded library. Note that this header itself also includes other header
files. You can provide the MPLAB XC8 preprocessor with the path to this header
in the unzipped local directory, or alternatively the path to copies of this
header in your project directory. The -I
compiler option allows
the path to be specified, and this can be specified in the “Include directories”
field, found in the “Preprocessing and messages” options within the “XC8
Compiler” category of the Project Properties dialog. AVR-libc headers should be
scanned first before the standard headers; however, this will be the case if you
add the search path as described above.
On rare occasions, there might be conflicting information in code or headers provided by AVR-libc and those provided by the libraries shipped with MPLAB XC8. If the compiler sees a declaration from an AVR-libc header but links in the corresponding definition from the compiler-shipped library (or vice versa), the project might fail to build or execute correctly if there is a mismatch in that information. Where there is such a conflict, you could selectively copy those declarations required from AVR-libc headers and create your own header file from these.
strcmp_P()
) source file and disable
the const-in-progmem feature (highlighted)
strcmp_P()
function call cannot
be simply swapped to a call to strcmp()
in the default standard
C library, the source file (strcmp_P.S
) for the
strcmp_P
function was copied from the ARV-libc archive into
the project directory and added to the project's Source files folder in the
MPLAB X IDE (as shown in the above figure). The source code contained in this
file includes the header <macros.inc>
(which itself
includes <sectionname.h>
). These headers were also copied
to a subdirectory in the project directory and the path to that directory
specified in the “Include directories” field of the Project Properties dialog.
The prototype for the strcmp_P()
function can be found in the
<avr/pgmspace.h>
header. This header was also copied
to the
subdirectory./* const-in-progmem Feature Disabled */
#include <string.h>
#include <pgmspace.h>
const char ro_ram_1[] = "another"; // I am in RAM
const char ro_ram_2[] = "string"; // So am I
const PROGMEM char ro_prog_1[] = "another"; // I am in program memory
volatile int diffs;
int main(void) {
if (strcmp(ro_ram_1, ro_ram_2)) // compare RAM to RAM
diffs++;
if (strcmp_P(ro_ram_1, ro_prog_1)) // compare RAM to prog memory
diffs++;
while (1) {
}
}
Leave the const-in-progmem Feature Enabled
To follow the second migration approach, ensure the const-in-progmem feature is
enabled by selecting the "Enables access of const variables directly from flash"
checkbox in the “XC8 Global option” category in the MPLAB X IDE Project
Properties dialog. With this feature enabled, objects defined using the
const
qualifier and that have static storage duration will
be placed in program memory and any pointer with a
const
-qualified target will be able to read from either data or
program memory space.
Migrated projects using this feature typically fail when pointers to
non-const
objects are assigned addresses of objects in
program memory. Code or linker options that expect any
const
-qualified object to be present in RAM will fail. Code
that assumes a pointer has a certain address might fail. Direct access of
const
objects will work as expected.
readChar()
and
the arguments of the last two
calls.char readChar(char * cp) {
return * cp;
}
char foobar_in_ram = 0x33;
const char foobar_as_const[4] = { 3, 2, 4, 6 };
int main(void) {
volatile char result;
result = readChar(&foobar_in_ram);
result = readChar(&foobar_as_const[2]); // this won't work
result = readChar("hi there"); // neither will this
}
The temptation is to cast the address of these calls so they match
the prototype of the function, as
follows: result = readChar((char *)&foobar_as_const[2]); // No!
result = readChar((char *)"hi there"); // No!
Such a
change will indeed suppress the errors; however the code will fail when
executing. If a pointer needs to be able to access program memory, ensure it is
a pointer to a const
-qualified type, that is, the prototype for
readChar()
needs to be char readChar(const char *
cp)
.strcmp()
or strcmp_P()
to compare strings;
the standard strcmp()
function provided in the standard library
should be used to compare any string, regardless of where it is
located./* const-in-progmem Feature Enabled */
#include <string.h>
char ro_ram_1[] = "another"; // I am in RAM
char ro_ram_2[] = "string"; // So am I
const PROGMEM char ro_prog_1[] = "another"; // I am in program memory
volatile int diffs;
int main(void) {
if (strcmp(ro_ram_1, ro_ram_2)) // compare RAM to RAM
diffs++;
if (strcmp(ro_ram_1, ro_prog_1)) // compare RAM to prog memory
diffs++;
while (1) {
}
}
%S
printf-family
conversion specifier provided by AVR-libc is also not required when the
const-in-progmem feature is enabled. Use the standard %s
specifier to print any string, for
example: printf("These are the RAM characters: %s\n", ro_ram_1);
printf("These are the ROM characters: %s\n", ro_prog_1);