跳到主要内容

7.5.3 MCU 系统说明

编译系统基本说明

MCU 的编译系统基于 Scons3.0.0创建(Scons 3.0.0用户手册官网)。

MCU1编译系统

MCU1编译系统位于 mcu/Build/FreeRtos_mcu1,具体目录结构,如下图所示:

FreeRtos_mcu1
├── build_freertos.py # 编译的入口脚本
├── SConstruct_Lite_FRtos_S100_sip_B # Scons参与编译文件夹以及输出目录
├── settings_freertos.py # Scons编译命令参数相关的文件
└── Linker # 编译link脚本所在目录
└── gcc
└── S100.ld

MCU1编译系统位于 mcu/Build/FreeRtos_mcu1,具体目录结构,如下图所示:

FreeRtos_mcu1
├── build_config # 编译所需yaml文件,增删编译文件夹的文件
└── S600
└── lite-matrix-B-mcu1.yaml
├── settings_files # gcc编译链接等参数
└── gcc
└── settings_lite_freertos.py
├── site_scons # Scons编译链接命令文件
└── site_tools
└── gcc_arm.py
├── Linker # 编译link脚本所在目录
└── gcc
└── S600.ld
└── build_freertos.py # 编译的入口脚本

编译流程介绍

编译中的重点文件关系介绍

build_freertos.py 是编译的整体入口,但是实际调度到 scons 时,能够对 scons 编译环境/流程产生影响的方式有以下几个:

  1. SConstruct 文件:SConstruct 文件是 scons 编译的定义文件,它和每个模块内的 Sconscript 组成了 Cmake 里 Cmakefile;Make 系统里 makefile 的作用;
  2. settings_freertos.py:该文件生效的入口实际上是 SConstruct 里面的“Variables”类的初始化,核心在于引入一系列静态定义的编译环境变量;环境变量的变量名就是 settings_freertos.py 里面的变量名,变量值就是 settings_freertos.py 里面的变量名对应的变量值;“Variables”类实例化后的示例会被 Environment 类使用,用于 scons 的编译
  3. gcc_arm.py:实际定义编译命令的定义文件,真正生效的入口是 settings_freertos.py 里面定义的“COMPILER_TOOL”字段,COMPILER_TOOL 字段进一步会被 Sconscruct 文件的 Variables 添加并最后被 env 获取到其中的“CC”等配置

build_freertos.py 是编译的整体入口,但是实际调度到 scons 时,能够对 scons 编译环境/流程产生影响的方式有以下几个:

  1. SConstruct 文件:SConstruct 文件是 scons 编译的定义文件,它和每个模块内的 Sconscript 组成了 Cmake 里 Cmakefile;Make 系统里 makefile 的作用;
  2. settings_freertos.py:该文件生效的入口实际上是 SConstruct 里面的“Variables”类的初始化,核心在于引入一系列静态定义的编译环境变量;环境变量的变量名就是 settings_freertos.py 里面的变量名,变量值就是 settings_freertos.py 里面的变量名对应的变量值;“Variables”类实例化后的示例会被 Environment 类使用,用于 scons 的编译
  3. gcc_arm.py:实际定义编译命令的定义文件,真正生效的入口是 settings_freertos.py 里面定义的“COMPILER_TOOL”字段,COMPILER_TOOL 字段进一步会被 Sconscruct 文件的 Variables 添加并最后被 env 获取到其中的“CC”等配置
  4. lite-matrix-B-mcu1.yaml:被编译的文件夹,在该文件中增删编译涉及到的文件夹

MCU1镜像 layout

区域名称起始地址占用大小作用
FLASH_STARTUP0x0CAB00001K启动代码位置
FLASH0x0CAB04002731K代码、数据、栈等使用的区域
FREERTOS_HEAP0x0CD5B000512K堆空间
LOG_SHARE_Reserved0x0CDDB0008KMCU1 log 存放的空间,log 会循环覆盖
SCMI_IPC_Reserved0x0CDDD00012KSCMI IPC 通信需要的空间,用于 buffer 及关键数据

在上述 SRAM 的排布中, 强烈不建议客户修改 SCMI_IPC_Reserved 及其之后的区域。 这些部分大部分都是其他域一定会使用的关键位置,修改可能导致异常。如果要修改,请先咨询地瓜相关支持人员。

下面是地瓜版本中的链接文件,解释了链接脚本中提供的一些变量作用:

