Skip to content

Commit d926911

Browse files
KerogitAlan C. Assis
authored andcommitted
arch/avr/avrdx: do not copy const variables into RAM
AVR uses Hardward architecture with separate address space for program memory (flash) and data memory (RAM). Normal program flow can only access data memory which means that all variables - including const variables - have to be copied into RAM to be accessible. (This happens automatically during startup.) It is possible to work around this limitation in software but that can have severe impact on performance and/or API complexity. It is hardly feasible to change NuttX interfaces in a way that would allow to make use of this workaround. On newer AVR families, there is an alternative option enabled by this patch. These chips map part of their program memory (a 32kB window) into data memory address space. This patch leverages this feature and adds support for placing const variables into the mapped window. No copy to RAM is done for them. Const variables are therefore loaded directly from flash (not consuming RAM) while still being available to be used by any NuttX interface. Linker script of breadxavr board is changed to make use of these changes. Tested by verifying string addresses - parameters in printf call in a custom application (and also by running the application and verifying its output.) Documentation tested by build. Signed-off-by: Kerogit <[email protected]>
1 parent 14e4466 commit d926911

File tree

8 files changed

+267
-8
lines changed

8 files changed

+267
-8
lines changed

Documentation/platforms/avr/avrdx/boards/breadxavr/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _breadxavr_board:
2+
13
========================
24
AVR128DA28 on breadboard
35
========================

Documentation/platforms/avr/common/constants-in-progmem.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,48 @@ Note that both ``IOBJ`` and ``IPTR`` need to be activated by
178178
If this configuration option is not set, both macros are defined
179179
to be empty and all strings will be copied to RAM (performance penalty
180180
discussed above is therefore removed as well.)
181+
182+
Using memory-mapped flash
183+
=========================
184+
185+
Newer AVR devices - tinyAVR and AVR DA/DB family - have their program
186+
memory mapped into upper 32kB half of data memory address space.
187+
(If the program memory size exceeds 32kB, only a 32kB-sized window
188+
is mapped. This is controlled by NVM peripheral within the chip.
189+
On current chips, the top window is mapped by default.)
190+
191+
This can be leveraged in a way that makes these AVR devices behave
192+
as a von Neumann architecture. With proper configuration in a linker
193+
script, all constants can be placed into the mapped program memory
194+
region where they will be accessible for both load from program memory
195+
instructions and load from data address space instructions.
196+
197+
As long as these constants fit into the 32kB window, this is a best
198+
available option on devices that support it. It combines advantages
199+
of all previous options and doesn't have any of their drawbacks.
200+
The performance penalty is negligible (flash read is few cycles slower
201+
than RAM read), RAM is not consumed and all variables are fully
202+
available to be used as parameters for any kernel interface.
203+
204+
Unlike previous options, using this one is fully controlled by board's
205+
linker script. The linker script needs to place the constants
206+
(eg. ``rodata`` section) to appropriate memory location.
207+
208+
Despite that, there is still a configuration option
209+
:menuselection:`System Type --> Use memory-mapped access to flash`,
210+
which is selected by default on devices that support this method
211+
of not copying data from program memory to RAM. Setting it unlocks
212+
additional configuration options
213+
:menuselection:`Size of .rodata FLMAP section` and
214+
:menuselection:`Offset of .rodata FLMAP section` which may be used
215+
to further configure section sizes. Note that these values are
216+
only made available to the linker and board's linker script needs
217+
to be designed to obey them.
218+
219+
To have these configuration options available, the board needs
220+
to select ``AVR_HAVE_BOARD_FLMAP`` in its configuration. It declares
221+
that its linker script will obey ``__RODATA_SIZE__`` and
222+
``__RODATA_OFFSET__`` symbols (which are set by the above-mentioned
223+
configuration options.)
224+
225+
See the linker script of :ref:`breadxavr_board` for an example.

arch/avr/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ config ARCH_CHIP_AVRDX
2828
select ARCH_FAMILY_AVR
2929
select MM_SMALL
3030
select ARCH_HAVE_TICKLESS
31+
select AVR_HAVE_FLMAP
3132
---help---
3233
Atmel/Microchip AVR 32/64/128 DA/DB core family.
3334

arch/avr/src/avr/Kconfig

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,38 @@ config AVR_BUILDROOT_TOOLCHAIN
6868

6969
endchoice # Toolchain
7070

71+
choice
72+
prompt "Const variable placement"
73+
default AVR_CONST_TO_FLMAP if AVR_HAVE_BOARD_FLMAP && AVR_HAVE_FLMAP
74+
default AVR_HAS_MEMX_PTR if ARCH_DEBUG_H
75+
default AVR_CONST_TO_RAM
76+
77+
config AVR_CONST_TO_RAM
78+
bool "No special handling, copy to RAM"
79+
---help---
80+
Initialization code will copy const variables into RAM.
81+
This is a standard option available for all compilers and it is
82+
fully supported in the kernel (because there are no special
83+
requirements for such support.)
84+
85+
Main disadvantage of this option is that it may severely reduce
86+
RAM available for the application and it may even be impossible
87+
to fit all data into the RAM.
88+
7189
config AVR_HAS_MEMX_PTR
72-
bool "Enable in-flash static const strings"
90+
bool "Mark const variables with __memx"
7391
depends on AVR_ATMEL_AVR_TOOLCHAIN || AVR_LINUXGCC_TOOLCHAIN
74-
default y if ARCH_DEBUG_H
75-
default n
7692
---help---
7793
Enabling this option activates IOBJ and IPTR qualifiers
7894
for pointers in the source code. Compiler will then be allowed
79-
to place constants into program memory without copying it to RAM,
95+
to place constants into program memory without copying them to RAM,
8096
reducing amount of RAM needed to hold static data.
8197

8298
The compiler then extends pointers with these qualifiers enabled
8399
to 24bit length with highest bit set for data that reside in RAM.
84100
Based on this bit, it will then read the data using instructions
85101
appropriate for the underlying storage. As such, there is
86-
a performance tradeoff.
102+
a potentially significant performance tradeoff.
87103

88104
Additionally, if this is enabled, all constant strings used
89105
for debugging and assertion are placed into program memory,
@@ -94,8 +110,87 @@ config AVR_HAS_MEMX_PTR
94110
pointers in arbitrary interaction with the kernel. Not all API
95111
functions have these qualifiers added to their parameters.
96112

113+
config AVR_CONST_TO_FLMAP
114+
bool "Use memory-mapped access to flash"
115+
depends on AVR_HAVE_BOARD_FLMAP && AVR_HAVE_FLMAP
116+
---help---
117+
Newer AVR chips - namely tinyAVR and AVR families - have (part of)
118+
their program memory (flash) mapped into data memory address space.
119+
The mapping is limited to 32kB window.
120+
121+
With this option enabled, const variables are kept in the program
122+
memory and no copy to RAM is done. Yet it is still possible to use
123+
such variables in any interaction with the kernel as they are
124+
visible in data memory address space.
125+
126+
Note that this option only triggers some basic configuration
127+
in the init function. It is the linker script of the board that needs
128+
to ensure variables are placed correctly.
129+
130+
Beware that FLMAP bits in NVMCTRL.CTRLB I/O register which select
131+
the segment of program memory to be mapped may not be changed freely
132+
by the application. If the application needs to change the mapping,
133+
it may only do so while observing these rules:
134+
135+
1. No kernel function must be called until the original value
136+
is restored.
137+
138+
2. Interrupts must be disabled for as long as the value is changed.
139+
140+
endchoice # Const variable placement
141+
142+
config AVR_FLMAP_RODATA_SIZE
143+
int "Size of .rodata FLMAP section"
144+
depends on AVR_CONST_TO_FLMAP
145+
default 4096
146+
---help---
147+
Specify size of .rodata memory section, ie. the section that stores
148+
const variables. This will be passed as a parameter to the linker
149+
to be used by the board's linker script.
150+
151+
Value must be divisible by 512 and no more than 32 kilobytes
152+
is possible.
153+
154+
config AVR_FLMAP_RODATA_OFFSET
155+
int "Offset of .rodata FLMAP section"
156+
depends on AVR_CONST_TO_FLMAP
157+
default 0
158+
---help---
159+
Specify size of memory block between end of the .rodata section
160+
(counting its full size as defined in AVR_FLMAP_RODATA_SIZE)
161+
and the end of flash.
162+
163+
This value is intended to leave the end of the flash unused,
164+
presumably for the purpose of placing APPDATA section in there
165+
(see the chip documentation for details about subdividing
166+
the program flash to BOOT, APPCODE and APPDATA sections.)
167+
168+
Note that this value is only passed to the linker to be used
169+
by the linker script - the script then needs to place
170+
the .rodata section accordingly.
171+
172+
Value must be divisible by 512 and no more than 31 kilobytes
173+
is possible. Sum of this value and AVR_FLMAP_RODATA_SIZE must
174+
also not exceed 32kB.
97175

98176
config AVR_HAS_RAMPZ
99177
bool
100178

179+
config AVR_HAVE_BOARD_FLMAP
180+
bool
181+
---help---
182+
This configuration option is supposed to be selected by board's
183+
Kconfig if the board's linker script responds to __RODATA_SIZE__
184+
and __RODATA_OFFSET__ passed by the linker and if it configures
185+
.rodata section's size and position accordingly. Configuration
186+
options that allow the user to configure values in these symbols
187+
are unlocked if this is set and if the chip has support
188+
for memory-mapped flash
189+
190+
config AVR_HAVE_FLMAP
191+
bool
192+
---help---
193+
This configuration option is set by chips that have (at least
194+
some part of their) flash mapped into data memory address space.
195+
101196
endif # ARCH_FAMILY_AVR

arch/avr/src/avr/Toolchain.defs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ ifeq ($(CONFIG_DEBUG_OPT_UNUSED_SECTIONS),y)
106106
ARCHOPTIMIZATION += -ffunction-sections -fdata-sections
107107
endif
108108

109+
# .rodata size for FLMAP configuration
110+
ifeq ($(CONFIG_AVR_CONST_TO_FLMAP),y)
111+
LDFLAGS += --defsym=__RODATA_SIZE__=$(CONFIG_AVR_FLMAP_RODATA_SIZE)
112+
endif
113+
114+
# .rodata offset for FLMAP configuration
115+
ifeq ($(CONFIG_AVR_CONST_TO_FLMAP),y)
116+
LDFLAGS += --defsym=__RODATA_OFFSET__=$(CONFIG_AVR_FLMAP_RODATA_OFFSET)
117+
endif
118+
109119
ifeq ($(CONFIG_DEBUG_LINK_MAP),y)
110120
LDFLAGS += -Map=$(call CONVERT_PATH,$(TOPDIR)$(DELIM)nuttx.map)
111121
endif

arch/avr/src/avrdx/avrdx_head.S

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,61 @@ __start:
492492
out _SFR_IO_ADDR(SPH), r29
493493
out _SFR_IO_ADDR(SPL), r28
494494

495+
496+
#ifdef CONFIG_AVR_HAVE_FLMAP
497+
498+
/* Configure FLMAP bits in NVMCTRL.CTRLB in case this was software
499+
* reset and they are not in default state.
500+
*
501+
* Don't care if the application actually changes the register. These
502+
* bits are not under configuration protection (CCP) and runaway
503+
* application can possibly change them.
504+
*
505+
* This is executed if the chip supports it, regardless of if the board
506+
* declared it is using the feature by setting AVR_HAVE_BOARD_FLMAP
507+
* in Kconfig (it may be using it without setting that option.)
508+
*/
509+
510+
ldi r26, lo8(NVMCTRL_CTRLB)
511+
ldi r27, hi8(NVMCTRL_CTRLB)
512+
ld r16, X
513+
514+
/* Default value (on current chips anyway) uses all bits set to one. */
515+
516+
# if !defined(NVMCTRL_FLMAP_1_bm)
517+
518+
/* Might be there won't be such (supported) device ever. */
519+
520+
# error Chips without FLMAP bit 1 are not supported
521+
522+
# elif !defined(NVMCTRL_FLMAP_2_bm)
523+
524+
/* Expected case */
525+
526+
ldi r17, NVMCTRL_FLMAP_0_bm | NVMCTRL_FLMAP_1_bm;
527+
528+
# else
529+
530+
/* Future device with more than 128kB RAM. The default is expected
531+
* to be all bits set to one but it needs to be verified when
532+
* support for such chip is added. Issue a warning.
533+
*/
534+
535+
# warning Support for more than 128kB flash was done blindly here
536+
537+
ldi r17, NVMCTRL_FLMAP_0_bm | NVMCTRL_FLMAP_1_bm | NVMCTRL_FLMAP_2_bm;
538+
539+
# endif
540+
541+
/* As long as we are always setting bits to one, we don't need
542+
* to clear them in the original value
543+
*/
544+
545+
or r16, r17
546+
st X, r16
547+
548+
#endif
549+
495550
/* Copy initial global data values from FLASH into RAM */
496551

497552
.global __do_copy_data; /* Required to suppress dragging in logic from libgcc */

