由 Computer Architectures Education 项目开发 在捷克技术大学。
您的组织是否在使用 QtRvSim? 请在讨论中告诉我们!
QtRVSim 是可用于 WebAssembly 的实验性功能,它可以在大多数浏览器中运行 无需安装。QtRVSim 在线
请注意,WebAssembly 版本是实验性的。请通过 GitHub 问题报告任何困难。
- Qt 5 (最低测试版本为 5.9.5),对 Qt 6 的实验性支持
- elfutils (可选;libelf 也可以,但可能会有一些问题)
在 Linux 上,您可以使用包装器 Makefile 并在项目根目录中运行。它将创建一个构建目录
并在其中运行 CMake。可用目标包括:(默认)和 .make
release
debug
打包者注意事项:创建源存档时,CMake 会删除此 Makefile,以避免任何歧义。包 应该直接调用 CMake。
cmake -DCMAKE_BUILD_TYPE=Release /path/to/qtrvsim make
其中 是此项目根目录的路径。构建的二进制文件可以在
build 目录(调用 cmake 的那个目录)。/path/to/qtrvsim
target
-DCMAKE_BUILD_TYPE=Debug
构建包含排错剂的开发版本。
如果未提供构建类型,则为默认值。Debug
从 App Store 安装最新版本的 Xcode。然后打开一个终端并执行
安装 Command Line Tools。然后打开 Xcode,接受许可协议并等待它安装任何其他
组件。在您最终看到 “Welcome to Xcode” 屏幕后,从顶部栏
选择并选择 SDK 版本。xcode-select --install
Xcode -> Preferences -> Locations -> Command Line Tools
安装 Homebrew 并使用它来安装 Qt。(macOS 版本必须使用捆绑的 libelf)
brew install qt
现在,以与一般编译(上述)相同的方式构建项目。
- https://github.com/cvut/qtrvsim/releases
- 带有 Windows 和通用 GNU/Linux 二进制文件的档案
- https://build.opensuse.org/repositories/home:jdupak/qtrvsim
- https://software.opensuse.org/download.html?project=home%3Ajdupak&package=qtrvsim
- Open Build Service 二进制包
- https://launchpad.net/~qtrvsimteam/+archive/ubuntu/ppa
- Ubuntu PPA 存档
sudo add-apt-repository ppa:qtrvsimteam/ppa sudo apt-get update sudo apt-get install qtrvsim
QtRVSim 提供了一个 Nix 包作为存储库的一部分。您可以通过以下命令构建和安装它。更新 必须通过签出 git 来手动完成。NIXPKGS 软件包处于 PR 阶段。
nix-env -if .
测试由 CTest(CMake 的一部分)管理。要构建并运行所有测试,请使用以下命令:
cmake -DCMAKE_BUILD_TYPE=Release /path/to/QtRVSim make ctest
主要文档在本 README 以及子目录 docs/user
和 docs/developer
中提供。
该项目作为 Karel Kočí、Jakub Dupak 和 Max Hollmann 的论文进行开发和扩展。有关链接和参考资料,请参阅 Resources and Publications 部分。
模拟器接受为 RISC-V 目标 () 编译的 ELF 静态链接可执行文件。
模拟器将根据 ELF 文件头自动选择字节序。
仿真将根据 ELF 文件头以 XLEN=32 或 XLEN=32 执行。--march=rv64g
- 支持 64 位 RISC-V ISA RV64IM 和 32 位 RV32IM ELF 可执行文件。
- 尚不支持压缩指令。
您可以使用专门的 RISC-V GCC/Binutils 工具链 () 或使用
将 Clang/LLVM 工具链与 LLD 统一。如果您已安装 Clang,则不需要任何
其他工具。Clang 可以在 Linux、Windows、macOS 和其他平台上使用......riscv32-elf
clang --target=riscv32 -march=rv32g -nostdlib -static -fuse-ld=lld test.S -o test llvm-objdump -S test
riscv32-elf-as test.S -o test.o riscv32-elf-ld test.o -o test riscv32-elf-objdump -S test
或
riscv32-elf-gcc test.S -o test riscv32-elf-objdump -S test
支持 64 位嵌入式工具链的 Multilib 可用于构建可执行文件
riscv64-unknown-elf-gcc -march=rv32i -mabi=ilp32 -nostdlib -o test test.c crt0local.S -lgcc
必须设置全局指针和堆栈才能设置符合运行时 C 代码的环境。当没有其他 C 库
used 然后 next simple 可以使用 。crt0local.S
示例代码
/* 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
.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" tabindex="0" role="button">
基本集成汇编器包含在模拟器中。GNU 汇编器指令的一小部分也被认可。next 指令是
已识别:、 、 / 和 .其他一些指令被简单地忽略:、、 、 和 .
这允许编写可由集成和全功能汇编器编译的代码。地址已分配
到存储在 Symbol Table 中的标签/符号。加法、减法、乘法、除法和按位 AND 或
认可。.word
.orig
.set
.equ
.ascii
.asciz
.data
.text
.globl
.end
.ent
操作 “Build executable by external make” 调用 “make” 程序。如果调用了该操作,并且某些源代码编辑器 在主 Windows 选项卡中选择,则 “make” 将在相应的目录中启动。上次选择的 Else 目录 editor 被选中。如果未打开编辑器,则最后加载的 ELF 可执行文件的目录将用作“make”启动路径。如果 即使这不是一个选项,则使用模拟器启动时的默认目录。
模拟 LCD、旋钮、按钮、串行端口、定时器...
模拟器目前实现了两个外围设备的仿真。
第一个是简单串行端口 (UART)。它支持传输
(Tx) 和接收 (Rx)。接收器状态寄存器 ()
实现两个位。只读位 0 ()
如果接收器数据寄存器 () 中有未读字符可用,则设置为 1。位 1
() 可以写入 1 以在未读字符可用时启用中断请求。这
发射机状态寄存器 () 位 0
(SERP_TX_ST_REG_READY) 通过值 1 发出信号,表明 UART 已准备就绪,可以接受要发送的下一个字符。位 1
() 启用中断的生成。寄存器是实际的 Tx 缓冲区。LSB 字节
的文字被传输到终端窗口。外设基址和寄存器的定义
offsets () 和单个字段 masks () 紧随其后SERP_RX_ST_REG
SERP_RX_ST_REG_READY
SERP_RX_DATA_REG
SERP_RX_ST_REG_IE
SERP_TX_ST_REG
SERP_TX_ST_REG_IE
SERP_TX_DATA_REG
_o
_m
#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
#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" tabindex="0" role="button">
UART 寄存器区域镜像在地址0xffff0000上,以便使用最初编写的程序 用于 SPIM 或 MARS 仿真器。
另一个外设允许设置连接到单个字(只读寄存器)的三个字节值
从由旋钮设置的用户面板,以十六进制、十进制和二进制格式(寄存器)显示一个字。那里
是另外两个可写的单词,用于控制 RGB LED 1 和 2 的颜色
(寄存器和 )。KNOBS_8BIT
LED_LINE
LED_RGB1
LED_RGB2
#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
#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" tabindex="0" role="button">
实现了简单的每像素 16 位 (RGB565) 帧缓冲和 LCD。framebuffer 映射到 range starting
at 地址。显示尺寸为 480 x 320 像素。像素格式 RGB565 预期红色分量位为 11..
15,绿色分量在第 5..10 位,蓝色分量在第 0..4 位。LCD_FB_START
#define LCD_FB_START 0xffe00000
#define LCD_FB_END 0xffe4afff
RISC-V Advanced Core 本地中断器的基本实现 通过对
- 计算机级计时器设备 (MTIMER)
- 机器级软件中断设备 (MSWI)
#define ACLINT_MSWI 0xfffd0000 // core 0 machine SW interrupt request
#define ACLINT_MTIMECMP 0xfffd4000 // core 0 compare value
#define ACLINT_MTIME 0xfffdbff8 // timer base 10 MHz
#define ACLINT_SSWI 0xfffd0000 // core 0 system SW interrupt request
有关 ACLINT 的更多信息,请参阅 RISC-V 高级内核本地中断器规范。
实施的 CSR 寄存器及其使用
中断源列表:
Irq 编号 | mie / mip 比特 | 源 |
---|---|---|
3 | 3 | 机器软件中断请求 |
7 | 7 | 机器定时器中断 |
16 | 16 | 有接收的字符可供读取 |
17 | 17 | 串行端口已准备好接受 Tx 的字符 |
可识别以下 Control Status 寄存器
数 | 名字 | 描述 |
---|---|---|
0x300 | mstatus | 机器状态寄存器。 |
0x304 | 三重 | 机器中断启用寄存器。 |
0x305 | mtvec | Machine trap-handler 基址。 |
0x340 | mscratch 的 | 机器陷阱处理程序的暂存寄存器。 |
0x341 | MEPC | 计算机异常程序计数器。 |
0x342 | 因为 | 机器陷阱原因。 |
0x343 | MTVAL | 机器地址或指令错误。 |
0x344 | MIP | 计算机中断挂起。 |
0x34A | mtinsr | 机器陷阱指令 (转换)。 |
0x34B | MTVAL2 | 机器客户机物理地址错误。 |
0xB00 | mcycle | 机器循环计数器。 |
0xB02 | 吟游诗人 | 机器说明-已停用的计数器。 |
0xF11 | mvendorid | 供应商 ID。 |
0xF12 | 马尔基德 | 架构 ID。 |
0xF13 | 米皮德 | 实现 ID。 |
0xF14 | 哈迪 | 硬件线程 ID。 |
csrr
、 、 、 和 用于从 RISC-V 控制状态寄存器复制和交换值。csrw
csrrs
csrrs
csrrw
启用串口接收中断的顺序:
首先确定中断服务例程的位置。公共陷阱处理程序的地址由 register 定义,然后在接受异常或中断时将 PC 设置为此地址。mtvec
启用机器 Interrupt-Enable 寄存器 () 中的第 16 位。确保将机器状态寄存器的第 3 位( - 机器全局中断启用)设置为 1。mie
mstatus.mie
在接收器状态寄存器中启用中断(的第 1 位)。SERP_RX_ST_REG
将 character 写入终端。如果启用了中断,则串行端口接收器应立即使用它
在。CPU 应报告中断异常,并且当它传播到执行阶段时设置为
中断例程 start address。SERP_RX_ST_REG
PC
Syscall 表和文档
该模拟器包括对一些 Linux 内核系统调用的支持。使用了 RV32G ilp32 ABI。
注册 | 在输入时使用 | 在输出时使用 | 调用约定 |
---|---|---|---|
零 (x0) | — | - | 硬连线零 |
RA (x1) | — | (保留) | 退货地址 |
SP (x2) | — | (被调用方已保存) | 堆栈指针 |
GP (x3) | — | (保留) | 全局指针 |
传送卷 (x4) | — | (保留) | 线程指针 |
t0 ..T2 (x5 .. x7) | — | - | 临时 |
S0/FP (x8) | — | (被调用方已保存) | 保存的寄存器/帧指针 |
S1 四门轿车 (x9) | — | (被调用方已保存) | 已保存的寄存器 |
A0 四门轿车 (x10) | 第 1 个 syscall 参数 | 返回值 | 函数参数/返回值 |
A1 四门轿车 (x11) | 第二个 syscall 参数 | - | 函数参数/返回值 |
a2 ..A5 (x12 .. x15) | syscall 参数 | - | 函数参数 |
A6 四门轿车 (x16) | - | - | 函数参数 |
A7 四门轿车 (x17) | 系统调用编号 | - | 函数参数 |
S2 ..S11 (x18 .. x27) | — | (被调用方已保存) | 保存的寄存器 |
t3 ..T6 (x28 .. x31) | — | - | 临时 |
所有系统调用的 input 参数都在 register 中传递。
支持的 syscall:
无效退出 (int 状态) __NR_exit (93)
停止/结束程序的执行。参数是 exit status code,0 表示 OK,其他值通知 error。
ssize_t read(int fd, void *buf, size_t count) __NR_read (63)
从打开的文件描述符 中读取字节。仿真器将文件描述符 0、1 和 2 映射到内部
终端/控制台仿真器。无需调用即可使用它们。如果没有更多字符要从
console 中,会附加 newline 来获取。读取的 count 字节最多存储到 argument 指定的内存位置。返回实际读取的字节数。count
fd
open
buf
ssize_t write(int fd, const void *buf, size_t count) __NR_write (64)
将字节从内存位置写入打开的文件描述符 。文件句柄 0、1 和 2 的控制台与 .count
buf
fd
read
int close(int fd) __NR_close (57)
关闭与 descriptor 和 release descriptor 关联的文件。fd
int openat(int dirfd, const char *pathname, int flags, mode_t mode) __NR_openat (56)
打开文件并将其与第一个未使用的文件描述符编号关联,然后返回该编号。如果
选项 -> 不为空,则从模拟环境接收的文件路径将附加到指定的路径
由。主机文件系统受到保护,防止尝试使用 path 元素遍历到随机目录。如果未指定 root,则所有打开的文件都将定向到模拟终端。仅支持 ( = -100) 模式。OS Emulation
Filesystem root
pathname
Filesystem root
..
TARGET_AT_FDCWD
dirfd
void * brk(void *addr) __NR_brk (214)
设置程序 data/bss 结束后标准堆使用的区域的结尾。syscall 由 dummy 模拟 实现。最多 0xffff0000 个地址空间由自动连接的 RAM 备份。
int ftruncate(int fd, off_t length) __NR_truncate (46)
将 指定的打开文件的长度设置为新的 .即使在 32 位系统上,参数也是 64 位的,并且它作为
第二个和第三个参数。fd
length
length
ssize_t readv(int fd, const struct iovec *iov, int iovcnt) __NR_Linux (65)
要读取的数据所在的系统调用的变体将存储到由成对
基址,地址传入时存储在内存中的长度对。read
iovcnt
iov
ssize_t writev(int fd, const struct iovec *iov, int iovcnt) __NR_Linux (66)
要写入数据的系统调用变体由基址对定义,长度对在地址传入时存储在内存中。write
iovcnt
iov
- 请参阅当前支持的说明列表。
- 目前仅实现了对特权指令的极少支持(mret)。
- 仅实施计算机模式和计算机 CSR 的最小子集。
- 未实现 TLB 和虚拟内存。
- 不支持浮点
- 内存访问停顿(由于缓存未命中而导致执行停顿对用户来说非常烦人,因此 缓存和内存只是在收集的统计信息中)
- 对中断和异常的有限支持。当识别到指令时,Linux 内核系统的一小部分调用
可以通过 Trap Handler 进行模拟或将模拟器配置为继续
在地址上。
ecall
mtvec
- RV32I:
- 负载:
lw, lh, lb, lwu, lhu, lbu
- 商店:
sw, sh, sb, swu, shu, sbu
- OP的:
add, sub, sll, slt, sltu, xor, srl, sra, or, and
- 杂项 MEM:
fence, fence.i
- OP-IMM:
addi, sll, slti, sltiu, xori, srli, srai, ori, andi, auipc, lui
- 分公司:
beq, bne, btl, bge, bltu, bgtu
- 跳跃:
jal, jalr
- 系统:
ecall, mret, ebreak, csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci
- 负载:
- RV64I:
- 加载/存储:
lwu, ld, sd
- OP-32:
addw, subw, sllw, srlw, sraw, or, and
- OP-IMM-32 的:
addiw, sllw, srliw, sraiw
- 加载/存储:
- 伪指令
- 基本:
nop
- 负载: ,
la, li
- OP的:
mv, not, neg, negw, sext.b, sext.h, sext.w, zext.b, zext.h, zext.w, seqz, snez, sltz, slgz
- 分公司:
beqz, bnez, blez, bgez, bltz, bgtz, bgt, ble, bgtu, bleu
- 跳跃:
j, jal, jr, jalr, ret, call, tail
- 基本:
- 扩展
- RV32M/RV64M:
mul, mulh, mulhsu, div, divu, rem, remu
- RV64M 的:
mulw, divw, divuw, remw, remuw
- RV32A/RV64A:
lr.w, sc.w, amoswap.w, amoadd.w, amoxor.w, amoand.w, amoor.w, amomin.w, amomax.w, amominu.w, amomaxu.w
- RV64A:
lr.d, sc.d, amoswap.d, amoadd.d, amoxor.d, amoand.d, amoor.d, amomin.d, amomax.d, amominu.d, amomaxu.d
- Zicsr:
csrrw, csrrs, csrrc, csrrwi, csrrsi, csrrci
- RV32M/RV64M:
有关 RISC-V 的详细信息,请参阅 ISA 规范:https://riscv.org/technical/specifications/。
-
位于布拉格的捷克技术大学的计算机体系结构页面 https://comparch.edu.cvut.cz/
-
杜帕克,J.;比萨,P.;斯捷潘诺夫斯基,M.;Koci, K. QtRVSim – 用于计算机架构课程的 RISC-V 模拟器:2022 年嵌入式世界会议。哈尔:WEKA FACHMEDIEN GmbH,2022 年。第 775-778 页。国际标准书号 978-3-645-50194-1。(幻灯片)
如果您在教育或研究相关材料和出版物中使用 QtRvSim,请参考上述文章。
- FEE CTU - B35APO - 计算机体系结构
- 本科计算机体系结构类教材 ( 捷克语)(英语)
- FEE CTU - B4M35PAP - 高级计算机体系结构
- 研究生计算机体系结构课程材料(捷克语/英语)
- 图形化 RISC-V 架构仿真器 - 内存模型和项目管理
- Jakub Dupak 的论文
- 文档 2020-2021 QtMips 和 QtRvSim 开发
- 具有缓存可视化功能的图形 CPU 模拟器
- Karel Koci 的论文
- 记录初始 QtMips 开发
-
QtMips - 此模拟器的前身 https://github.com/cvut/QtMips/
-
RARS - RISC-V 汇编器和运行时 模拟器 https://github.com/TheThirdOne/rars
- 版权所有 (c) 2017-2019 Karel Koci cynerd@email.cz
- 版权所有 (c) 2019-2024 Pavel Pisa pisa@cmp.felk.cvut.cz
- 版权所有 (c) 2020-2024 Jakub Dupak dev@jakubdupak.com
- 版权所有 (c) 2020-2021 Max Hollmann hollmmax@fel.cvut.cz
此项目已获得 许可。许可证的全文位于 LICENSE 文件中。这
许可证 适用于除 named 目录及其中的文件之外的所有文件。外部目录中的文件
具有与 Projects 许可证兼容的单独许可证。GPL-3.0-or-later
external
本程序是自由软件:您可以根据自由软件基金会发布的 GNU 通用公共许可证的条款重新分发和/或修改它,无论是许可证的第 3 版,还是(根据您的选择)任何更高版本。
分发此程序是希望它有用,但没有任何保证;甚至没有对适销性或特定用途适用性的暗示保证。有关更多详细信息,请参阅 GNU 通用公共许可证。
您应该已经收到了 GNU 通用公共许可证的副本以及此程序。如果没有,请参阅 https://www.gnu.org/licenses/。