MEMORY
{
FLASH_STARTUP(rx) : org = 0x0CAB0000, len = 1K
FLASH(rw) : org = 0x0CAB0400, len = 2731K
FREERTOS_HEAP(rw) : org = 0x0CD5B000, len = 512K
LOG_SHARE_Reserved(rw) : org = 0x0CDDB000, len = 8K
SCMI_IPC_Reserved(rw) : org = 0x0CDDD000, len = 12K
}

/* Define output sections */
SECTIONS {
.EL2_core_exceptions_table :
{
. = ALIGN(32);
_start = .;
*(.EL2_core_exceptions_table)
. = ALIGN(32);
} > FLASH_STARTUP

.EL2_Reset_Handler :
{
. = ALIGN(32);
*(.EL2_Reset_Handler)
. = ALIGN(32);
} > FLASH_STARTUP

.EL1_core_exceptions_table :
{
. = ALIGN(32);
*(.EL1_core_exceptions_table)
. = ALIGN(32);
} > FLASH_STARTUP

.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
. = ALIGN(4);
} > FLASH

.shell :
{
_shell_command_start = .;
KEEP (*(shellCommand))
_shell_command_end = .;
} > FLASH

.mcal_text :
{
*(.mcal_text)
} > FLASH

.mcal_const_cfg :
{
*(.mcal_const_cfg)
} > FLASH

.mcal_const :
{
*(.mcal_const)
} > FLASH

.common_text :
{
*(.common_text)
PROVIDE(__TEXT_END = .);
} > FLASH
/******************text end******************/

.const :
{
. = ALIGN(32);
*(.const)
*(.rodata)
} > FLASH


.heap :
{
. = ALIGN(64);
__HEAP_START = .;
__end__ = .;
__heap_start__ = .;
PROVIDE(end = .);
PROVIDE(_end = .);
PROVIDE(__end = .);
__HeapBase = .;
. += HEAP_SIZE;
__HeapLimit = .;
__heap_limit = .;
__heap_end__ = .;
} > FLASH

.u_boot_list :
{
. = ALIGN(4);
*(SORT(.u_boot_list*))
. = ALIGN(4);
} > FLASH

.global_data :
{
. = ALIGN(64);
__DATA_RAM = .;
__data_start__ = .; /* Create a global symbol at data start. */
*(.data) /* .data sections */
. = ALIGN(64);
__data_end__ = .; /* Define a global symbol at data end. */
PROVIDE(__DATA_END = .);
PROVIDE(__DATA_ROM = .);
} > FLASH

.stack (NOLOAD) :
{
. = ALIGN(64);
__STACK_START = .;
__StackLimit = .;
__stack_start__ = .;
. += STACK_SIZE;
__stack_end__ = .;
__StackTop = .;
} > FLASH

.stack_exc (NOLOAD) :
{
. = ALIGN(64);
__StackLimit_exc = .;
__stack_start_exc__ = .;
. += STACK_SIZE_EXC;
__stack_end_exc__ = .;
__StackTop_exc = .;
__STACK_END = .;
} > FLASH

.init_table :
{
. = ALIGN(64);
__COPY_TABLE = .;
KEEP(*(.init_table))
} > FLASH

.zero_table :
{
. = ALIGN(64);
__ZERO_TABLE = .;
KEEP(*(.zero_table))
} > FLASH

.interrupts :
{
__VECTOR_TABLE = .;
__interrupts_start__ = .;
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
__interrupts_end__ = .;
. = ALIGN(4);
} > FLASH

__VECTOR_RAM = __VECTOR_TABLE;
__RAM_VECTOR_TABLE_SIZE = 0x0;
__VECTOR_TABLE_COPY_END = __VECTOR_TABLE + __RAM_VECTOR_TABLE_SIZE;

.interrupt_drv_shared_memory :
{
*(.interrupt_drv_shared_memory)
} > FLASH

.handlers :
{
. = ALIGN(32);
*(.handlers)
} > FLASH

.mcal_data :
{
*(.mcal_data)
} > FLASH

.mcal_shared_data :
{
*(.mcal_shared_data)
} > FLASH

.bss (NOLOAD) :
{
. = ALIGN(64);
__BSS_START = .;
__bss_start__ = .;
*(.bss)
} > FLASH

.mcal_bss (NOLOAD) :
{
. = ALIGN(64);
*(.mcal_bss)
} > FLASH

.mcal_shared_bss (NOLOAD) :
{
. = ALIGN(64);
*(.mcal_shared_bss)
__DATA_RAM_END = .;
__m_ram_init_end = .;
__bss_end__ = .;
__BSS_END = .;
} > FLASH

.ipc_mdma :
{
*(.ipc_mdma)
} > FLASH

