Using Shared Memory With Low Power Radar

Table of Contents

Overview

The purpose of this document is to discuss implementation specific considerations when using shared memory on the low power device platform which includes the xWRL1432, xWRL6432, and xWRL6844.

For general shared memory usage and documentation, please find the links to the SDK below:

xWRLx432 - HWASS/FECSS memory initialization requirements

The 160 KB HWASS RAM and the 96 KB HWASS/FECSS shared RAM both require initialization to be accessed. This has the following implications:

xWRLx432 - Avoiding Shared Memory Corruption

As discussed in the AWRL6432 Device Silicon Errata, IWRL6432 Device Silicon Errata, AWRL1432 Device Silicon Errata, and IWRL1432 Device Silicon Errata, when the shared memory is allocated to APPSS/M4F layer, a full word write access to location A followed by a sub-word write access - e.g., byte write - to location B corrupts data in location A due to ECC data transfer logic. As a result, this can limit the flexibility of the shared memory, but there are a few ways to avoid this corruption by doing the following:

1. When shared memory is allocated to the APPSS/M4F, use shared memory for read-only sections

Since this issue partially originates from write accesses, allocating read-only sections, such as program code (.text) and read-only data (.rodata), to the shared memory prevents write accesses from occurring. This can be done by editing the linker file of a project. For example, shown below is a snippet from the linker.cmd file for the mmWave demo where the .text and .rodata sections have been allocated to M4F_SHM_MEM:

SECTIONS
{
    /* This has the M4F entry point and vector table, this MUST be at 0x0 */
    .vectors:{} palign(8) > M4F_VECS
    .bss:    {} palign(8) > M4F_RAM12     /* This is where uninitialized globals go */
    RUN_START(__BSS_START)
    RUN_END(__BSS_END)
    .text:   {} align(8) >> M4F_SHM_MEM     /* This is where code resides */
    .data:   {} align(8) >> M4F_RAM12 | M4F_RAM3     /* This is where initialized globals and static go */
    .rodata: {} align(8) >> M4F_SHM_MEM     /* This is where const's go */
    .sysmem: {} palign(8) > M4F_RBL     /* This is where the malloc heap goes */
    .stack:  {} palign(8) > M4F_RBL     /* This is where the main() stack goes */
    .l3:     {} palign(8) > HWASS_SHM_MEM     /* This is where L3 data goes */
}

MEMORY
{
    M4F_VECS : ORIGIN = 0x00400000 , LENGTH = 0x00000200
    M4F_RAM12  : ORIGIN = 0x00400200 , LENGTH = (0x00058000 - 0x200) /* 32KB of RAM2 is being used by RBL */
    M4F_RBL    : ORIGIN = 0x00458000 , LENGTH = 0x8000 /* 32KB of RAM2 is being used by RBL */
    M4F_RAM3  : ORIGIN = 0x00460000 , LENGTH = 0x00020000

    M4F_SHM_MEM : ORIGIN = 0x00480000 , LENGTH = 0x00040000 /* 256KB in APPSS PD */

    HWASS_SHM_MEM : ORIGIN = 0x60000000, LENGTH = 0x00040000 /* 96KB in FECSS PD and 160KB in HWA PD */
}

2. If using a non functional safety device, disable the ECC for shared memories. Note: The ECC for shared memories should already be disabled by the ROM Bootloader (RBL) for non functional safety devices.

To identify whether a device is considered a non functional safety device, connect to the device in the CCS debugger and open up View -> Memory Browser. Navigate to address 0x5A02002C and read the three least significant nibbles which is the part number. If the part number, matches any IDs from the list, it is a non functional safety device, and the shared memory ECCs should be disabled automatically by the RBL.

📝 Automotive Restriction
For automotive devices such as the AWRL6432, it is not possible to disable the ECC as they are all functional safety devices.

To identify if an ECC is disabled, the provided function can be used to read ECC enable fields of the control register. It will return either a 0 (ECC disabled) or a 1 (ECC enabled).

#define ECC_VEC_RD_SVBUS_MASK 0x00008000
#define ECC_VEC_RD_SVBUS_DONE_MASK 0x01000000

#define ECC_VEC_APP_SHM_RAM0_ID 0x00000008
#define ECC_VEC_APP_SHM_RAM1_ID 0x00000009
#define ECC_VEC_FEC_SHM_RAM_ID 0x00000002

