QtRVSim(二)一个 RISC-V 程序的解码流程
继上一篇文章简单代码分析后,本文主要调研如何实现对指令的解析运行。
调试配置
使用 gdb 工具跟踪调试运行。
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.cppint main(int argc, char *argv[]) {//省略一系列预配置,在 machine.play() 函数中执行程序machine.play();}
void Machine::play() {//set status//start execute each instructionsstep_internal(true);
}void Machine::step_internal(bool skip_break) {//set statustry {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) {//pcstate.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.cppDecodeState 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.cppvoid 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}, // LOADIM_UNKNOWN, // LOAD-FPIM_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-32IM_UNKNOWN, // 48b{"store", IT_I, NOALU, NOMEM, STORE_map, {}, 0x23, 0x7f, { .subfield = {3, 12} }, nullptr}, // STOREIM_UNKNOWN, // STORE-FPIM_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-32IM_UNKNOWN, // 64bIM_UNKNOWN, // MADDIM_UNKNOWN, // MSUBIM_UNKNOWN, // NMSUBIM_UNKNOWN, // NMADDIM_UNKNOWN, // OP-FPIM_UNKNOWN, // reservedIM_UNKNOWN, // custom-2/rv128IM_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}, // JALRIM_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}, // SYSTEMIM_UNKNOWN, // reservedIM_UNKNOWN, // custom-3/rv128IM_UNKNOWN, // >= 80b
};
下一步目标:从最基础的 F load, F store 指令开始实现。先达成能成功识别指令的目标。
相关文章:

QtRVSim(二)一个 RISC-V 程序的解码流程
继上一篇文章简单代码分析后,本文主要调研如何实现对指令的解析运行。 调试配置 使用 gdb 工具跟踪调试运行。 c_cpp_properties.json 项目配置: {"name": "QtRvSim","includePath": ["${workspaceFolder}/**&quo…...

x-cmd pkg | httpx - 为 Python 设计的下一代 HTTP 客户端库
目录 简介首次用户功能特点进一步探索 简介 HTTPX 是一个为 Python 设计的下一代 HTTP 客户端库,由 Tom Christie 创建。它提供了同步和异步的 API,并支持 HTTP/1.1 和 HTTP/2 协议。与 Requests 库类似,但增加了对异步请求的支持和 HTTP/2 …...
代码随想录算法训练营第四十二天(动态规划篇)|62. 不同路径
62. 不同路径 题目链接:62. 不同路径 - 力扣(LeetCode) 思路 dp[i][j]: 从0到位置[i, j]共有dp[i][j]条路径。 dp[i][j] dp[i-1][j] dp[i][j-1] 到位置[i,j],可以从它的上面或者左边来,所以路径和为这两个方向的路…...

YOLO 全面回顾:从最初的YOLOv1到最新的YOLOv8、YOLO-NAS,以及整合了 Transformers 的 YOLO
YOLO 全面回顾 综述评估指标YOLO v1YOLO v2YOLO v3YOLO v4YOLOv5 与 Scaled-YOLOv4 YOLORYOLOXYOLOv6YOLOv7DAMO-YOLOYOLOv8PP-YOLO, PP-YOLOv2, and PP-YOLOEYOLO-NASYOLO with Transformers 综述 论文:https://arxiv.org/pdf/2304.00501.pdf 代码:gi…...
Android双指缩放ScaleGestureDetector检测放大因子大图移动到双指中心点ImageView区域中心,Kotlin(2)
Android双指缩放ScaleGestureDetector检测放大因子大图移动到双指中心点ImageView区域中心,Kotlin(2) 在 Android ScaleGestureDetector检测双指缩放Bitmap基于Matrix动画移动到双指捏合中心点ImageView区域中心,Kotlin-CSDN博客 …...

同为科技(TOWE)自动控制循环定时插座
随着科技的发展,智能化家居已成为我们生活的重要组成部分。作为国内领先的智能家居品牌,同为科技(TOWE)推出的自动控制循环定时插座,无疑将科技与生活完美地结合在一起。 1.外观设计 同为科技(TOWE&#x…...

游戏设计模式
单列模式 概念 单例模式是一种创建型设计模式,可以保证一个类只有一个实例,并提供一个访问该实例的全局节点。 优点 可以派生:在单例类的实例构造函数中可以设置以允许子类派生。受控访问:因为单例类封装他的唯一实例…...
CUBEMX与FreeRTOS在Arm Compiler 6下的配置方法
在嵌入式开发中,STM32是一种广泛使用的微控制器。为了提高开发效率,我们通常会利用ST公司提供的STM32CubeMX工具来配置硬件,并结合FreeRTOS这一实时操作系统来进行多任务处理。本文将深入探讨如何在这一框架下,使用Arm Compiler 6…...

Android Studio 提示Use app:drawableStartCompat instead of android:drawableStart
每次提交代码时,AS这个老妈子总爱唠叨一堆warning,这些Warning都在讲什么? 1.Use app:drawableStartCompat instead of android:drawableStart 在Android开发中,android:drawableStart和app:drawableStartCompat是两个用于设置…...

C# wpf 实现任意控件(包括窗口)更多调整大小功能
WPF拖动改变大小系列 第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小 第五章 拓展更多调整大小功能(本章) 文章目录 WPF拖动改变大小系列前言一、添加的功能1、任意控件Drag…...

Vue+OpenLayers7入门到实战:快速搭建Vue+OpenLayers7地图脚手架项目。从零开始构建Vue项目并整合OpenLayers7.5.2
返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 前言 本章针对Vue初学者,对Vue不熟悉,甚至还不会Vue的入门学生读者。 本章会详细讲解从NodeJS环境到npm环境的各个步骤,再到使用vue-cli脚手架快速生成项目,以及添加OpenLayers7地图库依赖,编写简单的xyz高德地图显示…...
mysql-线上常用运维sql
1.表备份 INSERT INTO table1 SELECT * FROM table2; 2.用一个表中的字段更新另一张表中的字段 UPDATE table2 JOIN table1 ON table2.id table1.id SET table2.column2 table1.column1; 3.在MySQL中,查询一个表的列字段值是否包含另一个表的字段,…...

Linux之进程间通信(system V 共享内存)
目录 一、共享内存 1、基本原理 2、共享内存的创建 3、共享内存的释放 4、共享内存的关联 5、共享内存的去关联 6、查看IPC资源 二、完整通信代码 三、共享内存的特点 四、信号量 1、相关概念 2、信号量概念 进程间通信的本质就是让不同的进程看到同一个资源。而前…...

数据库 sql select *from account where name=‘张三‘ 执行过程
select *from account where name张三分析上面语句的执行过程 用到了索引 由于是根据 1.name字段进行查询,所以先根据name张三’到name字段的二级索引中进行匹配查 找。但是在二级索引中只能查找到 Arm 对应的主键值 10。 2.由于查询返回的数据是*,…...

力扣日记1.27-【回溯算法篇】131. 分割回文串
力扣日记:【回溯算法篇】131. 分割回文串 日期:2023.1.27 参考:代码随想录、力扣 131. 分割回文串 题目描述 难度:中等 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可…...

如何用web界面打开华为防火墙
目录 1.创建一个虚拟网卡 2.cloud操作 3.防火墙上操作 4. 登录 1.创建一个虚拟网卡 2.cloud操作 3.防火墙上操作 4. 登录...

力扣20、有效的括号(简单)
1 题目描述 图1 题目描述 2 题目解读 给定的字符串只包含括号,判断这个字符串中的括号是否按照正确顺序出现,即这个字符串是否有效。 3 解法一:栈 C的STL中的stack,在解题时非常好用。 3.1 解题思路 使用栈stk,并枚举…...

Android 系统启动过程
当按下电源时,引导芯片代码会从预定义的地方(固化在ROM) 开始执行,加载引导程序BootLoader到RAM,然后执行。 启动内核的第一个进程idle(pid0),idle进程是Linux系统第一个进程,是init进程和kthreadd进程的父进程。 idle的主要作用 初始化进程以及内存管…...

基于STM32的智能手环设计与实现
需要原理图工程,源码,PCB工程的朋友收藏,这篇文章关注我,私我吧!!! 基于STM32的智能手环设计与实现 摘要一、研究背景及意义二、实现功能三、系统方案设计系统方案设计框图3.1 单片机芯片选择3…...

[BJDCTF2020]The mystery of ip
hint 猜测ip和XFF有关 加一个XFF 下面这一步是看了wp出来的:存在ssti 这里尝试用jinja的注入方法,页面回显了是php的smarty框架 查了一下smarty的注入方法,发现可以直接执行php命令 在根目录找到flag...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor
1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...
第22节 Node.js JXcore 打包
Node.js是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。 JXcore是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。 本文主要介绍JXcore的打包功能。 JXcore 安装 下载JXcore安装包&a…...

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题
20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题 2025/6/9 20:54 缘起,为了跨网段推流,千辛万苦配置好了网络参数。 但是命令iptables -t filter -F tetherctrl_FORWARD可以在调试串口/DEBUG口正确执行。…...