.ucheap_section (NOLOAD) :
{
. = ALIGN(64);
KEEP(*(.ucheap_section))
. = ALIGN(64);
} > FREERTOS_HEAP

.log (NOLOAD) :
{
*(.log)
} > LOG_SHARE_Reserved

/*-------- LABELS USED IN CODE -------------------------------*/
SRAM_START_ADDR = ORIGIN(FLASH_STARTUP);
MCU_LOG_START_ADDR = ORIGIN(LOG_SHARE_Reserved);
__SCMI_IPC_START_ADDR = ORIGIN(SCMI_IPC_Reserved);
NON_SECURE_START_ADDR = ORIGIN(LOG_SHARE_Reserved);

PROVIDE(SRAM_SIZE = 0x34FFFF);
}
区域名称起始地址占用大小作用
FLASH_STARTUP0x0CAB00001K启动代码位置
FLASH0x0CAB04002667K代码、数据、栈等使用的区域(不含 Can)
CAN_Reserved0x0CD4B00064KCan 模块代码、数据的加载区域
FREERTOS_HEAP0x0CD5B000512K堆空间
LOG_SHARE_Reserved0x0CDDB0008KMCU1 log 存放的空间,log 会循环覆盖
SCMI_IPC_Reserved0x0CDDD00012KSCMI IPC 通信需要的空间,用于 buffer 及关键数据
ATCM_Reserved0x0A00000064KCan 模块代码、数据的运行使用区域

在上述 SRAM 的排布中, 强烈不建议客户修改 SCMI_IPC_Reserved 及其之后的区域。 这些部分大部分都是其他域一定会使用的关键位置,修改可能导致异常。如果要修改,请先咨询地瓜相关支持人员。

下面是地瓜版本中的链接文件,解释了链接脚本中提供的一些变量作用:

MEMORY
{
FLASH_STARTUP(rx) : org = 0x0CAB0000, len = 1K
FLASH(rw) : org = 0x0CAB0400, len = 2667K
CAN_Reserved(rw) : org = 0x0CD4B000, len = 64K
FREERTOS_HEAP(rw) : org = 0x0CD5B000, len = 512K
LOG_SHARE_Reserved(rw) : org = 0x0CDDB000, len = 8K
SCMI_IPC_Reserved(rw) : org = 0x0CDDD000, len = 12K
ATCM_Reserved(rw) : org = 0x0A000000, len = 64K
}

/* Define output sections */
SECTIONS
{
.EL2_core_exceptions_table :
{
. = ALIGN(32);
_start = .;
*(.EL2_core_exceptions_table)
. = ALIGN(32);
} > FLASH_STARTUP

.EL2_Reset_Handler :
{
. = ALIGN(32);
*(.EL2_Reset_Handler)
. = ALIGN(32);
} > FLASH_STARTUP

.EL1_core_exceptions_table :
{
. = ALIGN(32);
*(.EL1_core_exceptions_table)
. = ALIGN(32);
} > FLASH_STARTUP

.EL1_core_exceptions_table_MCU2 :
{
. = ALIGN(32);
*(.EL1_core_exceptions_table_MCU2)
. = ALIGN(32);
} > FLASH_STARTUP

.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
. = ALIGN(4);
} > FLASH

.shell :
{
_shell_command_start = .;
KEEP (*(shellCommand))
_shell_command_end = .;
} > FLASH

.mcal_text :
{
*(.mcal_text)
} > FLASH

.mcal_const_cfg :
{
*(.mcal_const_cfg)
} > FLASH

.mcal_const :
{
*(.mcal_const)
} > FLASH

.common_text :
{
*(.common_text)
PROVIDE(__TEXT_END = .);
} > FLASH
/******************text end******************/

.const :
{
. = ALIGN(32);
*(.const)
*(.rodata)
} > FLASH


.heap :
{
. = ALIGN(64);
__HEAP_START = .;
__end__ = .;
__heap_start__ = .;
PROVIDE(end = .);
PROVIDE(_end = .);
PROVIDE(__end = .);
__HeapBase = .;
. += HEAP_SIZE;
__HeapLimit = .;
__heap_limit = .;
__heap_end__ = .;
} > FLASH

.u_boot_list :
{
. = ALIGN(4);
*(SORT(.u_boot_list*))
. = ALIGN(4);
} > FLASH

.global_data :
{
. = ALIGN(64);
__DATA_RAM = .;
__data_start__ = .; /* Create a global symbol at data start. */
*(.data) /* .data sections */
. = ALIGN(64);
__data_end__ = .; /* Define a global symbol at data end. */
PROVIDE(__DATA_END = .);
PROVIDE(__DATA_ROM = .);
} > FLASH

