PolarFire® SoC mss_enty.S 分析
Demo 来自 mpfs-mmuart-interrupt
deepseek 给增加的中文注释, 总结如下:
mss_entry.S 是 Microchip MPFS HAL 嵌入式系统的启动引导汇编代码,负责系统上电或复位后的底层初始化工作,为后续 C 语言环境的执行做好准备。
核心功能模块
-
启动模式判断
-
根据
IMAGE_LOADED_BY_BOOTLOADER宏决定执行路径:-
自主启动:完成完整的硬件初始化。
-
Bootloader 加载:利用 Bootloader 已完成的初始化,仅做必要设置。
-
-
-
基础硬件初始化
-
设置机器模式陷阱向量 (
mtvec)。 -
禁用全局中断 (
mstatus.MIE)、清除中断使能 (mie) 和挂起位 (mip)。 -
初始化关键 CSRs:
mscratch,mcause,mepc, 委托寄存器 (mideleg,medeleg)。 -
禁用所有 PMP 区域。
-
清零所有通用寄存器 (x1-x31)。
-
-
多核(Hart)处理
-
读取
mhartid区分不同核心(E51 或 U54)。 -
为每个 Hart 设置独立的栈空间(栈顶和栈底)。
-
主 Hart (通常为 Hart0):执行全面的内存初始化(堆、栈、DTIM、L2 LIM),清除总线错误寄存器,最后跳转到
main_first_hart。 -
其他 Hart:初始化后进入
WFI状态,等待主 Hart 通过软件中断 (IPI) 唤醒,然后跳转到main_other_hart。
-
-
内存初始化
-
栈和堆:清零栈空间和堆空间。
-
关键内存区域:可选清除 DTIM 和 L2 LIM,防止未初始化内存访问导致 ECC 错误。
-
代码重定位:提供
copy_section和copy_switch_code函数,用于复制只读数据段和硬件配置代码到执行地址。
-
5 陷阱处理
-
-
统一的机器模式陷阱入口
trap_vector。 -
保存所有寄存器到栈上,调用 C 函数
trap_from_machine_mode处理异常。 -
处理完成后恢复寄存器并通过
mret返回。
-
6 辅助函数
-
-
zero_section:清零内存区域。 -
count_section:用递增数值填充内存(可用于调试)。 -
clear_ras:通过两级函数调用清除处理器内部的返回地址栈 (RAS)。 -
record_ecc_error_counts:记录初始 ECC 错误计数,用于后续检测内存错误。
-
设计特点
-
灵活性:支持自主启动和 Bootloader 加载两种模式。
-
多核支持:清晰区分主核与其他核的初始化流程,通过 HLS (Hart Local Storage) 进行核间通信。
-
安全性:彻底清零敏感寄存器与内存区域,防止未初始化数据使用。
-
可维护性:大量使用宏和辅助函数,结构清晰,注释详尽。
-
兼容性:处理了 RISC-V 架构版本(32/64位)和浮点单元的兼容性。
此文件是系统启动过程中最底层的软件组件,为后续高级语言(如 C)程序的运行奠定了坚实的基础。
/******************************************************************************* * Copyright 2019-2021 Microchip Corporation. * * SPDX-License-Identifier: MIT * * MPFS HAL Embedded Software * */ /******************************************************************************* * @file entry.S * @author Microchip-FPGA Embedded Systems Solutions * @brief entry functions. * */ #include "../common/bits.h" #include "../common/encoding.h" #include "../common/mss_mtrap.h" #include "system_startup_defs.h" #include "mpfs_hal_config/mss_sw_config.h" .option norvc /* 禁止压缩指令,确保指令对齐 */ .section .text.init,"ax", %progbits /* 定义初始化代码段,可分配、可执行 */ .globl reset_vector /* 声明全局符号 reset_vector */ .globl _start /* 声明全局符号 _start */ reset_vector: _start: #if (IMAGE_LOADED_BY_BOOTLOADER == 0) /* 如果不是由Bootloader加载的镜像,则执行以下初始化 */ /* * clear the Return Address Stack */ call .clear_ras /* 清除返回地址栈(RAS)*/ /* Setup trap handler */ la a4, trap_vector /* 加载陷阱向量地址到a4 */ csrw mtvec, a4 /* 设置机器模式陷阱向量地址 */ /* Make sure that mtvec is updated before continuing */ 1: csrr a5, mtvec /* 读取mtvec确认写入成功 */ bne a4, a5, 1b /* 若未成功,则循环等待 */ /* Disable and clear all interrupts */ li a2, MSTATUS_MIE /* 加载机器模式中断使能位掩码 */ csrc mstatus, a2 /* 清除mstatus中的MIE位,禁用中断 */ csrw mie, zero /* 禁用所有中断源 */ csrw mip, zero /* 清除所有中断挂起位 */ # Init delegation registers, mideleg, medeleg, if a U54 # 这些寄存器在硬件中未初始化,处于随机状态,需手动清零(仅U54核需处理) csrr a0, mhartid /* 读取当前hart ID */ beqz a0, .skip_e51 /* 若是E51核(hart0),跳过 */ csrw mideleg, 0 /* 清零机器中断委托寄存器 */ csrw medeleg, 0 /* 清零机器异常委托寄存器 */ .skip_e51: # mscratch must be init to zero- we are not using scratch memory csrw mscratch, zero /* 初始化mscratch寄存器为0 */ csrw mcause, zero /* 清零异常原因寄存器 */ csrw mepc, zero /* 清零异常程序计数器 */ /* * clear PMP enables */ csrw pmpcfg0, zero /* 禁用PMP0区域 */ csrw pmpcfg2, zero /* 禁用PMP2区域 */ /* * clear regs */ /* 清零所有通用寄存器x1-x31 */ li x1, 0 li x2, 0 ... /* 此处省略多个li指令 */ li x31,0 # enable FPU and accelerator if present, setting ignored on E51 li t0, MSTATUS_FS | MSTATUS_XS /* 加载浮点单元和加速器状态位掩码 */ csrs mstatus, t0 /* 启用浮点单元和加速器状态(E51忽略)*/ # Init floating point control register to zero # skip if e51 csrr a0, mhartid /* 再次读取hart ID */ beqz a0, .no_float /* 若是E51,跳过浮点初始化 */ #ifdef __riscv_flen fscsr x0 /* 初始化浮点控制状态寄存器为0 */ #endif .no_float: # make sure XLEN agrees with compilation choice, if not will loop here .LxlenCheck: csrr t0, misa /* 读取misa寄存器检查架构 */ #if __riscv_xlen == 64 bltz t0, .LxlenPass /* 若为64位架构,检查成功则跳转 */ #else bgez t0, .LxlenPass /* 若为32位架构,检查成功则跳转 */ #endif j .LxlenCheck /* 若不匹配,则循环等待 */ .LxlenPass: # initialize global pointer, global data # __global_pointer$在链接脚本中定义,指向sdata起始后2K位置,用于优化全局变量访问 .option push .option norelax /* 临时禁止链接器松弛优化 */ la gp, __global_pointer$ /* 加载全局指针 */ .option pop /* 恢复优化设置 */ # get core id csrr a0, mhartid /* 读取当前hart ID */ li a1, 0 beq a0, a1, .hart0 /* 根据hart ID跳转到对应栈设置 */ li a1, 1 beq a0, a1, .hart1 li a1, 2 beq a0, a1, .hart2 li a1, 3 beq a0, a1, .hart3 li a1, 4 beq a0, a1, .hart4 .hart0: la a4, __stack_bottom_h0$ /* 保存hart0栈底地址到a4 */ la sp, __stack_top_h0$ /* 设置hart0栈顶指针 */ j .continue .hart1: la a4, __stack_bottom_h1$ /* hart1栈底 */ la sp, __stack_top_h1$ /* hart1栈顶 */ j .continue .hart2: la a4, __stack_bottom_h2$ /* hart2栈底 */ la sp, __stack_top_h2$ /* hart2栈顶 */ j .continue .hart3: la a4, __stack_bottom_h3$ /* hart3栈底 */ la sp, __stack_top_h3$ /* hart3栈顶 */ j .continue .hart4: la a4, __stack_bottom_h4$ /* hart4栈底 */ la sp, __stack_top_h4$ /* hart4栈顶 */ .continue: # clear HLS and stack mv a5, sp /* 保存栈顶到a5 */ .init_stack: STORE x0, 0(a4) /* 从栈底开始清零栈空间 */ add a4, a4, __SIZEOF_POINTER__ /* 移动到下一个指针大小位置 */ blt a4, a5, .init_stack /* 若未到栈顶,继续清零 */ # Allocate some space at top of stack for the HLS addi sp, sp, -HLS_DEBUG_AREA_SIZE /* 在栈顶预留HLS调试区域 */ # HLS grows up from new top of stack mv tp, sp /* 设置线程指针tp指向HLS起始 */ # get core id csrr a0, mhartid /* 再次读取hart ID */ li a1, MPFS_HAL_FIRST_HART /* 加载第一个主hart的ID */ bne a0, a1, .LOtherHartstoWFI /* 若不是主hart,跳转到等待WFI */ # clear the common heap la a4, __heap_start /* 堆起始地址 */ la a5, __heap_end /* 堆结束地址 */ .init_heap: STORE x0, 0(a4) /* 清零堆空间 */ add a4, a4, __SIZEOF_POINTER__ blt a4, a5, .init_heap /* 若未到堆末尾,继续清零 */ # # clear DTIM - this is required to stop memory errors on initial access by # cache # Also, stops x propagation in simulation, when cache/stack reads unused # area # li a2, MPFS_HAL_CLEAR_MEMORY /* 检查是否需要清除内存 */ beq x0, a2, .skip_mem_clear /* 若配置为不清除,则跳过 */ call .clear_dtim /* 清除E51 DTIM内存 */ call .clear_l2lim /* 清除L2 LIM内存 */ .skip_mem_clear: /* * Clear bus error unit accrued register on start-up * This is cleared by the first hart only */ /* 清零总线错误单元累积寄存器(仅由第一个hart执行)*/ la a4,0x01700020UL sb x0, 0(a4) ... /* 此处省略多个类似操作 */ la a4,0x01704020UL sb x0, 0(a4) # now core MPFS_HAL_FIRST_HART jumps to main_first_hart .main_hart: # pass HLS address mv a0, tp /* 将HLS地址作为参数传递给主函数 */ j main_first_hart /* 跳转到主hart的C入口函数 */ .LoopForeverMain: #in case of return, loop forever. nop's added so can be seen in debugger nop nop j .LoopForeverMain /* 主函数返回后在此无限循环 */ .LOtherHartstoWFI: /* 其他hart的初始化流程:禁用中断,等待主hart信号,然后进入WFI */ li a2, MSTATUS_MIE csrc mstatus, a2 /* 禁用中断 */ csrw mie, zero csrw mip, zero li a2, MIP_MSIP /* 仅使能软件中断(IPI)*/ csrw mie, a2 # # Wait here until main hart is up and running # li a3, HLS_MAIN_HART_STARTED /* 主hart启动完成标志 */ la a1, (__stack_top_h0$ - HLS_DEBUG_AREA_SIZE) /* 主hart的HLS地址 */ .wait_main_hart: LWU a2, 0(a1) /* 读取主hart的HLS状态 */ bne a3, a2, .wait_main_hart /* 若主hart未就绪,则循环等待 */ # Flag we are here to the main hart li a1, HLS_OTHER_HART_IN_WFI /* 设置本hart状态为“进入WFI” */ sw a1, 0(tp) /* 写入本hart的HLS */ /* flush the instruction cache */ fence.i /* 指令缓存同步 */ .LwaitOtherHart: wfi /* 等待中断(WFI)*/ # 确保只有MSIP中断才能唤醒 csrr a2, mip andi a2, a2, MIP_MSIP /* 检查是否为软件中断 */ beqz a2, .LwaitOtherHart /* 若不是,继续等待 */ /* Disable and clear all interrupts- should be only a sw interrupt */ li a2, MSTATUS_MIE csrc mstatus, a2 /* 再次禁用中断 */ csrw mie, zero csrw mip, zero # set marker as to where we are li a1, HLS_OTHER_HART_PASSED_WFI /* 设置状态为“已通过WFI” */ sw a1, 0(tp) # pass HLS address mv a0, tp /* 传递HLS地址给其他hart的C入口函数 */ j main_other_hart .LoopForeverOther: nop nop j .LoopForeverOther /* 其他hart主函数返回后无限循环 */ #else /* IMAGE_LOADED_BY_BOOTLOADER == 1 */ /*********************************************************************************** *The program has been loaded by a bootloader * a0 - contains the hart ID * a1 - contains pointer to bootloader -Hart Local Storage, for this hart. */ _start_non_bootloader_image: /* 由Bootloader加载的镜像入口点 */ /* 可选的调试断点(通常仅在调试版本启用)*/ /* li a2, DEBUG_EBREAK_AT_START beq x0, a2, 1f ebreak */ 1: /* 保存从bootloader传递的参数:a0=hart ID, a1=HLS指针(可能为0)*/ /* 设置陷阱向量 */ la a4, trap_vector csrw mtvec, a4 /* 确保mtvec更新成功 */ 2: csrr a5, mtvec bne a4, a5, 2b /* 禁用中断(假设bootloader已做,但为确保安全)*/ /* 初始化委托寄存器(仅U54核)*/ beqz a0, 3f csrw mideleg, 0 csrw medeleg, 0 3: csrw mscratch, zero csrw mcause, zero csrw mepc, zero /* 初始化浮点控制寄存器(非E51核)*/ beqz a0, 1f #ifdef __riscv_flen fscsr x0 #endif 1: /* 检查XLEN匹配 */ csrr t0, misa #if __riscv_xlen == 64 bltz t0, 2f #else bgez t0, 2f #endif j 1b 2: /* 初始化全局指针 */ .option push .option norelax la gp, __global_pointer$ .option pop /* 清零应用程序栈 */ la a4, __app_stack_bottom la a5, __app_stack_top la sp, __app_stack_top 1: STORE x0, 0(a4) add a4, a4, __SIZEOF_POINTER__ blt a4, a5, 1b /* 清零堆 */ la a4, __heap_start la a5, __heap_end 2: STORE x0, 0(a4) add a4, a4, __SIZEOF_POINTER__ blt a4, a5, 2b # check if HLS passed by BL, if not allocate one here bnez a1, 1f /* 若bootloader提供了HLS指针,直接使用 */ /* 否则在栈顶分配HLS空间 */ addi sp, sp, -HLS_DEBUG_AREA_SIZE mv tp, sp mv a0, tp j u54_single_hart /* 跳转到单hartC入口函数 */ 1: mv a0, a1 /* 使用bootloader提供的HLS指针 */ j u54_single_hart 2: nop nop j 2b /* 无限循环 */ #endif /* IMAGE_LOADED_BY_BOOTLOADER */ /******************************************************************************/ /******************************interrupt handeling below here******************/ /******************************************************************************/ trap_vector: /* 机器模式陷阱处理程序入口 */ addi sp, sp, -INTEGER_CONTEXT_SIZE /* 在栈上分配空间保存寄存器上下文 */ /* 保存所有通用寄存器到栈上 */ STORE sp, 2*REGBYTES(sp) /* 保存SP */ STORE a0, 10*REGBYTES(sp) STORE a1, 11*REGBYTES(sp) ... /* 省略保存其他寄存器 */ STORE t6,31*REGBYTES(sp) # Invoke the handler. mv a0, sp /* a0 = 寄存器上下文指针 */ csrr a1, mbadaddr /* a1 = 出错地址(mtval的旧名)*/ csrr a2, mepc /* a2 = 异常PC */ jal trap_from_machine_mode /* 跳转到C语言陷阱处理函数 */ restore_regs: /* 从栈上恢复所有寄存器 */ LOAD ra, 1*REGBYTES(sp) LOAD gp, 3*REGBYTES(sp) ... /* 省略恢复其他寄存器 */ LOAD t6,31*REGBYTES(sp) LOAD sp, 2*REGBYTES(sp) /* 恢复SP */ addi sp, sp, +INTEGER_CONTEXT_SIZE /* 释放寄存器上下文空间 */ mret /* 从陷阱返回 */ /*****************************************************************************/ /******************************interrupt handeling above here*****************/ /*****************************************************************************/ .enable_sw_int: /* 启用软件中断函数 */ li a2, MIP_MSIP csrw mie, a2 /* 使能机器模式软件中断 */ li a2, MSTATUS_MIE csrs mstatus, a2 /* 启用机器模式全局中断 */ fence.i /* 指令缓存同步 */ ret /*********************************************************************************** * * The following init_memory() symbol overrides the weak symbol in the HAL and does * a safe copy of RW data and clears zero-init memory * */ // zero_section helper function: // a0 = exec_start_addr // a1 = exec_end_addr // .globl zero_section .type zero_section, @function zero_section: bge a0, a1, .zero_section_done sd zero, (a0) /* 清零内存 */ addi a0, a0, 8 j zero_section .zero_section_done: ret // count_section helper function: // a0 = exec_start_addr // a1 = exec_end_addr // a2 = start count // .globl count_section .type count_section, @function count_section: beq a0, a1, .count_section_done sd a2, (a0) /* 以递增数值填充内存 */ addi a0, a0, 8 addi a2, a2, 8 j count_section .count_section_done: ret // copy_section helper function: // a0 = load_addr // a1 = exec_start_addr // a2 = exec_end_addr .globl copy_section .type copy_section, @function copy_section: beq a1, a0, .copy_section_done /* 若加载地址与执行地址相同,跳过 */ .check_if_copy_section_done: beq a1, a2, .copy_section_done .keep_copying: ld a3, 0(a0) /* 从加载地址读取 */ sd a3, 0(a1) /* 写入执行地址 */ addi a0, a0, 8 addi a1, a1, 8 j .check_if_copy_section_done .copy_section_done: ret /*********************************************************************************** * * The following copy_switch_code() symbol overrides the weak symbol in the HAL and does * a safe copy of HW config data */ .globl copy_switch_code .type copy_switch_code, @function copy_switch_code: la a5, __sc_start /* 开关代码执行起始地址 */ la a4, __sc_load /* 开关代码加载地址 */ beq a5,a4,.copy_switch_code_done /* 若相同,无需复制 */ la a3, __sc_end /* 结束地址 */ beq a5,a3,.copy_switch_code_done /* 若长度为0,跳过 */ .copy_switch_code_loop: lw a2,0(a4) /* 从加载地址读取 */ sw a2,0(a5) /* 写入执行地址 */ addi a5,a5,4 addi a4,a4,4 bltu a5,a3,.copy_switch_code_loop .copy_switch_code_done: ret /******************************************************************************* * */ #define START__OF_LIM 0x08000000 #define END__OF_LIM 0x08200000 #define START__OF_DTM 0x01000000 #define END__OF_DTM 0x01002000 .clear_l2lim: /* 清除L2 LIM内存,防止ECC错误 */ la a2, _start la a4, 0x08000000 /* LIM起始地址 */ and a2, a2, a4 bnez a2, .done_clear /* 若当前在LIM中运行,跳过清除(避免破坏代码)*/ la a5, 0x08200000 /* LIM结束地址 */ j 1f .clear_dtim: /* 清除E51 DTIM内存,防止ECC错误 */ la a4, 0x01000000 /* DTIM起始 */ la a5, 0x01002000 /* DTIM结束 */ 1: /* 通用内存清除循环 */ sd x0, 0(a4) /* 写入0 */ add a4, a4, __SIZEOF_POINTER__ blt a4, a5, 1b .done_clear: ret /* * record_ecc_error_counts on reset * These are non-zero in the coreplex. * Can be checked later on to see if values have changed * a0 = mECCDataFailCount save address a1 = mECCDataCorrectionCount save address a2 = mECCDirFixCount save address */ .record_ecc_error_counts: /* 记录初始ECC错误计数,可用于后续检测是否发生新错误 */ #define mECCDataFailCount 0x02010168U la a5, mECCDataFailCount mv a4, a0 lw t2,0(a5) sw t2,0(a4) /* 保存数据失败计数 */ #define mECCDataCorrectionCount 0x02010148U la a5, mECCDataCorrectionCount mv a4, a1 lw t2,0(a5) sw t2,0(a4) /* 保存数据纠正计数 */ #define mECCDirFixCount 0x02010108u la a5, mECCDirFixCount mv a4, a2 lw t2,0(a5) sw t2,0(a4) /* 保存目录修复计数 */ ret /* * clear_ras , clear_ras_2_deep * Two deep function calls. * Used to clear the interal processor Return Address Stack * This is belt and braces, may not be required */ .clear_ras: /* 清除返回地址栈(RAS)的辅助函数 */ mv a5, x1 /* 保存返回地址 */ nop call .clear_ras_2_deep /* 调用两级函数以清除RAS */ nop nop nop nop nop nop mv x1, a5 /* 恢复返回地址 */ ret .clear_ras_2_deep: nop nop nop nop nop nop ret /* 通过多次nop和返回操作清除RAS */
浙公网安备 33010602011771号