PolarFire® SoC mss_enty.S 分析

  Demo 来自  mpfs-mmuart-interrupt

deepseek 给增加的中文注释, 总结如下:

mss_entry.S 是 Microchip MPFS HAL 嵌入式系统的启动引导汇编代码,负责系统上电或复位后的底层初始化工作,为后续 C 语言环境的执行做好准备。

核心功能模块

  1. 启动模式判断

    • 根据 IMAGE_LOADED_BY_BOOTLOADER 宏决定执行路径:

      • 自主启动:完成完整的硬件初始化。

      • Bootloader 加载:利用 Bootloader 已完成的初始化,仅做必要设置。

  2. 基础硬件初始化

    • 设置机器模式陷阱向量 (mtvec)。

    • 禁用全局中断 (mstatus.MIE)、清除中断使能 (mie) 和挂起位 (mip)。

    • 初始化关键 CSRs:mscratchmcausemepc, 委托寄存器 (midelegmedeleg)。

    • 禁用所有 PMP 区域。

    • 清零所有通用寄存器 (x1-x31)。

  3. 多核(Hart)处理

    • 读取 mhartid 区分不同核心(E51 或 U54)。

    • 为每个 Hart 设置独立的栈空间(栈顶和栈底)。

    • 主 Hart (通常为 Hart0):执行全面的内存初始化(堆、栈、DTIM、L2 LIM),清除总线错误寄存器,最后跳转到 main_first_hart

    • 其他 Hart:初始化后进入 WFI 状态,等待主 Hart 通过软件中断 (IPI) 唤醒,然后跳转到 main_other_hart

  4. 内存初始化

    • 栈和堆:清零栈空间和堆空间。

    • 关键内存区域:可选清除 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 */

 

posted on 2025-09-02 17:23  所长  阅读(18)  评论(0)    收藏  举报

导航