.stack (NOLOAD) :
{
. = ALIGN(64);
__STACK_START = .;
__StackLimit = .;
__stack_start__ = .;
. += STACK_SIZE;
__stack_end__ = .;
__StackTop = .;
} > FLASH

.stack_mcu2 (NOLOAD) :
{
. = ALIGN(64);
__STACK_START_MCU2 = .;
__StackLimit_MCU2 = .;
__stack_start_mcu2__ = .;
. += STACK_SIZE_MCU2;
__stack_end_mcu2__ = .;
__StackTop_MCU2 = .;
} > FLASH

.stack_exc (NOLOAD) :
{
. = ALIGN(64);
__StackLimit_exc = .;
__stack_start_exc__ = .;
. += STACK_SIZE_EXC;
__stack_end_exc__ = .;
__StackTop_exc = .;
__STACK_END = .;
} > FLASH

.stack_exc_mcu2 (NOLOAD) :
{
. = ALIGN(64);
__StackLimit_exc_MCU2 = .;
__stack_start_exc_mcu2__ = .;
. += STACK_SIZE_EXC_MCU2;
__stack_end_exc_mcu2__ = .;
__StackTop_exc_MCU2 = .;
__STACK_END_MCU2 = .;
} > FLASH

.init_table :
{
. = ALIGN(64);
__COPY_TABLE = .;
KEEP(*(.init_table))
} > FLASH

.zero_table :
{
. = ALIGN(64);
__ZERO_TABLE = .;
KEEP(*(.zero_table))
} > FLASH

.interrupts :
{
__VECTOR_TABLE = .;
__interrupts_start__ = .;
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
__interrupts_end__ = .;
. = ALIGN(4);
} > FLASH

__VECTOR_RAM = __VECTOR_TABLE;
__RAM_VECTOR_TABLE_SIZE = 0x0;
__VECTOR_TABLE_COPY_END = __VECTOR_TABLE + __RAM_VECTOR_TABLE_SIZE;


.interrupt_drv_shared_memory :
{
*(.interrupt_drv_shared_memory)
} > FLASH

.handlers :
{
. = ALIGN(32);
*(.handlers)
} > FLASH

.mcal_data :
{
*(.mcal_data)
} > FLASH

.mcal_shared_data :
{
*(.mcal_shared_data)
} > FLASH

.bss (NOLOAD) :
{
. = ALIGN(64);
__BSS_START = .;
__bss_start__ = .;
*(.bss)
} > FLASH

.mcal_bss (NOLOAD) :
{
. = ALIGN(64);
*(.mcal_bss)
} > FLASH

.mcal_shared_bss (NOLOAD) :
{
. = ALIGN(64);
*(.mcal_shared_bss)
__DATA_RAM_END = .;
__m_ram_init_end = .;
__bss_end__ = .;
__BSS_END = .;
} > FLASH

.ipc_mdma :
{
*(.ipc_mdma)
} > FLASH

.ucheap_section (NOLOAD) :
{
. = ALIGN(64);
KEEP(*(.ucheap_section))
. = ALIGN(64);
} > FREERTOS_HEAP

.log (NOLOAD) :
{
*(.log)
} > LOG_SHARE_Reserved

.tcm_code :
{
KEEP(*(.tcm_code))
KEEP(*(.tcm_data))
} > ATCM_Reserved

/*-------- LABELS USED IN CODE -------------------------------*/
SRAM_START_ADDR = ORIGIN(FLASH_STARTUP);
MCU_LOG_START_ADDR = ORIGIN(LOG_SHARE_Reserved);
__SCMI_IPC_START_ADDR = ORIGIN(SCMI_IPC_Reserved);
NON_SECURE_START_ADDR = ORIGIN(LOG_SHARE_Reserved);
CAN_START_ADDR = ORIGIN(CAN_Reserved);
ATCM_START_ADDR = ORIGIN(ATCM_Reserved);

PROVIDE(SRAM_SIZE = 0x34FFFF);
}

startup.s 启动代码简介

  1. 启动的第一条指令就是进入 EL2_core_exceptions_table 向量表
    .text
.align 4
.section ".EL2_core_exceptions_table", "ax"
.globl EL2_core_exceptions_table
.type EL2_core_exceptions_table, %function
EL2_core_exceptions_table:
b EL2_Reset_Handler /* Reset Handler */
b EL2_Undefined_Handler /* Undefined Handler */
b EL2_HVC_Handler /* SVCall Handler */
b EL2_Prefetch_Handler /* Prefetch Handler */
b EL2_Abort_Handler /* Abort Handler */
b EL2_Trap_Handler /* Reserved */
b EL2_IRQ_Handler /* IRQ Handler */
b EL2_FIQ_Handler /* FIQ Handler */
  1. 从而进入 EL2_Reset_Handler 函数,正式开始启动;
