我使用 UART 在 RISC-V qemu 上制作了一个 helloworld 程序,使用 https://popovicu.com/posts/bare-metal-programming-risc-v/ Uros Popovicu 的 RISC-V 汇编指南。我想知道如何使用 c 和 RISC-V qemu 写入 UART。 我试过了:
#define UART_TX 0x10000000
void _start() {
char *str = "Hello, World!\n";
for (int i = 0; str[i] != '\0'; ++i) {
*(volatile char*)UART_TX = str[i];
}
while (1);
}
我尝试编译c代码:
riscv64-linux-gnu-gcc -S -o hello.s hello.c
riscv64-linux-gnu-as -march=rv64i -mabi=lp64 -o hello.o -c hello.s
到 RISC-V 并使用与 popovicu 示例中相同的链接器文件:
riscv64-linux-gnu-ld -T linker.ld --no-dynamic-linker -m elf64lriscv -static -nostdlib -s -o hello hello.o
使用以下链接器文件(仅进行一些细微修改):
ENTRY(_start)
MEMORY {
dram_space (rwx) : ORIGIN = 0x80000000, LENGTH = 1024
}
SECTIONS {
.text : {
hello.o(.text.bios)
} > dram_space
}
并使用以下命令运行它:
qemu-system-riscv64 -machine virt -bios hello
这会导致不打印任何内容。这是访问 UART 的正确方法还是我的错误出在其他地方?预先感谢。
您尝试做的问题是您没有正确设置 C 可以使用的环境。
_start
符号通常总是在汇编中。
特别是,使用 GDB 附加到 QEMU,我看到堆栈指针
sp
仍将具有默认的 0x0
值。 C 代码依赖于有效内存中的堆栈,并且系统在程序的第二条指令处崩溃,该指令尝试写入堆栈上的区域。
使用
-bios
QEMU 标志,您表示您将完成所有设置工作(除了将 HART ID 加载到 a0
然后跳转到您的代码的一些指令之外。)您需要从汇编开始,设置一个可用的环境(包括加载具有所需值的寄存器),然后您可以跳转到 C 代码。
顺便说一句,所有内容都在您链接到的文章中汇编程序的
.text.bios
部分中。 C 程序的情况并非如此,您应该使用较少的链接器脚本。虽然链接了正确的部分,但 hello.o(.text.bios)
并没有按照您的想法进行操作。