9.11 Variable Attributes

The compiler keyword __attribute__ allows you to specify special attributes of variables or structure fields. This keyword is followed by an attribute specification inside double parentheses.

To specify multiple attributes, separate them by commas within the double parentheses, for example:

__attribute__ ((aligned (16), packed)).

Note: It is important to use variable attributes consistently throughout a project. For example, if a variable is defined in file A with the aligned attribute, and declared extern in file B without aligned, then a link error may result.

address (addr)

Specify an absolute virtual address for the variable. This attribute can be used in conjunction with a section attribute.

Note: For a data variable on a target device without an L1 cache, the address is typically in the range [0xA0000000,0xA00FFFFC], as defined in the linker script as the kseg1_data_mem region. For data variables on a target feature an L1 data cache, the address is typically in the range [0x80000000,0x800FFFFC] as defined in the linker script as the kseg0_data_mem region. Take special care to use the correct kseg region for your device or more than one variable might be allocated to the same physical address.

This attribute can be used to start a group of variables at a specific address:

  int foo __attribute__((section("mysection"),address(0xA0001000)));
  int bar __attribute__((section("mysection")));
  int baz __attribute__((section("mysection")));

Keep in mind that the compiler performs no error checking on the specified address. The section will be located at the specified address regardless of the memory-region ranges listed in the linker script or the actual ranges on the target device. This application code is responsible for ensuring that the address is valid for the target device and application.

Also, be aware that variables attributed with an absolute address are not accessed via GP-relative addressing. This means that they may be more expensive to access than non-address attributed variables.

In addition, to make effective use of absolute sections and the best-fit allocator, standard program-memory and data-memory sections should not be mapped in the linker script. The built-in linker script does not map most standard sections such as the .text, .data, .bss, or .ramfunc section. By not mapping these sections in the linker script, we allow these sections to be allocated using the best-fit allocator rather than the sequential allocator. Sections that are unmapped in the linker script can flow around absolute sections whereas sections that are linker-script mapped are grouped together and allocated sequentially, potentially causing conflicts with absolute sections.

Finally, note that “small” data and bss (.sdata, .sbss, etc.) sections are still mapped in the built-in default linker script. This is because “small” data variables must be grouped together so that they are within range of the more efficient GP-relative addressing mode. To avoid conflict with these linker-script mapped sections, choose high addresses for your absolute-address variables.

Note: In almost all cases, you will want to combine the address attribute with the space attribute to indicate code or data with space(prog) or space(data), respectively. Also, see the description for the attribute space.

aligned (n)

The attributed variable will be aligned on the next n byte boundary.

The aligned attribute can also be used on a structure member. Such a member will be aligned to the indicated boundary within the structure.

If the alignment value n is omitted, the alignment of the variable is set 8 (the largest alignment value for a basic data type).

Note that the aligned attribute is used to increase the alignment of a variable, not reduce it. To decrease the alignment value of a variable, use the packed attribute.

cleanup (function)

Indicate a function to call when the attributed automatic function scope variable goes out of scope.

The indicated function should take a single parameter, a pointer to a type compatible with the attributed variable, and have void return type.

coherent

The coherent variable attribute causes the compiler/linker to place the variable into a unique section that is allocated to the kseg1 region, rather than the kseg0 region (which is the default on L1 cached devices). This means that the variable is accessed through the uncached address.

For devices featuring an L1 data cache, data variables are allocated to the KSEG0 data-memory region (kseg0_data_mem), making it accessible through the L1 cache. Likewise, the linker-allocated heap and stack are allocated to the KSEG0 region.

The is a coherent variable attribute that allows you to create a DMA buffer allocated to the kseg1_data_mem region:

unsigned int __attribute__((coherent)) buffer [1024];

When combining the coherent attribute with the address attribute, be sure to use the default data-memory region address for the device. On devices featuring an L1 data cache, the default data-memory region is kseg0_data_mem:

unsigned int __attribute__((coherent,address(0x80001000))) buffer[1024]