EL2_Reset_Handler:
mov r0, #0
mov r1, r0
mov r2, r0
mov r3, r0
mov r4, r0
mov r5, r0
mov r6, r0
mov r7, r0
mov r8, r0
mov r9, r0
mov r10, r0
mov r11, r0
mov r12, r0
ldr r0, =0x23000003
MCR p15, 0, r0, c15, c0, 0

b MPU_Init
  1. 在做其他操作前,通过 MPU 配置后续可能用到的各个地址空间,在 MPU_Init 这个标号;MPU region1是 MCU sram 区域, 用户可以按照自己的需求,切分 sram,并配置各个切分区域的属性 ,比如 sharebility/non-cache 等等;启动代码对这一部分的配置仅供参考,对于 MCU sram 区域的划分,参考上一节 MCU1镜像 layout,MCU0相关区域的划分因 MCU0的缘故不做展示;
MPU_Init:
//.....其他省略

/*-----region 1 MCU sram示例配置,根据自己需求修改-------*/
/*---------------region 1 mcu sram---------------*/
/* normal memory attribute */
ldr r0, =1 /* Region 1 */
mcr p15, 4, r0, c6, c2, 1 /* Write HPRSELR */
mcr p15, 0, r0, c6, c2, 1 /* Write PRSELR */

ldr r0, =0x0C800000 /* Start address */
orr r0, r0, #0x2 /* SH=0, AP=1, XN=0*/
mcr p15, 4, r0, c6, c3, 0 /* Write HPRBAR */
mcr p15, 0, r0, c6, c3, 0 /* Write PRBAR */

ldr r0, =0x0CDFFFFF /* End address */
and r0, r0, #0xFFFFFFC0
orr r0, r0, #0x3 /* AttrIndex=1, non-cacheable, enable region */
mcr p15, 4, r0, c6, c3, 1 /* Write HPRLAR */
mcr p15, 0, r0, c6, c3, 1 /* Write PRLAR */

/*---------------region 5 internal gic & peripheral---------------*/
/* device memory attribute */
ldr r0, =5 /* Region 5 */
mcr p15, 4, r0, c6, c2, 1 /* Write HPRSELR */
mcr p15, 0, r0, c6, c2, 1 /* Write PRSELR */

ldr r0, =0x22000000 /* Start address */
orr r0, r0, #0x13 /* SH=2, AP=1, XN=1*/
mcr p15, 4, r0, c6, c3, 0 /* Write HPRBAR */
mcr p15, 0, r0, c6, c3, 0 /* Write PRBAR */

ldr r0, =0x223FFFFF /* End address */
sub r0, r0, #1
and r0, r0, #0xFFFFFFC0
orr r0, r0, #0x7 /* AttrIndex=3, device memory, enable region */
mcr p15, 4, r0, c6, c3, 1 /* Write HPRLAR */
mcr p15, 0, r0, c6, c3, 1 /* Write PRLAR */

//.....后续省略
  1. 地瓜版本中重要的 MPU region 说明如下:
警告

ARM R52的 background region 和 RDK-S100芯片上实际实现的 memory map 是有差异的。

比如0x2200_0000在 ARM 的 background region 中默认是属于 normal memory 空间,但在 RDK-S100芯片上这段地址空间对应是 GIC 这种 device 类的寄存器空间。

所以,像这种芯片实际与 background region 有差异的区域,在进行访问之前,一定要通过 MPU 将 memory 类型和 RDK-S100芯片实际实现保持一致,否则会导致访问异常。

这些区域都是 MCU 正常运行可能需要访问的空间,缺少这些空间的配置,可能会导致运行异常。客户在自己的版本中 请保持和地瓜给的代码一样的地址空间属性配置。SRAM 区域,客户则可以根据自己项目的需求做不同的切分。