boards/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ config ARCH_BOARD_AVRDX_BREADXAVR
9494
select ARCH_HAVE_LEDS
9595
select ARCH_HAVE_BUTTONS
9696
select ARCH_HAVE_IRQBUTTONS
97+
select AVR_HAVE_BOARD_FLMAP
9798
---help---
9899
This is a board used to make something use an AVRnDx core
99100
so it can be developed and tested.

boards/avr/avrdx/breadxavr/scripts/breadxavr.ld

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,55 @@
3030
* *Memory configuration A
3131
*/
3232

33+
/* Use this instead of repeated magical number */
34+
__CHIP_FLASH_SIZE__ = 128K;
35+
36+
/* VMA above 0x800000, must not collide with anything else,
37+
* arbitrary value otherwise.
38+
*/
39+
__RODATA_VMA__ = 0xa00000;
40+
/* Application configuration is supposed to pass this value
41+
* to linker so the default should not be used.
42+
*/
43+
__RODATA_SIZE__ = DEFINED(__RODATA_SIZE__) ? __RODATA_SIZE__ : 4K;
44+
/* Size increment matches chip's memory organization (boot, application
45+
* code and application data can be sized with 512B increments.)
46+
*/
47+
ASSERT (__RODATA_SIZE__ % 512 == 0,
48+
"__RODATA_SIZE__ must be a multiple of 512")
49+
/* Only 32kB of flash is mapped to data memory */
50+
ASSERT (__RODATA_SIZE__ <= 32K,
51+
"__RODATA_SIZE__ must not be larger than 32kB")
52+
53+
/* Same as for __RODATA_SIZE__ */
54+
__RODATA_OFFSET__ = DEFINED(__RODATA_OFFSET__) ? __RODATA_OFFSET__ : 0;
55+
/* Size increment matches chip's memory organization (boot, application
56+
* code and application data can be sized with 512B increments.)
57+
*/
58+
ASSERT (__RODATA_OFFSET__ % 512 == 0,
59+
"__RODATA_OFFSET__ must be a multiple of 512")
60+
/* Only 32kB of flash is mapped to data memory */
61+
ASSERT (__RODATA_OFFSET__ <= 31K,
62+
"__RODATA_OFFSET__ must not be larger than 31kB")
63+
64+
/* Also verify sum of size and offset */
65+
ASSERT (__RODATA_SIZE__ + __RODATA_OFFSET__ <= 32K,
66+
"Sum of __RODATA_SIZE__ and __RODATA_OFFSET__ must not be larger than 32kB")
67+
68+
/* Set the origin to the very end of flash. 64K is a magical number
69+
* that constitutes of 32K (start of memory-mapped flash in data
70+
* memory address space) and 32K (end of the area, size of the rodata
71+
* section is subtracted from it.)
72+
*/
73+
__RODATA_ORIGIN__ = __RODATA_VMA__ + 64K - __RODATA_SIZE__ - __RODATA_OFFSET__;
74+
__RODATA_FLASH_START = __CHIP_FLASH_SIZE__ - __RODATA_SIZE__ - __RODATA_OFFSET__;
75+
3376
MEMORY
3477
{
35-
flash (rx) : ORIGIN = 0, LENGTH = 128K
78+
flash (rx) : ORIGIN = 0, LENGTH = __CHIP_FLASH_SIZE__
3679
sram (rw!x) : ORIGIN = 0x804000, LENGTH = 16K
3780
eeprom (rw!x) : ORIGIN = 0x801400, LENGTH = 512
81+
rodata (r!x) : ORIGIN = __RODATA_ORIGIN__, LENGTH = __RODATA_SIZE__
3882
}
3983

4084
ENTRY(__start)
@@ -129,8 +173,6 @@ SECTIONS
129173
{
130174
_sdata = ABSOLUTE(.);
131175
*(.data .data.*)
132-
*(.rodata)
133-
*(.rodata*)
134176
*(.gnu.linkonce.d.*)
135177
CONSTRUCTORS
136178
_edata = ABSOLUTE(.);
@@ -154,6 +196,14 @@ SECTIONS
154196
_enoinit = ABSOLUTE(.);
155197
} > sram
156198

199+
.rodata ABSOLUTE(__RODATA_ORIGIN__) : AT (ABSOLUTE(__RODATA_FLASH_START))
200+
{
201+
*(.rodata)
202+
*(.rodata*)
203+
*(.gnu.linkonce.r*)
204+
_rodata_end = ABSOLUTE(.);
205+
} > rodata
206+
157207
.eeprom :
158208
{
159209
_seeprom = ABSOLUTE(.);

0 commit comments

Comments
 (0)