You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FPGA上RV32I CPU的链接脚本问题:栈位置不符合预期

RV32I CPU裸机C程序栈基址不符问题

我在FPGA上实现了一款RV32I CPU,此前仅用汇编代码测试。本周尝试运行一个简单C测试程序,用于观察寄存器a的移位1模式,但反汇编后发现栈基址被设为4GB上限以下的最高地址,和我在链接脚本中指定的内存区域不符。

C测试代码

int main(){
        register unsigned int a = 1;
        register unsigned int b = 1;
        while(1){
                a = a << b;
                if(a == 0x80000000){
                        a = 1;
                }
        }
        return 0;
}

反汇编结果

test.elf:     file format elf32-littleriscv


Disassembly of section .text:

00000000 <main>:
   0:   ff010113                add     sp,sp,-16
   4:   00112623                sw      ra,12(sp)
   8:   00812423                sw      s0,8(sp)
   c:   00912223                sw      s1,4(sp)
  10:   01010413                add     s0,sp,16
  14:   00100093                li      ra,1
  18:   00100493                li      s1,1
  1c:   009090b3                sll     ra,ra,s1
  20:   800007b7                lui     a5,0x80000
  24:   fef09ce3                bne     ra,a5,1c <main+0x1c>
  28:   00100093                li      ra,1
  2c:   ff1ff06f                j       1c <main+0x1c>

链接脚本

ENTRY(main)

BRAM_SIZE = 1024;

MEMORY{
        INSTR(RX)  : ORIGIN =0x00000000 , LENGTH = BRAM_SIZE 
        DATA(RWX)  : ORIGIN =0x01000000 , LENGTH = BRAM_SIZE
}

STACK_SIZE = 0x100;

/* Section Definitions */
SECTIONS
{
    .text :
    {
        KEEP(*(.vectors .vectors.*))
        *(.text*)
        *(.rodata*)
    } > INSTR

    /* .bss section which is used for uninitialized data */
    .bss (NOLOAD) :
    {
        *(.bss*)
        *(COMMON)
    } > DATA

    .data :
    {
        *(.data*);
    } > DATA AT >INSTR

    /* stack section */
    .stack (NOLOAD):
    {
        . = ALIGN(8);
        . = . + STACK_SIZE;
        . = ALIGN(8);
    } > DATA

    _end = . ;
}

编译命令

riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -ffreestanding -T  linkerscript_reduced.ld -o test.elf test.c

问题

明明在链接脚本里指定了内存区域,为什么编译器把栈基址设为4GB上限以下的最高地址?哪里出错了?


原因与解决方法

你的链接脚本仅定义了.stack段的大小和位置,但没有向链接器提供栈指针的初始值。在无操作系统的裸机环境下,链接器默认会将栈指针(sp)初始化为RV32I虚拟地址空间的最高端(0xFFFFFFFF附近),这就是栈基址不符合预期的原因。

解决需要两步:

  1. 在链接脚本中定义栈顶符号
    修改.stack段的定义,明确标记栈顶地址(RISC-V栈向下生长,栈顶是栈区域的起始地址):
/* stack section */
.stack (NOLOAD):
{
    . = ALIGN(8);
    __stack_top = .;  /* 定义栈顶符号 */
    . = . + STACK_SIZE;
    __stack_bottom = .;  /* 可选,标记栈底 */
    . = ALIGN(8);
} > DATA

同时把链接脚本开头的ENTRY(main)改为ENTRY(_start),因为后续要添加自定义启动代码。

  1. 添加启动代码初始化栈指针
    由于使用了-nostdlib,没有标准启动代码,需要自己编写汇编启动代码(比如创建start.S),在进入main前初始化sp:
.section .vectors, "ax"
.globl _start
_start:
    la sp, __stack_top  /* 加载栈顶地址到sp寄存器 */
    j main              /* 跳转到main函数 */

编译时将启动代码加入编译命令:

riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -ffreestanding -T linkerscript_reduced.ld -o test.elf start.S test.c

补充说明

  • 你代码中的register关键字只是提示编译器优先分配寄存器,最终寄存器分配由编译器决定,当前反汇编中a分配到ra、b分配到s1是正常行为。
  • 确认.stack段未超出DATA区域:DATA起始地址0x01000000,大小1024字节,STACK_SIZE为256字节,栈顶地址会落在0x01000000到0x01000400范围内,不会越界。

内容的提问来源于stack exchange,提问作者lazerbeam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 05:55:37