MPU region起始地址结束地址memory 类型说明
00x0800_00000x0AFF_FFFFnormal memoryvdsp tcm 及 MCU TCM 所在空间,SPL 运行会使用 vdsp tcm
70x2200_00000x223F_FFFFdevice memoryMCU GIC 寄存器所在地址空间,影响对 GIC 寄存器的访问
80x2300_00000x25FF_FFFFdevice memoryMCU peri 寄存器所在空间,影响对 MCU 上 peri 寄存器的访问
90x2600_00000x7FFF_FFFFdevice memoryCPUSYS 各种寄存器所在空间,影响 MCU 对 Acore 侧的寄存器访问
100x8000_00000xFFFF_FFFFnormal memoryDDR 所在地址空间,影响 MCU 对 DDR 访问
110x1800_00000x1FFF_FFFFdevice memoryXSPI 所在地址空间,影响 MCU 对 flash 的使用
  1. 启动代码接下来做了 enable_prefetch/enable_peri_secure/使能 VFP/配置 SYSCNT 寄存器等操作,建议客户保留这些代码;
  2. 紧接着让当前 core 从 hyper 模式跳转到 el1;
     /* Init ELR_hyp with stack_initialization address - init the return address when jumping from EL2 into EL1 */
ldr r0, =EL1_Reset_Handler
msr ELR_hyp, r0

//省略部分代码

/* Exception return - will jump to address pointed by ELR_hyp (main) */
eret /* When executed in Hyp mode, ERET loads the PC from ELR_hyp and loads the CPSR from SPSR_hyp */
  1. 接下来是做栈的初始化,每个 core 都有自己的一段栈区域。启动代码只初始化了 R52+上 abort/undefined/system mode 的栈寄存器 ,并没有初始化配置 irq/fiq mode 等等栈寄存器。客户需要根据自己 OS 的情况,确定 R52+各个模式的栈寄存器是否需要初始化;
     /* Setup the stack for supervisor mode (entered from reset) */
mrs r0, cpsr
and r0, r0, #~0x00FF
orr r0, r0, #0x0033
msr cpsr_c, r0
sub r3, r3, r1
mov SP, r3 /* top of stack to SP_svc */

ldr r3, =__StackTop_exc
ldr r2, =__StackLimit_exc
sub r2, r3, r2 /* r2 : size in bytes */
mov r4, #4
udiv r1, r2, r4 /* r1 : size divided by 4 */
and r1, r1, #~0x0f /* r1 size alligned to 16 bytes */

/* Go to FIQ mode and set stack (below the previous one) */
mrs r0, cpsr
and r0, r0, #~0x003F
orr r0, r0, #0x0031
msr cpsr_c, r0
sub r3, r3, r1
mov SP, r3

//.....后续省略
  1. 跳转到 main
    /* Enable IRQ and FIQ interrupts for the system/user mode */
cpsie i /* Unmask interrupts (IRQ)*/
cpsie f /* Unmask fast interrupts (FIQ)*/

/* Go to supervisor mode */
/* mrs r0, cpsr */
/* and r0, r0, #~0x00FF */
/* orr r0, r0, #0x0033 */
/* msr cpsr_c, r0 */

/* Jump to the main() method */
bl main

/* Should never get here */
b .
.end
  1. 启动的第一条指令就是进入 EL2_core_exceptions_table 向量表
    .text
.align 4
.section ".EL2_core_exceptions_table", "ax"
.globl EL2_core_exceptions_table
.type EL2_core_exceptions_table, %function
EL2_core_exceptions_table:
b EL2_Reset_Handler /* Reset Handler */
b EL2_Undefined_Handler /* Undefined Handler */
b EL2_HVC_Handler /* SVCall Handler */
b EL2_Prefetch_Handler /* Prefetch Handler */
b EL2_Abort_Handler /* Abort Handler */
b EL2_Trap_Handler /* Reserved */
b EL2_IRQ_Handler /* IRQ Handler */
b EL2_FIQ_Handler /* FIQ Handler */
  1. 从而进入 EL2_Reset_Handler 函数,正式开始启动;
EL2_Reset_Handler:
mov r0, #0
mov r1, r0
mov r2, r0
mov r3, r0
mov r4, r0
mov r5, r0
mov r6, r0
mov r7, r0
mov r8, r0
mov r9, r0
mov r10, r0
mov r11, r0
mov r12, r0
ldr r0, =0x23000003
MCR p15, 0, r0, c15, c0, 0

b MPU_Init
  1. 在做其他操作前,通过 MPU 配置后续可能用到的各个地址空间,在 MPU_Init 这个标号;MPU region1是 MCU sram 区域, 用户可以按照自己的需求,切分 sram,并配置各个切分区域的属性 ,比如 sharebility/non-cache 等等;启动代码对这一部分的配置仅供参考,对于 MCU sram 区域的划分,参考上一节 MCU1镜像 layout,MCU0相关区域的划分因 MCU0的缘故不做展示;
MPU_Init:
//.....其他省略

