iTranslated by AI
Reading the Arduino UNO R4 Minima Bootloader Source Code (SystemInit Part)
Revision History
- rev.1 2025/01/14 Added revision history. Fixed the description regarding
SystemCoreClockin "[N/A] Early Initialization of Uninitialized BSP Variables." - rev.0 2025/01/12 Initial creation
1. Introduction
In this article, I will summarize the details confirmed about the SystemInit function, which was omitted in the previous article.
The SystemInit function executed by the built bootloader is included in Renesas's Flexible Software Package (FSP). This function incorporates necessary processes according to the features of the Renesas MCU and build settings. While it includes many processes not applicable to the Cortex-M4, such as errata fixes for the Cortex-M85 and TrustZone-related settings, I have confirmed as much as possible even for the non-applicable features for study purposes.
However, regarding the clock configuration process (bsp_clock_init function) executed within the SystemInit function, I have decided to describe it in the next article due to the large amount of information. The confirmation of the bootloader for Minima until it starts the application is expected to conclude in the next article.
- Past Related Articles
- Building the Bootloader for Arduino UNO R4 Minima
- Reading the Bootloader Source Code for Arduino UNO R4 Minima (Application Startup Edition)
2. SystemInit Function
The code blocks of the SystemInit function are described below in sequential order, divided into sections. For non-applicable code, "[N/A]" is noted in the section name.
[N/A] Initialization of Cortex-M85 Cache and Branch Functions
These settings are for MCUs equipped with the Cortex-M85. They configure the enabling of instruction cache, branch prediction, and low-overhead branch extensions.
Furthermore, in the latest release version (v5.7.0), errata workarounds are implemented in addition to the initialization process.
-
Erratum 3175626
It seems that on the AXI bus, a load process might hang due to a WAW hazard between cache eviction and store processes. To avoid this problem, the cache policy is forced to write-through[1],[2]. -
Erratum 3190818
It is reported that under specific conditions, LDM instructions to non-cacheable locations may fail to complete[3]. This issue can be avoided by setting the 16th bit[4], which is a reserved area of the ACTLR (Auxiliary Control Register), to 1. However, it was noted that this affects the performance of LDM instructions to non-cacheable regions.
Since the ra4m1 installed in the Minima is a Cortex-M4, this block is not applicable. In the case of the Minima, -mcpu=cortex-m4 is specified in the compilation options, and __ARM_ARCH_7EM__ is defined as a preprocessor macro as shown below.
$ arm-none-eabi-gcc -mcpu=cortex-m4 -dM -E - < /dev/null | grep __ARM_ARCH_7EM__
#define __ARM_ARCH_7EM__ 1
As a result, RENESAS_CORTEX_M4 is defined in renesas.h at the following path:
tinyusb/hw/mcu/renesas/fsp/ra/fsp/src/bsp/cmsis/Device/RENESAS/Include/renesas.h
Enabling FPU
This is the process to enable the FPU. RA4M1 series MCUs are equipped with a single-precision floating-point unit (FPv4-SP FPU [5]) [6].
Access to the FPU can be enabled (full access) by writing 0b11 to both CP10 and CP11 of the CPACR (Coprocessor Access Control Register) [7],[8].
The constant __FPU_USED is defined in tinyusb/lib/CMSIS_5/CMSIS/Core/Include/core_cm4.h. This file is part of the CMSIS_5 library obtained via get_deps.py during the bootloader build.
__VFP_FP__ and __SOFTFP__ are preprocessor macros defined by GCC. When the compilation option -mfloat-abi is set to hard, __SOFTFP__ is not defined, but __VFP_FP__ is defined for both soft and hard.
$ arm-none-eabi-gcc -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -dM -E - < /dev/null | grep -E '__VFP_FP__|__SOFTFP__'
#define __VFP_FP__ 1
$ arm-none-eabi-gcc -mcpu=cortex-m4 -mfloat-abi=soft -dM -E - < /dev/null | grep -E '__VF
P_FP__|__SOFTFP__'
#define __SOFTFP__ 1
#define __VFP_FP__ 1
Also, __FPU_PRESENT is defined as 1 in R7FA4M1AB.h.
[N/A] Sealing Stack
This block is a security measure for Armv8-M and Armv8.1-M equipped with TrustZone. For the Cortex-M series TrustZone, there is an attack that exploits non-secure state transitions to misinterpret values pointed to by the secure stack pointer (MSP_S or PSP_S) as function return addresses or integrity signatures upon interrupt return [9].
To prevent this attack, 0xFEF5EDA5 (BSP_TZ_STACK_SEAL_VALUE) is set at the address pointed to by the initial value of MSP_S. This value is a special pattern that is not interpreted as a normal return address or integrity signature.
With this setting, for instance, when returning to Reset_Handler after exiting the SystemInit function and switching the stack pointer to the Process Stack Pointer (PSP_S) while the secure main stack is empty, the attack can be prevented. On the other hand, if switching to PSP_S when MSP_S is not at its initial value (the secure main stack pointer is not empty), this process alone is insufficient. In such cases, it is necessary to manually set 0xFEF5EDA5 at the address pointed to by MSP_S [10].
VTOR Initialization
In the case of Cortex-M33, the value of VTOR at reset is defined as UNKNOWN[11]. Therefore, the address pointed to by the __Vectors symbol is set as VTOR. This process is executed in secure projects. Meanwhile, non-secure projects skip this because SCB_NS->VTOR is set by the secure project before they start.
In the case of Cortex-M4, the value of VTOR at reset is initialized to 0x00000000[12]. Since Cortex-M4 does not have TrustZone, there is no distinction between non-secure and secure projects, but the VTOR initialization process is performed just like in a secure build. Also, even if this code is executed on the application side, this process allows for reconfiguring VTOR with its own vector table.
Stopping the Battery Power Switch
As noted in the comments, the setting flow for the VBTCR1.BPWSWSTP bit described in the user's manual is being executed. Even when using the VBATT function, this bit must be set to 1 following the flow after a power-on reset[13].
Note that the battery backup registers used by the Minima to enter bootloader mode become available for reading and writing after this operation[14],[15].
[N/A] Module Start of TFU (Trigonometric Function Unit)
This releases the module stop state for the TFU, which is an accelerator for trigonometric function calculations. This accelerator is equipped in the RA4T1, which is for motor control in the RA4 series.
[N/A] Early Initialization of Uninitialized BSP Variables
Variables placed in the .noinit section (regions not initialized by the C runtime) are being initialized. Variables placed in this section are available in the R_BSP_WarmStart function, which is called before C runtime initialization. The variables to be initialized include counters for managing pin access enabling/disabling, callback function arrays for each HMI_Handler trigger, counter arrays for managing register protection enabling/disabling, and the SystemCoreClock variable.
SystemCoreClock is initialized with BSP_MOCO_HZ. The clock source for the system clock at reset is the 8 MHz Middle-speed On-chip Oscillator (MOCO) [16],[17]. Therefore, this constant is likely defined as 8000000 in tinyusb/hw/bsp/ra/boards/uno_r4/fsp_cfg/bsp/bsp_mcu_family_cfg.h.
However, there is a frequency divider between the system clock (ICLK) and the clock source [18]. The division ratio is determined by ICK[2:0] of the SCKDIVCR register; in the case of the RA4M1, the value after reset is a division by 16 [19]. Therefore, it seems necessary to put BSP_MOCO_HZ/16 into SystemCoreClock, but the reset value of this register appears to vary depending on the MCU [20].
Note that in this version of FSP, BSP_MOCO_HZ does not seem to be referenced anywhere else. I also searched the documentation, but I was unfortunately unable to confirm the details of this setting value. It might be a setting value intended for MCUs where BSP_MOCO_HZ is expected to store the value after division, or where the default division ratio is 1. If enabling this block, it would be best to verify this point.
The R_BSP_WarmStart function is executed at three timings within the SystemInit process: immediately after reset, after clock configuration, and after C runtime initialization. The reason for the call can be determined by the argument. The body of this function is weakly defined as a template. Since the built bootloader does not override BSP_WarmStart, I assume this block is considered not applicable.
R_BSP_WarmStart Function (BSP_WARM_START_RESET)
This is the hook process immediately after a reset. In this case, since the R_BSP_WarmStart function is a skeleton, no specific processing is performed.
[N/A] Initialization of Clock Variables
This process initializes clock variables while skipping the clock configuration. This process will be described in another article along with the bsp_clock_init function.
bsp_clock_init Function (Clock Configuration)
This function configures the system clock. This process will be described in the next article.
Resetting the TRNG (True Random Number Generator)
The RA4M1 series is equipped with a Secure Cryptographic Engine (SCE5), which includes a random number generator[21]. Here, initialization is performed on unused circuits to prevent unexpected current draw after clock initialization[22].
[N/A] Enabling the Graphics Power Domain
This process is for RA series MCUs equipped with a graphics power domain controller, such as the RA8D1[23]. In the latest FSP (v5.7.0), the corresponding process has been moved to after C runtime initialization.
R_BSP_WarmStart Function (BSP_WARM_START_POST_CLOCK)
This is the hook process after clock configuration. In this case, no specific processing is performed.
Enabling MSP Monitoring (SPMON)
This part enables MSP monitoring (SPMON). It sets the OAD bit so that a non-maskable interrupt (NMI) is generated if the MSP underflows or overflows[24].
R_ICU->NMIER is the Non-Maskable Interrupt Enable Register, which allows enabling interrupts for various causes. Here, CPU stack pointer monitor interrupts are being enabled. Each bit in this register, except for reserved bits, can only be written as 1 once after a reset. Also, writing 0 is invalid. Therefore, instead of a read-modify-write operation, the CPU stack pointer monitor interrupt enable bit is set to 1 while other bits are set to 0[25].
Note that the definitions for BSP_PRV_STACK_LIMIT and BSP_PRV_STACK_TOP are located in system.c.
Additionally, BSP_CFG_STACK_MAIN_BYTES is defined in tinyusb/hw/bsp/ra/boards/uno_r4/fsp_cfg/bsp/bsp_cfg.h with a value of 0x800 (2KiB).
[N/A] MSPLIM Register Setting
While the previous section was about Renesas's MSP monitoring, this one is an Arm feature. The definition of __set_MSPLIM can be found in cmsis_gcc.h within CMSIS_5.
MSPLIM is a special register that allows setting a lower limit for the Main Stack Pointer[26]. If the pointer falls below the set limit, a UsageFault occurs[27].
Additionally, besides MSPLIM, PSPLIM also exists. Furthermore, these registers may be implemented separately for secure regions (MSPLIM_S, PSPLIM_S) and non-secure regions (MSPLIM_NS, PSPLIM_NS). The presence of MSPLIM and PSPLIM depends on whether the Armv8-M is Mainline or Baseline, as well as the presence of security extensions (TrustZone)[28].
When this special register exists, the previously mentioned SPMON tends to be omitted. Also, in the latest FSP (v5.7.0), the code restricting this to the Cortex-M33 has been removed.
C Runtime Initialization
Initialization of .bss Section
This clears the .bss section to zero. Static and global variables without initial values placed in the .bss section are initialized to 0 here.
Initialization of .data Section
The .data section contains static and global variables with initial values, as well as RAM execution functions placed in the .code_in_ram section. The initial data for this section is stored in flash memory by AT > FLASH in the linker script. The beginning of the initial data is indicated by the __etext symbol. The linker script is stored at the following location:
tinyusb/hw/bsp/ra/linker/gcc/fsp.ld
[N/A] Initialization of ITCM and DTCM
This initializes data placed in ITCM and DTCM. Since these are not included in the Cortex-M4, this code is not applicable.
Execution of Initialization Functions
This executes functions at the addresses registered in .init_array. If C++ static or global variables are initialized with constructors upon definition, the address to that initialization process is registered here. Additionally, the address of any function with __attribute__((constructor)) specified is also registered. When built with GCC, these execution codes are categorized into the .text.startup section at compile time, and it seems the addresses to the processes placed in .text.startup are registered in .init_array during linking.
SystemCoreClockUpdate Function
Updates the SystemCoreClock variable. This process is also called within the bsp_clock_init function. Therefore, this process will also be described in another article along with the bsp_clock_init function.
Resetting the RTC
Executes the reset process for the RTC. This content will also be described in another article along with the bsp_clock_init function.
[N/A] Releasing Write Protection for PmnPFS Registers
This releases the write protection for the PmnPFS registers [29], which are used for configuring I/O pin functions. It clears the PWPR.B0WI bit to enable writing to PWPR.PFSWE, and then sets PWPR.PFSWE to 1 to enable writing to the PmnPFS registers [30]. In the built bootloader, this block is not applicable.
[N/A] Resetting Port Security Attribute Registers
Resets the security attributes for each port. The n-th bit of PMSAR[m] allows setting the security attribute (0: secure, 1: non-secure) for Pmn [31]. The default value of the security attribute seems to vary by RA series. Specifically, in the RA8 series, the default value of the security attribute is secure [32], while in earlier series, it is non-secure. In the version of FSP currently being checked, it always initializes as non-secure because the RA8 series was not supported, but in v5.7.0, it is handled as shown below.
[N/A] Initialization of Security Functions
This performs initialization of security functions. According to user settings (macros), it configures non-secure side FPU access settings and security attribute settings for memory and peripheral modules. Additionally, functions for configuring security settings for pins and ELC (Event Link Controller) are weakly defined and executed within this function.
R_BSP_WarmStart Function (BSP_WARM_START_POST_C)
This is the hook process after C runtime initialization. In this case, no specific processing is performed.
Interrupt Settings
This part configures settings related to interrupts. The bsp_irq_cfg function is defined in tinyusb/hw/mcu/renesas/fsp/ra/fsp/src/bsp/mcu/all/bsp_irq.c.
In the case of the RA4M1, security-related processes are non-applicable, so the processing content is as follows.
IELSR is the ICU Event Link Setting Register[33]. The choice of IRQ triggers used by the NVIC is configured according to the value of the weakly defined g_interrupt_event_link_select.
The index of the array corresponds to the IRQ number. These numbers are assigned sequentially starting from 0 for exception number 16 in the vector table[34].
For example, the trigger for an interrupt handler registered at exception number 16 in the vector table is the event signal set by IELS[7:0] of IELSR0. Event signals are described in "Table 13.4 Event Number" of the hardware manual[35]. If you want to register the USBFS_USBI interrupt handler at exception number 16 in the vector table, set this field to 033h. Note that in FSP, it is assumed that the vector table region where the source is IELSRn is placed in the .application_vector section as an application vector.
In tinyusb, g_interrupt_event_link_select is defined in tinyusb/hw/bsp/ra/family.c along with the application vector, but for the bootloader built this time, a patch was applied to link the one defined in arduino-renesas-bootloader/src/irq_table.c.
Note that the BSP_PRV_IELS_ENUM macro is a macro that prepends ELC_ to the argument, defined in tinyusb/hw/bsp/ra/boards/uno_r4/fsp_cfg/bsp/bsp_mcu_family_cfg.h. The resulting macro is defined in fsp/ra/fsp/src/bsp/mcu/ra4m1/bsp_elc.h.
bsp_init Function
The bsp_init function has a skeleton weakly defined in tinyusb/hw/mcu/renesas/fsp/ra/fsp/src/bsp/mcu/all/bsp_common.c, and in this case, it is linked. Therefore, no specific processing is performed.
-
Arm "Cortex-M85 AT640 and Cortex-M85 with FPU AT641 Software Developer Errata Notice Document version: 15.0 Document ID: SDEN-2236668", p.32, " 3175626 AXI hang due to dependency between read data channel and write response channel " ↩︎
-
Arm "Arm® Cortex®-M85 Processor Technical Reference Manual Revision: r1p1", p.88, "5.14 MSCR, Memory System Control Register" ↩︎
-
Arm "Cortex-M85 AT640 and Cortex-M85 with FPU AT641 Software Developer Errata Notice Document version: 15.0 Document ID: SDEN-2236668", p.56, "3190818 Under limited circumstances, LDM to normal non-cacheable AXI location can not complete" ↩︎
-
Arm "Arm® Cortex®-M85 Processor Technical Reference Manual Revision: r1p1", p.69, "5.9 ACTLR, Auxiliary Control Register" ↩︎
-
Arm "ARMv7-M Architecture Reference Manual Version: E.e", p.A2-34, "A2.5 The optional Floating-point Extension" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.74, "2.1.1 CPU" ↩︎
-
Arm "ARMv7-M Architecture Reference Manual Version: E.e", p.A2-61, "A2.6 Coprocessor support" ↩︎
-
Arm "ARMv7-M Architecture Reference Manual Version: E.e", p.B3-614, "B3.2.20 Coprocessor Access Control Register, CPACR" ↩︎
-
Arm "Stack sealing and why it is needed in TrustZone for Armv8-M Document ID: 102446". Specific attack methods are described in "5. Example" of the document. ↩︎
-
Arm "Stack sealing and why it is needed in TrustZone for Armv8-M Document ID: 102446", p.11, "4.4 Stack sealing value" ↩︎
-
Arm "Arm® Cortex® -M33 Devices Revision: r0p4 Generic User Guide", p4-267, "4.2.1 System control block registers summary" ↩︎
-
Arm "Cortex-M4 Revision r0p0 Technical Reference Manual ARM DDI 0439B (ID030210)", p4-3, "4.2 Register summary" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.239, "11.2.1 VBATT Control Register 1 (VBTCR1)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.239, "11.2.1 VBATT Control Register 1 (VBTCR1)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.256, "11.3.4 Usage of VBATT Backup Registers" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.141, "8.1 Overview" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.148, "8.2.2 System Clock Source Control Register (SCKSCR)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.179, "8.7.1 System Clock (ICLK)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.146, "8.2.1 System Clock Division Control Register (SCKDIVCR)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4T1 Group User's Manual: Hardware Rev.1.20", Dec 2024, p.141, "8.2.2 SCKDIVCR: System Clock Division Control Register", in this case, the reset value of ICK[2:0] is a division by 4. ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.1548, "46.1 Overview" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.235, "10.9.14 Module Stop Function for Unused Circuits" ↩︎
-
Renesas Electronics Corporation, "Renesas RA8D1 Group User's Manual: Hardware Rev.1.20", Nov 2024, p.310, "10.2.12 PDCTRGD: Graphics Power Domain Control Register" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.310, "15.2.2 Overflow and Underflow Errors" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.270, "13.2.3 Non-Maskable Interrupt Enable Register (NMIER)" ↩︎
-
Arm "Arm® v8-M Architecture Reference Manual DDI0553B.y ID09082024", p.1757, "D1.2.177 MSPLIM, Main Stack Pointer Limit Register" ↩︎
-
Arm "Arm® v8-M Architecture Reference Manual DDI0553B.y ID09082024", p.1897, "D1.2.270 UFSR, UsageFault Status Register" ↩︎
-
Arm "Arm® v8-M Architecture Reference Manual DDI0553B.y ID09082024", p.115, "B3.21 Stack limit checks" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.429, "19.2.5 Port mn Pin Function Select Register (PmnPFS/PmnPFS_HA/PmnPFS_BY) (m = 0 to 9; n = 00 to 15)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.436, "19.5.1 Setting Procedure for Pin Functions" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4T1 Group User's Manual: Hardware Rev.1.20", Dec 2024, p.431, "18.2.8 PmSAR: Port Security Attribute Register (m = 0 to 5, 8)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA8M1 Group User's Manual: Hardware Rev.1.20", Nov 2024, p.638, "19.2.9 PmSAR: Port Security Attribute Register (m = 0 to 9, A, B)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.275, "13.2.6 ICU Event Link Setting Register n (IELSRn)" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.280, "13.3.1 Interrupt Vector Table Table 13.3 Interrupt Vector Table" ↩︎
-
Renesas Electronics Corporation, "Renesas RA4M1 Group User's Manual: Hardware Rev.1.10", 2023.09, p.282, "13.3.2 Event Number" ↩︎
Discussion