源码链接:https://gitee.com/ingenic-dev/libbare-cpu/tree/develop/master/cpu/core-riscv
简介
Ingenic X2600 是一款双核异构的芯片,主核使用主频1.2GHz的MIPS架构的Xburst2,小核使用主频600MHz的RISCV架构的Victory。其中对于RISCV小核无法做到单独使用,依赖于大核的启动对其进行加载和通讯,软件SDK采用libbare-cpu对其进行编程。本篇主要对跑在RISCV核上的FreeRTOS程序的代码编译和第一个程序的执行等内容进行分析
代码编译
具体的代码编译规则是由CMakeLists.txt 和 Makefile两种方式进行构建的,本部分对此不做解释,而是对链接脚本文件ld.lds文件做分析
ld.lds
文件(也叫 Linker Script)是 GCC 编译器中 ld
链接器使用的链接脚本,用来控制最终生成的可执行文件或固件的 内存布局,在CMakefiles.txt中可以通过target_link_libraries的-T参数将链接脚本文件指定,而Makefile也可以通过-T参数进行指定传递给LDFALGS进一步给gcc进行编译
本部分展示的ld文件来源于2025年5月25日版本,如果需要查看最新的内容参阅:https://gitee.com/ingenic-dev/libbare-cpu/blob/develop/master/cpu/core-riscv/ld.lds#
1 | // GNU链接器脚本 中的一个指令,用来指定最终输出文件的目标架构(Target Architecture) |
对于以上诸多的段代码,至于链接器会将哪些内容放到对应的段,有些是通识定义比如:(函数体)默认放在 .text
段;初始化的全局变量放 .data
;未初始化的全局变量放 .bss
;常量字符串放 .rodata
,而自定义的段可以通过__attribute__((section(".my_section")))
来显式声明
启动流程
由于使用FreeRTOS所以启动过程较复杂,主要包括的文件包括start.S、genex.S、portASM.S文件。对于文件内容较多的不做全部解析,只对重要的语句进行解释
- start.S: 通常是启动代码(startup code)文件,上电或复位之后执行的第一段汇编代码
- genex.S: 可能是generated exceptions生成的异常的缩写,里面内容则是与系统异常处理相关的汇编代码
- portASM.S: 通常是“porting assembly”的缩写,移植层的汇编代码,用于对接到FreeRTOS的汇编代码
启动汇编代码
start.S
(或 start.s
)是启动汇编文件,在嵌入式系统、操作系统内核、引导程序等项目中极其关键。它是系统上电或重启后 第一段被执行的代码。
本部分展示的ld文件来源于2025年5月25日版本,如果需要查看最新的内容参阅:https://gitee.com/ingenic-dev/libbare-cpu/blob/develop/master/cpu/core-riscv/start.S
1 |
|
异常处理代码
Ingenic-x2600支持将中断放置到RISCV-RAM中执行,而不在DDR执行,这样就减少了DDR的压力对中断实时性的影响。该功能的实现需要使能CONFIG_IRQ_IN_TCSM宏并配置CMAKE编译,这里不对此部分作说明,因此代码说明部分将删除掉对应的宏控内容
本部分展示的ld文件来源于2025年5月25日版本,如果需要查看最新的内容参阅:https://gitee.com/ingenic-dev/libbare-cpu/blob/develop/master/cpu/core-riscv/port_freertos/genex.S
1 | .extern reset_handler |
对于以上设置的 __MTVEC 是 RISC-V 架构下的一个关键寄存器名称,它是 mtvec(Machine Trap-Vector Base Address Register) 的符号别名或宏定义,用来设置中断/异常的跳转入口地址,而不同地址位置的异常是需要满足RISCV标准定义的

FreeRTOS汇编对接代码
本部分展示的ld文件来源于2025年5月25日版本,如果需要查看最新的内容参阅:https://gitee.com/ingenic-dev/libbare-cpu/blob/develop/master/cpu/core-riscv/port_freertos/portASM.S#
1 |
|
整体梳理
编译部分
通过CMAE指定 -T 参数可以指定链接文件,编译器将按照我们的规定的格式进行代码布局,如下图所示是基于以上文件编译出来的ELF文件,可以看到每个Section依次按照我们预期的布局进行排布,同时地址也是我们对应的内容区域
程序入口地址 _reset_entry同样符合预期,偏移0x80位置
这里还有一个需要注意的是 section to segment mapping,section是链接阶段的概念,而segment是运行时的概念,执行时相同属性的section会合并成一个segment加载到内存中,当然也可以手动指定
执行部分
- 代码编译出来之后将根据segment装载到对应的地址位置
- 解析elf文件头和其它信息
- 跳到entry point地址进入到代码入口处
这里简单看一下内存布局,vectors是第一个section因此__MTVEC符号对应的代码将放到0x7f00000位置(instrram内存区域),具体内容则是中断向量表 + 0x80的reset_handler + 0x200的资源表 —- 0x1000(4K对齐),至此vector的section结束
当代码开始执行将跳到base+0x80执行也就是reset_handler,此时开始真正执行代码:初始化RISCV寄存器;开启mie;将中断向量表地址设置到RISCV MRW 机器陷阱处理寄存器里;初始化栈;清除BSS段;跳转到c_main函数,这里就可以调用到板级和应用相关的代码。
RISC-V通用寄存器
编号 | 名称 | ABI 名称 | 用途描述 |
---|---|---|---|
x0 | zero | 恒为 0(读为0,写无效) | |
x1 | ra | 返回地址(return address) | 子程序返回地址 |
x2 | sp | 栈指针(stack pointer) | 栈顶位置 |
x3 | gp | 全局指针(global pointer) | 用于全局数据寻址 |
x4 | tp | 线程指针(thread pointer) | 多线程环境中的线程本地存储地址 |
x5–x7 | t0–t2 | 临时寄存器(caller saved) | |
x8 | s0/fp | 保存寄存器/帧指针 | |
x9 | s1 | 保存寄存器 | |
x10–x17 | a0–a7 | 函数参数/返回值(caller saved) | |
x18–x27 | s2–s11 | 保存寄存器(callee saved) | |
x28–x31 | t3–t6 | 临时寄存器(caller saved) |
CSR寄存器
RISC-V 中的 CSR(Control and Status Registers,控制和状态寄存器) 是一种用于控制处理器行为和状态的特殊寄存器,广泛用于异常处理、中断、性能监控、特权级切换等系统功能。
🧠基本概念
- CSR 是一个特殊的寄存器集合,用于配置 CPU 行为、保存运行状态。
- 访问 CSR 只能通过特定的指令:
CSRRW
,CSRRS
,CSRRC
等。 - 通常只在特权级(如 M-mode、S-mode)中可用。
🧩常见 CSR 列表
- 系统控制类
CSR 名称 | 地址 | 描述 |
---|---|---|
mstatus |
0x300 | M 模式状态寄存器 |
misa |
0x301 | ISA 特性描述寄存器 |
mie |
0x304 | 中断使能控制 |
mtvec |
0x305 | 异常/中断向量表入口地址 |
mscratch |
0x340 | 保存临时数据(中断使用) |
mepc |
0x341 | 异常发生时的 PC(返回地址) |
mcause |
0x342 | 异常或中断的原因 |
mtval |
0x343 | 与异常相关的值(如地址) |
mip |
0x344 | 中断挂起状态 |
- 性能计数类
CSR 名称 | 地址 | 描述 |
---|---|---|
cycle |
0xC00 | Cycle 计数器(低 32 位) |
time |
0xC01 | 时间计数器(低 32 位) |
instret |
0xC02 | 已完成指令数 |