/*-----region 1 MCU sram示例配置,根据自己需求修改-------*/
/*---------------region 1 mcu sram---------------*/
/* normal memory attribute */
ldr r0, =1 /* Region 1 */
mcr p15, 4, r0, c6, c2, 1 /* Write HPRSELR */
mcr p15, 0, r0, c6, c2, 1 /* Write PRSELR */

ldr r0, =0x0C800000 /* Start address */
orr r0, r0, #0x2 /* SH=0, AP=1, XN=0*/
mcr p15, 4, r0, c6, c3, 0 /* Write HPRBAR */
mcr p15, 0, r0, c6, c3, 0 /* Write PRBAR */

ldr r0, =0x0CDFFFFF /* End address */
and r0, r0, #0xFFFFFFC0
orr r0, r0, #0x3 /* AttrIndex=1, non-cacheable, enable region */
mcr p15, 4, r0, c6, c3, 1 /* Write HPRLAR */
mcr p15, 0, r0, c6, c3, 1 /* Write PRLAR */

/*---------------region 5 internal gic & peripheral---------------*/
/* device memory attribute */
ldr r0, =5 /* Region 5 */
mcr p15, 4, r0, c6, c2, 1 /* Write HPRSELR */
mcr p15, 0, r0, c6, c2, 1 /* Write PRSELR */

ldr r0, =0x22000000 /* Start address */
orr r0, r0, #0x13 /* SH=2, AP=1, XN=1*/
mcr p15, 4, r0, c6, c3, 0 /* Write HPRBAR */
mcr p15, 0, r0, c6, c3, 0 /* Write PRBAR */

ldr r0, =0x223FFFFF /* End address */
sub r0, r0, #1
and r0, r0, #0xFFFFFFC0
orr r0, r0, #0x7 /* AttrIndex=3, device memory, enable region */
mcr p15, 4, r0, c6, c3, 1 /* Write HPRLAR */
mcr p15, 0, r0, c6, c3, 1 /* Write PRLAR */

//.....后续省略
  1. 地瓜版本中重要的 MPU region 说明如下:
警告

ARM R52的 background region 和 RDK-S600芯片上实际实现的 memory map 是有差异的。

比如0x2200_0000在 ARM 的 background region 中默认是属于 normal memory 空间,但在 RDK-S600芯片上这段地址空间对应是 GIC 这种 device 类的寄存器空间。

所以,像这种芯片实际与 background region 有差异的区域,在进行访问之前,一定要通过 MPU 将 memory 类型和 RDK-S600芯片实际实现保持一致,否则会导致访问异常。

这些区域都是 MCU 正常运行可能需要访问的空间,缺少这些空间的配置,可能会导致运行异常。客户在自己的版本中 请保持和地瓜给的代码一样的地址空间属性配置。SRAM 区域,客户则可以根据自己项目的需求做不同的切分。

MPU region起始地址结束地址memory 类型说明
00x4000_00000x0BFF_FFFFnormal memoryvdsp tcm 及 MCU TCM 所在空间
10x1800_00000x1FFF_FFFFdevice memoryXSPI 所在地址空间, 影响 MCU 对 flash 的使用
20x2200_00000x23FF_FFFFdevice memoryMCU GIC 寄存器所在地址空间, 影响对 GIC 寄存器的访问
30x8000_00000x8000_03FFnormal memory休眠唤醒功能需要访问的 ddr
40xB400_00000xB5FF_FFFFnormal memoryIPC 功能可能需要访问的 ddr 空间
50xCF00_00000xCF4F_FFFFnormal memoryvspi flash 功能需要访问的 ddr
60x2500_00000x5FFF_FFFFdevice memoryAcore 寄存器区域 影响 MCU 对 Acore 寄存器的访问
  1. 启动代码接下来配置 EL2与 EL1异常中断向量表,根据 Core ID 选择并设置 EL1异常向量表;
/* Init HVBAR (Hypervisor Vector Base Address Register) */
ldr r0, =EL2_core_exceptions_table
mcr p15, 4, r0, c12, c0, 0 /* Move to Coprocessor from ARM Register */

/* Init VBAR (Vector Base Address Register) */
mrc p15, 0, r1, c0, c0, 5
and r1, r1, #0x03

cmp r1, #0
beq set_mcu1_vbar
cmp r1, #1
beq set_mcu2_vbar

set_mcu1_vbar:
ldr r0, =EL1_core_exceptions_table
b vbar_set_done

set_mcu2_vbar:
ldr r0, =EL1_core_exceptions_table_MCU2

