5.4 Cortex-M4 Guide

This chapter describes the two Cortex-M4 core examples of N32G41x: M4_BitBand modifies individual bits of an SRAM variable through the bit-band alias address, and M4_Mode_Privilege switches the thread-mode stack and privilege level. Neither example calls a standard peripheral library driver; the interfaces come from the CMSIS core headers. Read the bit-band alias region and the SRAM mapping together with the “Memory and bus architecture” chapter of the user manual, refer to the Arm Cortex-M4 Technical Reference Manual for the core exception model, and take the errata sheet as the authority on known chip issues.

5.4.1 Related files

Relative path Purpose
firmware/CMSIS/core/core_cm4.h Defines the Cortex-M4 core register access interfaces __set_CONTROL, __get_CONTROL, __set_PSP and __get_PSP plus the SCB and SysTick core structures, and includes the matching CMSIS adapter header per compiler
firmware/CMSIS/device/n32g41x.h Defines the peripheral register structures and interrupt numbers of N32G41x and includes core_cm4.h and system_n32g41x.h
projects/n32g41x_EVAL/examples/Cortex-M4/ Cortex-M4 example root directory, containing the 2 examples M4_BitBand and M4_Mode_Privilege
projects/n32g41x_EVAL/examples/Cortex-M4/M4_Mode_Privilege/ The directory composition of this example: MDK-ARM/, EWARM/, inc/, src/, readme.txt
projects/n32g41x_EVAL/bsp/ Board support files shared by the two examples: serial log initialization and print functions

5.4.2 API reference

This module uses no standard peripheral library driver; the examples call CMSIS core interfaces directly. Through cmsis_compiler.h the header firmware/CMSIS/core/core_cm4.h includes the adapter header of the active compiler and provides core register access functions such as __set_CONTROL, __get_CONTROL, __set_PSP and __get_PSP: M4_Mode_Privilege uses them to read and write CONTROL and PSP, while M4_BitBand writes the bit-band alias address directly and involves no library function.

5.4.3 Demo descriptions

This section describes the 2 Cortex-M4 examples: M4_BitBand modifies individual bits of an SRAM variable through the bit-band alias address and prints the result over the serial port, and M4_Mode_Privilege switches the process stack and the privilege level in thread mode.

5.4.3.1 M4_BitBand Bit-band alias access

Example directory: projects/n32g41x_EVAL/examples/Cortex-M4/M4_BitBand/

Initialize the serial log→Compute the bit-band alias address of variable Data→Set bits 0-7 to 1 one by one and print→Clear the same bit to 0 and print

Item Source analysis
Function Modifies bits 0-7 of the global variable Data one by one through the bit-band alias address, setting each bit to 1 and clearing it to 0, and prints the value after every write over the serial port.
Source files src/main.c: RAM_BASE, RAM_BB_BASE and the three bit-band macros, the global variable Data and main;
inc/main.h: includes n32g41x.h;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt service functions;
readme.txt: function and environment description;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration RAM_BASE is 0x20000000 and the alias region base RAM_BB_BASE is 0x22000000;
the alias address is formed by ORing RAM_BB_BASE, the byte offset of the variable address relative to RAM_BASE shifted left by 5 and the bit number shifted left by 2;
the global variable Data is __IO uint32_t. The system clock is HSI+PLL, 80 MHz for N32G412 and 96 MHz for N32G415.
Program and data path main calls log_init and prints Cortex-M4F BitBand;
it stores &Data into DataAddr, then in the for (i = 0; i < 8; i++) loop calls Data_SetBit_BB for bit i and prints Data, then calls Data_ResetBit_BB and prints Data again;
after the loop it enters an empty while(1).
Main APIs This example calls no library function and writes the bit-band alias address directly;
printf and log_init come from the board-level log support.
Verification After reset the serial port first prints Cortex-M4F BitBand, then 8 pairs of lines show the value after setting the bit and 0x00 after clearing it.
Use note The bit-band alias target address must fall inside the mapped SRAM bit-band region;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 and the hardware is N32G412/N32G415.
Core-code walkthrough and porting

The example computes the bit-band alias address of the global variable Data at run time, then sets and clears individual bits through that address and prints each result.

Data path: main -> &Data stored in DataAddr -> Data_SetBit_BB and Data_ResetBit_BB form the alias address and write a whole word of 1 or 0 -> the value of Data is printed over the serial port by printf

1. Define the bit-band base addresses and the access macros. RAM_BASE and RAM_BB_BASE give the base addresses of the SRAM bit-band region and its alias region, and Data_SetBit_BB and Data_ResetBit_BB write a whole word of 1 or 0 to the computed alias address.

src/main.csrc/main.c
/* RAM address */
#define RAM_BASE    ((uint32_t)0x20000000)
/* RAM bitband address */
#define RAM_BB_BASE ((uint32_t)0x22000000)

__IO uint32_t Data = 0, DataAddr = 0, DataBitValue = 0;

#define Data_ResetBit_BB(DataAddr, BitNumber)                                                                          \
    (*(__IO uint32_t*)(RAM_BB_BASE | ((DataAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 0)

#define Data_SetBit_BB(DataAddr, BitNumber)                                                                            \
    (*(__IO uint32_t*)(RAM_BB_BASE | ((DataAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 1)

#define Data_GetBit_BB(DataAddr, BitNumber) (Data >> BitNumber)

When porting to another part with bit-band regions, RAM_BASE and RAM_BB_BASE must be changed to the base addresses of that part’s SRAM bit-band region and alias region.

2. Take the variable address and set each bit to 1. main stores &Data into DataAddr, then calls the set macro for bit i inside the loop through the same alias address.

src/main.csrc/main.c · main
    /* Get the variable address */
    DataAddr = (uint32_t)&Data;

    /* Modify variable bit using bit-band access */

    for (i = 0; i < 8; i++)
    {
        /* Modify Data variable bit i */
        Data_SetBit_BB(DataAddr, i); /* Data = (0x00000001 << i) */

The loop bound 8 determines how many bits are exercised; to use another variable, only &Data has to be replaced with the target variable address.

3. Clear each bit and print every write. Each bit is cleared right after it is set, and the value after both writes is sent to the serial port.

src/main.csrc/main.c · main
        printf("Set bit %d of variable Data to 1  = 0x%02X \r\n", i, Data);

        Data_ResetBit_BB(DataAddr, i); /* Data = 0x00000000 */
        printf("Set bit %d of variable Data to 0  = 0x%02X \r\n", i, Data);
    }

Corresponding source files: src/main.c

5.4.3.2 M4_Mode_Privilege Thread-mode privilege and stack switch

Example directory: projects/n32g41x_EVAL/examples/Cortex-M4/M4_Mode_Privilege/

Initialize the serial log→Clear the process stack buffer and set the PSP→Switch thread mode to the process stack→Enter unprivileged mode by writing a CONTROL bit→Restore privileged mode through the SVC exception

Item Source analysis
Function Switches thread mode from the main stack to the process stack, then toggles the thread-mode access level between unprivileged and privileged, and prints every result over the serial port.
Source files src/main.c: the four mode bit macros, the per-compiler implementations of __SVC, the process stack array PSPMemAlloc and main;
inc/main.h: includes n32g41x.h;
src/n32g41x_it.c, inc/n32g41x_it.h: interrupt service functions including SVC_Handler;
readme.txt: function and environment description;
MDK-ARM/, EWARM/: Keil and IAR projects.
Key configuration SP_PROCESS_SIZE is 0x200 and the process stack is reserved by __IO uint8_t PSPMemAlloc[0x200];
SP_PROCESS is 0x02, SP_MAIN is 0x00, THREAD_MODE_PRIVILEGED is 0x00 and THREAD_MODE_UNPRIVILEGED is 0x01;
under __CC_ARM, __ICCARM__ and __GNUC__ the __SVC implementation triggers svc 0x01 with assembly or inline assembly.
The system clock is HSI+PLL, 80 MHz for N32G412 and 96 MHz for N32G415.
Program and data path main calls log_init and prints a first line;
after clearing PSPMemAlloc, __set_PSP sets the process stack top and __set_CONTROL writes SP_PROCESS to switch to the process stack, reading back CurrentStack and PSPValue;
it then ORs THREAD_MODE_UNPRIVILEGED with SP_PROCESS into CONTROL and prints the result;
writing THREAD_MODE_PRIVILEGED cannot restore privilege, so __SVC triggers SVC_Handler, which runs __set_CONTROL(0x00) to restore privilege and prints the result.
Main APIs __set_PSP, __set_CONTROL, __get_CONTROL, __get_PSP
Verification After reset the serial port prints Cortex-M4F Mode Privilege, then Cortex-M4F enter Unprivileged Mode OK and Cortex-M4F enter Privileged Mode OK in turn;
the thread-mode stack and access level can be inspected in the compiler’s Cortex register window.
Use note In unprivileged thread mode the MSR instruction and the access to System Control Space registers such as NVIC and SysTick are restricted;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 and the hardware is N32G412/N32G415.
Core-code walkthrough and porting

The example first switches to the process stack, then toggles the privilege level in thread mode through the CONTROL register, and finally returns to privileged mode through the SVC exception.

Data path: main -> __set_PSP writes the process stack top -> __set_CONTROL writes SP_PROCESS to select the process stack -> __set_CONTROL writes CONTROL[0] to switch the privilege level -> __SVC triggers SVC_Handler -> __set_CONTROL(0x00) restores privilege

1. Define the mode bit macros, __SVC and the process stack. The top of the file gives the four mode bit macros, __SVC triggers svc 0x01 through a per-compiler branch, and the process stack is reserved by PSPMemAlloc.

src/main.csrc/main.c
#define SP_PROCESS_SIZE          0x200 /* Process stack size */
#define SP_PROCESS               0x02  /* Process stack */
#define SP_MAIN                  0x00  /* Main stack */
#define THREAD_MODE_PRIVILEGED   0x00  /* Thread mode has privileged access */
#define THREAD_MODE_UNPRIVILEGED 0x01  /* Thread mode has unprivileged access */

/* clang-format off */
#if defined ( __CC_ARM   )
  __ASM void __SVC(void) 
  { 
    SVC 0x01 
    BX R14
  }
#elif defined ( __ICCARM__ )
  static __INLINE  void __SVC()                     { __ASM ("svc 0x01");}
#elif defined   (  __GNUC__  )
  static __INLINE void __SVC()                      { __ASM volatile ("svc 0x01");}
#endif
/* clang-format on */

__IO uint8_t PSPMemAlloc[SP_PROCESS_SIZE];
__IO uint32_t Index = 0, PSPValue = 0, CurrentStack = 0, ThreadMode = 0;

When porting to another compiler, a matching __SVC implementation must be added beyond the __CC_ARM, __ICCARM__ and __GNUC__ branches.

2. Switch to the process stack and read back the current stack. main first writes the process stack top into PSP, then selects the process stack and reads back the stack in use and the stack pointer.

src/main.csrc/main.c · main
    __set_PSP((uint32_t)PSPMemAlloc + SP_PROCESS_SIZE);

    /* Select Process Stack as Thread mode Stack */
    __set_CONTROL(SP_PROCESS);

    /* Get the Thread mode stack used */
    if ((__get_CONTROL() & 0x02) == SP_MAIN)
    {
        /* Main stack is used as the current stack */
        CurrentStack = SP_MAIN;
    }
    else
    {
        /* Process stack is used as the current stack */
        CurrentStack = SP_PROCESS;

        /* Get process stack pointer value */
        PSPValue = __get_PSP();
    }

The process stack top is the end of the PSPMemAlloc array, so changing SP_PROCESS_SIZE must keep the array size and the stack top consistent.

3. Enter unprivileged mode and request a restore. Writing bit 0 of CONTROL enters unprivileged mode and prints the result; since the privilege bit cannot be written back directly in unprivileged mode, the example raises the SVC exception instead.

src/main.csrc/main.c · main
    /* Switch Thread mode from privileged to unprivileged */
    /* Thread mode has unprivileged access */
    __set_CONTROL(THREAD_MODE_UNPRIVILEGED | SP_PROCESS);

    /* Unprivileged access mainly affect ability to:
        - Use or not use certain instructions such as MSR fields
        - Access System Control Space (SCS) registers such as NVIC and SysTick */

    /* Check Thread mode privilege status */
    if ((__get_CONTROL() & 0x01) == THREAD_MODE_PRIVILEGED)
    {
        /* Thread mode has privileged access  */
        ThreadMode = THREAD_MODE_PRIVILEGED;
        printf("Cortex-M4F enter Unprivileged Mode Fail \r\n");
    }
    else
    {
        /* Thread mode has unprivileged access*/
        ThreadMode = THREAD_MODE_UNPRIVILEGED;
        printf("Cortex-M4F enter Unprivileged Mode OK \r\n");
    }

    /* Switch back Thread mode from unprivileged to privileged */
    /* Try to switch back Thread mode to privileged (Not possible, this can be
       done only in Handler mode) */
    __set_CONTROL(THREAD_MODE_PRIVILEGED | SP_PROCESS);

    /* Generate a system call exception, and in the INTSTS switch back Thread mode to privileged */
    __SVC();

In unprivileged thread mode the MSR instruction and the access to System Control Space registers such as NVIC and SysTick are restricted.

4. Restore privileged mode in SVC_Handler. The SVC exception switches the processor to handler mode, where it runs __set_CONTROL(0x00); after the return to thread mode the access is privileged again.

src/n32g41x_it.csrc/n32g41x_it.c · SVC_Handler
void SVC_Handler(void)
{
    /* Switch Thread mode from privileged to unprivileged */
    __set_CONTROL(0x00);
}

Corresponding source files: src/main.c, src/n32g41x_it.c

5.4.4 Notes and troubleshooting

Scenario Check & action Source
Computing a bit-band alias address The Bit banding section of the “Memory and bus architecture” chapter of the user manual gives the mapping formula bitband_byte_addr = bitband_base + (byte_offset x 32) + (bit_number x 4), where byte_offset is the number of the byte that contains the target bit in the bit-band region and bit_number is the position of the target bit in that byte (0-7);
the base address of the SRAM bit-band alias region is 0x22000000.
User manual
Address range covered by the bit-band region The SRAM section of the same chapter states that the SRAM of N32G412/N32G415 is at most 24 KB and covers the address range 0x20000000-0x20005FFF;
the SRAM is mapped into the bit-band region, so a bit-band alias access completes an atomic read-modify-write.
User manual

The N32G412 and N32G415 series errata sheet V1.0.0 (2026.6.30) lists a single row for revision A chips with an empty errata link column, and gives no Cortex-M4 related entries.

Nations Technologies DevKit Docs

Datasheets, SDK, driver APIs and demo guides for Nations Technologies MCU development kits, organized by chip series.

© 2026 Nations Technologies Inc. All rights reserved.粤ICP备11069485号This site uses cookie-free anonymous analytics.