10.10 Variable Attributes
The MPLAB XC16 C Compiler uses attributes to indicate memory allocation, type and other configuration for variables, structure members and types. Other attributes are available for functions, and these are described in the Function Attributes section. Qualifiers are used independently to attributes (see the Compiler-Specific Type Qualifiers section). They only indicate how objects are accessed, but must be used where necessary to ensure correct code operation.
The compiler keyword __attribute__
allows you to specify
the attributes of objects. This keyword is followed by an attribute specification inside
double parentheses. The attributes below are currently supported for variables.
You may also specify attributes with __
(double
underscore) preceding and following each keyword (e.g., __aligned__
instead of aligned
). This allows you to use them in header files without
being concerned about a possible macro of the same name.
To specify multiple attributes, separate them by commas within the double parentheses, for example:
__attribute__ ((aligned (16), packed)).
far
attribute and declared extern
in file B without
far
, then a link error may result.address (addr)
The address
attribute specifies an absolute address for
the variable. This attribute can be used in conjunction with a section
attribute. This can be used to start a group of variables at a specific address:
int foo __attribute__((section("mysection"),address(0x900)));
int bar __attribute__((section("mysection")));
int baz __attribute__((section("mysection")));
A variable with the address
attribute cannot be placed
into the auto_psv
space (see the space()
attribute or the
-mconst-in-code
option); attempts to do so will cause a warning and the
compiler will place the variable into the PSV space. If the variable is to be placed into a
PSV section, the address should be a program memory address.
aligned (alignment)
This attribute specifies a minimum alignment for the variable, measured in bytes. The alignment must be a power of two. For example, the declaration:
int x __attribute__ ((aligned (16))) = 0;
causes the compiler to allocate the global variable x
on
a 16-byte boundary. On the dsPIC DSC device, this could be used in conjunction with an
asm
expression to access DSP instructions and addressing modes that
require aligned operands.
As in the preceding example, you can explicitly specify the alignment (in bytes) that you wish the compiler to use for a given variable. Alternatively, you can leave out the alignment factor and just ask the compiler to align a variable to the maximum useful alignment for the dsPIC DSC device. For example, you could write:
short array[3] __attribute__ ((aligned));
Whenever you leave out the alignment factor in an aligned attribute specification, the compiler automatically sets the alignment for the declared variable to the largest alignment for any data type on the target machine – which in the case of the dsPIC DSC device is two bytes (one word).
The aligned
attribute can only increase the alignment;
you can decrease it by specifying packed
(see below). The
aligned
attribute conflicts with the reverse
attribute. It is an error condition to specify both.
The aligned
attribute can be combined with the
section
attribute. This will allow the alignment to take place in a
named section. By default, when no section is specified, the compiler will generate a
unique section for the variable. This will provide the linker with the best opportunity for
satisfying the alignment restriction without using internal padding that may happen if
other definitions appear within the same aligned section.
boot
This attribute can be used to define protected variables in Boot Segment (BS) RAM:
int __attribute__((boot)) boot_dat[16];
Variables defined in BS RAM will not be initialized on startup. Therefore
all variables in BS RAM must be initialized using inline code. A diagnostic will be
reported if initial values are specified on a boot
variable.
An example of initialization is as follows:
int __attribute__((boot)) time = 0; /* not supported */
int __attribute__((boot)) time2;
void __attribute__((boot)) foo()
{
time2 = 55; /* initial value must be assigned explicitly */
}
deprecated
The deprecated
attribute causes the declaration to which
it is attached to be specially recognized by the compiler. When a
deprecated
function or variable is used, the compiler will emit a
warning.
A deprecated
definition is still defined and therefore
present in any object file. For example, compiling the following file:
int __attribute__((__deprecated__)) i;
int main() {
return i;
}
will produce the warning:
deprecated.c:4: warning: `i’ is deprecated (declared
at
deprecated.c:1)
i
is still defined in the resulting object file in the
normal way.
eds
In the attribute context, the eds
(extended data space)
attribute indicates to the compiler that the variable will be allocated anywhere within
data memory. Variables with this attribute will likely also have the
__eds__
type qualifier (see the Extended Data Space Access section) for the
compiler to properly generate the correct access sequence. Not that the
__eds__
qualifier and the eds
attribute are closely
related, but not identical. On some devices, eds may need to be specified when allocating
variables into certain memory spaces such as space (ymemory)
or
space (dma)
as this memory may only exist in the extended data
space.
fillupper
This attribute can be used to specify the upper byte of a variable stored
into a space(prog)
section.
For example:
int foo[26] __attribute__((space(prog),fillupper(0x23))) = {
0xDEAD };
will fill the upper bytes of array foo
with 0x23, instead
of 0x00. foo[0]
will still be initialized to 0xDEAD.
The command line option -mfillupper=0x23
will perform the
same function.
far
The far
attribute tells the compiler that the variable
will not necessarily be allocated in near (first 8 KB) data space, (i.e., the variable can
be located anywhere in data memory between 0x0000 and 0x7FFF).
mode (mode)
This attribute specifies the data type for the declaration as whichever
type corresponds to the mode mode
. This in effect lets you request
an integer or floating point type according to its width. Valid values for
mode
are as follows:
Mode | Width | Compiler Type |
---|---|---|
QI | 8 bits | char |
HI | 16 bits | int |
SI | 32 bits | long |
DI | 64 bits | long long |
SF | 32 bits | float |
DF | 64 bits | long double |
This attribute is useful for writing code that is portable across all supported compiler targets. For example, the following function adds two 32-bit signed integers and returns a 32-bit signed integer result:
typedef int __attribute__((__mode__(SI))) int32;
int32
add32(int32 a, int32 b)
{
return(a+b);
}
You may also specify a mode of byte
or
__byte__
to indicate the mode corresponding to a one-byte integer,
word
or __word__
for the mode of a one-word integer,
and pointer
or __pointer__
for the mode used to represent
pointers.
near
The near
attribute tells the compiler that the variable
is allocated in near data space (the first 8 KB of data memory). Such variables can
sometimes be accessed more efficiently than variables not allocated (or not known to be
allocated) in near data space.
int num __attribute__ ((near));
noload
The noload
attribute indicates that space should be
allocated for the variable, but that initial values should not be loaded. This attribute
could be useful if an application is designed to load a variable into memory at run time,
such as from a serial EEPROM.
int table1[50] __attribute__ ((noload)) = { 0 };
packed
The packed
attribute specifies that a structure member
should have the smallest possible alignment unless you specify a larger value with the
aligned
attribute.
Here is a structure in which the member x
is packed, so
that it immediately follows a
, with no padding for alignment:
struct foo
{
char a;
int x[2] __attribute__ ((packed));
};
packed
attribute to avoid run-time addressing errors.page
This attribute specifies that the object cannot exceed a page boundary.
The page boundary applied depends upon where the object is allocated. An object located in
a psv
space cannot cross a 32K boundary; an object located in
prog
space cannot cross a 64K boundary.
unsigned int var[10] __attribute__
((space(auto_psv)));
The space(auto_psv)
or space(psv)
attribute will use a single memory page by default.
__eds__ unsigned int var[10] __attribute__ ((eds,
page));
When dealing with eds
, please refer to Extended Data Space Access for more
information.
persistent
The persistent
attribute specifies that the variable
should not be initialized or cleared at startup. A variable with the
persistent
attribute could be used to store state information that will
remain valid after a device Reset.
int last_mode __attribute__ ((persistent));
Persistent data is not normally initialized by the C run-time. However, from a cold-restart, persistent data may not have any meaningful value. This code example shows how to safely initialize such data:
#include <p24Fxxxx.h>
int last_mode __attribute__((persistent));
int main()
{
if ((RCONbits.POR == 0) &&
(RCONbits.BOR == 0)) {
/* last_mode is valid */
} else {
/* initialize persistent data */
last_mode = 0;
}
}
This attribute can only be used in conjunction with a RAM resident object, i.e. not in FLASH.
preserved
The preserved
attribute can be applied to a variable to
indicate that this variable's value should be preserved on a restart. A restart is a
user-defined event which can be different from a cold or warm reset. Preserved variables
require information from a previously linked executable in order to function; please see
the linker option --preserved=
.
priority(n)
The priority
attribute can be applied to a variable to
group initializations together. n must be between 1 and 65535, with 1 being the highest
level. All initializations with the same priority are initialized before moving onto the
next priority level. Level 1 variables are initialized first and variables without a
priority level are initialized last. The attribute can also be applied to
void
functions (void
result and argument types); in
this case the function(s) for level n will be executed immediately after all the
initializations for level n are complete.
reverse (alignment)
The reverse
attribute specifies a minimum alignment for
the ending address of a variable, plus one. The alignment is specified in bytes and must be
a power of two. Reverse-aligned variables can be used for decrementing modulo buffers in
dsPIC DSC assembly language. This attribute could be useful if an application defines
variables in C that will be accessed from assembly language.
int buf1[128] __attribute__ ((reverse(256)));
The reverse
attribute conflicts with the
aligned
and section
attributes. An attempt to name a
section for a reverse-aligned variable will be ignored with a warning. It is an error
condition to specify both reverse
and aligned
for the
same variable. A variable with the reverse
attribute cannot be placed into
the auto_psv
space (see the space()
attribute or the
-mconst-in-code
option); attempts to do so will cause a warning and the
compiler will place the variable into the PSV space.
section ("section-name")
By default, the compiler places the objects it generates in sections such
as .data
and .bss
. The section
attribute
allows you to override this behavior by specifying that a variable (or function) lives in a
particular section.
struct a { int i[32]; };
struct a buf __attribute__((section("userdata"))) =
{{0}};
secure
This attribute can be used to define protected variables in Secure Segment (SS) RAM:
int __attribute__((secure)) secure_dat[16];
Variables defined in SS RAM will not be initialized on startup. Therefore
all variables in SS RAM must be initialized using inline code. A diagnostic will be
reported if initial values are specified on a secure
variable.
String literals can be assigned to secure variables using inline code, but they require extra processing by the compiler. For example:
char *msg __attribute__((secure)) = "Hello!\n"; /* not supported */
char *msg2 __attribute__((secure));
void __attribute__((secure)) foo2()
{
*msg2 = "Goodbye..\n"; /* value assigned explicitly */
}
In this case, storage must be allocated for the string literal in a memory space which is accessible to the enclosing secure function. The compiler will allocate the string in a psv constant section designated for the secure segment.
sfr (address)
The sfr
attribute tells the compiler that the variable is
an SFR and may also specify the run-time address of the variable, using the
address
parameter.
extern volatile int __attribute__
((sfr(0x200)))u1mod;
The use of the extern
specifier is required in order to
not produce an error.
sfr
attribute is used
only in processor header files. To define a general user variable at a specific address use
the address
attribute in conjunction with near
or
far
to specify the correct addressing mode.shared
Used with co-resident applications. The variable may be used outside of the application. A data item will be initialized at startup of any application in the co-resident set.
space (space)
space
attribute can be used to direct the compiler to allocate a
variable in specific memory spaces. Memory spaces are discussed further in Address Spaces The following arguments
to the space attribute are accepted:data
– Allocate the variable in general data space. Variables in general data space can be accessed using ordinary C statements. This is the default allocation.dataflash
– Allocate the variable in dataflash.xmemory - dsPIC30F, dsPIC33EP/F DSCs only
– Allocate the variable in X data space. Variables in X data space can be accessed using ordinary C statements. An example ofxmemory
space allocation is:int x[32] __attribute__ ((space(xmemory)));
ymemory - dsPIC30F, dsPIC33EP/F DSCs only
– Allocate the variable in Y data space. Variables in Y data space can be accessed using ordinary C statements. An example ofymemory
space allocation is:int y[32] __attribute__ ((space(ymemory)));
prog
– Allocate the variable in program space, in a section designated for executable code. Variables in program space can not be accessed using ordinary C statements. They must be explicitly accessed by the programmer, usually using table-access inline assembly instructions, the program space visibility window, or by the methods described in Access of Objects in Program Memoryauto_psv
Allocate the variable in program space, in a compiler-managed section designated for automatic program space visibility window access. Variables in
auto_psv
space can be read (but not written) using ordinary C statements, and are subject to a maximum of 32K total space allocated. When specifyingspace(auto_psv)
, it is not possible to assign a section name using thesection
attribute; any section name will be ignored with a warning. A variable in theauto_psv
space cannot be placed at a specific address or given a reverse alignment.Note: Variables placed in theauto_psv
section are not loaded into data memory at startup. This attribute may be useful for reducing RAM usage.dma - PIC24E/H MCUs, dsPIC33E/F DSCs only
– Allocate the variable in DMA memory. Variables in DMA memory can be accessed using ordinary C statements and by the DMA peripheral.__builtin_dmaoffset()
and__builtin_dmapage()
can be used to find the correct offset for configuring the DMA peripheral. See Built-in Functions for details.#include <p24Hxxxx.h> unsigned int BufferA[8] __attribute__((space(dma))); unsigned int BufferB[8] __attribute__((space(dma))); int main() { DMA1STA = __builtin_dmaoffset(BufferA); DMA1STB = __builtin_dmaoffset(BufferB); /* ... */ }
psv
– Allocate the variable in program space, in a section designated for program space visibility window access. The linker will locate the section so that the entire variable can be accessed using a single setting of the PSVPAG register. Variables in PSV space are not managed by the compiler and can not be accessed using ordinary C statements. They must be explicitly accessed by the programmer, usually using table-access inline assembly instructions, or using the program space visibility window.eedata - PIC24F, dsPIC30F/33F DSCs only
– Allocate the variable in EEPROM Data (EEData) space. Variables in EEData space can not be accessed using ordinary C statements. They must be explicitly accessed by the programmer, usually using table-access inline assembly instructions, or using the program space visibility window.pmp
– Allocate the variable in off chip memory associated with the PMP peripheral. For complete details please see the Parallel Master Port Access section.external
– Allocate the variable in a user defined memory space. For complete details please see theExternal Memory Access section.
transparent_union
This attribute, attached to a function parameter which is a
union
, means that the corresponding argument may have the type of any
union member, but the argument is passed as if its type were that of the first union
member. The argument is passed to the function using the calling conventions of the first
member of the transparent union, not the calling conventions of the union itself. All
members of the union must have the same machine representation; this is necessary for this
argument passing to work properly.
unordered
The unordered
attribute indicates that the placement of
this variable may move relative to other variables within the current C source file.
const int __attribute__ ((unordered)) i;
unsupported(message)
This attribute will display a custom message when the object is used.
int foo __attribute__((unsupported("This object is
unsupported"));
Access to foo
will generate a warning message.
unused
This attribute, attached to a variable, means that the variable is meant to be possibly unused. The compiler will not produce an unused variable warning for this variable.
update
The update attribute can be applied to a variable to indicate that this
variable should be initialized on a restart. This is particularly useful if
-mpreserve-all
or --preserve-all
is being used.
weak
The weak
attribute causes the declaration to be emitted
as a weak symbol. A weak symbol may be superseded by a global definition. When
weak
is applied to a reference to an external symbol, the symbol is not
required for linking. For example:
extern int __attribute__((__weak__)) s;
int foo() {
if (&s) return s;
return 0; /* possibly some other value */
}
In the above program, if s
is not defined by some other
module, the program will still link but s
will not be given an address.
The conditional verifies that s
has been defined (and returns its value if
it has). Otherwise ‘0’
is returned. There are many uses for this feature,
mostly to provide generic code that can link with an optional library.
The weak
attribute may be applied to functions as well as
variables:
extern int __attribute__((__weak__)) compress_data(void *buf);
int process(void *buf) {
if (compress_data) {
if (compress_data(buf) == -1) /* error */
}
/* process buf */
}
In the above code, the function compress_data
will be
used only if it is linked in from some other module. Deciding whether or not to use the
feature becomes a link-time decision, not a compile time decision.
The affect of the weak
attribute on a definition is more
complicated and requires multiple files to describe:
/* weak1.c */
int __attribute__((__weak__)) i;
void foo() {
i = 1;
}
/* weak2.c */
int i;
extern void foo(void);
void bar() {
i = 2;
}
main() {
foo();
bar();
}
Here the definition in weak2.c
of i
causes the symbol to become a strong definition. No link error is emitted and both
i
’s refer to the same storage location. Storage is allocated for
weak1.c
’s version of i
, but this space is not
accessible.
There is no check to ensure that both versions of i
have
the same type; changing i
in weak2.c
to be of type
float
will still allow a link, but the behavior of function
foo
will be unexpected. foo
will write a value into
the least significant portion of our 32-bit float value. Conversely, changing the type of
the weak definition of i
in weak1.c
to type
float
may cause disastrous results. We will be writing a 32-bit
floating point value into a 16-bit integer allocation, overwriting any variable stored
immediately after our i
.
In the cases where only weak
definitions exist, the
linker will choose the storage of the first such definition. The remaining definitions
become inaccessible.
The behavior is identical, regardless of the type of the symbol; functions and variables behave in the same manner.