#define ECC_CON_REG_OFFSET 0x00140000
#define ECC_CON_ECC_EN_MASK 0x000000007

volatile uint32_t* appEccAggEccVector;
volatile uint32_t* appEccAggEccControl;

/* Returns enable status of an ECC given its ID. 
   status = 0 (ECC disabled) | status = 1 (ECC enabled) */
uint32_t ECC_enableStatus(uint32_t id)
{
    appEccAggEccVector = (volatile uint32_t*)0x56F7EC08;
    appEccAggEccControl = (volatile uint32_t*)0x56F7EC14;
    uint32_t status = 0;

    // Read ECC Control Register value
    *appEccAggEccVector = (ECC_CON_REG_OFFSET | id); // Set register and endpoint to read
    *appEccAggEccVector = (*appEccAggEccVector | ECC_VEC_RD_SVBUS_MASK); // Trigger read
    while((*appEccAggEccVector & ECC_VEC_RD_SVBUS_DONE_MASK) != ECC_VEC_RD_SVBUS_DONE_MASK);

    // Check enable status
    if((*appEccAggEccControl & ECC_CON_ECC_EN_MASK) == ECC_CON_ECC_EN_MASK) status = 0x1;

    return status;
}

If the ECC for a shared memory is not being disabled by the RBL, then the following function can also be used to disable all shared memory ECCs. The code utilizes the enable status function previously defined and a new function for writing to the control register.

🛑 DISCLAIMER
The ECC_disable_shared_memory() function must be called before any peripheral drivers are initialized through the Drivers_open() function. Otherwise, the main application code will crash.

#define ECC_VEC_RD_SVBUS_MASK 0x00008000
#define ECC_VEC_RD_SVBUS_DONE_MASK 0x01000000

#define ECC_VEC_APP_SHM_RAM0_ID 0x00000008
#define ECC_VEC_APP_SHM_RAM1_ID 0x00000009
#define ECC_VEC_FEC_SHM_RAM_ID 0x00000002

#define ECC_CON_ECC_EN_MASK 0x00000007
#define ECC_CON_REG_OFFSET 0x00140000

#define TOP_EFUSE_ALL_SHM_EN_MASK 0x00000218

volatile uint32_t* appEccAggEccVector;
volatile uint32_t* appEccAggEccControl;
volatile uint32_t* topEfuseRamEccCfg;

/* Returns enable status of an ECC given its ID.
   status = 0 (ECC disabled) | status = 1 (ECC enabled) */
uint32_t ECC_enableStatus(uint32_t id)
{
    appEccAggEccVector = (volatile uint32_t*)0x56F7EC08;
    appEccAggEccControl = (volatile uint32_t*)0x56F7EC14;
    uint32_t status = 0;

    // Read ECC Control Register value
    *appEccAggEccVector = (ECC_CON_REG_OFFSET | id); // Set register and endpoint to read
    *appEccAggEccVector = (*appEccAggEccVector | ECC_VEC_RD_SVBUS_MASK); // Trigger read
    while((*appEccAggEccVector & ECC_VEC_RD_SVBUS_DONE_MASK) != ECC_VEC_RD_SVBUS_DONE_MASK);

    // Check enable status
    if((*appEccAggEccControl & ECC_CON_ECC_EN_MASK) == ECC_CON_ECC_EN_MASK) status = 0x1;

    return status;
}

/* Writes a given value to the ECC control register while masking out other bits */
void ECC_controlWrite(uint32_t id, uint32_t mask, uint32_t shift, uint32_t value)
{
    appEccAggEccVector = (volatile uint32_t*)0x56F7EC08;
    appEccAggEccControl = (volatile uint32_t*)0x56F7EC14;

    // Write value to control register of given ECC
    *appEccAggEccVector = id; // Select ECC
    *appEccAggEccControl = ((*appEccAggEccControl & ~mask) | ((value << shift) & mask)); // Write value to ECC control register

    return;
}

