# notice and this notice are preserved.
# RODATA_PM_OFFSET
-# If empty, .rodata sections will be part of .data. This is for
-# devices where it is not possible to use LD* instructions to read
-# from flash.
+# If empty and not HAVE_FLMAP, .rodata sections will be part of .data.
+# This is for devices where it is not possible to use LD* instructions
+# to read from flash.
#
# If non-empty, .rodata is not part of .data and the .rodata
# objects are assigned addresses at an offest of RODATA_PM_OFFSET.
# LD* instructions, provided the addresses are offset by
# __RODATA_PM_OFFSET__ (which defaults to RODATA_PM_OFFSET).
+# HAVE_FLMAP
+# The .rodata section is located in program memory. Devices from
+# the AVR64* and AVR128* families (from avrxmega2 and avrxmega4)
+# see a 32k segment of their program memory in their RAM address
+# space. Which 32k segment is visible is determined by the
+# bit-field NVMCTRL_CTRLB.FLMAP.
+# Output section .rodata is placed in MEMORY region rodata.
+# The LMA of the .rodata section can be set by means of:
+# * __flmap specifies which 32k block is visible in RAM.
+# * __RODATA_FLASH_START__ specifies the byte address of the
+# rodata LMA and is used if __flmap is undefined.
+# * When __flmap and __RODATA_FLASH_START__ are undefined, then an
+# emulation-specific default is used (the last 32k block).
+
+# MAYBE_FLMAP
+# For devices from avrxmega2 and avrxmega4: The user can chose whether
+# or not .rodata is located in flash (if HAVE_FLMAP) or located in
+# in RAM (if not HAVE_FLMAP by means of -mrodata-in-ram). This is
+# achieved by new emulations avrxmega2_flmap and avrxmega4_flmap that
+# are selected by compiler option -mno-rodata-in-ram.
+#
+# In order to facilitate initialization of NVMCTRL_CTRLB.FLMAP in
+# the startup code irrespective of HAVE_FLMAP, the follwing symbols
+# are defined in order to communicate with the startup code.
+# Notice that the hardware default for FLMAP is the last 32k block,
+# so that explicit initialization of FLMAP is only required when the
+# user wants to deviate from the defauls.
+#
+# __flmap = HAVE_FLMAP
+# ? given by __flmap resp. __RODATA_FLASH_START__ >> 15
+# : 0;
+#
+# __flmap_lsl4 = __flmap << 4;
+#
+# __flmap_init_label = HAVE_FLMAP
+# ? __flmap_init_start
+# : __flmap_noinit_start;
+# Supposed to be used as a jump target for RJMP so that the code
+# can initialize FLMAP / skip initialization of FLMAP depending
+# on the chosen emulation, and without the need to support two code
+# versions of crt<mcu>.o for the two possble emulations.
+#
+# __do_init_flmap = HAVE_FLMAP ? 1 : 0;
+# Whether or not FLMAP is supposed to be initialized according
+# to, and for the purpose of, .rodata in flash.
+#
+# Apart from that, the compiler (device-specs actually) defines the
+# following macros:
+#
+# __AVR_HAVE_FLMAP__
+# Defined if a device has the NVMCTRL_CTRLB.FLMAP bitfield
+# *AND* if it's unknown at compile-time / assembler-time whether
+# emulation avrxmega* is used or avrxmega*_flmap.
+
cat <<EOF
/* Copyright (C) 2014-2023 Free Software Foundation, Inc.
__SIGNATURE_REGION_LENGTH__ = DEFINED(__SIGNATURE_REGION_LENGTH__) ? __SIGNATURE_REGION_LENGTH__ : $SIGNATURE_LENGTH;
${USER_SIGNATURE_LENGTH+__USER_SIGNATURE_REGION_LENGTH__ = DEFINED(__USER_SIGNATURE_REGION_LENGTH__) ? __USER_SIGNATURE_REGION_LENGTH__ : $USER_SIGNATURE_LENGTH;}
${RODATA_PM_OFFSET+__RODATA_PM_OFFSET__ = DEFINED(__RODATA_PM_OFFSET__) ? __RODATA_PM_OFFSET__ : $RODATA_PM_OFFSET;}
+${HAVE_FLMAP+__RODATA_VMA__ = ${RODATA_VMA};}
+${HAVE_FLMAP+__RODATA_LDS_OFFSET__ = DEFINED(__RODATA_LDS_OFFSET__) ? __RODATA_LDS_OFFSET__ : ${RODATA_LDS_OFFSET};}
+${HAVE_FLMAP+__RODATA_REGION_LENGTH__ = DEFINED(__RODATA_REGION_LENGTH__) ? __RODATA_REGION_LENGTH__ : ${RODATA_LENGTH};}
+${HAVE_FLMAP+__RODATA_ORIGIN__ = __RODATA_VMA__ + __RODATA_LDS_OFFSET__;}
MEMORY
{
text (rx) : ORIGIN = 0, LENGTH = __TEXT_REGION_LENGTH__
lock (rw!x) : ORIGIN = 0x830000, LENGTH = __LOCK_REGION_LENGTH__
signature (rw!x) : ORIGIN = 0x840000, LENGTH = __SIGNATURE_REGION_LENGTH__
${USER_SIGNATURE_LENGTH+ user_signatures (rw!x) : ORIGIN = 0x850000, LENGTH = __USER_SIGNATURE_REGION_LENGTH__}
+${HAVE_FLMAP+ rodata (r!x) : ORIGIN = __RODATA_ORIGIN__, LENGTH = __RODATA_REGION_LENGTH__}
}
EOF
# need .rodata to be part of .data because the compiler will use LD*
# instructions and LD* cannot access flash.
-if test -z "$RODATA_PM_OFFSET" && test -n "${RELOCATING}"; then
+if test -z "$RODATA_PM_OFFSET" && test -z "${HAVE_FLMAP}" && test -n "${RELOCATING}"; then
cat <<EOF
*(.rodata) /* We need to include .rodata here if gcc is used */
*(.rodata*) /* with -fdata-sections. */
} ${RELOCATING+ > data}
EOF
+# Devices like AVR128DA32 and AVR64DA32 see a 32 KiB block of their program
+# memory at 0x8000 (RODATA_LDS_OFFSET). Which portion will be determined by
+# bitfield NVMCTRL_CTRLB.FLMAP.
+
+if test -n "${MAYBE_FLMAP}" && test -n "${RELOCATING}"; then
+ cat <<EOF
+
+__do_init_flmap = ${HAVE_FLMAP-0};
+
+EOF
+fi
+
+if test -z "${HAVE_FLMAP}" && test -n "${RELOCATING}"; then
+ cat <<EOF
+
+PROVIDE (__flmap_init_label = DEFINED(__flmap_noinit_start) ? __flmap_noinit_start : 0) ;
+PROVIDE (__flmap = DEFINED(__flmap) ? __flmap : 0) ;
+PROVIDE (__flmap_lsl4 = __flmap << 4) ;
+
+EOF
+fi
+
+if test -n "${HAVE_FLMAP}"; then
+ cat <<EOF
+
+${RELOCATING+
+PROVIDE (__flmap_init_label = DEFINED(__flmap_init_start) ? __flmap_init_start : 0) ;
+/* User can specify position of .rodata in flash (LMA) by supplying
+ __RODATA_FLASH_START__ or __flmap, where __flmap takes precedence. */
+__RODATA_FLASH_START__ = DEFINED(__flmap)
+ ? __flmap * 32K
+ : DEFINED(__RODATA_FLASH_START__) ? __RODATA_FLASH_START__ : ${RODATA_FLASH_START};
+ASSERT (__RODATA_FLASH_START__ % 32K == 0, \"__RODATA_FLASH_START__ must be a multiple of 32 KiB\")
+__flmap = ${FLMAP_MASK} & (DEFINED(__flmap) ? __flmap : __RODATA_FLASH_START__ >> 15);
+__RODATA_FLASH_START__ = __flmap << 15;
+__rodata_load_start = MAX (__data_load_end, __RODATA_FLASH_START__);
+PROVIDE (__flmap_lsl4 = __flmap << 4) ;
+__rodata_start = __RODATA_ORIGIN__ + __rodata_load_start - __RODATA_FLASH_START__;}
+
+ .rodata ${RELOCATING+ __rodata_start} ${RELOCATING-0} : ${RELOCATING+ AT (__rodata_load_start)}
+ {
+ *(.rodata)
+ ${RELOCATING+ *(.rodata*)}
+ ${RELOCATING+ *(.gnu.linkonce.r*)}
+ ${RELOCATING+ __rodata_end = ABSOLUTE(.) ;}
+ } ${RELOCATING+ > rodata}
+
+${RELOCATING+ __rodata_load_end = __rodata_load_start + __rodata_end - __rodata_start;}
+
+EOF
+fi
+
if test -n "${EEPROM_LENGTH}"; then
cat <<EOF