Memory Hierarchy for RTL87x3EP
This document introduces the memory system of RTL87x3EP generally. RTL87x3EP's memory system consists of ROM, RAM, SPIC Flash, and eFuse, all of which has a flexible configuration mechanism. The flexible configurability and extensibility make RTL87x3EP support a wide range of applications with different memory usage.
Memory Type |
Start Addr |
End Addr |
Size (KB) |
Block Size and Count |
|---|---|---|---|---|
ROM |
0x00000000 |
0x0009FFFF |
640 |
|
ITCM1 RAM |
0x00200000 |
0x0021FFFF |
128 |
4 x 32KB |
DTCM0 RAM |
0x00280000 |
0x0028FFFF |
64 |
2 x 32KB |
DTCM1 RAM |
0x002C0000 |
0x002F7FFF |
224 |
7 x 32KB |
Buffer RAM |
0x00400000 |
0x0040FFFF |
64 |
4 x 16KB |
DSP RAM (Shared as General RAM) |
DSP Share Start Address |
DSP Share End Address |
80 or 320 |
(10 x 8KB) or (40 x 8KB) |
0x02000000 |
0x03FFFFFF |
|||
0x04000000 |
0x05FFFFFF |
|||
SPIC2 QPI PSRAM; QPI Flash |
0x10000000 |
0x1FFFFFFF |
||
eFuse |
||||
PSRAM LPC CTRL OPI Psram |
0x0A000000 |
0xA1FFFFFF |
Note
The address and size of shared DSP RAM depend on the configuration, which should match with DSP images.
The SPIC0 flash size of RTL87x3EP supports up to 32M external Flash, which defaults to 4M or 2M.
DSP RAM is not shared as general RAM by default, shared memory can only be 80KB or 320KB.
eFuse doesn't support access directly by address.
The size and block size of SPIC0/SPCI1/SPIC2/SPIC3/PSRAM depend on the type of flash/PSRAM connected.
ROM
Many fundamental functions are built in ROM, such as Bootloader, RTOS, Bluetooth Stack, and IO Drivers. The ROM code is located at 0x00000000 - 0x000A0000. RTK offers abundant API for users to access most ROM functions, which can improve users' development efficiency and reduce code size.
RAM
RTL87x3EP has five types of RAM: ITCM1, DTCM0, DTCM1, Buffer, and DSP RAM. And ITCM1/DTCM0/DTCM1 SRAM are internal SRAM of the ARM core, which are close to the CPU to get good performance. On the contrary, Buffer SRAM and DSP share RAM are relatively slow for CPU access. All types of memory could be used to store data and execute code. ITCM1 is recommended for RAM code and DTCM0/1 is recommended for storage data.
The following picture shows the default RAM configuration. Some parts are already used by RTK internal functions marked as RTK Used. The remaining parts should be allocated for global variables, RAM code, and heap area. Users can configure these parts with the header file mem_config.h:
The remaining 89KB RAM of ITCM1 is recommended to be configured as APP RAM TEXT, and if any SRAM remains, could be configured as
APP HEAP.DTCM0 is recommended to be configured as
APP GLOBALandAPP HEAP.

RAM Configuration
There are several physical heap areas, but all of them are connected logically. Users can use malloc() to request RAM from the heap area dynamically. Total heap RAM size is 323KB by default, and some of it has already been used by RTK, which will be slightly different with different configurations generated by the MCUConfig Tool. As for the heap for the application, the RTK framework will consume about 32KB, though this size may vary depending on the features supported.

Heap Size
The size of APP RAM TEXT is 8KB (depending on the application) by default, which is used to run RAM code. The size of the APP GLOBAL area is 10KB (depending on the application) by default, which is used to store global variables. The RTK framework will cost about 1.9KB (depending on the application) global size. Users can configure them by modifying two macros in mem_config.h, and other macros cannot be modified in mem_config.h.
#define APP_RAM_TEXT_SIZE (8*1024)
#define APP_GLOBAL_SIZE (10*1024)

APP Global Size
Users can use API malloc() to allocate RAM from the heap. If the heap is insufficient, a hardfault is generated. In this case, you can see from the following log. It is necessary to confirm whether too many features are enabled, or there is a problem in the code writing, and there is a memory leak, resulting in the heap consumption.
0007138 03-02 09:29:53.651 154 0319630.477 [PATCH] !!!ALLOC FAIL!RAM type:5,wanted sz:2056,remain sz:4176
FAQs
-
How to get the current remaining heap size?
The
mem_peek()API inos_mem.hcan return the current remaining heap size. -
How to get heap water level and task stack water level?
The
monitor_memory_and_timer()API inos_ext.hwill set up a timer to periodically print the heap water level, the current heap remaining size, the number of remaining timers, and the water levels of each task stack. -
How to get the heap size used by a piece of code?
Refer to the following code:
size_t free_heap_start = mem_peek(); test_api(); size_t free_heap_end = mem_peek(); size_t used_heap = free_heap_start - free_heap_end;
-
How to get the size currently used by
APP RAM TEXTandAPP GLOBAL?The
app.mapfile shows the size currently used byAPP_RAM_TEXTandAPP_RAM_GLOBAL.Execution Region RAM_TEXT (Exec base: 0x00208800, Load base: 0x021453cc, Size: 0x000011fc, Max: 0x00001400, ABSOLUTE) Execution Region RAM_GLOBAL (Exec base: 0x002c0000, Load base: 0x02144d0c, Size: 0x00002918, Max: 0x00003800, ABSOLUTE)
Flash
The RTL87x3EP supports external SPI flash memory. Onboard SPI flash is controlled by an SPIC that maps memory accordingly. The flash size mapping varies with different SPICs: up to 32MB for SPIC0 and SPIC1, 8MB for SPIC2, and 256MB each for SPIC3 and SPIC4. The RTL87x3EP features a flash cache for SPIC0, which enhances data read and code execution efficiency from SPI flash. Additionally, it provides a mapping address range from 0x4000000 to 0x5FFFFFF, enabling the CPU to access the SPIC0 flash directly. For further details, please refer to Flash.
eFuse
eFuse is a block of one-time programming memory that is used to store important and fixed information, such as UUID, security key, and many other configurations. Only certain sections can be modified by the user. The single bit of eFuse cannot be changed from 0 to 1, and there is no erase operation for eFuse, so be careful when updating eFuse.
PSRAM
The RTL87x3EP chipset supports external memory via SPI PSRAM, which is managed by an SPIC. The onboard SPI PSRAM is controlled by the SPIC, which maps memory to it. The maximum mapping size varies based on the different SPIC mapping memory ranges: up to 32MB for SPIC0 and SPIC1, 8MB for SPIC2, and 256MB for both SPIC3 and SPIC4. OPI PSRAMs and QPI PSRAM are supported. Specifically, the RTL87x3EP supports the APS1604M PSRAM model through SPIC1, SPIC2, and SPIC3. RTL87x3EP supports two OPI PSRAMs, APS6408L, and W955D8MBYA, through SPIC1; one QPI PSRAM, APS6404L, through all SPICs except for SPIC0. Besides, with cache, both data reading and code execution efficiencies on PSRAM could be largely increased. Further, Realtek offers APIs to initialize PSRAM where PSRAM can be read and written like SRAM after being initialized.
PSRAM Type |
Chip Models |
Support SPIC |
Selection Basis |
|---|---|---|---|
QPI PSRAM |
APS6408L, W955D8MBYA |
SPIC1 |
Low power consumption and simple design. |
OPI PSRAM |
APS6404L |
SPIC2, SPIC3, SPIC4 |
High transmission rate requirements. |
How to Init PSRAM?
Before using PSRAM, you must initialize it. For example, to initialize a Winbond OPI-interface PSRAM, use the following code:
if (fmc_psram_winbond_opi_init(FMC_SPIC_ID_1))
{
DBG_DIRECT("WB OPI psram init success!");
}
else
{
DBG_DIRECT("WB OPI psram init fail!");
}
Note
Different hardware platforms may support different types or brands of PSRAM. Please refer to the relevant hardware documentation to ensure PSRAM compatibility and correct initialization procedures.
fmc_psram_winbond_opi_initis used to initialize the Winbond OPI PSRAM on the SPIC1 controller.
How to Use PSRAM?
This guide provides examples of how to place code and global data in PSRAM using Keil and GCC toolchains.
Placing Code in PSRAM
-
Add a PSRAM text section.
-
For Keil (
app.sct):PSRAM_TEXT PSRAM_CODE_START_ADDR PSRAM_CODE_SIZE { * (.psram.text) }
-
For GCC (
app.ld):MEMORY { // other sections PSRAM_TEXT(rw) : ORIGIN = 0x4000000, LENGTH = 0x400000 // user defined // other sections } PSRAM_TEXT_SECTION : { __psram_text_start = .; *(.psram.text) __psram_text_end = .; } > PSRAM_TEXT AT > FLASH __psram_text_length__ = __psram_text_end - __psram_text_start; __psram_text_load_addr__ = LOADADDR(PSRAM_TEXT_SECTION); __psram_text_dst_addr__ = ADDR(PSRAM_TEXT_SECTION);
-
-
Define the section macro in
mem_config.h:#define PSRAM_TEXT_SECTION __attribute__((section(".psram.text"))) -
Copy code from flash to PSRAM:
void prepare_psram_text_to_ram(void) { #ifdef __CC_ARM extern unsigned int Image$$PSRAM_TEXT$$Base; extern unsigned int Load$$PSRAM_TEXT$$Base; extern unsigned int Image$$PSRAM_TEXT$$Length; void *image_base = (void *)&Image$$PSRAM_TEXT$$Base; void *load_base = (void *)&Load$$PSRAM_TEXT$$Base; unsigned int size = (unsigned int)&Image$$PSRAM_TEXT$$Length; memcpy(image_base, load_base, size); #elif defined(__GNUC__) extern char __psram_text_start[]; extern char __psram_text_end[]; extern char __psram_text_load_addr__[]; uint32_t len = __psram_text_end - __psram_text_start; memcpy(__psram_text_start, __psram_text_load_addr__, len); #endif }
-
Mark functions for placement in PSRAM using the macro:
PSRAM_TEXT_SECTION void psram_test_code(void) { // function implementation }
Placing Global Data in PSRAM
-
Add a PSRAM data section.
-
For Keil (
app.sct):PSRAM_DATA PSRAM_DATA_START_ADDR PSRAM_DATA_SIZE { * (.psram.data) }
-
For GCC (
app.ld):MEMORY { // other sections PSRAM_DATA(rw) : ORIGIN = 0x4000000, LENGTH = 0x400000 // user defined // other sections } PSRAM_DATA_SECTION : { __psram_data_start = .; *(.psram.data) __psram_data_end = .; } > PSRAM_DATA AT > FLASH __psram_data_length__ = __psram_data_end - __psram_data_start; __psram_data_load_addr__ = LOADADDR(PSRAM_DATA_SECTION); __psram_data_dst_addr__ = ADDR(PSRAM_DATA_SECTION);
-
-
Define the section macro in
mem_config.h:#define PSRAM_DATA_SECTION __attribute__((section(".psram.data"))) -
Copy data from flash to PSRAM before use:
void prepare_psram_data_to_ram(void) { #ifdef __CC_ARM extern unsigned int Load$$PSRAM_DATA$$RW$$Base; extern unsigned int Image$$PSRAM_DATA$$RW$$Base; extern unsigned int Image$$PSRAM_DATA$$RW$$Length; extern unsigned int Load$$PSRAM_DATA$$ZI$$Base; extern unsigned int Image$$PSRAM_DATA$$ZI$$Base; extern unsigned int Image$$PSRAM_DATA$$ZI$$Length; uint32_t load_addr = (uint32_t)&Load$$PSRAM_DATA$$RW$$Base; uint32_t dest_addr = (uint32_t)&Image$$PSRAM_DATA$$RW$$Base; uint32_t len = (uint32_t)&Image$$PSRAM_DATA$$RW$$Length; memcpy((uint8_t *)dest_addr, (uint8_t *)load_addr, len); dest_addr = (uint32_t)&Image$$PSRAM_DATA$$ZI$$Base; len = (uint32_t)&Image$$PSRAM_DATA$$ZI$$Length; memset((uint8_t *)dest_addr, 0, len); #elif defined(__GNUC__) extern char __psram_data_start[]; extern char __psram_data_end[]; extern char __psram_data_load_addr__[]; uint32_t len = __psram_data_end - __psram_data_start; memcpy(__psram_data_start, __psram_data_load_addr__, len); #endif }
-
Mark global variables for placement in PSRAM using the macro:
PSRAM_DATA_SECTION uint32_t psram_global = 0
See Also
Please refer to the relevant API Reference: