600 行 ANSI C 代码实现 RISC-V CPU 核
600 行 ANSI C 代码实现 RISC-V CPU 核今天在 GitHub 上看到一个 C 语言项目,用大约 600 行代码实现了一个 RISC-V CPU 核,甚为感叹,分享一下。不管是学习 C,还是学习 RISC-V,这个项目都有非常高的学习价值,开源万岁!
rv用 ANSI C 编写的 RISC-V CPU 内核。特征:
[*]RV32IMC 用户级实现
[*]通过 riscv 测试中所有支持的测试
[*]~600 行代码
[*]不使用任何大于 32 位的整数类型,即使对于乘法也是如此
[*]简单 API(两个函数,加上您提供的两个内存回调函数)
[*]无内存分配
应用程序接口/* Memory access callbacks: data is input/output, return RV_BAD on fault, 0 otherwise */
typedef rv_res (*rv_store_cb)(void *user, rv_u32 addr, rv_u8 data);
typedef rv_res (*rv_load_cb)(void *user, rv_u32 addr, rv_u8 *data);
/* Initialize CPU. */
void rv_init(rv *cpu, void *user, rv_load_cb load_cb, rv_store_cb store_cb);
/* Single-step CPU. Returns 0 on success, one of RV_E* on exception. */
rv_u32 rv_step(rv *cpu);
用法#include <stdio.h>
#include <string.h>
#include "rv.h"
rv_res load_cb(void *user, rv_u32 addr, rv_u8 *data) {
if (addr - 0x80000000 > 0x10000) /* Reset vector is 0x80000000 */
return RV_BAD;
*data = ((rv_u8 *)(user));
return RV_OK;
}
rv_res store_cb(void *user, rv_u32 addr, rv_u8 data) {
if (addr - 0x80000000 > 0x10000)
return RV_BAD;
((rv_u8 *)(user)) = data;
return RV_OK;
}
rv_u32 program = {
/* _start: */
0x02A88893, /* add a7, a7, 42 */
0x00000073/* ecall */
};
int main(void) {
rv_u8 mem;
rv cpu;
rv_init(&cpu, (void *)mem, &load_cb, &store_cb);
memcpy((void *)mem, (void *)program, sizeof(program));
while (rv_step(&cpu) != RV_EECALL) {
}
printf("Environment call @ %08X: %u\n", cpu.pc, cpu.r);
return 0;
}
为rv编译程序
使用 riscv-gnu-toolchain 工具链和 rv 链接脚本。建议使用gcc命令行:riscv64-unknown-elf-gcc example.S -nostdlib -nostartfiles -Tlink.ld -march=rv32imc -mabi=ilp32 -o example.o -e _start -g -no-pie
然后用 obj 工具将0x80000000起始的二进制代码生成能被rv加载的二进制文件:riscv64-unknown-elf-objcopy -g -O binary example.o example.bin
支持的指令列表
参见 支持指令列表。
参考资料
rv: https://github.com/mnurzia/rv
riscv-gnu-toolchain: https://github.com/riscv-collab/riscv-gnu-toolchain
rv 链接脚本: https://github.com/mnurzia/rv/blob/main/tools/link.ld
支持指令列表: https://github.com/mnurzia/rv#instruction-list
页:
[1]