动手写riscv emulator(一)
CPU是计算机中最重要的核心部件之一。模拟器除了要模拟CPU之外,还需要模拟其他的部件,才能够让模拟器运行起来,例如:dram、bus等等。这个章节我们要完成以下两个目标:
-
先把模拟器的代码结构和编译搭建起来
-
能够把可执行文件转换成二进制文件,并读取二进制文件内容
本文使用的是vscode + gcc + gdb进行编译和调试,其中使用vscode + gdb在vscode上进行断点调试。编译使用的是CMake + gcc的方式。vscode + gdb进行调试需要配置vscode,可以参考gdb远程调试 - cockpunctual - 博客园,只需要参考该博客的第二个小节:配置vscode。
使用C++面向对象的思想来写这个CPU模拟器。
1 类定义
定义一个CPU类,负责指定代码:
class CPU {
public:
CPU(std::vector<char>& code);
void decode();
private:
std::vector<uint64_t> int_reg;
std::vector<char> code;
uint64_t pc;
uint64_t csrs;
};
decode函数是负责解析指令的,本章节还不涉及,暂时把读取到的指令输出出来:
CPU::CPU(std::vector<char>& code)
{
this->code = code;
}
void CPU::decode()
{
for(char instruction : this->code) {
printf("0x%x\n", (unsigned char)instruction);
}
}
2 读取文件
这里要解决的是前面提到的第二个目标。我们的测试用例是把下面的汇编代码转换成二进制文件读取出来:
addi x29, x0, 5
addi x30, x0, 37
add x31, x30, x29
上面的测试用例放在test/add/addi.s。我们需要对上面的汇编代码进行转换:
riscv64-unknown-linux-gnu-gcc -Wl,-Ttext=0x80000000 -nostdlib -o addi.elf addi.s
riscv64-unknown-linux-gnu-objcopy -O binary addi.elf addi.bin
上面的命令:
-
把addi.s文件编译成elf文件
-
把elf文件中指令部分拷贝出来存放到addi.bin文件中
编译完bin文件后,我们接下来的操作就是要把二进制文件内容读取出来,为后续的章节做好铺垫。
std::vector<char> read_file(const std::string path)
{
std::vector<char> buf;
std::streampos file_size;
std::ifstream file(path, std::ios::in | std::ios::binary);
if (!file.is_open()) {
std::cout << path << " is not opened." << std::endl;
return buf;
}
file.seekg(0, std::ios::end);
file_size = file.tellg();
file.seekg(0, std::ios::beg);
buf.resize(file_size);
file.read(buf.data(), file_size);
file.close();
return buf;
}
3 测试
我们在main函数中定义CPU对象,并且读取bin文件,对bin文件内容进行打印:
int main()
{
std::cout << "riscv emu" << std::endl;
std::vector<char> code = read_file("../test/add/addi.bin");
CPU riscv_cpu(code);
riscv_cpu.decode();
return 0;
}
到这里第一章节的内容就完成了,后续我们需要继续添加其他的部件(如dram、bus等)。

浙公网安备 33010602011771号