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

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

调试配置

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

image-20240124233037109

c_cpp_properties.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:

{
    // 使用 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 扩展,因此我集中精力于对于指令集的解码、执行、写回过程,对于取指过程不同扩展区别不大暂不过多关注。

程序执行流程跟踪分析:

//main.cpp

int main(int argc, char *argv[]) {
    //省略一系列预配置,在 machine.play() 函数中执行程序
    machine.play();
    
}
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
    }
}
//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。单条指令解码由

//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等信息,并返回
}
//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 扩展逐层实现。

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 指令开始实现。先达成能成功识别指令的目标。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/351508.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

21.云原生之GitLab pipline语法实战(CI基础)

云原生专栏大纲 文章目录 gitlab-ci.yml 介绍GitLab中语法检测gitlab-ci.yml 语法job定义作业before_script和after_scriptstages定义阶段tages指定runnerallow_failure运行失败when控制作业运行retry重试timeout超时parallel并行作业only & exceptrulescache 缓存cache:p…

ETL能实现什么流程控制方式?

随着大数据时代的到来&#xff0c;数据处理工具成为各个行业中不可或缺的一部分。运用数据处理工具&#xff0c;能够大幅度帮助开发人员进行数据处理等工作&#xff0c;以及能够更好的为企业创造出有价值的数据。那在使用ETL工具时&#xff0c;我们往往会通过ETL平台所携带的组…

萝卜大杂烩 | 一篇文章扫盲Python、NumPy 和 Pandas,建议收藏!(适合初学者、python入门)

本文来源公众号“萝卜大杂烩”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;长文预警&#xff0c;一篇文章扫盲Python、NumPy 和 Pandas&#xff0c;建议收藏慢慢看 Python作为简单易学的编程语言&#xff0c;想要入门还是比较容…

2、鼠标事件、键盘事件、浏览器事件、监听事件、冒泡事件、默认事件、属性操作

一、鼠标事件 1、单击事件&#xff1a;onclick <body><header id"head">我是头部标签</header> </body> <script> var head document.getElementById("head")head.onclick function () {console.log("我是鼠标单击…

单片机设计_智能蓝牙电子秤(51单片机、HX711AD)

想要更多项目私wo!!! 一、电路设计 智能蓝牙电子称由51单片机、HX711AD称重模块、HC-05蓝牙模块、LCD1602等电路组成硬件部分,然后上传至APP。 二、运行结果 三、部分代码 #include "main.h" #include "HX711.h" #include "uart.h" #include …

podman+centos和docker+alpine中作性能对比遇到的问题及解决

1.dockeralpine中遇到这个问题 这是由于缺少相关的配置和依赖造成的 通过以下命令在alpine中安装相关配置 apk add --no-cache build-base cairo-dev cairo cairo-tools jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev 2.alpine中python找…

API网关-Apisix RPM包方式自动化安装配置教程

文章目录 前言一、简介1. etcd简介2. APISIX简介3. apisix-dashboard简介 二、Apisix安装教程1. 复制脚本2. 增加执行权限3. 执行脚本4. 浏览器访问5. 卸载Apisix 三、命令1. Apisix命令1.1 启动apisix服务1.2 停止apisix服务1.3 优雅地停止apisix服务1.4 重启apisix服务1.5 重…

【云原生】认识docker容器操作命令

目录 一、容器操作命令 1、创建容器 2、删除容器以及停止容器运行 3、查看容器的运行状态 4、查看容器的详细信息 5、将容器的文件传输到宿主机以及将宿主机的文件传输到容器中 6、批量删除容器 7、进入容器 二、容器的迁移 1、先在容器中创建测试文件 2、将容器存储…

洛谷 P5635 【CSGRound1】天下第一

原址链接 P5635 【CSGRound1】天下第一 先看标签 搜索&#xff1f;模拟&#xff1f;用不着这么复杂 创建函数a(int x,int y,int p) a(int x,int y,int p){if(x<0){return 1;}x (xy)%p;if(y<0){return 2;}y (xy)%p;return a(x,y,p); }写入主函数 #include<iostrea…

