I have a K8x and need to place large const data on external (QSPI XiP) flash. I am using an external linker script.
Here are the relevant excerpts:
k81_memory.ld
MEMORY
{
PROGRAM_FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x040000
EXT_PGM_FLASH (rx) : ORIGIN = 0x04020000, LENGTH = 0x100000
/* 0x04020000-0x0405FFFF is XiP alias for 0x68020000-0x6805FFFF */
SRAM_LOWER (rwx) : ORIGIN = 0x1fff0000, LENGTH = 0x010000
SRAM_UPPER (rwx) : ORIGIN = 0x20000000, LENGTH = 0x030000
/* First 128K infineon has 4K erase, use for Configs */
TERM_SETTINGS (rx) : ORIGIN = 0x68000000, LENGTH = 0x018000 /* 96K */
MODULE_CONFIG (rx) : ORIGIN = 0x68018000, LENGTH = 0x008000 /* 32K */
XIP_RESERVED (rx) : ORIGIN = 0x68020000, LENGTH = 0x100000 /* 1M */
OTA_UPDATE (rx) : ORIGIN = 0x68120000, LENGTH = 0x100000 /* 1M */
}
k81.ld
INCLUDE "k81_memory.ld"
STACK_SIZE = 0x1000;
ENTRY(ResetISR)
SECTIONS
{
/* EXTERNAL QSPI XiP FLASH */
.text_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.text_Flash2*)
*(.text_EXT_PGM_FLASH*)
*(.text.$Flash2*)
*(.text.$EXT_PGM_FLASH*)
} > EXT_PGM_FLASH
.data_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.rodata_Flash2*)
*(.rodata_EXT_PGM_FLASH*)
*(.rodata.$Flash2*)
*(.rodata.$EXT_PGM_FLASH*)
}> EXT_PGM_FLASH
If I place code in external flash, e.g.
void __attribute__((section (".text_Flash2"))) delay_ms(uint16_t ms)
{
volatile uint32_t i = 0U;
for (i = 0U; i < (10000U*ms); ++i)
{
__asm("NOP"); /* delay */
}
}
... and build:
...then the function is correctly placed on the external flash.
However, if I instead place const data in external flash
const TsBigBitMap __attribute__((section (".data_Flash2"))) Logo_bmp =
{
3, // BitMapType
512, // width_pixels
384, // height_pixels
192, // width_bytes
73728, // byte_count
{
// Line 0
0xFF, 0xFF, 0xF0, 0x0A, ... 0xFF, 0xFF 0xFF, 0xFF,
}
};
.. and build:
... then it appears to NOT be placed in external flash.
What am I not seeing?
Solved! Go to Solution.
I was able to solve this. It has to do with the memory aliasing that's used by the K8x. My own comment is actually the solution staring me in the face!
/* 0x04020000-0x0405FFFF is XiP alias for 0x68020000-0x6805FFFF */
EXT_PGM_FLASH is the address region used by the K8x program counter. XIP_RESERVED is the physical QSPI mapped address region. The internal code always uses the EXT_PGM_FLASH, but when programming the chip the debug probe must use XIP_RESERVED. So the region must be associated with the physical location by using the AT> option.
} > EXT_PGM_FLASH AT> XIP_RESERVED
MEMORY
{
PROGRAM_FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x040000
EXT_PGM_FLASH (rx) : ORIGIN = 0x04020000, LENGTH = 0x100000
/* 0x04020000-0x0405FFFF is XiP alias for 0x68020000-0x6805FFFF */
SRAM_LOWER (rwx) : ORIGIN = 0x1fff0000, LENGTH = 0x010000
SRAM_UPPER (rwx) : ORIGIN = 0x20000000, LENGTH = 0x030000
/* First 128K infineon has 4K erase, use for Configs */
TERM_SETTINGS (rx) : ORIGIN = 0x68000000, LENGTH = 0x018000 /* 96K */
MODULE_CONFIG (rx) : ORIGIN = 0x68018000, LENGTH = 0x008000 /* 32K */
XIP_RESERVED (rx) : ORIGIN = 0x68020000, LENGTH = 0x100000 /* 1M */
OTA_UPDATE (rx) : ORIGIN = 0x68120000, LENGTH = 0x100000 /* 1M */
}
STACK_SIZE = 0x1000;
ENTRY(ResetISR)
SECTIONS
{
/* EXTERNAL QSPI XiP FLASH */
.text_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.text_Flash2*)
*(.text_EXT_PGM_FLASH*)
*(.text.$Flash2*)
*(.text.$EXT_PGM_FLASH*)
}> EXT_PGM_FLASH AT> XIP_RESERVED
.data_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.rodata_Flash2*)
*(.rodata_EXT_PGM_FLASH*)
*(.rodata.$Flash2*)
*(.rodata.$EXT_PGM_FLASH*)
}> EXT_PGM_FLASH AT> XIP_RESERVED
Note that EXT_PGM_FLASH and XIP_RESERVED are the same size. These are, respectively, the Alias and Physical address regions.
Cheers!
Denis
I was able to solve this. It has to do with the memory aliasing that's used by the K8x. My own comment is actually the solution staring me in the face!
/* 0x04020000-0x0405FFFF is XiP alias for 0x68020000-0x6805FFFF */
EXT_PGM_FLASH is the address region used by the K8x program counter. XIP_RESERVED is the physical QSPI mapped address region. The internal code always uses the EXT_PGM_FLASH, but when programming the chip the debug probe must use XIP_RESERVED. So the region must be associated with the physical location by using the AT> option.
} > EXT_PGM_FLASH AT> XIP_RESERVED
MEMORY
{
PROGRAM_FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x040000
EXT_PGM_FLASH (rx) : ORIGIN = 0x04020000, LENGTH = 0x100000
/* 0x04020000-0x0405FFFF is XiP alias for 0x68020000-0x6805FFFF */
SRAM_LOWER (rwx) : ORIGIN = 0x1fff0000, LENGTH = 0x010000
SRAM_UPPER (rwx) : ORIGIN = 0x20000000, LENGTH = 0x030000
/* First 128K infineon has 4K erase, use for Configs */
TERM_SETTINGS (rx) : ORIGIN = 0x68000000, LENGTH = 0x018000 /* 96K */
MODULE_CONFIG (rx) : ORIGIN = 0x68018000, LENGTH = 0x008000 /* 32K */
XIP_RESERVED (rx) : ORIGIN = 0x68020000, LENGTH = 0x100000 /* 1M */
OTA_UPDATE (rx) : ORIGIN = 0x68120000, LENGTH = 0x100000 /* 1M */
}
STACK_SIZE = 0x1000;
ENTRY(ResetISR)
SECTIONS
{
/* EXTERNAL QSPI XiP FLASH */
.text_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.text_Flash2*)
*(.text_EXT_PGM_FLASH*)
*(.text.$Flash2*)
*(.text.$EXT_PGM_FLASH*)
}> EXT_PGM_FLASH AT> XIP_RESERVED
.data_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.rodata_Flash2*)
*(.rodata_EXT_PGM_FLASH*)
*(.rodata.$Flash2*)
*(.rodata.$EXT_PGM_FLASH*)
}> EXT_PGM_FLASH AT> XIP_RESERVED
Note that EXT_PGM_FLASH and XIP_RESERVED are the same size. These are, respectively, the Alias and Physical address regions.
Cheers!
Denis
Hi Diego,
I am taking over for Denis who is on vacation. I am able to see the build map to the correct address, but it does not load in the external flash.
Here's snippet of the map file where I try to place the const variable ipsLogo_bmp in external flash:
.text_Flash2 0x04020000 0x0
FILL mask 0xff
*(SORT_BY_ALIGNMENT(.text_Flash2*))
*(SORT_BY_ALIGNMENT(.text_EXT_PGM_FLASH*))
*(SORT_BY_ALIGNMENT(.text.$Flash2*))
*(SORT_BY_ALIGNMENT(.text.$EXT_PGM_FLASH*))
.data_Flash2 0x04020000 0x1200c
FILL mask 0xff
*(SORT_BY_ALIGNMENT(.data_Flash2*))
.data_Flash2 0x04020000 0x1200c ./source/Display/Resources/resources.o
0x04020000 ipsLogo_bmp
*(SORT_BY_ALIGNMENT(.data_EXT_PGM_FLASH*))
*(SORT_BY_ALIGNMENT(.data.$Flash2*))
*(SORT_BY_ALIGNMENT(.data.$EXT_PGM_FLASH*))
Memory region Used Size Region Size %age Used
PROGRAM_FLASH: 199388 B 256 KB 76.06%
EXT_PGM_FLASH: 73740 B 1 MB 7.03%
SRAM_LOWER: 0 GB 64 KB 0.00%
SRAM_UPPER: 79728 B 192 KB 40.55%
TERM_SETTINGS: 0 GB 96 KB 0.00%
MODULE_CONFIG: 0 GB 32 KB 0.00%
XIP_RESERVED: 0 GB 1 MB 0.00%
OTA_UPDATE: 0 GB 1 MB 0.00%
Do I have to add any drivers to download to QuadSPI flash in the project settings:
Hi @deniscollis
I hope that you are doing awesome!
There are several alternatives to place const data on a specific flash region.
I have not tested this yet, as currently I was locked down with no boards due to covid, but here is a method I used.
I see that you are working with Kinetis, all the Kinetis MCUs have at structure located at 0x400 thanks to linker.
// Flash Configuration block : 16-byte flash configuration field that stores
// default protection settings (loaded on reset) and security information that
// allows the MCU to restrict access to the Flash Memory module.
// Placed at address 0x400 by the linker script.
//*****************************************************************************
__attribute__ ((used,section(".FlashConfig"))) const struct {
unsigned int word1;
unsigned int word2;
unsigned int word3;
unsigned int word4;
} Flash_Config = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF3DFE};
On a similar way I created my array
__attribute__ ((used,section(".FlashConf"))) const uint8_t roarray[1024*6] = {0xAE}
I proceed to create a secondary flash space. In the MCUXpresso it looks like this on the MCU settings.
Then, I disabled managed linker script option
And I modified the linker to place my array at any desired offset on my secondary flash space.
Then the output was the following.
All the best,
Diego.
Hi Diego,
This is using internal flash, which I have no problem with. I'm having an issue trying to achieve the same with external QSPI/XiP flash.
In any case, thanks for the useful info,
Denis
The .text_flash2 section contains a .text_flash2 section.
The .data_flash2 section does not contain a .data_flash2 section.
Add
*(.data_Flash2*)
Hi Bob,
I fixed that, but the data is still placed in internal flash.
.data_Flash2 : ALIGN(4)
{
FILL(0xff)
*(.data_Flash2*)
*(.data_EXT_PGM_FLASH*)
*(.data.$Flash2*)
*(.data.$EXT_PGM_FLASH*)
}> EXT_PGM_FLASH
const uint8_t[] __attribute__((section (".data_Flash2"))) bigdata =
{
......
};
Building target: XXX.axf
Invoking: MCU Linker
arm-none-eabi-gcc -nostdlib -Xlinker -Map="XXX.map" -Xlinker --gc-sections -Xlinker -print-memory-usage -Xlinker --sort-section=alignment -Xlinker --cref -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -T k81.ld -L ../startup/ -o "XXX.axf" ...
Memory region Used Size Region Size %age Used
PROGRAM_FLASH: 196340 B 256 KB 74.90%
EXT_PGM_FLASH: 0 GB 1 MB 0.00%
SRAM_LOWER: 0 GB 64 KB 0.00%
SRAM_UPPER: 79688 B 192 KB 40.53%
Finished building target: XXX.axf
Try adding KEEP. Example from my Linker file, below.
The gc-sections that is in your linker will cause any section of the output to be removed if the compiler thinks it is not referenced.
Also what does the XXX.sym file(s) show?
Is there actual reference to a symbol in the external section?
Check for miss-spellings.
There are also two versions of the sym file, see blow, sorted by address and sorted by size.
KEEP example:
/* The startup code goes first into INTERNAL_FLASH: */
.isr_vector :
{
__vector_table = ABSOLUTE(.) ; /* Address of the ISR Vector Table */
__isr_vectors_start = .;
/*
* When link-time garbage collection is in use (-gc-sections), it is
* often useful to mark sections that should not be eliminated. This is
* accomplished by surrounding an input section's wild-card entry with
* KEEP(), as in KEEP(*(.init)) or KEEP(SORT(*)(.ctors)).
*/
KEEP(*(.isr_vector))
__isr_vectors_end = .;
} > VECTORS =0xFF
# Create a symbol table from ELF output file.
# Order by address.
%.$(NEW_BUILDNUMBER).sym: %.elf
echo $(SYMBOL_TABLE_MSG) $@
echo NM="$(NM)" -n $(OBJDIR)/$< > $(OBJDIR)/$@
$(NM) -n $(OBJDIR)/$< > $(OBJDIR)/$@
cp $(OBJDIR)/$@ $(TARGET_DIR)/$(TARGET).sym
# Order by size.
%.$(NEW_BUILDNUMBER).sym2: %.elf
echo $(SYMBOL_TABLE_MSG) $@
echo NM="$(NM)" -S --size-sort -s $(OBJDIR)/$< > $(OBJDIR)/$@
$(NM) -S --size-sort -s $(OBJDIR)/$< > $(OBJDIR)/$@
cp $(OBJDIR)/$@ $(TARGET_DIR)/$(TARGET).sym2
Also due to a really obscure linker bug the __Start/__End address need to be inside the section blocks, not outside of them. Not really your issue here, just be aware of it.
Hi, am writing firmware for a new board, K81 and Winbond QSPI external flash. I've never used external flash. Can you suggest where I might start, so I need to learn about memory aliasing , XIP, how to place code in that flash, etc? Thanks
I've not used the K81 and access to the documentation is restricted, so I don't know that part.
First check that the K81 does support Execute In Place (XIP).
The Reference Manual and the Data Sheets are always the place to start.
Read them thoroughly before making any schematic.
Some programming pods such as the PEMicro Universal FX will program QSPI Flash parts.
Check if the native bootloader, if there is one, supports loading such a part.
If not create such a bootloader.
As you are new here, I'll also mention that it is considered poor etiquette to hijack someone else's thread. Always better to start a new one. It will get more views and response that way too.
We are also using K81/K82 and we can confirm it does support XIP in external QSPI flash. Also the Segger JLINK flasher we are using also supports loading into external QSPI flash as well.
Feel free to start a new thread if needed.
Thanks for the pointers Bob. And for the word about etiquette - I wasn't aware, but now I am