# qtrvsim test **Repository Path**: yunxiangluo/qtrvsim-test ## Basic Information - **Project Name**: qtrvsim test - **Description**: qtrvsim test project - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 4 - **Created**: 2023-11-21 - **Last Updated**: 2024-11-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # QtRvSim 使用手册和测试报告 本文档列出的安装和使用方法均经过测试验证,文中截图为验证后采集。 ## 1. QtRvSim简介 [QtRvSim](https://github.com/cvut/qtrvsim) 是一个GPL 3 协议图形化 RISC-V CPU 模拟器,其提供了基于 WebAssembly 的(实验性)版本,在浏览器中无须安装即可运行。除此之外,也提供基于 Qt 的版本,[GitHub release](https://github.com/cvut/qtrvsim/releases) 中包含了 macOS、Windows (mingw32)、AppImage 等多种构建。软件已在 Ubuntu [PPA](https://launchpad.net/~qtrvsimteam/+archive/ubuntu/ppa)、Arch Linux [AUR](https://aur.archlinux.org/pkgbase/qtrvsim) 和 [nixpkgs](https://github.com/NixOS/nixpkgs/blob/release-23.05/pkgs/applications/science/computer-architecture/qtrvsim/default.nix) 等上架。也可以从 [openSUSE Build Service](https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim) 获取各个发行版可用的软件包。QtRvSim由捷克技术大学开发,参考[Computer Architectures Education项目](http://comparch.edu.cvut.cz) 。 **可接受的二进制格式** - 模拟器接受为 RISC-V 目标编译的 ELF 静态链接可执行文件(`--march=rv64g`)。 - 模拟器会根据ELF文件头自动选择字节序。 - 仿真将根据 ELF 文件头以 XLEN=32 或 XLEN=32 执行。 - 支持 64 位 RISC-V ISA RV64IM 和 32 位 RV32IM ELF 可执行文件。 - 尚不支持压缩指令。 您可以使用专门的 RISC-V GCC/Binutils 工具链(`riscv32-elf`)或使用带有 [LLD](https://lld.llvm.org/) 的统一 Clang/LLVM 工具链来编译模拟代码。 如果您安装了 Clang,则不需要任何其他工具。 Clang 可以在 Linux、Windows、macOS 等上使用。 ![QtRvSim screenshot](data/screenshots/intro.webp) ## 2. QtRvSim的安装 ### 2.1 基于源码安装 依赖: * Qt 5(最低测试版本为5.9.5),实验性支持Qt 6 * elfutils(libelf 理论上也可以使用,但可能会出现一些问题) 1. 克隆官方项目`https://github.com/Pagerd/qtrvsim.git` ```bash git clone https://github.com/cvut/qtrvsim.git --depth=1 -b master cd qtrvsim cmake -DCMAKE_BUILD_TYPE=Release . make ``` 输入`cd target`进入可执行程序目录,输入`qtrvsim_cli`启动命令行,输入`qtrvsim_gui`启动图形化程序。 在 Linux 上,您可以使用包装器 Makefile 并在项目根目录中运行`make`。 它将创建一个构建目录并在其中运行 CMake。 可用的目标有`release` 和`debug`。 ![cmake_install](data/screenshots/cmake_install_0.png) ![cmake_install](data/screenshots/cmake_install_2.png) `cmake -DCMAKE_BUILD_TYPE=Release /path/to/qtrvsim`,其中`/path/to/qtrvsim`是该项目根目录的路径,测试中如上图使用了./qtrvsim,给出步骤中使用了当前目录`.`。 构建的二进制文件可以在构建目录(调用 cmake 的目录)的`target`目录中找到。 `-DCMAKE_BUILD_TYPE=Debug`构建开发版本。如果未提供构建类型,则默认为`debug`。 注: > 当未安装依赖时,按照文档,无法通过编译。报 `/usr/lib/qt5/bin/uic: error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory`。 ### 2.2 基于OBS构建软件包 1. 建立新的 debian changelog entry Debian 软件包通常需要这个步骤,当然也可以不做 可以在 extras/packaging/deb/debian/changelog 找到现有的 changelog ,最新版本为 1.9.5-1 建立新的 branch 来测试构建 ```bash git branch test_build git checkout test_build ``` 建立新的 entry ```bash EDITOR=vim bash extras/packaging/add-to-changelog.sh 0.9.5-1.1 ``` 屏幕反馈 ```bash Press enter to continue /usr/lib/git-core/git-gui: line 10: exec: wish: not found error: tag 'v0.9.5-1.1' not found. error: gpg failed to sign the data: gpg: directory '/home/hachi/.gnupg' created gpg: keybox '/home/hachi/.gnupg/pubring.kbx' created gpg: skipped "weilinfox ": No secret key [GNUPG:] INV_SGNR 9 weilinfox [GNUPG:] FAILURE sign 17 gpg: signing failed: No secret key error: unable to sign the tag The tag message has been left in .git/TAG_EDITMSG ``` 注意 ``EDITOR`` 环境变量不可为空。可以看到这个脚本依赖 ``git gui`` 来添加 tag 。在此测试的机器上并没有图形界面,所以会失败,但这并不影响新的 changelog entry 的添加。 直接提交 ```bash git add extras/packaging/deb/debian/changelog git commit -s -m "Edit changelog" git push --set-upstream origin test_build ``` 2. 运行 GitHub Workflow 从 GitHub Web 页面启用 Workflow ![](data/Run_workflow.png) release.yml 功能在于建立 OBS 构建软件包所需的文件,产物为 obs-package-bundle.zip ![](data/Workflow_artifacts.png) 解压 ```bash unzip obs-package-bundle.zip ``` 屏幕反馈 ```bash Archive: obs-package-bundle.zip inflating: appimage.yml inflating: PKGBUILD inflating: qtrvsim.spec inflating: qtrvsim_0.9.5-1.debian.tar.xz inflating: qtrvsim_0.9.5-1.dsc inflating: qtrvsim_0.9.5.orig.tar.xz ``` 可以看到其中包括打包 deb 、 rpm 、 pkg 和 Appimage 所需的配置 2. 使用 OBS 构建 由于 build.openeuler.openatom.cn 在上传软件包时报 ``HTTP Error 413: Request Entity Too Large`` ,这里使用 build.opensuse.org ```bash osc checkout home:weilinfox cd home:weilinfox osc mkpac qtrvsim_test ``` 复制所有文件到 ``qtrvsim_test`` 目录 ```bash osc add * osc commit ``` 屏幕反馈 ``` Sending meta data... Done. Sending qtrvsim_test Sending qtrvsim_test/appimage.yml Sending qtrvsim_test/PKGBUILD Sending qtrvsim_test/qtrvsim_0.9.5-1.debian.tar.xz Sending qtrvsim_test/qtrvsim_0.9.5-1.dsc Sending qtrvsim_test/qtrvsim_0.9.5.orig.tar.xz Sending qtrvsim_test/qtrvsim.spec Transmitting file data ...... Committed revision 1. ``` 打开 build.opensuse.org ,已经开始构建。 ![](data/OBS_build.png) 对所有形式的包进行构建测试,可以看到均可以构建成功 ![](data/OBS_success.png) 以 Appimage 为例查看构建产物 ![](data/OBS_artifacts.png) 在 Archlinux 测试 Appimage 和 Archlinux 软件包,能够正常使用 ![](data/Qtrvsim_run.png) ### 2.3 WebAssembly 版本构建 WASM 版本是由 GitHub actions 自动构建的。构建结果文件可以在 `Actions`/`` 的 `Artifacts` 部分找到文件 `target-wasm-Linux-qtx.xx.x`。下载这个文件并跳到部署章节。详细的构建流程可以在 `.github/workflows/cmake.yml` 找到。 #### 2.3.1 起步阶段 安装 `emsdk` 和 `aqt` (详细内容见下)。 使用项目根目录中准备好的辅助 make 工具。 ```shell make wasm # build make wasm-clean # clean wasm build make wasm-clean-deap # also clean cached external libraries - when changing compile settings, of when it just does not work ;) make wasm-install-qt # install appropriate qt for wasm using aqt ``` 这些命令的行为已在 `.dev-config.local.mk` 中根据本地系统进行了定制。该文件以模板形式提供,进一步的更改将被忽略。 #### 2.3.2 依赖 - WASM compiler (Emscripten/EMSDK) - WASM compiled Qt - bash compatible shell (not fish, unfortunately) 步骤: - 安装 `emsdk` 工具包。仅安装 Emscripten 编译器可能可行,但我不建议这样做。 - 选择工具包的版本并运行 `emsdk activate `。只要所有组件都是与兼容版本一起编译的,您可以自由选择版本。Qt5 分发版本是 使用版本 `1.39.8` 构建的。一些发行版提供了像 `qt5-wasm` 这样的软件包。必须使用相同的工具链编译项目,否则可能无法链接。 - 根据 activate 命令的输出建议,引用工具包。 - 下载或编译 Qt 为 wasm 。通常的做法是将其存储在 `/opt/` 。我使用了一个非官方的 Qt 下载器 [`aqtinstall`](https://pypi.org/project/aqtinstall/). - `python -m aqt install 5.12.2 linux desktop wasm_32 --outputdir "/opt/qt"` #### 2.3.3 构建 - 进入构建目录(我使用 `build/wasm` )并运行(根据需要替换路径并使用您喜欢的构建系统): ```shell emcmake cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/opt/qt/5.13.1/wasm_32/ -DCMAKE_FIND_ROOT_PATH=/opt/qt/5.13.1/wasm_32/ -Wno-dev -G Ninja ``` - 将生成的 `.js` 和 `.wasm` 文件以及 `data/wasm` 中的所有内容到 `target/wasm` 。其中包含加载器脚本、图标和其他支持文件。 #### 2.3.4 部署 - 将 `target/wasm` 中的文件移动到 Web 服务器。 - 备注:由于 CORS 的缘故, Web 服务器必须使用。 #### 2.3.5 技巧 如果你希望使用不兼容 bash 的 shell ,运行 bash ,引用文件,然后以 bash 的环境运行 shell 。 ### 2.4 基于二进制包的安装 #### 2.4.1 Ubuntu ```bash sudo add-apt-repository ppa:qtrvsimteam/ppa sudo apt update sudo apt install -y qtrvsim ``` ![cmake_install](data/screenshots/cmake_install_4.png) #### 2.4.2 Debian Unstable / 11 前往 [OBS](https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim) 获取软件包。 请注意下方的链接未必是最新。 1. Debian Unstable ```bash echo 'deb http://download.opensuse.org/repositories/home:/jdupak/Debian_Unstable/ /' | sudo tee /etc/apt/sources.list.d/home:jdupak.list curl -fsSL https://download.opensuse.org/repositories/home:jdupak/Debian_Unstable/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_jdupak.gpg > /dev/null sudo apt update sudo apt install qtrvsim ``` 2. Debian 11 ```bash echo 'deb http://download.opensuse.org/repositories/home:/jdupak/Debian_11/ /' | sudo tee /etc/apt/sources.list.d/home:jdupak.list curl -fsSL https://download.opensuse.org/repositories/home:jdupak/Debian_11/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/home_jdupak.gpg > /dev/null sudo apt update sudo apt install qtrvsim ``` #### 2.4.3 Arch Linux 从 [AUR](https://aur.archlinux.org/pkgbase/qtrvsim) 获取,可使用任意 AUR Helper。 ```bash yay -S qtrvsim 或 paru -S qtrvsim ``` 或者添加 OBS 软件源(以 root 用户执行): ```bash cat << "EOF" >> /etc/pacman.conf [home_jdupak_Arch] Server = https://download.opensuse.org/repositories/home:/jdupak/Arch/$arch EOF key=$(curl -fsSL https://download.opensuse.org/repositories/home:jdupak/Arch/$(uname -m)/home_jdupak_Arch.key) fingerprint=$(gpg --quiet --with-colons --import-options show-only --import --fingerprint <<< "${key}" | awk -F: '$1 == "fpr" { print $10 }') pacman-key --init pacman-key --add - <<< "${key}" pacman-key --lsign-key "${fingerprint}" pacman -Sy home_jdupak_Arch/qtrvsim ``` #### 2.4.4 Fedora ```bash dnf config-manager --add-repo https://download.opensuse.org/repositories/home:jdupak/Fedora_37/home:jdupak.repo dnf install qtrvsim ``` #### 2.4.5 Windows 从 [GitHub Release](https://github.com/cvut/qtrvsim/releases) 获取最新二进制 `qtrvsim-mingw32`,下载并解压。 #### 2.3.6 macOS 从 App Store 安装最新版本的 Xcode。 然后打开终端并执行`xcode-select --install`来安装命令行工具。 然后打开 Xcode,接受许可协议并等待它安装任何其他组件。 最终看到“欢迎使用 Xcode”屏幕后,从顶部栏中选择“Xcode -> 首选项 -> 位置 -> 命令行工具”,然后选择 SDK 版本。 安装 [Homebrew](https://brew.sh/) 并使用它来安装 Qt 和 libelf。 (安装 libelf 是可选的。如果系统中未找到 libelf,则使用本地回退.) ```shell brew install qt libelf ``` 现在以与常规编译相同的方式构建项目 ,参考`2.1 基于源码安装`。 #### 2.4.6 官方网页版(无需安装) `https://comparch.edu.cvut.cz/qtrvsim/app` 打开 [网页版](https://comparch.edu.cvut.cz/qtrvsim/app/)。 ## 3. QtRvSim测试 测试使用了官方文档测试使用的CTest,构建并运行测试,使用以下命令: ```bash cmake -DCMAKE_BUILD_TYPE=Release /path/to/qtRVSim make ctest ``` ![cmake_install](data/screenshots/cmake_install_1.png) 测试结论:官方提供了共9个测试用例,其中8个通过,1个失败,测试失败的为cli_stalls用例,如上图。 ## 4. QtRvSim使用方法 QtRvSim包括GUI图形化和CLI命令行工具,其中rv64只能在命令行工具下使用。 ### 4.1 GUI 图形化工具 QtRvSim 既可以在 GUI 使用,也可以在 CLI 使用。本文档GUI参考官方在线版应用[QtRVSim online](https://comparch.edu.cvut.cz/qtrvsim/app),功能如下图。 ![functions](data/functions.png) 也可以下载 GitHub release 中提供的版本,或者也可自行编译。构建时依赖 Qt5 和 elfutils,需要提前安装。以下以网页在线版演示。 打开 [QtRVSim online](https://comparch.edu.cvut.cz/qtrvsim/app),选择(默认的) `No pipeline no cache`,点击 `Start Empty`。 ![Wasm_GUI](data/Wasm_UI.png) 用户主界面各部分功能如下图 ![interface](data/interface.png) 本文使用程序自带的模板进行演示。点击 `File > Examples > simple-lw-sw-ia.S` ![example](data/example.png) 可以看到,模板程序是一个简单的存取操作 ```asm // Template file with simple memory example // QtRVSim simulator https://github.com/cvut/qtrvsim/ // // template-os.S - example file // // (C) 2021 by Pavel Pisa // e-mail: pisa@cmp.felk.cvut.cz // homepage: http://cmp.felk.cvut.cz/~pisa // work: http://www.pikron.com/ // license: public domain // Directives to make interesting windows visible #pragma qtrvsim show registers #pragma qtrvsim show memory .globl _start .option norelax .text _start: loop: // load the word from absolute address lw x2, 0x400(x0) // store the word to absolute address sw x2, 0x404(x0) // stop execution wait for debugger/user // break // ensure that continuation does not // interpret random data beq x0, x0, loop nop nop ebreak .data .org 0x400 src_val: .word 0x12345678 dst_val: .word 0 // Specify location to show in memory window #pragma qtrvsim focus memory src_val ``` 接下来,先点击右上角的箭头图标(Compile source and update memory);然后点击左上角的运行(Run)按钮。 ![download_compile](data/compile.png) 可以看到,程序开始按步骤执行: ![compile_run](data/run.png) 左侧 Program 窗口的字段分别为:Bp/Breakpoint(断点)、Address(指令地址)、Code(指令值)、Instruction(指令本身)。 右侧 memory 窗口中的值是对应内存中数据值,可以看到 `0x400` 处 和 `0x404` 处都被赋值为 `0x12345678`。 上方寄存器窗口存放了寄存器中存储的数据,作为临时存储的 `x2` 值被赋值为 `0x12345678`。 我们在第一条指令 `0x00000200` Bp 处双击打断点,这时可以看到 Core 中具体数据状态。 ![Bp](data/Bp.png) > 注意,可能需要调整左侧窗口大小才会显示出 Code 字段。 上方窗格显示了各寄存器状态。 `pc` 处是当前指令位置,`Instruction` 取得当前指令值, 上方 `Control Unit` 标明:当前指令为读取内存,读到寄存器中,且要对 `src` 地址做运算,因此开启了相应状态位。 `rs1 rs2 rd` 标明源寄存器和目标寄存器,`x0` 为 `rs1`,`x2` 为 `rd`。 立即数是指定 `x0` 偏移地址为 `0x400`。 在第二条指令 `0x00000204` 上打断点,此时 Core 状态如下: ![Core](data/Bp204.png) PC 值随着指令执行而逐渐增加,`Control Unit` 中的标志位标明:将数据写入内存,且要对 `src` 地址进行运算。 `x0` 为 `rs1`,`x0` 为 `rs2`,此指令不存在 `rd`。 可以看到寄存器中传输了 `0x12345678` 数据到 `Data Memory` 中。 立即数是指定 `x0` 偏移地址为 `0x404`。 重新在最后一条指令 `0x00000208` 上打断点,Core 执行状态如下: ![Bp208](data/Bp208.png) 主控单元标明此指令为一个判断跳转指令,`x0=0` 时跳转到 `loop` 处也就是 `0x00000200`。 这里立即数采用的是 `-8` 的补码表示,表示若判断成功应 `+(-8)`跳转到 `0x00000200` 处。 ### 4.2 CLI 命令行工具 下载 QtRvSim 二进制,或自行构建。 以下使用 Arch Linux 演示,系统已安装 RISC-V 工具链: ```bash sudo pacman -Sy gcc riscv64-elf-gcc riscv64-linux-gnu-gcc ``` QtRvSim 本体从 [AUR](https://aur.archlinux.org/pkgbase/qtrvsim) 或 [OBS](https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim) 获取。 ![CLI](data/CLI.png) #### 4.2.1 CLI 命令参数 ``` $ qtrvsim_cli --help Usage: qtrvsim_cli [options] FILE QtMips CLI machine simulator Options: -h, --help Displays help on commandline options. --help-all Displays help including Qt specific options. -v, --version Displays version information. --asm Treat provided file argument as assembler source. --pipelined Configure CPU to use five stage pipeline. --no-delay-slot Disable jump delay slot. --hazard-unit Specify hazard unit implementation [none|stall|forward]. --trace-fetch, --tr-fetch Trace fetched instruction (for both pipelined and not core). --trace-decode, --tr-decode Trace instruction in decode stage. (only for pipelined core) --trace-execute, --tr-execute Trace instruction in execute stage. (only for pipelined core) --trace-memory, --tr-memory Trace instruction in memory stage. (only for pipelined core) --trace-writeback, --tr-writeback Trace instruction in write back stage. (only for pipelined core) --trace-pc, --tr-pc Print program counter register changes. --trace-wrmem, --tr-wr Trace writes into memory. --trace-rdmem, --tr-rd Trace reads from memory. --trace-gp, --tr-gp Print general purpose register changes. You can use * for all registers. --dump-registers, --d-regs Dump registers state at program exit. --dump-cache-stats Dump cache statistics at program exit. --dump-cycles Dump number of CPU cycles till program end. --dump-range Dump memory range. --load-range Load memory range. --expect-fail Expect that program causes CPU trap and fail if it doesn't. --fail-match Program should exit with exactly this CPU TRAP. Possible values are I(unsupported Instruction), A(Unsupported ALU operation), O(Overflow/underflow) and J(Unaligned Jump). You can freely combine them. Using this implies expect-fail option. --d-cache Data cache. Format policy,sets,words_in_blocks,associativity where policy is random/lru/lfu --i-cache Instruction cache. Format policy,sets,words_in_blocks,associativity where policy is random/lru/lfu --read-time Memory read access time (cycles). --write-time Memory read access time (cycles). --burst-time Memory read access time (cycles). --serial-in, --serin File connected to the serial port input. --serial-out, --serout File connected to the serial port output. --os-emulation, --osemu Operating system emulation. --std-out, --stdout File connected to the syscall standard output. --os-fs-root, --osfsroot Emulated system root/prefix for opened files Arguments: FILE Input ELF executable file or assembler source ``` #### 4.2.2 实例分析1:求和函数 例如运行一个简单的求和函数(来源:[Introduction to the labs, compiler, simulator and data representations](https://cw.fel.cvut.cz/b222/courses/b35apo/en/tutorials/01/start)) 分别创建两个源码文件,并使用 RISC-V 工具链编译出目标文件。 sum2vars.c ```C //#include int var_a = 0x1234; int var_b = 0x2222; int var_c = 0x3333; int main() { var_c = var_a + var_b; //printf("sum %d + %d -> %d\n", var_a, var_b, var_c); //printf("sum 0x%x + 0x%x -> 0x%x\n", var_a, var_b, var_c); return 0; } ``` start.S ```asm .globl _start .text .option norelax _start: la x2, _end+0x4000 la x3, __global_pointer$ jal main ebreak ``` 接下来使用工具链进行编译: ```bash riscv64-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -c sum2vars.c -o sum2vars.o riscv64-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -c start.S -o start.o riscv64-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib sum2vars.o start.o -o sum2vars-riscv -lgcc ``` 然后可以使用 `qtrvsim_cli` 加载运行二进制: ```bash qtrvsim_cli sum2vars-riscv ``` 结果如下。 ![CLI_run](data/CLI_run.png) 可以看到程序执行完成。此外,可以开启 QtRvSim 自带的一些调试信息: ```bash qtrvsim_cli --trace-pc --trace-fetch --trace-wrmem --trace-rdmem --dump-cycles sum2vars-riscv ``` 打开取值、pc、内存读写、总周期数调试选项后,输出如下: ```bash $ qtrvsim_cli --trace-pc --trace-fetch --trace-wrmem --trace-rdmem --dump-cycles sum2vars-riscv [INFO] machine.ProgramLoader: Loaded executable: 32bit Fetch: auipc x2, 0x5 PC: 100cc Fetch: addi x2, x2, 36 PC: 100d0 Fetch: auipc x3, 0x2 PC: 100d4 Fetch: addi x3, x3, -2032 PC: 100d8 Fetch: jal x1, 0x10094 PC: 100dc Fetch: addi x2, x2, -16 PC: 10094 Fetch: sw x8, 12(x2) PC: 10098 MEM[150ec]: WR 0 Fetch: addi x8, x2, 16 PC: 1009c Fetch: lui x15, 0x11 PC: 100a0 Fetch: lw x14, 228(x15) PC: 100a4 MEM[110e4]: RD 1234 Fetch: lui x15, 0x11 PC: 100a8 Fetch: lw x15, 232(x15) PC: 100ac MEM[110e8]: RD 2222 Fetch: add x14, x14, x15 PC: 100b0 Fetch: sw x14, -2040(x3) PC: 100b4 MEM[110ec]: WR 3456 Fetch: addi x15, x0, 0 PC: 100b8 Fetch: addi x10, x15, 0 PC: 100bc Fetch: lw x8, 12(x2) PC: 100c0 MEM[150ec]: RD 0 Fetch: addi x2, x2, 16 PC: 100c4 Fetch: jalr x0, 0(x1) PC: 100c8 Machine stopped on BREAK exception. Machine state report: cycles: 20 stalls: 0 Fetch: ebreak PC: 100e0 ``` #### 4.2.3 模拟器功能验证 以一个简单的加法程序为例 ```c // sum2vars.c int var_a = 0x1234; int var_b = 0x5678; int var_c = 0x0000; int main() { var_c = var_a + var_b; var_a = var_c + var_b; return 0; } ``` 由于该程序只有一个 ``main()`` ,我们需要首先初始化 CPU 的状态,再跳转到 main() 函数入口 ```c // start.S .globl _start .text .option norelax _start: la x2, _end+0x4000 la x3, __global_pointer$ jal main ebreak ``` 构建所需的 ELF 裸机二进制 ```bash $ riscv64-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -c sum2vars.c -o sum2vars.o $ riscv64-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -c start.S -o start.o $ riscv64-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib sum2vars.o start.o -o sum2vars-riscv -lgcc ``` 验证一下确实可以能跑 ```bash $ qtrvsim_cli sum2vars-riscv [INFO] machine.ProgramLoader: Loaded executable: 32bit Machine stopped on BREAK exception. ``` 可以看到模拟器载入二进制并运行完毕,且没有报错 #### 4.2.4 追踪运行过程 为了方便功能演示,这里用汇编重写上面的加法程序 ```c // sum2vars.S .globl _start .text _start: lw x4, var_a(x0) lw x5, var_b(x0) add x6, x4, x5 sw x6, var_c(x0) add x4, x6, x5 sw x4, var_a(x0) addi x2, x0, 0 jr ra .data .org 0x400 var_a: .word 0x1234 var_b: .word 0x5678 var_c: .word 0x0000 ``` 可以使用 ``--asm`` 参数指定直接运行汇编文件而不是二进制 ```bash $ qtrvsim_cli --asm sum2vars.S Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 $ echo $? 1 ``` 出现了报错,使用 ``--trace-pc`` 查看程序寄存器的变化 ```bash $ qtrvsim_cli --asm --trace-pc sum2vars.S PC: 200 PC: 204 PC: 208 PC: 20c PC: 210 PC: 214 PC: 218 PC: 21c Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 同时使用 ``--trace-fetch`` 查看取指 ```bash qtrvsim_cli --asm --trace-pc --trace-fetch sum2vars.S Fetch: lw x4, 1024(x0) PC: 200 Fetch: lw x5, 1028(x0) PC: 204 Fetch: add x6, x4, x5 PC: 208 Fetch: sw x6, 1032(x0) PC: 20c Fetch: add x4, x6, x5 PC: 210 Fetch: sw x4, 1024(x0) PC: 214 Fetch: addi x2, x0, 0 PC: 218 Fetch: jalr x0, 0(x1) PC: 21c Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到我们关心的指令都有被正常运行。 下面使用 ``--trace-gp '*'`` 查看所有寄存器的变化 ```bash $ qtrvsim_cli --asm --trace-pc --trace-gp '*' sum2vars.S PC: 200 GP 4: 1234 PC: 204 GP 5: 5678 PC: 208 GP 6: 68ac PC: 20c PC: 210 GP 4: bf24 PC: 214 PC: 218 GP 2: 0 PC: 21c GP 0: 220 Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到两次加法过后 6 号寄存器和 4 号寄存器分别发生的变化 最后使用 ``--trace-wrmem`` 和 ``--trace-rdmem`` 追踪数据与存储器的交互情况 ```bash $ qtrvsim_cli --asm --trace-pc --trace-wrmem --trace-rdmem sum2vars.S PC: 200 MEM[400]: RD 1234 PC: 204 MEM[404]: RD 5678 PC: 208 PC: 20c MEM[408]: WR 68ac PC: 210 PC: 214 MEM[400]: WR bf24 PC: 218 PC: 21c Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到 ``lw`` 指令和 ``sw`` 指令从/向内存特定的位置读取/写入了数据 #### 4.2.4 五级流水线模式 使用 ``--pipelined`` 参数切换到五级流水线模式,有四个选项 ``--trace-decode`` 、 ``--trace-execute`` 、 ``--trace-memory`` 和 ``--trace-writeback`` 仅用于该模式 ```bash qtrvsim_cli --asm --pipelined --trace-fetch --trace-decode --trace-execute --trace-memory --trace-writeback sum2vars.S Fetch: nop Decode: nop Memory: nop Writeback: nop Fetch: nop Decode: nop Memory: nop Writeback: nop Fetch: nop Decode: nop Memory: nop Writeback: nop Fetch: nop Decode: nop Memory: nop Writeback: nop Fetch: lw x4, 1024(x0) Decode: lw x4, 1024(x0) Memory: lw x4, 1024(x0) Writeback: lw x4, 1024(x0) Fetch: lw x5, 1028(x0) Decode: lw x5, 1028(x0) Memory: lw x5, 1028(x0) Writeback: lw x5, 1028(x0) Fetch: nop Decode: nop Memory: nop Writeback: nop Fetch: add x6, x4, x5 Decode: add x6, x4, x5 Memory: add x6, x4, x5 Writeback: add x6, x4, x5 Fetch: sw x6, 1032(x0) Decode: sw x6, 1032(x0) Memory: sw x6, 1032(x0) Writeback: sw x6, 1032(x0) Fetch: add x4, x6, x5 Decode: add x4, x6, x5 Memory: add x4, x6, x5 Writeback: add x4, x6, x5 Fetch: sw x4, 1024(x0) Decode: !sw x4, 1024(x0) Memory: sw x4, 1024(x0) Writeback: sw x4, 1024(x0) Fetch: addi x2, x0, 0 Decode: addi x2, x0, 0 Memory: addi x2, x0, 0 Writeback: addi x2, x0, 0 Fetch: jalr x0, 0(x1) Decode: jalr x0, 0(x1) Memory: jalr x0, 0(x1) Writeback: jalr x0, 0(x1) Fetch: nop Decode: !nop Memory: nop Writeback: nop Fetch: nop Decode: nop Memory: nop Writeback: nop Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到指令流入过程 #### 4.2.5 处理器状态 使用 ``--dump-registers`` 查看程序结束后的寄存器情况 ```bash $ qtrvsim_cli --asm --dump-registers sum2vars.S Machine state report: PC:0x00000004 R0:0x00000000 R1:0x00000000 R2:0x00000000 R3:0x00000000 R4:0x0000bf24 R5:0x00005678 R6:0x000068ac R7:0x00000000 R8:0x00000000 R9:0x00000000 R10:0x00000000 R11:0x00000000 R12:0x00000000 R13:0x00000000 R14:0x00000000 R15:0x00000000 R16:0x00000000 R17:0x00000000 R18:0x00000000 R19:0x00000000 R20:0x00000000 R21:0x00000000 R22:0x00000000 R23:0x00000000 R24:0x00000000 R25:0x00000000 R26:0x00000000 R27:0x00000000 R28:0x00000000 R29:0x00000000 R30:0x00000000 R31:0x00000000 mvendorid: 0x00000000 marchid: 0x00000000 mimpid: 0x00000000 mhardid: 0x00000000 mstatus: 0x00000000 mie: 0x00000000 mtvec: 0x00000000 mscratch: 0x00000000 mepc: 0x00000000 mcause: 0x00000000 mtval: 0x00000000 mip: 0x00000000 mtinsr: 0x00000000 mtval2: 0x00000000 mcycle: 0x00000009 minstret: 0x00000008 Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到 R4 和 R6 中的加法运算结果 使用 ``--dump-cache-stats`` 查看 Cache 信息 ```bash $ qtrvsim_cli --asm --dump-cache-stats sum2vars.S Cache statistics report: i-cache:reads: 9 i-cache:hit: 0 i-cache:miss: 0 i-cache:hit-rate: 0.000 i-cache:stalled-cycles: 81 i-cache:improved-speed: 100.000 d-cache:reads: 2 d-cache:hit: 0 d-cache:miss: 0 d-cache:hit-rate: 0.000 d-cache:stalled-cycles: 36 d-cache:improved-speed: 100.000 Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到 Cache 的读写,以及命中率等信息 使用 ``--dump-cycles`` 查看处理器运行周期 ```bash $ qtrvsim_cli --asm --dump-cycles sum2vars.S Machine state report: cycles: 9 stalls: 0 Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` 可以看到一共 9 个周期完成程序的运行 #### 4.2.6 存储器状态 使用 ``--dump-range`` 和 ``--load-range`` 导出和载入存储器数据 从之前的输出可以看到,三个变量的实际物理地址为 1024 、 1028 和 1032 ,故这里演示使用 ``--dump-range`` 参数导出从 1000 开始长度为 64 字节一共 16 个字的数据的内存数据 ```bash $ qtrvsim_cli --asm --dump-range 1000,64,dump.txt sum2vars.S $ cat dump.txt 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x0000bf24 0x00005678 0x000068ac 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 ``` 可以清晰看到 ``sw`` 指令写入内存物理地址分别为 1024 和 1032 的数据。这通常在有大量数据需要处理的情况下比较实用 #### 4.2.7 处理器错误监视 使用 ``--expect-fail`` 参数期望程序出现处理器陷阱,否则报错 ```bash $ qtrvsim_cli --asm --expect-fail sum2vars.S Machine state report: Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 $ echo $? 0 ``` 可以看到返回值变成了 0 使用 ``--fail-match`` 参数在发生该陷阱时退出执行。尽管帮助信息中描述了四种,但是其中三种没有正常执行 ```bash $ qtrvsim_cli --asm --fail-match I sum2vars.S Machine state report: Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 $ qtrvsim_cli --asm --fail-match A sum2vars.S Unknown fail condition: A Machine state report: Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 $ qtrvsim_cli --asm --fail-match O sum2vars.S Unknown fail condition: O Machine state report: Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 $ qtrvsim_cli --asm --fail-match J sum2vars.S Unknown fail condition: J Machine state report: Machine trapped: UnsupportedInstruction: Instruction with following encoding is not supported: 0 ``` #### 4.2.8 Cache 模式 使用 ``--d-cache`` 和 ``--i-cache`` 参数更改 Cache 模式 #### 4.2.9 存储器存取时间 使用 ``--read-time`` 、 ``--write-time`` 和 ``--burst-time`` 修改存储器存取实际消耗的周期,但是实际在五级流水线模式下并没有发现 ``lw`` 和 ``sw`` 消耗的时钟周期有所变化 #### 4.2.10 延迟槽 使用 ``--no-delay-slot`` 禁用延迟槽。 #### 4.2.11 操作系统模拟相关 ``--serial-in`` 、 ``--serial-out`` 、 ``--os-emulation`` 、 ``--std-out`` 和 ``--os-fs-root`` 提供了一些便于较大型程序模拟的功能 ## 5. 结论 QtRvSim 提供了一个有趣的在线 RISC-V 架构实现平台,非常适合初学者学习,实现一些 RISC-V 模块,目前只实现了 RV32/64G、RV32/64M 和一些伪指令,Wasm 实现仍处于实验性阶段。 - Cycle accuracy (yes) - RISC-V official tests 兼容(yes) - ISA extensions支持(支持RV32IM 图像和命令行模式,RV64IM 命令行模式, Zicsr) - Virtual memory/MMU(不支持) 待开发功能 - Compressed ISA 支持(可视化) - Instruction encoding detailed view - Run minimal riscv64-linux-elf - rv64 可视化 - MMU-虚拟内存 - Pipeline utilization graph - time-travel debugging(step back) - 用于教学的课件嵌入 - 参考https://github.com/sparcians/map/tree/master/helios - 参考https://github.com/cvut/qtrvsim/issues/1 - 参考https://github.com/cvut/qtrvsim/issues/2 - finish porting more complex unit tests and extend them - DWARF location display (when C code is simulated) - declarative specification of (at least simple) pseudoinstructions (for both encoding and decoding). Right now, it is a special case for compilation only. - detail view of instruction encoding - recording of pipeline utilization with visualization like this (https://image.slidesharecdn.com/chapter4theprocessor-100520050519-phpapp01/95/chapter-4-the-processor-55-728.jpg?cb=1274344463) ## 6. 参考资源 ### 6.1 文献 - Computer architectures pages at Czech Technical University in Prague [https://comparch.edu.cvut.cz/](https://comparch.edu.cvut.cz/) - Dupak, J.; Pisa, P.; Stepanovsky, M.; Koci, K. [QtRVSim – RISC-V Simulator for Computer Architectures Classes](https://comparch.edu.cvut.cz/publications/ewC2022-Dupak-Pisa-Stepanovsky-QtRvSim.pdf) In: [embedded world Conference 2022](https://events.weka-fachmedien.de/embedded-world-conference). Haar: WEKA FACHMEDIEN GmbH, 2022. p. 775-778. ISBN 978-3-645-50194-1. ([Slides](https://comparch.edu.cvut.cz/slides/ewc22-qtrvsim.pdf)) 如果您在教育或研究相关材料和出版物中使用 QtRvSim,请参考上述文章。 - [FEE CTU - B35APO - Computer Architectures](https://cw.fel.cvut.cz/wiki/courses/b35apo) - Undergraduate computer architecture class materials ( Czech) ([English](https://cw.fel.cvut.cz/wiki/courses/b35apo/en/start)) - [FEE CTU - B4M35PAP - Advanced Computer Architectures](https://cw.fel.cvut.cz/wiki/courses/b4m35pap/start) - Graduate computer architecture class materials (Czech/English) - [Graphical RISC-V Architecture Simulator - Memory Model and Project Management](https://dspace.cvut.cz/bitstream/handle/10467/94446/F3-BP-2021-Dupak-Jakub-thesis.pdf) - Jakub Dupak's thesis - Documents 2020-2021 QtMips and QtRvSim development - [Graphical CPU Simulator with Cache Visualization](https://dspace.cvut.cz/bitstream/handle/10467/76764/F3-DP-2018-Koci-Karel-diploma.pdf) - Karel Koci's thesis - Documents initial QtMips development ### 6.2 项目 - **RARS** - RISC-V Assembler and Runtime Simulator [https://github.com/TheThirdOne/rars](https://github.com/TheThirdOne/rars) ### 6.3 许可协议 This project is licensed under `GPL-3.0-or-later`. ## 7. 附件(来源于官方文档) ### 7.1 LLVM工具链的使用 ```shell clang --target=riscv32 -march=rv32g -nostdlib -static -fuse-ld=lld test.S -o test llvm-objdump -S test ``` ### 7.2 GNU工具链的使用 ```shell riscv32-elf-as test.S -o test.o riscv32-elf-ld test.o -o test riscv32-elf-objdump -S test ``` 或者 ```shell riscv32-elf-gcc test.S -o test riscv32-elf-objdump -S test ``` ### 7.3 用于 RV32I 的 GNU 64 位工具链 支持 64 位嵌入式工具链的 Multilib 可用于构建可执行文件 ```shell riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -o test test.c crt0local.S -lgcc ``` 必须设置全局指针和堆栈以设置运行时 C 代码的一致环境。 当没有使用其他 C 库时,可以使用简单的“crt0local.S”。
example code ```asm /* minimal replacement of crt0.o which is else provided by C library */ .globl main .globl _start .globl __start .option norelax .text __start: _start: .option push .option norelax la gp, __global_pointer$ .option pop la sp, __stack_end addi a0, zero, 0 addi a1, zero, 0 jal main quit: addi a0, zero, 0 addi a7, zero, 93 /* SYS_exit */ ecall loop: ebreak beq zero, zero, loop .bss __stack_start: .skip 4096 __stack_end: .end _start ```
### 7.4 集成汇编器 模拟器中包含基本的集成汇编器。 [GNU 汇编器](https://sourceware.org/binutils/docs/as/)指令的一小部分也被识别。 以下指令也可以识别:`.word`、`.orig`、`.set`、 `.equ`、`.ascii` 和 `.asciz`。 以下的一些指令则是简单的忽略:`.data`、`.text`、`.globl`、`.end` 和 `.ent`。 这允许编写可以由集成和全功能汇编器编译的代码。 地址被分配给存储在符号表中的标签/符号。 可以识别加法、减法、乘法、除法以及按位与或。 ### 7.5 支持调用外部make实用程序 通过外部 make 构建可执行文件操作调用make程序。 如果调用该操作,并且在主窗口选项卡中选择了一些源编辑器,则在相应的目录中启动make。 否则将选择最后选择的编辑器的目录。 如果没有打开编辑器,则最后加载的 ELF 可执行文件的目录将用作make启动路径。 如果未使用该选项,则使用模拟器启动时的默认目录。 ### 7.6 最新功能 #### 7.6.1 外部设备
Emuated LCD, knobs, buttons, serial port... 模拟器目前实现了两个外设的模拟。 第一个是简单串行端口(UART)。 它支持发送(Tx)和接收(Rx)。 接收器状态寄存器(`SERP_RX_ST_REG`)实现两个位。 只读位 0 (`SERP_RX_ST_REG_READY`) 如果接收器数据寄存器(`SERP_RX_DATA_REG`)中有可用的未读字符,则设置为 1。 当有未读字符时,可以将位 1(`SERP_RX_ST_REG_IE`) 写入 1 以启用中断请求。 发送器状态寄存器 (`SERP_TX_ST_REG`) 位 00(SERP_TX_ST_REG_READY) 通过值 1 发出信号,表示 UART 已准备好并可以接受下一个要发送的字符。 位 1 (`SERP_TX_ST_REG_IE`) 允许生成中断。 寄存器“SERP_TX_DATA_REG”是实际的 Tx 缓冲区。 写入字的 LSB 字节被传输到终端窗口。 外设基址和寄存器偏移量 (`_o`) 以及各个字段掩码 (`_m`) 的定义如下 ```c #define SERIAL_PORT_BASE 0xffffc000 #define SERP_RX_ST_REG_o 0x00 #define SERP_RX_ST_REG_READY_m 0x1 #define SERP_RX_ST_REG_IE_m 0x2 #define SERP_RX_DATA_REG_o 0x04 #define SERP_TX_ST_REG_o 0x08 #define SERP_TX_ST_REG_READY_m 0x1 #define SERP_TX_ST_REG_IE_m 0x2 #define SERP_TX_DATA_REG_o 0x0c ``` UART 寄存器区域镜像在地址 0xffff0000 上,以便能够使用 [SPIM](http://spimsimulator.sourceforge.net/) 或 [MARS](http://courses.missouristate.edu/KenVollmar/MARS/)模拟器。 另一个外设允许从用户面板通过旋钮设置连接到单个word(只读 KNOBS_8BIT 寄存器)的三个字节值,并以十六进制、十进制和二进制格式显示一个字(“LED_LINE”寄存器)。 还有另外两个可写word控制 RGB LED 1 和 2 的颜色(寄存器“LED_RGB1”和“LED_RGB2”)。 ```c #define SPILED_REG_BASE 0xffffc100 #define SPILED_REG_LED_LINE_o 0x004 #define SPILED_REG_LED_RGB1_o 0x010 #define SPILED_REG_LED_RGB2_o 0x014 #define SPILED_REG_LED_KBDWR_DIRECT_o 0x018 #define SPILED_REG_KBDRD_KNOBS_DIRECT_o 0x020 #define SPILED_REG_KNOBS_8BIT_o 0x024 ``` 实现了简单的每像素 16 位 (RGB565) 帧缓冲区和 LCD。 帧缓冲区映射到从“LCD_FB_START”地址开始的范围。 显示尺寸为 480 x 320 像素。 像素格式 RGB565 期望红色分量位于位 11到15 中,绿色分量位于位 5到10 中,蓝色分量位于位 0到4 中。 ```c #define LCD_FB_START 0xffe00000 #define LCD_FB_END 0xffe4afff ``` 限制:内存视图更新和访问的实际概念不允许可靠地读取外设寄存器和 I/O 内存内容。 当选择对内存进行缓存(从 CPU 角度)访问时,可以写入帧缓冲内存。
#### 7.6.2 中断、控制和状态寄存器
Implemented CSR registers and their usage (注意:Coprocessor0 必须替换为 RISC-V 状态寄存器) 中断源列表: | Irq number | Cause/Status Bit | Source | | ----------:| ----------------:|:--------------- | | 2 / HW0 | 10 | 串行端口准备好接受字符到 Tx | | 3 / HW1 | 11 | 有接收到的字符可供读取 | | 7 / HW5 | 15 | 计数器达到比较寄存器中的值 | 可以识别以下协处理器 0 寄存器 | Number | Name | Description | | ------:|:--------- |:---------------- | | $4,2 | UserLocal | 通常被操作系统用作 TLS 基础 | | $8,0 | BadVAddr | 报告最近的地址相关异常的地址 | | $9,0 | Count | 处理器周期计数 | | $11,0 | Compare | 定时器中断控制 | | $12,0 | Status | 处理器状态和控制 | | $13,0 | Cause | 最后一个异常的原因 | | $14,0 | EPC | 最后异常时的程序计数器 | | $15,1 | EBase | 异常向量基址寄存器 | | $16,0 | Config | 配置寄存器 | `mtc0` 和 `mfc0` 用于将值从通用寄存器复制到协处理器 0 寄存器或从协处理器 0 寄存器复制值。 实现的硬件/特殊寄存器: | Number | Name | Description | | ------:|:---------- |:------------------- | | 0 | CPUNum | CPU编号,固定为0 | | 1 | SYNCI_Step | 指令缓存同步所需的增量 | | 2 | CC | 循环计数器 | | 3 | CCRes | 周期计数器分辨率,固定为 1 | | 29 | UserLocal | 协处理器 0 $4,2 寄存器的只读值 | 使能串口接收中断的顺序: 首先确定中断服务程序的位置。 默认地址是0x80000180。 可以更改基址(“EBase”寄存器),然后将 PC 设置为地址 EBase + 0x180。 这符合 MIPS 第 1 版和第 2 版手册。 启用状态寄存器中的位 11(中断屏蔽)。 确保位 1 (`EXL`) 为零,位 0 (`IE`) 设置为 1。 在接收器状态寄存器中启用中断(“SERP_RX_ST_REG”的位 1)。 将字符写入终端。 如果在`SERP_RX_ST_REG`中启用了中断,它应该立即被串行端口接收器消耗。 CPU 应报告中断异常,并且当它传播到执行阶段时,“PC”被设置为中断例程的起始地址。 一些提示如何指导链接器将中断处理程序例程放置在适当的地址。 在新部分中实现中断例程 ``` .section .irq_handler, "ax" ``` 使用下一个链接器选项将节起始位置放置在正确的地址处 ``` -Wl,--section-start=.irq_handler=0x80000180 ```
#### 7.6.3 系统调用支持
Syscall table and documentation 该模拟器支持一些 Linux 内核系统调用。 使用 RV32G ilp32 ABI。 | Register | use on input | use on output | Calling Convention | |:---------------------- |:-------------------- |:-------------- |:------------------------------ | | zero (x0) | — | - | Hard-wired zero | | ra (x1) | — | - | Return address | | sp (x2) | — | (caller saved) | Stack pointer | | gp (x3) | — | (caller saved) | Stack pointer | | tp (x4) | — | (caller saved) | Thread pointer | | t0 .. t2 (x5 .. x7) | — | - | Temporaries | | s0/fp (x8) | — | (caller saved) | Saved register/frame pointer | | s1 (x9) | — | (caller saved) | Saved register | | a0 (x10) | 1st syscall argument | return value | Function argument/return value | | a1 (x11) | 2nd syscall argument | - | Function argument/return value | | a2 .. a5 (x12 .. x15) | syscall arguments | - | Function arguments | | a6 (x16) | - | - | Function arguments | | a7 (x17) | syscall number | - | Function arguments | | s2 .. s11 (x18 .. x27) | — | (caller saved) | Saved registers | | t3 .. t6 (x28 .. x31) | — | - | Temporaries | 所有系统调用输入参数都在寄存器中传递。 支持的系统调用: void [exit](http://man7.org/linux/man-pages/man2/exit.2.html)(int status) __NR_exit (93) 停止/结束程序的执行。 参数是退出状态代码,零表示正常,其他值表示错误。 ssize_t [read](http://man7.org/linux/man-pages/man2/read.2.html)(int fd, void *buf, size_t count) __NR_read (63) 从打开的文件描述符`fd`读取`count`字节。 模拟器将文件描述符 0、1 和 2 映射到内部终端/控制台模拟器。 它们可以在没有`open`调用的情况下使用。 如果没有更多字符可从控制台读取,则会附加换行符。 最多读取的 count 个字节存储到“buf”参数指定的内存位置。 返回实际读取的字节数。 ssize_t [write](http://man7.org/linux/man-pages/man2/write.2.html)(int fd, const void *buf, size_t count) __NR_write (64) 将内存位置`buf`中的`count`字节写入打开的文件描述符`fd`。 文件句柄 0、1 和 2 的控制台与`read`相同。 int [close](http://man7.org/linux/man-pages/man2/close.2.html)(int fd) __NR_close (57) 关闭与描述符`fd`关联的文件并释放描述符。 int [openat](http://man7.org/linux/man-pages/man2/open.2.html)(int dirfd, const char *pathname, int flags, mode_t mode) __NR_openat (56) 打开文件并将其与第一个未使用的文件描述符编号关联并返回该编号。 如果选项`OS Emulation`->`Filesystem root`不为空,则将从模拟环境接收的文件路径`pathname`附加到`Filesystem root`指定的路径。 主机文件系统受到保护,防止尝试使用`..`路径元素遍历到随机目录。 如果未指定根目录,则所有打开的文件都将定位到模拟终端。 void * [brk](http://man7.org/linux/man-pages/man2/brk.2.html)(void *addr) __NR_brk (214) 设置程序数据/bss 结束后标准堆使用的区域结束。 系统调用由虚拟实现模拟。 直至 0xffff0000 的整个地址空间由自动连接的 RAM 进行备份。 int [ftruncate](http://man7.org/linux/man-pages/man2/ftruncate.2.html)(int fd, off_t length) __NR_truncate (46) 将“fd”指定的打开文件的长度设置为新的`length`。 即使在 32 位系统上,`length`参数也是 64 位,对于大端 MIPS,它在第二个和第三个参数中作为较高部分和较低部分。 ssize_t [readv](http://man7.org/linux/man-pages/man2/readv.2.html)(int fd, const struct iovec *iov, int iovcnt) __NR_Linux (65) `read`系统调用的变体,其中要读取的数据将存储到由`iovcnt`对指定的位置 存储在内存中的基址、长度对通过`iov`传递。 ssize_t [writev](http://man7.org/linux/man-pages/man2/writev.2.html)(int fd, const struct iovec *iov, int iovcnt) __NR_Linux (66) `write`系统调用的变体,其中要写入的数据由`iovcnt`定义 存储在内存中地址处的基地址对、长度对通过`iov`传递。
### 7.7 实施的局限性 ```- - Coprocessor0 必须移植到 RISC-V 状态寄存器。 当前支持的指令列表 - **RV32G**: - **LOAD**: `lw, lh, lb, lwu, lhu, lbu` - **STORE**: `sw, sh, sb, swu, shu, sbu` - **OP**: `add, sub, sll, slt, sltu, xor, srl, sra, or, and` - **MISC-MEM**: `fence, fence.i` - **OP-IMM**: `addi, sll, slti, sltiu, xori, srli, srai, ori, andi, auipc, lui` - **BRANCH**: `beq, bne, btl, bge, bltu, bgtu` - **JUMP**: `jal, jalr` - **SYSTEM**: `ecall, ebreak, csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci` - **RV64G**: - **LOAD/STORE**: `lwu, ld, sd` - **OP-32**: `addw, subw, sllw, srlw, sraw, or, and` - **OP-IMM-32**: `addiw, sllw, srliw, sraiw` - **Pseudoinstructions** - **BASIC**: `nop` - **LOAD**: `la, li`, - **OP**: `mv, not, neg, negw, sext.b, sext.h, sext.w, zext.b, zext.h, zext.w, seqz, snez, sltz, slgz` - **BRANCH**: `beqz, bnez, blez, bgez, bltz, bgtz, bgt, ble, bgtu, bleu` - **JUMP**: `j, jal, jr, jalr, ret, call, tail` - **Extensions** - **RV32M/RV64M**: `mul, mulh, mulhsu, div, divu, rem, remu` - **RV64M**: `mulw, divw, divuw, remw, remuw` - **Zicsr**: `csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci` 有关RISC-V的详细信息,请参考ISA规范: [https://riscv.org/technical/specifications/](https://riscv.org/technical/specifications/). ```