The __pic32_alloc_coherent(size_t) and __pic32_free_coherent(void*) functions allocate and free memory from the uncached kseg1_data_mem region. The default stack is allocated to the cached kseg0_data_mem region, but you may want to create an uncached DMA buffer, so you can use these functions to allocate an uncached buffer. These functions call the standard malloc()/free() functions, but the pointers that they use are translated from kseg0 to kseg1.

#include<xc.h>
void jak(void){
   char* buffer = __pic32_alloc_coherent(1024);
   if (buffer){
     /* do somehing */
   }
   else{
     /* handle error */
   }
   if (buffer){
     __pic32_free_coherent(buffer);
   }
}

deprecated

deprecated (msg)

When a variable specified as deprecated is used, a warning is generated. The optional msg argument, which must be a string, will be printed in the warning, if present.

externally_visible

This attribute when used with a global object, nullifies the effect of the -fwhole-program command-line option, so the object remains visible outside the current compilation unit. This might prevent certain optimizations from being performed on the object.

noload

The noload attribute causes the variable or function to be placed in a section that has the noload attribute set. This attribute tells consumers of the ELF file not to load the contents of the section. This attribute can be useful when you just want to reserve memory for something, but you don't want to clear or initialize memory.

persistent

The persistent attribute specifies that the variable should not be initialized or cleared at startup. Use a variable with the persistent attribute to store state information that will remain valid after a device Reset. The persistent attribute causes the compiler to place the variable in special .bss-like section that does not get cleared by the default startup code. Because the section is always in data space, this attribute is not compatible with the space attribute.

int last_mode __attribute__((persistent));

The persistent attribute implies the coherent attribute. That is, persistent attributed variables are accessed via the uncached address.

packed

The attributed variable or structure member will have the smallest possible alignment. That is, no alignment padding storage will be allocated for the declaration. Used in combination with the aligned attribute, packed can be used to set an arbitrary alignment restriction greater or lesser than the default alignment for the type of the variable or structure member.

section ("section-name")

Place the variable into the named section.

For example,

unsigned int dan __attribute__ ((section (".quixote")))

Variable dan will be placed in section .quixote.

The -fdata-sections command line option has no effect on variables defined with a section attribute unless unique_section is also specified.

space(memory-space)

The space attribute can be used to direct the compiler to allocate a variable in a specific memory space. Valid memory spaces are prog for program memory, data for data memory, and serial_mem for serial memory such as SPI Flash. The data space is the default space for non-const variables.

The prog, data, and serial_mem spaces normally correspond to the kseg0_prog_mem, ksegN_data_mem, and serial_mem memory regions, respectively, as specified in the default device-specific linker scripts.

This attribute also controls how initialized data is handled. The linker generates an entry in the data-initialization template for the default space(data). But, it does not generate an entry for space(prog) or space(serial_mem), since the variable is located in non-volatile memory. Typically, this means that space(data) applies to variables that will be initialized at runtime startup; while space(prog) and space(serial_mem) apply to variables that will be programmed by an in-circuit programmer or a bootloader.

For example:
const unsigned int __attribute__((space(prog))) jack = 10;
const unsigned int __attribute__((space(serial_mem))) zori = 1;
signed int __attribute__((space(data))) oz = 5;

unique_section

Place the variable in a uniquely named section, just as if -fdata-sections had been specified. If the variable also has a section attribute, use that section name as the prefix for generating the unique section name.

For example,

int tin __attribute__ ((section (".ofcatfood"), unique_section)

Variable tin will be placed in section .ofcatfood.

unused

Indicate to the compiler that the variable may not be used. The compiler will not issue a warning for this variable if it is not used.

used

Indicate to the compiler that the object is always used and storage must be allocated for the object, even if the compiler cannot see a reference to it. For example, if inline assembly is the only reference to an object.

weak

The weak attribute causes the declaration to be emitted as a weak symbol. A weak symbol indicates that if a global version of the same symbol is available, that version should be used instead.

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.