防御保护----防火墙的安全策略、NAT策略实验

实验拓扑&#xff1a; 实验要求&#xff1a; 1.生产区在工作时间&#xff08;9&#xff1a;00-18&#xff1a;00&#xff09;内可以访问DMZ区&#xff0c;仅可以访问http服务器&#xff1b; 2.办公区全天可以访问DMZ区&#xff0c;其中10.0.2.10可以访问FTP服务器和HTTP服务器…

Flink实现数据写入MySQL

先准备一个文件里面数据有&#xff1a; a, 1547718199, 1000000 b, 1547718200, 1000000 c, 1547718201, 1000000 d, 1547718202, 1000000 e, 1547718203, 1000000 f, 1547718204, 1000000 g, 1547718205, 1000000 h, 1547718210, 1000000 i, 1547718210, 1000000 j, 154771821…

Windows Server 安装 Docker

一、简介 Docker 不是一个通用容器工具&#xff0c;它依赖运行的 Linux 内核环境。Docker 实质上是在运行的 Linux 服务器上制造了一个隔离的文件环境&#xff0c;所以它执行的效率几乎等同于所部署的 Linux 主机服务器性能。因此&#xff0c;Docker 必须部署在 Linux 内核系统…

【保驾护航】HarmonyOS应用开发者基础认证-题库

通过系统化的课程学习&#xff0c;熟练掌握DevEco Studio&#xff0c;ArkTS&#xff0c;ArkUI&#xff0c;预览器&#xff0c;模拟器&#xff0c;SDK等HarmonyOS应用开发的关键概念&#xff0c;具备基础的应用开发能力。 考试说明 1、考试需实名认证&#xff0c;请在考前于个…

【LeetCode: 135. 分发糖果 + 贪心】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

嵌入式-stm32-江科大-OLED调试工具

文章目录 一&#xff1a;OLED调试工具1.1 OLED显示屏介绍1.2 实验&#xff1a;在OLED显示屏的使用1.3 自己新增功能测试道友&#xff1a;今天没有开始的事&#xff0c;明天绝不会完成。 一&#xff1a;OLED调试工具 1.1 OLED显示屏介绍 学习任何一门语言就需要进行调试&#…

Java基础进阶03-注解和单元测试

目录 一、注解 1.概述 2.作用 3.自定义注解 &#xff08;1&#xff09;格式 &#xff08;2&#xff09;使用 &#xff08;3&#xff09;练习 4.元注解 &#xff08;1&#xff09;概述 &#xff08;2&#xff09;常见元注解 &#xff08;3&#xff09;Target &#x…

第13次修改了可删除可持久保存的前端html备忘录:删除按钮靠右,做了一个背景主题:现代深色

第13次修改了可删除可持久保存的前端html备忘录&#xff1a;删除按钮靠右&#xff0c;做了一个背景主题&#xff1a;现代深色 备忘录代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"vi…

LFU算法

LFU算法 Least Frequently Used&#xff08;最不频繁使用&#xff09; Leetcode有原题&#xff0c;之前手写过LRU&#xff0c;数据结构还是习惯于用java实现&#xff0c;实现是copy的评论题解。 题解注释写的很清楚 大致就是说LFUCache类维护一个存放node的map&#xff0c;同…

立创EDA学习:设计收尾工作

布线整理 ShiftM&#xff0c;关闭铺铜显示 调整结束后再使用快捷键”ShiftM“打开铺铜 过孔 在空白区域加上一些GND过孔&#xff0c;连接顶层与底层的铺铜。放置好”过孔“后&#xff0c;隐藏铺铜&#xff0c;观察刚才放置的过孔有没有妨碍到其他器件 调整铺铜 先打开铺铜区&…

php mysql字段默认值使用问题

前提是使用了事务&#xff0c;在第一个阶段 是A表操作保存&#xff0c;第二阶段操作B表&#xff0c;操作B表的时候使用了A表的一个字段&#xff0c;这个字段在第一阶段没有设置值&#xff0c;保存的时候使用字段默认值。 【这种情况 最好是在第一阶段 把后面要使用的字段设置好…