QtRVSim(二)一个 RISC-V 程序的解码流程

继上一篇文章简单代码分析后,本文主要调研如何实现对指令的解析运行。

调试配置

使用 gdb 工具跟踪调试运行。

c_cpp_properties.json 项目配置:

json 复制代码
{
      "name": "QtRvSim",
      "includePath": [
          "${workspaceFolder}/**"
      ],
      "defines": [
      ],
      "compilerPath": "/usr/bin/clang",
      "cStandard": "c11",
      "cppStandard": "c++17",
      "intelliSenseMode": "gcc-x64",
      "configurationProvider": "ms-vscode.cmake-tools"
    }

launch.json:

json 复制代码
{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。`                                                          
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch",
            "type": "cppdbg",
            "request": "launch",
            // "program": "${fileDirname}/${fileBasenameNoExtension}",
            "program": "${command:cmake.launchTargetPath}",
            "args": ["/home/jingqing3948/Develop/qtrvsim-test/sum2vars-riscv"],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [
                {
                    "name": "PATH",
                    "value":"${env:PATH):${command:cmake.getLaunchTargetDirectory]"
                }
            ],
            "externalConsole": true,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            //"preLaunchTask": "Tutorial",  
            "miDebuggerPath": "/usr/bin/gdb"
        }
    ]
}

程序解析流程

由于我的毕设任务主要是实现 F 扩展,因此我集中精力于对于指令集的解码、执行、写回过程,对于取指过程不同扩展区别不大暂不过多关注。

程序执行流程跟踪分析:

c 复制代码
//main.cpp

int main(int argc, char *argv[]) {
    //省略一系列预配置,在 machine.play() 函数中执行程序
    machine.play();
    
}
c 复制代码
void Machine::play() {
    //set status
    
    //start execute each instructions
    step_internal(true);
}

void Machine::step_internal(bool skip_break) {
    //set status
    
    try {
        QTime start_time = QTime::currentTime();
        do {
            //execute one step 
            cr->step(skip_break);
        } while (time_chunk != 0 && stat == ST_BUSY && !skip_break
                 && start_time.msecsTo(QTime::currentTime()) < (int)time_chunk);
    } catch (SimulatorException &e) {
        //set trapped status
    }
    if (regs->read_pc() >= program_end) {
        //set exit status
    } else {
        //set status as the previous status
    }
}
c 复制代码
//core.cpp

//step(): wrapper of do_step() function
void Core::step(bool skip_break) {
    //pc
    state.cycle_count++;
    do_step(skip_break);
    emit step_done(state);
}

void CoreSingle::do_step(bool skip_break) {
    Pipeline &p = state.pipeline;

    p.fetch = fetch(pc_if, skip_break);
    p.decode = decode(p.fetch.final);
    p.execute = execute(p.decode.final);
    p.memory = memory(p.execute.final);
    p.writeback = writeback(p.memory.final);

    regs->write_pc(mem_wb.computed_next_inst_addr);

    if (mem_wb.excause != EXCAUSE_NONE) {
        handle_exception(
            mem_wb.excause, mem_wb.inst, mem_wb.inst_addr, regs->read_pc(), prev_inst_addr,
            mem_wb.mem_addr);
        return;
    }
    prev_inst_addr = mem_wb.inst_addr;
}

由此可见,对于F扩展的实现,除去新硬件的实现,主要需要补充的就是 Pipeline.decode() , Pipeline.execute() , Pipeline.memory() , Pipeline.writeback() 以上内容。

首先我需要先实现 F 指令集的 decode。单条指令解码由

c 复制代码
//core.cpp

DecodeState Core::decode(const FetchInterstage &dt) {
    InstructionFlags flags;
    bool w_operation = this->xlen != Xlen::_64;
    AluCombinedOp alu_op {};
    AccessControl mem_ctl;
    ExceptionCause excause = dt.excause;

    // decode后把结果存在flags alu_op mem_ctl结构体里,也就是说指令解码具体实现由 instructions.cpp 的 flags_alu_op_mem_ctl 函数实现
    dt.inst.flags_alu_op_mem_ctl(flags, alu_op, mem_ctl);

    //flags结构体通过位域操作识别rs rd op等信息,并返回
}
c 复制代码
//instructions.cpp

void Instruction::flags_alu_op_mem_ctl(
    InstructionFlags &flags,
    AluCombinedOp &alu_op,
    AccessControl &mem_ctl) const {
    //通过 InstructionMapFind 函数查找指令表
    const struct InstructionMap &im = InstructionMapFind(dt);
    flags = (enum InstructionFlags)im.flags;
    alu_op = im.alu;
    mem_ctl = im.mem_ctl;
}

static inline const struct InstructionMap &InstructionMapFind(uint32_t code) {
    const struct InstructionMap *im = &C_inst_map[instruction_map_opcode_field.decode(code)];
    //递归解码,和指令集在程序中的存储方式有关
    while (im->subclass != nullptr) {
        im = &im->subclass[im->subfield.decode(code)];
    }
    if ((code ^ im->code) & im->mask) {
        return C_inst_unknown;
    }
    return *im;
}

也就是说对于一条指令,首先需要根据 instruction_map_opcode_field.decode(code) 判断其操作码类型,然后用对应操作码的解码表 C_inst_map 元素进行解码。并且解码是递归进行的,因为解码表的存储方式是递归存储(先大类后小类,比如 I_inst_map 下的 load 指令全部属于 LOAD_map ,包括 lb lh lw 等。)

C_inst_map 解码表目前只包含了 i 扩展的指令集,对于 F 扩展实现从 C_inst_map 开始参照 I 扩展逐层实现。

c 复制代码
static const struct InstructionMap C_inst_map[] = {
    IM_UNKNOWN,
    IM_UNKNOWN,
    IM_UNKNOWN,
    {"i", IT_UNKNOWN, NOALU, NOMEM, I_inst_map, {}, 0x3, 0x3, { .subfield = {5, 2} }, nullptr},
};

static const struct InstructionMap I_inst_map[] = {
    {"load", IT_I, NOALU, NOMEM, LOAD_map, {}, 0x03, 0x7f, { .subfield = {3, 12} }, nullptr}, // LOAD
    IM_UNKNOWN, // LOAD-FP
    IM_UNKNOWN, // custom-0
    {"misc-mem", IT_I, NOALU, NOMEM, MISC_MEM_map, {}, 0x0f, 0x7f, { .subfield = {3, 12} }, nullptr}, // MISC-MEM
    {"op-imm", IT_I, NOALU, NOMEM, OP_IMM_map, {}, 0x13, 0x7f, { .subfield = {3, 12} }, nullptr}, // OP-IMM
    {"auipc", IT_U, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {"d", "u"}, 0x17, 0x7f, { .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE | IMF_PC_TO_ALU }, nullptr}, // AUIPC
    {"op-imm-32", IT_I, NOALU, NOMEM, OP_IMM_32_map, {}, 0x1b, 0x7f, { .subfield = {3, 12} }, nullptr}, // OP-IMM-32    IM_UNKNOWN, // OP-IMM-32
    IM_UNKNOWN, // 48b
    {"store", IT_I, NOALU, NOMEM, STORE_map, {}, 0x23, 0x7f, { .subfield = {3, 12} }, nullptr}, // STORE
    IM_UNKNOWN, // STORE-FP
    IM_UNKNOWN, // custom-1
    {"amo", IT_R, NOALU, NOMEM, AMO_map, {}, 0x2f, 0x7f, { .subfield = {3, 12} }, nullptr}, // OP-32
    {"op", IT_R, NOALU, NOMEM, OP_map, {}, 0x33, 0x7f, { .subfield = {1, 25} }, nullptr}, // OP
    {"lui", IT_U, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {"d", "u"}, 0x37, 0x7f, { .flags = IMF_SUPPORTED | IMF_ALUSRC | IMF_REGWRITE }, nullptr}, // LUI
    {"op-32", IT_R, NOALU, NOMEM, OP_32_map, {}, 0x3b, 0x7f, { .subfield = {1, 25} }, nullptr}, // OP-32
    IM_UNKNOWN, // 64b
    IM_UNKNOWN, // MADD
    IM_UNKNOWN, // MSUB
    IM_UNKNOWN, // NMSUB
    IM_UNKNOWN, // NMADD
    IM_UNKNOWN, // OP-FP
    IM_UNKNOWN, // reserved
    IM_UNKNOWN, // custom-2/rv128
    IM_UNKNOWN, // 48b
    {"branch", IT_B, NOALU, NOMEM, BRANCH_map, {}, 0x63, 0x7f, { .subfield = {3, 12} }, nullptr}, // BRANCH
    {"jalr", IT_I, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {"d", "o(s)"}, 0x67, 0x7f, { .flags =
IMF_SUPPORTED | IMF_REGWRITE | IMF_BRANCH_JALR | IMF_ALUSRC | IMF_ALU_REQ_RS }, inst_aliases_jalr}, // JALR
    IM_UNKNOWN, // reserved
    {"jal", IT_J, { .alu_op=AluOp::ADD }, NOMEM, nullptr, {"d", "a"}, 0x6f, 0x7f, { .flags =
IMF_SUPPORTED |
IMF_REGWRITE | IMF_JUMP | IMF_PC_TO_ALU | IMF_ALUSRC }, inst_aliases_jal}, // JAL
    {"system", IT_I, NOALU, NOMEM, SYSTEM_map, {}, 0x73, 0x7f, { .subfield = {3, 12} }, nullptr}, // SYSTEM
    IM_UNKNOWN, // reserved
    IM_UNKNOWN, // custom-3/rv128
    IM_UNKNOWN, // >= 80b
};

下一步目标:从最基础的 F load, F store 指令开始实现。先达成能成功识别指令的目标。

相关推荐
sinovoip2 天前
香蕉派开源社区联合进迭进空重磅打造: BPI‑SM10(K3-Com260) 和 K3 Pico‑ITX 计算机将于5月11日全球发货
人工智能·开源·risc-v
嵌入式小企鹅2 天前
RISC-V车规专委会成立、AI模型集中开源、半导体产能加速爬坡
人工智能·学习·ai·程序员·算力·risc-v·半导体
国科安芯2 天前
空间激光通信系统中抗辐射 MCU 芯片应用研究
单片机·嵌入式硬件·架构·risc-v·安全性测试
极创信息3 天前
信创领域五种主流CPU架构(X86 / ARM / RISC-V / MIPS / LoongArch)
java·arm开发·数据库·spring boot·mysql·软件工程·risc-v
嵌入式小企鹅4 天前
CPU需求变化、RISC-V安全方案、DeepSeek V4适配、太空算力动态
人工智能·驱动开发·华为·开源·算力·risc-v
国科安芯6 天前
商业航天与航空安全场景下抗辐射 MCU 选型、应用实践及发展趋势
单片机·嵌入式硬件·无人机·cocos2d·risc-v
国科安芯6 天前
空间辐射环境下抗辐射 MCU 可靠性机理及航空安全应用研究综述
单片机·嵌入式硬件·macos·无人机·cocos2d·risc-v
国科安芯6 天前
航空安全关键系统抗辐射 MCU 加固技术、工程实现与典型应用
单片机·嵌入式硬件·无人机·cocos2d·risc-v
Captain_Data7 天前
AI 12小时设计CPU完整解析:从219字到RISC-V内核的技术突破
人工智能·python·ai·大模型·芯片设计·risc-v
圆山猫8 天前
[RISCV] 用 Rust 写一个 RISC-V BootROM:从 QEMU 到真实硬件(2)
rust·risc-v