Memory Hierarchy for RTL87x3D

This document introduces the memory system of RTL87x3D generally. RTL87x3D'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 RTL87x3D support a wide range of applications with different memory usage.

Memory Configuration

Memory Type

Start Addr

End Addr

Size (KB)

Block Size and Count

ROM

0x00000000

0x0011FFFF

1152

DATA RAM

0x00200000

0x0021FFFF

128

4 x 32KB

BUFFER RAM

0x00280000

0x0028FFFF

64

4 x 16KB

COMMON0 RAM

0x00300000

COMMON0 End Address

COMMON0 Size

N* x 32KB

COMMON1 RAM

COMMON1 Start Address

COMMON1 End Address

256KB - N* x 32KB

N* x 32KB

COMMON2 RAM (Shared from DSP as General RAM)

0x00500000

COMMON2 End Address

<=512

<= (16 x 32KB)

SPIC0 QPI Flash

0x02000000

0x03FFFFFF

Noncache Mapping Address to SPIC0

0x04000000

0x05FFFFFF

SPIC1 QPI PSRAM; QPI Flash

0x06000000

0x07FFFFFF

SPIC2 QPI PSRAM; QPI Flash

0x08000000

0x09FFFFFF

SPIC3 QPI PSRAM; QPI Flash

0x10000000

0x1FFFFFFF

SPIC4 QPI PSRAM; QPI Flash

0xC0000000

0xCFFFFFFF

eFuse

PSRAM LPC CTRL OPI PSRAM

0xA6000000

0xA7FFFFFF

Note

  1. COMMON0 and COMMON1 Size are configurable in mem_config.h for the application project and the total size is 256KB (COMMON0 and COMMON1 RAM are 32K bytes aligned). Recommend Settings: Text section located in COMMON0 RAM and data section located in COMMON1 RAM.

  2. The address and size of COMMON2 RAM depend on configuration, which should match with DSP images.

  3. DSP RAM is not shared as general RAM by default.

  4. eFuse doesn't support access directly by address.

ROM

Many fundamental functions are built in ROM, such as Bootloader, RTOS, Bluetooth Stack, and IO Drivers. The ROM code is located at 0x00000000 - 0x00120000 RTK offers abundant API for users to access most ROM functions, which can improve users' development efficiency and reduce code size.

RAM

RTL87x3D has three types of RAM: DATA, BUFFER, and COMMON (including COMMON0, COMMON1, and COMMON2). All types of memory could be used to store data and execute code. In the current SDK, Data RAM and Buffer RAM are provided for the vendor, and common RAM is provided for the user. COMMON1 RAM is used to store data while COMMON0 RAM is used to run RAM code.

The following picture shows the default RAM configuration. Some parts are already used by RTK internal functions marked as reserved 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:

  1. The COMMON0 is recommended to be configured as APP RAM TEXT.

  2. The COMMON1 is recommended to be configured as APP GLOBAL and APP HEAP.

../../../../_images/mem_ram_total_d.png

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 218KB by default, and some of it has already been used, which will be slightly different with different configurations generated by the MCUConfig Tool. As for the heap for the application, the RTK framework will cost about 37KB, which size will change when different features are supported.

../../../../_images/mem_heap_cfg_d.png

Heap Size

The size of APP RAM TEXT is 15KB (varies according to diffe rent applications) by default, which is used to run RAM code. The size of the APP GLOBAL area is 12KB (varies according to different applications) by default, which is used to store global variables. And RTK libs will cost about 4.3KB 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              (15*1024)
#define APP_GLOBAL_SIZE                (12*1024)
../../../../_images/mem_app_global_d.png

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 opened, 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

  1. How to get the current remaining heap size?

    The mem_peek() API in os_mem.h can return the current remaining heap size.

  2. How to get heap water level and task stack water level?

    The monitor_memory_and_timer() API in os_ext.h will 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.

  3. 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;
    
  4. How to get the size currently used by APP RAM TEXT and APP GLOBAL?

    The app.map file shows the size currently used by APP_RAM_TEXT and APP_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 RTL87x3D 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 RTL87x3D 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. A 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 RTL87x3D 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 RTL87x3D supports the APS1604M PSRAM model through SPIC1, SPIC2, and SPIC3.

As for OPI PSRAM, RTL87x3D supports external Winbond hyper bus interface PSRAM (HyperRAM) by installing a PSRAM Low Pin Count Controller (PSRAM LPC CTRL). The Winbond OPI PSRAM onboard is controlled by the PSRAM LPC CTRL which has a mapping memory to it. The maximum OPI PSRAM size considering PSRAM LPC CTRL mapping memory range is 32M bytes. RTL87x3D supports two OPI PSRAMs: W955D8MBYA whose size is 4M bytes and W956D8MBKX whose size is 8M bytes. Further, Realtek offers APIs to initialize both OPI and QPI PSRAM where PSRAM can be read and written like SRAM after being initialized.

PSRAM Types and Support

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

  1. 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.

  2. fmc_psram_winbond_opi_init is 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

  1. 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);
      
  2. Define the section macro in mem_config.h:

    #define PSRAM_TEXT_SECTION __attribute__((section(".psram.text")))
    
  3. 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
    }
    
  4. Mark functions for placement in PSRAM using the macro:

    PSRAM_TEXT_SECTION void psram_test_code(void)
    {
        // function implementation
    }
    

Placing Global Data in PSRAM

  1. 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);
      
  2. Define the section macro in mem_config.h:

    #define PSRAM_DATA_SECTION __attribute__((section(".psram.data")))
    
  3. 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
    }
    
  4. Mark global variables for placement in PSRAM using the macro:

    PSRAM_RAM_DATA_SECTION uint32_t psram_global = 0
    

See Also

Please refer to the relevant API Reference: