5.4 Cortex-M4F Guide
This chapter covers the Cortex-M4F core features of the N32H49x, including bit-band access, floating-point unit (FPU) arithmetic, and privilege-level switching in thread mode, together with the interface source the examples in this chapter depend on. Cortex-M4F is the processor core, so the SDK provides no dedicated driver file as a standard peripheral driver does; its interfaces come from the CMSIS Core headers and the device startup file, and this chapter is best read together with the “System” chapter of the user manual.
5.4.1 Related files
| Relative path | Purpose |
|---|---|
firmware/CMSIS/core/ |
CMSIS Core header directory: core_cm4.h and the compiler adaptation headers such as cmsis_armcc.h and cmsis_gcc.h, plus arm_math.h and mpu_armv7.h |
firmware/CMSIS/device/ |
Device definitions and startup files: n32h49x.h (with the interrupt-number enumeration IRQn_Type), system_n32h49x.c, and startup_n32h49x.s under startup/ |
projects/n32h49x_EVAL/examples/Cortex-M4F/ |
Root of the examples in this chapter, containing the 3 examples M4F_BitBand/, M4F_FPU/, and M4F_Mode_Privilege/ |
projects/n32h49x_EVAL/examples/Cortex-M4F/M4F_BitBand/ |
Project layout of the example: MDK-ARM/, EWARM/, inc/, src/, and readme.txt |
projects/n32h49x_EVAL/bsp/ |
Board support files used by the example: log initialization and print UART remapping |
5.4.2 API reference
Cortex-M4F is the processor core, so the SDK has no n32h49x_xxx.h/.c driver source files as a standard peripheral driver does; its interfaces come from the CMSIS Core headers and the device startup file:
firmware/CMSIS/core/: the CMSIS Core headers and the per-compiler adaptation headers (cmsis_armcc.h,cmsis_armclang.h,cmsis_gcc.h, andcmsis_iccarm.h), providing the inline functions__get_CONTROL/__set_CONTROL,__get_PSP/__set_PSP,__get_MSP/__set_MSP,__get_IPSR,__get_PRIMASK/__set_PRIMASK, and__get_FPSCR/__set_FPSCR, together with the core instruction wrappers__DSB,__ISB,__WFI,__enable_irq, and__disable_irq.firmware/CMSIS/core/core_cm4.h: the core peripheral definitions (SCB,SysTick, andNVIC) and the feature macro__FPU_USED.firmware/CMSIS/device/n32h49x.h: the device register definitions and the interrupt-number enumerationIRQn_Type.firmware/CMSIS/device/startup/startup_n32h49x.s: the vector table and the exception and interrupt entriesReset_Handler,NMI_Handler,HardFault_Handler,MemManage_Handler,BusFault_Handler,UsageFault_Handler,SVC_Handler,DebugMon_Handler,PendSV_Handler, andSysTick_Handler.
The interfaces used by the examples in this chapter center on stack and privilege control: __set_PSP writes the process stack top into the PSP, and __set_CONTROL switches the process stack and the privilege bit through the CONTROL register; __SVC, which raises the SVCall, is not a CMSIS interface but an inline function that the example defines itself for each compiler.
5.4.3 Demo descriptions
This section covers the 3 Cortex-M4F examples, which demonstrate bit-band access, floating-point unit arithmetic, and privilege-level switching in thread mode.
5.4.3.1 M4F_BitBand bit-band access
Example directory: projects/n32h49x_EVAL/examples/Cortex-M4F/M4F_BitBand/
Initialize the log UART and print the example name→Record the address of variable Data and obtain the SRAM bit-band alias base→Set bit i of variable Data to 1 in a loop and read it back for printing→Clear bit i in a second loop and read it back for printing→Enter the empty main loop after all 8 bits
| Item | Source analysis |
|---|---|
| Function | Demonstrates reading and writing a single bit of a variable through a bit-band alias address; it sets and clears bit i of variable Data in turn and reads back the whole variable for printing. |
| Source files | src/main.c: main and the bit-band macros Data_SetBit_BB, Data_ResetBit_BB, and Data_GetBit_BB;inc/main.h and inc/n32h49x_it.h: common declarations and the interrupt header;src/n32h49x_it.c: the exception and interrupt handlers;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | The clock source is HSI+PLL at 240 MHz; the print UART is PA9 at 115200 baud; the bit-band alias base is the SRAM alias base 0x22000000, and the accessed variable resides in SRAM. |
| Program and data path | After main calls log_init and prints the example name, it stores the address of variable Data into DataAddr;the loop calls the set macro for bit i and prints once, then calls the reset macro and prints once, reading Data for the whole-word result. |
| Main APIs | Data_SetBit_BB, Data_ResetBit_BB, Data_GetBit_BB, log_init |
| Verification | After reset the UART prints Cortex-M4F BitBand;it then prints the set result 8 times, from 0x01 at bit 0 up to 0x80 at bit 7; it then prints the cleared result 8 times, all 0x00. |
| Use note | The macro converts the target bit address into an alias address: the byte offset shifted left by 5 equals a multiply by 32, and the bit number shifted left by 2 equals a multiply by 4; writing one word to the alias region performs an atomic read-modify-write of the target bit. Toolchains are KEIL MDK-ARM V5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example uses the SRAM bit-band alias to map one bit of a variable to a readable and writable word, so a single bit can be operated atomically with an ordinary whole-word assignment.
Data path: main records the address of variable Data → the alias address is computed from the alias base, the byte offset, and the bit number → writing one word to the alias region sets the bit and reads the whole word back → writing another word clears the bit and reads the whole word back → after bits 0 to 7 the code enters the empty main loop
1. Take the bit-band and alias base addresses. The SRAM bit-band region starts at 0x20000000 and the matching alias region starts at 0x22000000; both bases are defined as macros first.
/* RAM address */
#define RAM_BASE ((uint32_t)0x20000000)
/* RAM bitband address */
#define RAM_BB_BASE ((uint32_t)0x22000000)
2. Compute the alias address from the byte offset and bit number. The source gives the mapping formula in a comment: the alias byte address equals the alias base plus the byte offset times 32, plus the bit number times 4. The << 5 in the macro is a multiply by 32 and the << 2 is a multiply by 4.
/* A mapping formula shows how to reference each word in the alias region to a
corresponding bit in the bit-band region. The mapping formula is:
bit_byte_addr = bit_band_base + (byte_offset x 32) + (bit_number x 4)
Parameter Description:
bit_byte_addr: The address of the byte in the alias memory region that maps to the targeted bit.
bit_band_base: The starting address of the alias region
byte_offset: The number of the byte in the bit-band region that contains the targeted bit
bit_number: The bit position (0-7) of the targeted bit */
3. Read one bit by shifting right. A read does not go through the alias region; the variable is simply shifted right by the bit number to take the lowest bit.
#define Data_GetBit_BB(DataAddr, BitNumber) (Data >> BitNumber)
4. Set and clear a bit atomically through the alias region in a loop. Each round sets bit i to 1 and prints the whole word, then clears bit i and prints the whole word, verifying that the alias write affects only the target bit.
for (i = 0; i < 8; i++)
{
/* Modify Data variable bit i */
Data_SetBit_BB(DataAddr, i); /* Data = (0x00000001 << i) */
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 M4F_FPU floating-point unit
Example directory: projects/n32h49x_EVAL/examples/Cortex-M4F/M4F_FPU/
Initialize the log UART and the general-purpose timer used for timing→Iterate the Julia set point by point in a buffer with a zoom factor→Read the timer count before and after the computation for the elapsed time→Print FPU On or FPU Off and the elapsed time according to a compile-time switch→Enter the empty main loop after printing
| Item | Source analysis |
|---|---|
| Function | Demonstrates the speedup of the Cortex-M4F floating-point unit on the same floating-point workload; it uses Julia set iteration as a benchmark and measures the elapsed time with the FPU on and off for printing. |
| Source files | src/main.c: main and InitTIMER, StartTIMER, and StopTIMER;src/julia_fpu.c: the floating-point intensive GenerateJulia_fpu;inc/julia_fpu.h: the iteration constants and the function declaration;inc/main.h and inc/n32h49x_it.h: common declarations and the interrupt header;readme.txt, MDK-ARM/, and EWARM/: usage plus the Keil and IAR projects. |
| Key configuration | The clock source is HSI+PLL at 240 MHz; the print UART is PA9 at 115200 baud; GTIM3 is used for timing with the prescaler HclkFreq/50000 and a period of 65535; the screen buffer is 100 x 50 bytes and the zoom table has 26 entries. |
| Program and data path | main calls log_init and then InitTIMER to configure GTIM3;it then calls StartTIMER to clear and start the count, runs GenerateJulia_fpu, and reads the count with StopTIMER, dividing by 20 to print milliseconds. |
| Main APIs | InitTIMER, StartTIMER, StopTIMER, GenerateJulia_fpu, log_init |
| Verification | After reset the UART prints Cortex-M4F FPU;it then prints FPU On : xxms or FPU Off : xxms, with the label chosen by the compile-time __FPU_USED;the same floating-point workload takes noticeably less time with the FPU on. |
| Use note | In Keil, use Floating Point Hardware on the Target page of Options for Target to enable or disable the FPU, then rebuild and compare the printed time;with the FPU off the floating-point work is done by the software floating-point library. Toolchains are KEIL MDK-ARM V5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example runs a floating-point intensive Julia set iteration and uses a general-purpose timer to measure the same workload with the FPU on and off.
Data path: main clears the screen and InitTIMER configures GTIM3 → StartTIMER clears and starts the count → GenerateJulia_fpu performs the float iteration and writes the buffer → StopTIMER reads the count for the elapsed time → the value is divided by 20 to print milliseconds
1. Tell the FPU state with a compile-time switch. __FPU_USED is provided by the compiler from the target settings, and the example uses it to pick the printed label so hardware and software floating point share one code path.
#if (__FPU_USED == 1)
#define SCORE_FPU_MODE "FPU On"
#define PROGRESS_FPU_MODE "FPU ON"
#else
#define SCORE_FPU_MODE "FPU Off"
#define PROGRESS_FPU_MODE "FPU OFF"
#endif /* __FPU_USED */
2. Measure the elapsed time with a general-purpose timer. The prescaler fixes the count frequency at 50 kHz and the period takes its maximum; the count is cleared and started before the measurement.
void InitTIMER(void)
{
RCC_ClocksType RCC_Clocks;
/* GPTIM3 clock enable */
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPHEN_GTIM3,ENABLE);
RCC_GetClocksFreqValue(&RCC_Clocks);
/* DeInit GTIM3 */
TIM_DeInit(GTIM3);
TIM_InitTimBaseStruct(&TIM_TimeBaseStructure);
/* Time Base configuration */
TIM_TimeBaseStructure.Prescaler = ((RCC_Clocks.HclkFreq / 50000));
TIM_TimeBaseStructure.CounterMode = TIM_CNT_MODE_UP;
TIM_TimeBaseStructure.Period = 65535;
TIM_TimeBaseStructure.ClkDiv = TIM_CLK_DIV1;
TIM_InitTimeBase(GTIM3, &TIM_TimeBaseStructure);
}
3. Write the float iteration into the buffer. Every round of the double Julia loop does several single-precision multiply-add operations; with the FPU off these operations are done by the software floating-point library and are much slower.
while ((i < ITERATION - 1) && (rayon < 4))
{
tmp1 = num_real * num_real;
tmp2 = num_img * num_img;
num_img = 2 * num_real * num_img + IMG_CONSTANT;
num_real = tmp1 - tmp2 + REAL_CONSTANT;
rayon = tmp1 + tmp2;
i++;
}
/* Store the value in the buffer */
buffer[x + y * size_x] = i;
4. Convert the count to milliseconds and print it. The count frequency is 50 kHz, so dividing the count by 20 gives milliseconds, printed together with the label.
/* Get elapsed time */
score_fpu = StopTIMER();
printf("%s : %dms\r\n", SCORE_FPU_MODE, score_fpu/20);
break;
Corresponding source files: src/main.c, src/julia_fpu.c
5.4.3.3 M4F_Mode_Privilege privilege-mode switching
Example directory: projects/n32h49x_EVAL/examples/Cortex-M4F/M4F_Mode_Privilege/
Initialize the log UART and zero the process-stack memory→Write the process stack top into the PSP and select the process stack→Switch thread mode to unprivileged access and check whether it took effect→Switch back to privileged access in handler mode through the SVC exception→Print the two switching results and enter the empty main loop
| Item | Source analysis |
|---|---|
| Function | Demonstrates stack and privilege-level switching in thread mode; it first switches the thread-mode stack from the main stack to the process stack, then switches to unprivileged access, and finally switches back to privileged access in handler mode through the SVC exception and prints the results. |
| Source files | src/main.c: main plus __SVC and the stack and privilege macro definitions;src/n32h49x_it.c: SVC_Handler and the exception handlers;inc/main.h and inc/n32h49x_it.h: common declarations and the interrupt header;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | The clock source is HSI+PLL at 240 MHz; the print UART is PA9 at 115200 baud; the process-stack array is 0x200 bytes and thread mode starts on the main stack. |
| Program and data path | After main calls log_init it zeroes the process-stack array, writes the process stack top into the PSP with __set_PSP, and selects the process stack with __set_CONTROL;it then switches thread mode to unprivileged and reads back CONTROL to check, and finally calls __SVC to raise the exception, where SVC_Handler switches back to privileged. |
| Main APIs | __set_PSP, __get_PSP, __set_CONTROL, __get_CONTROL, __SVC, SVC_Handler |
| Verification | After reset the UART prints Cortex-M4F Mode Privilege;it then prints Cortex-M4F enter Unprivileged Mode OK and Cortex-M4F enter Privileged Mode OK;the CONTROL and PSP values can also be inspected in the Cortex registers view of the debugger. |
| Use note | Thread mode itself cannot switch unprivileged back to privileged, so CONTROL must be changed in handler mode;the example does exactly that by using SVC to enter SVC_Handler;unprivileged access mainly restricts the use of certain MSR fields and access to system control space registers such as NVIC and SysTick.Toolchains are KEIL MDK-ARM V5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example shows thread-mode switching between the main stack and the process stack, between privileged and unprivileged, and how the SVC exception is used to switch back to privileged in handler mode.
Data path: main zeroes the process-stack array → __set_PSP writes the process stack top and __set_CONTROL selects the process stack → __set_CONTROL sets the unprivileged bit and reads it back to check → __SVC raises the SVCall → SVC_Handler clears CONTROL to switch back to privileged
1. Prepare a process stack for thread mode. The reserved array is zeroed first, the stack top (the end of the array) is written into the PSP, and finally CONTROL selects the process stack.
/* Switch Thread mode Stack from Main to Process */
/* Initialize memory reserved for Process Stack */
for (Index = 0; Index < SP_PROCESS_SIZE; Index++)
{
PSPMemAlloc[Index] = 0x00;
}
/* Set Process stack value */
__set_PSP((uint32_t)PSPMemAlloc + SP_PROCESS_SIZE);
/* Select Process Stack as Thread mode Stack */
__set_CONTROL(SP_PROCESS);
2. Switch to unprivileged and read back CONTROL. Setting bit 0 of CONTROL enters unprivileged access, and reading the bit back shows whether it took effect.
/* 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");
}
3. Define the __SVC that raises the SVCall per compiler. The example gives an inline implementation of __SVC for each of the three toolchains; executing the SVC 0x01 instruction enters the SVCall exception and execution continues after the exception returns.
#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
4. Clear CONTROL in SVC_Handler to switch back to privileged. CONTROL can be changed in handler mode, so the processor returns thread mode to privileged access before returning.
void SVC_Handler(void)
{
/* Switch Thread mode from privileged to unprivileged */
__set_CONTROL(0x00);
}
5. Check the privilege bit again after the switch back. After returning from the exception the code reads CONTROL again to confirm thread mode is privileged and prints the result.
/* 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 Privileged Mode OK \r\n");
}
else
{
/* Thread mode has unprivileged access */
ThreadMode = THREAD_MODE_UNPRIVILEGED;
printf("Cortex-M4F enter Privileged Mode Fail \r\n");
}
Corresponding source files: src/main.c, src/n32h49x_it.c
5.4.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| Coverage of the bit-band region | Only the SRAM, CCM, and peripheral address spaces are mapped into the bit-band region and can be accessed atomically through the alias region; the remaining address spaces have no bit-band alias. |
User manual |
| Alias-address formula | The alias address is computed as bitband_byte_addr = bitband_base + (byte_offset x 32) + (bit_number x 4), where bit_number is 0 to 7. |
User manual |
| Bit-band writes are atomic | Writing one word to the alias region is equivalent to a read-modify-write of the target bit in the bit-band region, so a bit-band write is atomic; reading an alias address returns 0x01 or 0x00 for that bit. |
User manual |
| SRAM alias-address example | For example, bit 4 of SRAM address 0x20000400 maps to alias address 0x22008010; writing that alias address is equivalent to a read-modify-write of bit 4 of 0x20000400. |
User manual |