/* Disables all shared memory ECCs */
void ECC_disable_shared_memory()
{
    appEccAggEccVector = (volatile uint32_t*)0x56F7EC08;
    appEccAggEccControl = (volatile uint32_t*)0x56F7EC14;
    topEfuseRamEccCfg = (volatile uint32_t*)0x5A0201AC;

    // Disable APP SHARED MEM0 ECC
    ECC_controlWrite(ECC_VEC_APP_SHM_RAM0_ID, ECC_CON_ECC_EN_MASK, 0, 0);

    // Verify respective ECC is disabled. Throw assertion if not disabled.
    if(ECC_enableStatus(ECC_VEC_APP_SHM_RAM0_ID)) DebugP_assert(0);

    // Disable APP SHARED MEM1 ECC
    ECC_controlWrite(ECC_VEC_APP_SHM_RAM1_ID, ECC_CON_ECC_EN_MASK, 0, 0);

    // Verify respective ECC is disabled. Throw assertion if not disabled.
    if(ECC_enableStatus(ECC_VEC_APP_SHM_RAM1_ID)) DebugP_assert(0);

    // Disable FEC SHARED MEM ECC
    ECC_controlWrite(ECC_VEC_FEC_SHM_RAM_ID, ECC_CON_ECC_EN_MASK, 0, 0);

    // Verify respective ECC is disabled. Throw assertion if not disabled.
    if(ECC_enableStatus(ECC_VEC_FEC_SHM_RAM_ID)) DebugP_assert(0);

    // Disable RAM ECC configuration for deep sleep exit
    *topEfuseRamEccCfg = ((*topEfuseRamEccCfg & ~TOP_EFUSE_ALL_SHM_EN_MASK) | 0);

    return;
}

xWRLx432 - Using Low Power Deep Sleep with Shared Memory

When using low power deep sleep (LPDS) with shared memory, there are some extra precautions that must be taken. The resume procedure from LPDS will re-enable the shared memory, and these shared memories will not be available until the device has fully recovered from LPDS. This can cause issues if the code for your LPDS resume hook and other power functions are in the shared memory. The following recommendations should be applied after configuring low power and shared memory.

The power_LPDSresumehook and Power_sleep functions must not be in the shared memory section. To ensure this does not occur, you can follow the steps outlined below:

  1. Define a section in a non-shared memory region of the RAM. For example, the linker snippet below defines the .ram3_text section in the M4F_RAM3 region which is a part of the Arm-M4F’s native RAM memory:

  1. Add the section attribute specifier with the previously defined non-shared memory section for the power_LPDSresumehook and Power_sleep function prototypes. For example, the power_LPDSresumehook and Power_sleep functions are allocated to the ram3_text section as shown below. The power_LPDSresumehook is typically found within the power_management.h file of most SDK projects, and Power_sleep can be found under <MMWAVE_LSDK_INSTALL_DIR>/source/drivers/power.h.

📝 NOTE
The power_LPDSresumehook and Power_sleep may already be allocated to non-shared memory depending on the linker configuration and project build. Double check the .map file generated during compilation to see if both functions are placed within a non-shared memory section. Additionally, the attribute specifier can be applied directly to the function definitions, but this will require recompilation for Power_sleep if utilizing the driver libraries, hence why its recommended to apply the attribute specifier to the prototype instead.

/**
*  @b Description
*  @n
*      This function is user configurable hook after exiting LPDS
*
*/
__attribute__((section(".ram3_text"))) void power_LPDSresumehook()
{...}

/*!
 *  @brief  Transition the device into a sleep state
 *
 *  This function is called from the power policy when it has made a decision
 *  to put the device in a specific sleep state.  This function returns to the
 *  caller (the policy function) once the device has awoken from sleep.
 *
 *  @warning This function must be called with interrupts disabled, and
 *  should not be called directly by the application, or by any drivers.
 *  This function does not check declared constraints; the policy function
 *  must check constraints before calling this function to initiate sleep.
 *
 *  @param[in]  sleepState    the sleep state
 *
 *  @retval  #Power_SOK on success, the device has slept and is awake again.
 *
 *  @retval  #Power_EFAIL if an error occurred during client notifications, or
 *  if a general failure occurred.
 *
 *  @retval  #Power_EINVALIDINPUT if the @p sleepState is invalid.
 *
 *  @retval  #Power_EBUSY if another transition is already in progress.
 */
__attribute__((section(".ram3_text"))) int_fast16_t Power_sleep(uint_fast16_t sleepState)
{...}

Need more help?

Please find additional resources relevant to this discussion below: