2. 最小固件
任务说明
本案例面向QEMU RISC-V环境,目标是编写一个最小固件程序,通过GPIO控制虚拟LED以固定周期闪烁。固件将直接操作内存映射的GPIO寄存器,不依赖任何操作系统或驱动库,以此演示RISC-V裸机开发的基本流程。
硬件模型与地址映射
QEMU的virt机器为RISC-V提供了一套标准的GPIO控制器(对应SiFive GPIO模型),其寄存器基地址为0x10002000。该控制器包含以下关键寄存器(均为32位宽):
- 输入值寄存器(
0x00):读取引脚电平。 - 输出值寄存器(
0x04):写入引脚输出电平。 - 输出使能寄存器(
0x08):置1的位对应引脚为输出模式。
本案例使用GPIO第0号引脚(对应寄存器bit 0)连接虚拟LED。通过将输出使能寄存器的bit 0置1,再交替写输出值寄存器的bit 0为1和0,即可控制LED亮灭。
固件实现
固件代码包含两个文件:启动汇编文件start.S负责设置栈指针并跳转到C入口;主程序main.c实现GPIO初始化和闪烁逻辑。延时采用简单的循环计数实现,不依赖定时器外设。
// start.S
.section .text.init
.globl _start
_start:
la sp, _stack_top // 设置栈指针
call main // 跳转到C主函数
loop:
j loop // 主函数返回后死循环
// main.c
#define GPIO_BASE 0x10002000UL
#define GPIO_OUTPUT_VAL (*(volatile unsigned int *)(GPIO_BASE + 0x04))
#define GPIO_OUTPUT_EN (*(volatile unsigned int *)(GPIO_BASE + 0x08))
void delay(unsigned int count) {
while (count--) {
// 空循环延时
__asm__ volatile("nop");
}
}
int main(void) {
// 配置GPIO0为输出
GPIO_OUTPUT_EN |= (1U << 0);
while (1) {
GPIO_OUTPUT_VAL |= (1U << 0); // 点亮LED
delay(500000);
GPIO_OUTPUT_VAL &= ~(1U << 0); // 熄灭LED
delay(500000);
}
return 0;
}
链接脚本与构建
链接脚本link.ld将代码段加载到QEMU默认的复位地址0x80000000,并预留栈空间。构建使用RISC-V交叉编译工具链,生成ELF文件后由QEMU直接加载执行。
/* link.ld */
OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS {
. = 0x80000000;
.text : { *(.text.init) *(.text) }
.rodata : { *(.rodata) }
.data : { *(.data) }
.bss : { *(.bss) }
. = ALIGN(16);
. += 0x1000; // 预留4KB栈空间
_stack_top = .;
}
# 构建命令
riscv64-unknown-elf-gcc -march=rv64gc -mabi=lp64d -nostdlib -ffreestanding \
-T link.ld -o firmware.elf start.S main.c
# 运行命令(QEMU)
qemu-system-riscv64 -machine virt -nographic -bios none -kernel firmware.elf
验证方法
运行上述QEMU命令后,观察QEMU监视器中的GPIO状态。在QEMU的virt机器中,GPIO0引脚电平变化会反映在虚拟设备状态中。可通过以下步骤验证:
- 启动QEMU后,在监视器(Ctrl+A然后按C进入)中执行
info qtree查看设备树,确认GPIO控制器存在。 - 使用
xp /4xw 0x10002000命令读取GPIO寄存器组,观察输出值寄存器(偏移0x04)的bit 0是否在0和1之间周期性变化。 - 若观察到bit 0以约0.5秒间隔交替变化,则说明LED闪烁功能正常,固件任务完成。
判定标准:输出值寄存器bit 0呈现周期性翻转,且翻转周期与代码中延时参数匹配(约1秒一个完整周期),即判定为通过。
预期行为说明
由于QEMU的virt机器没有真实LED外设,本案例通过读取GPIO输出值寄存器来间接观察LED状态。代码逻辑上,GPIO0输出电平以约1秒周期交替变化,对应LED以相同周期闪烁。若在QEMU监视器中观察到输出值寄存器bit 0周期性翻转,则证明固件正确执行了GPIO控制逻辑。