vbar_set_done:
mcr p15, 0, r0, c12, c0, 0 /* Move to Coprocessor from ARM Register */
  1. 在 EL2 阶段初始化栈指针,根据 Core ID 选择栈顶地址并设置 SP;
    mrc p15, 0, r1, c0, c0, 5
and r1, r1, #0x03

cmp r1, #0
beq init_mcu1_stack
cmp r1, #1
beq init_mcu2_stack

b init_mcu1_stack

init_mcu1_stack:
ldr r3, =__StackTop
b stack_init_done

init_mcu2_stack:
ldr r3, =__StackTop_MCU2
b stack_init_done

init_default_stack:
ldr r3, =__StackTop

stack_init_done:
mov SP, r3
  1. 进行系统初始化操作,包括 GIC/FPU/DATA 段/BSS 段/CPSR 等内容,并切换至 EL1,跳转至 EL1_Reset_Handler;
/* Call System Init */
bl SystemInit
bl init_data_bss
/* Init CPRS (Current Program Status Register) with the desired Mode (User, System, SVC, ..) */
mrs r0, cpsr /* Move to ARM register from system coprocessor register */
and r0, r0, #~0x0010 /* clear mode 10 - usr, 1f - system */
orr r0, r0, #0x0010 /* Software executing in System mode executes at PL1. System mode has the same registers available as User mode, and is not entered by any exception. An operating system runs applications in User mode to restrict the use of system resources. Software executing in User mode executes at PL0. Execution in User mode is sometimes described as unprivileged execution. */
msr cpsr, r0
/* Init ELR_hyp with stack_initialization address - init the return address when jumping from EL2 into EL1 */
ldr r0, =EL1_Reset_Handler
msr ELR_hyp, r0

mrs r0, SPSR_hyp
and r0, r0, #~0x00FF /* r0 = r0 & FFFF FFE0. Clear SPSR_hyp bits [4:0] -> Execution state bit + Mode bits. */

/* Software executing in System mode executes at PL1. System mode has the same registers available as User mode, and is not entered by any exception. An operating system runs applications in User mode to restrict the use of system resources. Software executing in User mode executes at PL0. Execution in User mode is sometimes described as unprivileged execution. */
/* 10 - usr, 1f - system */
orr r0, r0, #0x1f /* r0 = r0 | 0x1df set to system mode with AIF mask */
bic r0, r0, #(0x1 << 5)
msr SPSR_hyp, r0
/* Configure the GIC CPU Interface */
/* Disable group 0 interrupts */
mov r0, #0x00
mcr p15, 0, r0, c12, c12, 6 /* Write to ICC_IGRPEN0 */

/* Enable group 1 interrupts */
mov r0, #0x01
mcr p15, 0, r0, c12, c12, 7 /* Write to ICC_IGRPEN1 */

/* Set the interrupt priority mask to biggest value - 0x1F */
/* Interrupts with all priorities are allowed. */

mov r0, #0xF8 /* The priority bitfield is shifted with 3 bits - 0x1F becomes 0xF8 */
mcr p15, 0, r0, c4, c6, 0 /* Write to ICC_PMR */

/* Set the binary point for group 0 and group 1 interrupts */
mov r0, #0
mcr p15, 0, r0, c12, c8, 3 /* Write to ICC_BPR0 */
mcr p15, 0, r0, c12, c12, 3 /* Write to ICC_BPR1 */

/* Exception return - will jump to address pointed by ELR_hyp (main) */
eret /* When executed in Hyp mode, ERET loads the PC from ELR_hyp and loads the CPSR from SPSR_hyp */
  1. 接下来是做栈的初始化,每个 core 都有自己的一段栈区域。启动代码根据 Core ID 选择栈配置,为 SVC、FIQ、IRQ、ABORT、UNDEF、SYSTEM 模式分别设置栈指针;
EL1_Reset_Handler:
mrc p15, 0, r0, c0, c0, 5
and r0, r0, #0x03

mov r12, r0
cmp r0, #0
beq setup_mcu1_stack
cmp r0, #1
beq setup_mcu2_stack

//.....后续省略
  1. 跳转到 main
    /* Enable IRQ and FIQ interrupts for the system/user mode */
cpsie i /* Unmask interrupts (IRQ)*/
cpsie f /* Unmask fast interrupts (FIQ)*/

/* Go to supervisor mode */
/* mrs r0, cpsr */
/* and r0, r0, #~0x00FF */
/* orr r0, r0, #0x0033 */
/* msr cpsr_c, r0 */

/* Jump to the main() method */
bl main

/* Should never get here */
b .
.end