├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── configs ├── .gitignore └── Kconfig ├── docs ├── kdb.md ├── riscv.md └── tests.md ├── include ├── config │ └── config.h ├── cpu │ ├── core.h │ ├── cpu.h │ ├── riscv │ │ ├── aclint.h │ │ ├── core.h │ │ ├── cpu.h │ │ ├── csr.h │ │ ├── def.h │ │ └── local-include │ │ │ └── inst-list.h │ └── word.h ├── debug.h ├── device │ ├── bus.h │ ├── memory.h │ └── uart.h ├── isa │ ├── isa.h │ ├── riscv │ │ └── isa.h │ └── word.h ├── kdb │ ├── cmd.h │ ├── kdb.h │ └── rsp.h ├── log.h ├── macro.h └── utils │ ├── decoder.h │ ├── gdb-rsp.h │ ├── task-timer.h │ ├── tcp-server.h │ └── utils.h ├── scripts ├── config.mk ├── decoder-gen │ ├── gen.py │ ├── instpat.py │ └── main.py ├── filelist.mk ├── isa │ └── riscv.mk └── kdb │ └── dummy.kdb ├── src ├── cpu │ └── riscv │ │ ├── aclint.cpp │ │ ├── core.cpp │ │ ├── cpu.cpp │ │ ├── csr │ │ ├── csr-rw.cpp │ │ └── csr.cpp │ │ ├── inst │ │ ├── atomic.cpp │ │ ├── base.cpp │ │ ├── compressed.cpp │ │ ├── inst-decoder.cpp │ │ ├── local-decoder.h │ │ ├── mul.cpp │ │ ├── privileged.cpp │ │ └── zicsr.cpp │ │ ├── instpat │ │ ├── base.instpat │ │ └── compressed.instpat │ │ ├── memory.cpp │ │ └── privileged │ │ ├── interrupt.cpp │ │ └── trap.cpp ├── device │ ├── bus.cpp │ ├── memory.cpp │ └── uart.cpp ├── isa │ ├── riscv │ │ ├── disassemble.cpp │ │ └── riscv.cpp │ └── riscv32 │ │ ├── disassemble.cpp │ │ └── riscv32.cpp ├── kdb │ ├── cmd.cpp │ ├── cmd │ │ ├── completion.cpp │ │ ├── dbg.cpp │ │ ├── info.cpp │ │ ├── load.cpp │ │ ├── log.cpp │ │ ├── mem.cpp │ │ ├── show.cpp │ │ └── uart.cpp │ ├── cpu-exec.cpp │ ├── elf.cpp │ ├── kdb.cpp │ ├── memory.cpp │ ├── rsp │ │ └── rsp.cpp │ └── uart.cpp ├── log.cpp ├── main.cpp └── utils │ ├── decoder │ └── bitpat.cpp │ ├── gdb-rsp │ └── gdb-rsp.cpp │ ├── task-timer │ └── task-timer.cpp │ ├── tcp-server │ └── tcp-server.cpp │ └── utils.cpp ├── tests ├── abstract-machine │ ├── Makefile │ ├── am │ │ ├── Makefile │ │ ├── include │ │ │ ├── am-dev.h │ │ │ ├── am.h │ │ │ └── isa │ │ │ │ └── riscv.h │ │ └── src │ │ │ ├── isa │ │ │ └── riscv │ │ │ │ ├── csr.c │ │ │ │ ├── cte.c │ │ │ │ ├── halt.S │ │ │ │ ├── ioe │ │ │ │ ├── timer.c │ │ │ │ └── uart.c │ │ │ │ ├── start.S │ │ │ │ ├── trap.S │ │ │ │ └── vme.c │ │ │ └── trm.c │ ├── klib │ │ ├── Makefile │ │ ├── include │ │ │ ├── klib-macros.h │ │ │ └── klib.h │ │ └── src │ │ │ ├── ctype.c │ │ │ ├── int64.c │ │ │ ├── stdio.c │ │ │ ├── stdlib.c │ │ │ └── string.c │ └── scripts │ │ ├── kdb │ │ ├── debug.kdb │ │ ├── init.kdb │ │ └── run.kdb │ │ ├── linker.ld │ │ ├── riscv32.mk │ │ └── riscv64.mk ├── benchmarks │ └── microbench │ │ ├── Makefile │ │ ├── README.md │ │ ├── include │ │ └── benchmark.h │ │ ├── init.kdb │ │ └── src │ │ ├── 15pz │ │ ├── 15pz.cc │ │ ├── heap.h │ │ └── puzzle.h │ │ ├── bench.c │ │ ├── bf │ │ └── bf.c │ │ ├── dinic │ │ └── dinic.cc │ │ ├── fib │ │ └── fib.c │ │ ├── lzip │ │ ├── lzip.c │ │ ├── quicklz.c │ │ └── quicklz.h │ │ ├── md5 │ │ └── md5.c │ │ ├── qsort │ │ └── qsort.c │ │ ├── queen │ │ └── queen.c │ │ ├── sieve │ │ └── sieve.c │ │ └── ssort │ │ └── ssort.cc ├── cpu-tests │ ├── Makefile │ ├── include │ │ └── test.h │ └── tests │ │ ├── add-longlong.c │ │ ├── add.c │ │ ├── bit.c │ │ ├── bubble-sort.c │ │ ├── crc32.c │ │ ├── div.c │ │ ├── dummy.c │ │ ├── fact.c │ │ ├── fib.c │ │ ├── goldbach.c │ │ ├── hello-str.c │ │ ├── if-else.c │ │ ├── leap-year.c │ │ ├── load-store.c │ │ ├── matrix-mul.c │ │ ├── max.c │ │ ├── mersenne.c │ │ ├── min3.c │ │ ├── mov-c.c │ │ ├── movsx.c │ │ ├── mul-longlong.c │ │ ├── pascal.c │ │ ├── prime.c │ │ ├── quick-sort.c │ │ ├── recursion.c │ │ ├── select-sort.c │ │ ├── shift.c │ │ ├── shuixianhua.c │ │ ├── string.c │ │ ├── sub-longlong.c │ │ ├── sum.c │ │ ├── switch.c │ │ ├── to-lower-case.c │ │ ├── unalign.c │ │ └── wanshu.c ├── ioe-tests │ ├── makefile │ ├── uart.c │ └── uart.kdb ├── privileged-tests │ ├── intr.c │ ├── makefile │ └── uart.kdb └── riscv │ ├── Makefile │ ├── README.md │ ├── deleg │ └── src │ │ └── deleg.S │ ├── mtimer │ └── src │ │ └── timer.S │ ├── smode │ └── src │ │ └── smode.S │ ├── stimer │ └── src │ │ └── stimer.S │ ├── uart.kdb │ ├── vm32 │ ├── src │ │ ├── inc.S │ │ ├── main.c │ │ └── trap.S │ └── user-img │ │ ├── Makefile │ │ └── user.S │ └── vm64 │ ├── src │ ├── inc.S │ ├── main.c │ ├── trap.S │ └── vm.c │ └── user-img │ ├── Makefile │ └── user.S ├── tools └── kconfig │ ├── .config │ ├── .gitignore │ ├── Makefile │ ├── conf.c │ ├── confdata.c │ ├── expr.c │ ├── expr.h │ ├── lexer.l │ ├── list.h │ ├── lkc.h │ ├── lkc_proto.h │ ├── lxdialog │ ├── checklist.c │ ├── dialog.h │ ├── inputbox.c │ ├── menubox.c │ ├── textbox.c │ ├── util.c │ └── yesno.c │ ├── mconf.c │ ├── menu.c │ ├── parser.y │ ├── preprocess.c │ ├── symbol.c │ └── util.c └── utils └── uart └── uart.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | 3 | #vscode 4 | .vscode 5 | 6 | # clangd 7 | compile_commands.json 8 | .cache 9 | 10 | autogen 11 | autoconf.h 12 | 13 | __pycache__ 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "utils/mini-gdbstub"] 2 | path = utils/mini-gdbstub 3 | url = https://github.com/RinHizakura/mini-gdbstub.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include ./scripts/config.mk 2 | 3 | remove_quote = $(patsubst "%",%,$(1)) 4 | 5 | CXX = clang++ 6 | LD = clang++ 7 | CXXFLAGS = -Wall -Wextra -Werror -pedantic -Ofast -Wno-unused-command-line-argument -Wno-unused-parameter -Wno-unused-private-field -flto -funroll-loops 8 | ISA := $(call remove_quote,$(CONFIG_ISA)) 9 | BASE_ISA := $(call remove_quote,$(CONFIG_BASE_ISA)) 10 | 11 | SRC_DIR = ./src 12 | BUILD_DIR = ./build 13 | OBJ_DIR = ./build/$(ISA) 14 | 15 | SRCS += $(shell find $(SRC_DIR) -path $(SRC_DIR)/isa -prune -o -path $(SRC_DIR)/cpu -prune -o \( -name "*.cpp" -o -name "*.c" \) -print) 16 | SRCS += $(shell find $(SRC_DIR)/isa/$(BASE_ISA) -name "*.cpp" -or -name "*.c" ) 17 | # SRCS += $(shell find $(SRC_DIR)/isa/$(ISA) -name "*.cpp" -or -name "*.c" ) 18 | SRCS += $(shell find $(SRC_DIR)/cpu/$(CONFIG_SRC_CPU_ISA) -name "*.cpp" -or -name "*.c" ) 19 | OBJS += $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRCS)) 20 | DEPS = $(OBJS:.o=.d) 21 | 22 | INCPATH += $(abspath ./include) 23 | INCPATH += $(abspath ./tests/isa/$(ISA)) 24 | INCFLAGS = $(addprefix -I,$(INCPATH)) 25 | 26 | LIBS += readline 27 | LIBFLAGS = $(addprefix -l,$(LIBS)) 28 | 29 | CXXFLAGS += $(INCFLAGS) 30 | CXXFLAGS += $(LIBFLAGS) 31 | 32 | # for llvm 33 | CXXFLAGS += $(shell llvm-config --cxxflags) -fexceptions # for expection handling 34 | CXXFLAGS += $(shell llvm-config --libs) 35 | 36 | ifeq ($(shell which ccache > /dev/null 2>&1; echo $$?), 0) 37 | CXX := ccache $(CXX) 38 | endif 39 | 40 | TARGET = $(BUILD_DIR)/$(ISA)-kxemu 41 | 42 | $(TARGET): $(OBJS) 43 | $(info + CXX $@) 44 | @ mkdir -p $(BUILD_DIR) 45 | @ $(LD) $(OBJS) -o $(TARGET) $(LDFLAGS) $(CXXFLAGS) 46 | 47 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp 48 | $(info + CXX $<) 49 | @ mkdir -p $(dir $@) 50 | @ $(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@ 51 | 52 | -include $(DEPS) 53 | 54 | -include ./scripts/isa/$(BASE_ISA).mk 55 | 56 | kxemu: $(TARGET) 57 | 58 | run: $(TARGET) 59 | $(info + Running $(TARGET)) 60 | @ $(TARGET) $(FLAGS) 61 | 62 | clean: 63 | rm -rf $(BUILD_DIR) 64 | 65 | count: 66 | $(info Counting lines in src and include directories...) 67 | @find $(SRC_DIR) ./include -name '*.c' -or -name "*.cpp" -or -name "*.h" | xargs cat | sed '/^\s*$$/d' | wc -l 68 | 69 | tidy: 70 | clang-tidy $(SRCS) 71 | 72 | .PHONY: kxemu run clean 73 | .DEFAULT_GOAL := kxemu 74 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # KXemu TODO List 2 | 3 | 1. 移植nemu的cpu-tests部分 4 | 5 | 2. 为kdb编写命令行调试基础设施 6 | 7 | 3. 为riscv32指令集添加difftest 8 | 9 | 4. 实现riscv32-C指令集扩展 10 | 11 | 5. 龙芯指令集 12 | -------------------------------------------------------------------------------- /configs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !Kconfig -------------------------------------------------------------------------------- /configs/Kconfig: -------------------------------------------------------------------------------- 1 | mainmenu "KXemu Configurations" 2 | 3 | choice 4 | prompt "ISA" 5 | default ISA_riscv32 6 | 7 | config ISA_riscv32 8 | bool "riscv32" 9 | 10 | config ISA_riscv64 11 | bool "riscv64" 12 | 13 | endchoice 14 | 15 | config ISA 16 | string 17 | default riscv32 if ISA_riscv32 18 | default riscv64 if ISA_riscv64 19 | 20 | config BASE_ISA 21 | string 22 | default riscv if ISA_riscv32 23 | default riscv if ISA_riscv64 24 | 25 | config SRC_CPU_ISA 26 | string 27 | default riscv if ISA_riscv32 28 | default riscv if ISA_riscv64 29 | 30 | config LOG 31 | bool "LOG" 32 | 33 | config HINT 34 | bool "Show hints" 35 | -------------------------------------------------------------------------------- /docs/kdb.md: -------------------------------------------------------------------------------- 1 | # KDB文档 Documentation 2 | 3 | ## KDB简介 Introduction 4 | 5 | KDB是对KXemu的包装,用于用户控制KXemu中的CPU,并且连接CPU和其他外设。 6 | 7 | ## KDB启动参数 Startup Parameters 8 | 9 | 编译运行KXemu,会自动启动kdb命令行。kdb接受的命令行参数: 10 | 11 | When KXemu is compiled and executed, the KDB command-line interface is automatically launched. KDB accepts the following command-line arguments: 12 | 13 | - `-s --source [filename]` 在启动的时候自动运行脚本,这可以用于大多数的时候运行镜像文件。可以指定多个源文件,kdb会依次运行它们。Automatically executes a script at startup, often used for running image files. Multiple source files can be specified, and KDB will execute them in order. 14 | 15 | - `-e --elf [filename]` 指定elf文件,在命令中使用`load elf`来将这个elf加载到内存的指定位置。Specifies an ELF file that can be loaded into memory using the load elf command. 16 | 17 | ## KDB命令 18 | 19 | ### 运行 20 | 21 | - `step [n]` 运行当前选中的核心`n`步,遇到核心出现错误或者break时终止。`n`默认为`1`。默认会打印运行的指令和反汇编代码。Executes the selected core for n steps, stopping on errors or breaks. Defaults to n=1. The executed instruction and its disassembly are printed by default. 22 | 23 | - `run` 运行当前核心,直到核心出现错误或者break时终止。Runs the current core(or halt in the riscv) until an error or break occurs. 24 | 25 | ### 日志 26 | 27 | - `log on [DEBUG|INFO|WARN|PANIC]` 打开一项日志等级的输出,默认所有等级的输出都是开启的。Enables a specific log level output. By default, all log levels are enabled. 28 | 29 | - `log off [DEBUG|INFO|WARN|PANIC]` 关闭一项日志等级的输出。Disables a specific log level output. 30 | 31 | ### 加载 32 | 33 | 加载指令用于加载在命令行参数中指定的文件,如ELF文件。 34 | 35 | Load commands are used to load files specified in the command-line arguments, such as ELF files. 36 | 37 | - `load elf` 加载命令行中由参数`--elf`或`-e`指定的`elf`路径,可以在批处理下设置好内存映射后延迟加载ELF文件。Loads the ELF file path specified by the --elf or -e argument, allowing deferred loading after memory mapping in batch processing. 38 | 39 | ### 内存 40 | 41 | - `mem create [name] [addr] [size]` 创建一个名为`name`的、映射到`addr`位置的,长度为`size`的内存映射区域。Creates a memory-mapped region named name at address addr with a size of size. 42 | 43 | - `mem img [addr] [filename]` 将本地文件加载到内存`addr`的位置,`addr`处映射的内存必须是一个用于存储的区域。Loads a local file into memory at address addr. The memory at addr must be a storage region. 44 | 45 | - `x addr` 与gdb类似,打印内存中的数据。Similar to GDB, prints data from memory at the specified address. 46 | 47 | ### 串口 48 | 49 | - `uart add [port]` KXemu可以模拟一个UART16650设备,这条指令可以为CPU添加一个内存映射在`base`处的uart串口,如果指定端口,可以通过TCP套接字连接至串口,向端口`port`写入可以发送数据,同时可以从`port+1`端口接收KXemu的串口输出。如果不指定端口,则串口输出会自动输出至标准输出。在`./utils/uart`目录下有一个示例。KXemu can simulate a UART16650 device. This command maps a UART serial port to the CPU at the base address. If a port is specified, TCP sockets can be used to connect to the serial port, allowing data transmission to port and receiving KXemu's serial output from port+1. If no port is specified, output is directed to standard output. An example is available in the `./utils/uart` directory. 50 | 51 | - `uart puts [str]` 将`str`字符串的内容加入到串口的FIFO中。Adds the content of the str string to the UART's FIFO. 52 | 53 | ### Debug 54 | 55 | - `info ` 打印名字为`name`的寄存器。Prints the value of the register named name. 56 | 57 | - `info gpr` 打印当前核的所有通用寄存器。Prints all general-purpose registers of the current core(or halt). 58 | 59 | - `info pc` 打印当前核的PC。Prints the PC register of the current core(or halt). 60 | 61 | ### 其他 62 | 63 | - `source [filename]` 运行存储在本地的kdb命令文件。Executes a local KDB command file. 64 | -------------------------------------------------------------------------------- /docs/riscv.md: -------------------------------------------------------------------------------- 1 | # RISCV 2 | 3 | ## 已实现 4 | 5 | 1. 基本整数指令集、M扩展指令集、C扩展指令集的整数部分。Basic integer instruction set, M-extension, and the integer part of the C-extension. 6 | 7 | 2. `ecall`指令、`mret`、`sret`指令的基本功能,包括特权级的切换。Basic functionality of the ecall, mret, and sret instructions, including privilege level switching. 8 | 9 | 3. `mtime`和`mtimecmp`的计时功能,默认频率是10MHz。Timing functionality for mtime and mtimecmp, with a default frequency of 10 MHz. 10 | 11 | 4. 物理内存保护和虚拟内存管理。 Physical Memory Protection and Vitrual Memory. 12 | 13 | 5. Sstc扩展。 Sstc Extension. 14 | 15 | ## 计划实现 16 | 17 | 1. 原子指令A扩展。 18 | 19 | 2. 浮点指令F扩展和D扩展。 20 | -------------------------------------------------------------------------------- /docs/tests.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HD-CSKX/KXemu/0d7589c98924b1100e634359a3b84fff8809f54b/docs/tests.md -------------------------------------------------------------------------------- /include/config/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_CONFIG_CONFIG_H__ 2 | #define __KXEMU_CONFIG_CONFIG_H__ 3 | 4 | #include "autoconf.h" 5 | #include "macro.h" 6 | 7 | #if defined (CONFIG_ISA_riscv32) && CONFIG_ISA_riscv32 == 1 8 | #define KXEMU_ISA riscv32 9 | #define KXEMU_ISA32 10 | #define KXEMU_ISA_32BIT true 11 | #define KXEMU_ISA_64BIT false 12 | #elif defined (CONFIG_ISA_riscv64) && CONFIG_ISA_riscv64 == 1 13 | #define KXEMU_ISA riscv64 14 | #define KXEMU_ISA64 15 | #define KXEMU__ISA_32BIT false 16 | #define KXEMU__ISA_64BIT true 17 | #else 18 | #error "Unsupport ISA" 19 | #endif 20 | 21 | #define KXEMU_ISA_STR MACRO_TO_STRING(KXEMU_ISA) 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/cpu/core.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_CPU_CORE_H__ 2 | #define __KXEMU_CPU_CORE_H__ 3 | 4 | #include 5 | 6 | namespace kxemu::cpu { 7 | 8 | template 9 | class Core { 10 | public: 11 | virtual bool is_error() = 0; 12 | virtual bool is_break() = 0; 13 | virtual bool is_running() = 0; 14 | virtual bool is_halt() = 0; 15 | virtual ~Core() = default; 16 | 17 | virtual void reset(word_t entry) = 0; 18 | virtual void step() = 0; 19 | 20 | virtual void run(word_t *breakpoints = nullptr, unsigned int n = 0) = 0; 21 | 22 | virtual word_t get_pc() = 0; 23 | virtual word_t get_gpr(int idx) = 0; 24 | virtual word_t get_register(const std::string &name, bool &success) = 0; 25 | 26 | virtual word_t get_halt_pc() = 0; 27 | virtual word_t get_halt_code() = 0; 28 | 29 | // The interface for virtual address translation for KDB 30 | virtual word_t vaddr_translate(word_t vaddr, bool &valid) { 31 | valid = true; 32 | return vaddr; 33 | } 34 | }; 35 | 36 | } // namespace kxemu::cpu 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/cpu/cpu.h: -------------------------------------------------------------------------------- 1 | /*************************************************************** 2 | * Project Name: KXemu 3 | * File Name: include/cpu/cpu.h 4 | * Description: Define interface of the cpu and the cpu core for 5 | different ISA. 6 | ***************************************************************/ 7 | 8 | #ifndef __KXEMU_CPU_CPU_H__ 9 | #define __KXEMU_CPU_CPU_H__ 10 | 11 | #include "cpu/core.h" 12 | #include "device/bus.h" 13 | 14 | namespace kxemu::cpu { 15 | 16 | template 17 | class CPU { 18 | public: 19 | // flags for extension features 20 | virtual void init(device::Bus *memory, int flags, unsigned int coreCount) = 0; 21 | virtual void reset(word_t pc) = 0; 22 | virtual void step() = 0; 23 | virtual bool is_running() = 0; 24 | 25 | virtual unsigned int core_count() = 0; 26 | virtual Core *get_core(unsigned int coreID) = 0; 27 | 28 | virtual ~CPU() = default; 29 | }; 30 | 31 | } // namespace kxemu::cpu 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/cpu/riscv/aclint.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_CPU_RISCV_CLINT_H__ 2 | #define __KXEMU_CPU_RISCV_CLINT_H__ 3 | 4 | #include "cpu/word.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace kxemu::cpu { 10 | 11 | // This is an implementation of the Advanced Core Local Interruptor (ACLINT) for RISC-V. 12 | // See https://github.com/riscv/riscv-aclint/releases/download/v1.0-rc4/riscv-aclint-1.0-rc4.pdf 13 | // offset | Device | Description 14 | // 0x0000 - 0x3fff | MSWI | Machine-mode Software Interrupt Device 15 | // 0x4000 - 0x7ff0 | MTIMECMP | 16 | // 0xbff8 - 0xbfff | MTIME | 17 | // 0xc000 - 0xffff | SSWI | Supervisor-mode Software Interrupt Device 18 | class AClint { 19 | public: 20 | using callback_t = std::function; 21 | struct CoreInfo { 22 | void *core; // Pointer to the core object 23 | 24 | // The following fields are allocated in the core object 25 | uint64_t *mtimecmp; // Machine-mode Timecmp Register 26 | bool *msip; // Machine-mode Software Interrupt Device 27 | bool *ssip; // Supervisor-mode Software Interrupt Device 28 | 29 | callback_t set_mtimecmp; // Machine-mode Timecmp Register 30 | callback_t set_msip; // Machine-mode Software Interrupt Device 31 | callback_t set_ssip; // Supervisor-mode Software Interrupt Device 32 | }; 33 | 34 | AClint(); 35 | ~AClint(); 36 | 37 | void init(unsigned int coreCount); 38 | void reset(); 39 | 40 | word_t read(word_t addr, int size, bool &success); 41 | bool write(word_t addr, word_t value, int size); 42 | 43 | void register_core(unsigned int coreId, const CoreInfo &info); 44 | private: 45 | unsigned int coreCount; 46 | 47 | CoreInfo *cores; 48 | }; 49 | 50 | } // namespace kxemu::cpu 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /include/cpu/riscv/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_CPU_RISCV_CPU_H__ 2 | #define __KXEMU_CPU_RISCV_CPU_H__ 3 | 4 | #include "cpu/cpu.h" 5 | #include "cpu/riscv/aclint.h" 6 | #include "cpu/riscv/core.h" 7 | #include "utils/task-timer.h" 8 | 9 | namespace kxemu::cpu { 10 | 11 | #ifdef KXEMU_ISA64 12 | using word_t = uint64_t; 13 | #else 14 | using word_t = uint32_t; 15 | #endif 16 | 17 | class RVCPU : public CPU { 18 | private: 19 | RVCore *cores; 20 | unsigned int coreCount; 21 | 22 | AClint aclint; 23 | utils::TaskTimer taskTimer; 24 | 25 | public: 26 | ~RVCPU() override; 27 | 28 | void init(device::Bus *bus, int flags, unsigned int coreCount) override; 29 | void reset(word_t pc) override; 30 | void step() override; 31 | bool is_running() override; 32 | 33 | unsigned int core_count() override; 34 | RVCore *get_core(unsigned int coreID) override; 35 | }; 36 | 37 | } // namespace kxemu::cpu 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/cpu/riscv/csr.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_CPU_RISCV_CSR_H__ 2 | #define __KXEMU_CPU_RISCV_CSR_H__ 3 | 4 | #include "cpu/word.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace kxemu::cpu { 11 | 12 | class RVCSR { 13 | private: 14 | using csr_rw_func_t = word_t (RVCSR::*)(unsigned int addr, word_t value, bool &valid); 15 | struct CSR { 16 | csr_rw_func_t readFunc; 17 | csr_rw_func_t writeFunc; 18 | word_t value; 19 | word_t resetValue; 20 | }; 21 | std::unordered_map csr; 22 | void add_csr(unsigned int addr, word_t resetValue = 0, csr_rw_func_t readFunc = nullptr, csr_rw_func_t writeFunc = nullptr); 23 | 24 | using callback_t = std::function; 25 | callback_t update_stimecmp; 26 | 27 | struct PMPCfg { 28 | word_t start; 29 | word_t end; 30 | bool r; 31 | bool w; 32 | bool x; 33 | } pmpCfgArray[64]; 34 | unsigned int pmpCfgCount; 35 | void reload_pmpcfg(); 36 | PMPCfg *pmp_check(word_t addr, int len); 37 | 38 | std::function get_uptime; 39 | 40 | // misa 41 | word_t read_misa(unsigned int addr, word_t value, bool &valid); 42 | word_t write_misa(unsigned int addr, word_t value, bool &valid); 43 | 44 | // mip 45 | word_t read_mip(unsigned int addr, word_t value, bool &valid); 46 | word_t write_mip(unsigned int addr, word_t value, bool &valid); 47 | 48 | // mie 49 | word_t read_mie(unsigned int addr, word_t value, bool &valid); 50 | word_t write_mie(unsigned int addr, word_t value, bool &valid); 51 | 52 | // pmp 53 | word_t write_pmpcfg (unsigned int addr, word_t value, bool &valid); 54 | word_t write_pmpaddr(unsigned int addr, word_t value, bool &valid); 55 | 56 | // sip 57 | word_t read_sip(unsigned int addr, word_t value, bool &valid); 58 | word_t write_sip(unsigned int addr, word_t value, bool &valid); 59 | 60 | // stimecmp 61 | word_t write_stimecmp(unsigned int addr, word_t value, bool &valid); 62 | 63 | // sie 64 | word_t read_sie(unsigned int addr, word_t value, bool &valid); 65 | word_t write_sie(unsigned int addr, word_t value, bool &valid); 66 | 67 | // sstatus 68 | word_t read_sstatus(unsigned int addr, word_t value, bool &valid); 69 | word_t write_sstatus(unsigned int addr, word_t value, bool &valid); 70 | 71 | // satp 72 | word_t read_satp(unsigned int addr, word_t value, bool &valid); 73 | word_t write_satp(unsigned int addr, word_t value, bool &valid); 74 | 75 | // time 76 | word_t read_time(unsigned int addr, word_t value, bool &valid); 77 | 78 | public: 79 | RVCSR(); 80 | 81 | int privMode; 82 | 83 | void init(unsigned int hartId, std::function get_uptime); 84 | void init_callbacks(callback_t update_stimecmp); 85 | void reset(); 86 | 87 | word_t read_csr(unsigned int addr, bool &valid); 88 | word_t read_csr(unsigned int addr); 89 | bool write_csr(unsigned int addr, word_t value); 90 | 91 | word_t *get_csr_ptr(unsigned int addr); 92 | const word_t *get_csr_ptr_readonly(unsigned int addr) const; 93 | 94 | bool pmp_check_r(word_t addr, int len); 95 | bool pmp_check_w(word_t addr, int len); 96 | bool pmp_check_x(word_t addr, int len); 97 | }; // class RVCSR 98 | 99 | } // namespace kxemu::cpu 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /include/cpu/riscv/local-include/inst-list.h: -------------------------------------------------------------------------------- 1 | #include "config/config.h" 2 | 3 | #define _INST(name) void do_##name(); 4 | 5 | _INST(add) 6 | _INST(sub) 7 | _INST(and) 8 | _INST(or) 9 | _INST(xor) 10 | _INST(sll) 11 | _INST(srl) 12 | _INST(sra) 13 | _INST(slt) 14 | _INST(sltu) 15 | 16 | _INST(addi) 17 | _INST(andi) 18 | _INST(ori) 19 | _INST(xori) 20 | _INST(slli) 21 | _INST(srli) 22 | _INST(srai) 23 | _INST(slti) 24 | _INST(sltiu) 25 | 26 | _INST(lb) 27 | _INST(lbu) 28 | _INST(lh) 29 | _INST(lhu) 30 | _INST(lw) 31 | 32 | _INST(sb) 33 | _INST(sh) 34 | _INST(sw) 35 | 36 | _INST(beq) 37 | _INST(bge) 38 | _INST(bgeu) 39 | _INST(blt) 40 | _INST(bltu) 41 | _INST(bne) 42 | 43 | _INST(jal) 44 | _INST(jalr) 45 | 46 | _INST(lui) 47 | _INST(auipc) 48 | 49 | // RV64 only 50 | #ifdef KXEMU_ISA64 51 | _INST(addw) 52 | _INST(subw) 53 | _INST(sllw) 54 | _INST(srlw) 55 | _INST(sraw) 56 | 57 | _INST(addiw) 58 | _INST(slliw) 59 | _INST(srliw) 60 | _INST(sraiw) 61 | 62 | _INST(lwu) 63 | _INST(ld) 64 | 65 | _INST(sd) 66 | #endif 67 | 68 | // Integer multiplication and division extension 69 | _INST(mul) 70 | _INST(mulh) 71 | _INST(mulhsu) 72 | _INST(mulhu) 73 | _INST(div) 74 | _INST(divu) 75 | _INST(rem) 76 | _INST(remu) 77 | 78 | // RV64 only 79 | #ifdef KXEMU_ISA64 80 | _INST(mulw) 81 | _INST(divw) 82 | _INST(divuw) 83 | _INST(remw) 84 | _INST(remuw) 85 | #endif 86 | 87 | // Atomic extension 88 | _INST(lr_w) 89 | _INST(sc_w) 90 | _INST(amoswap_w) 91 | _INST(amoadd_w) 92 | _INST(amoxor_w) 93 | _INST(amoand_w) 94 | _INST(amoor_w) 95 | _INST(amomin_w) 96 | _INST(amomax_w) 97 | _INST(amominu_w) 98 | _INST(amomaxu_w) 99 | #ifdef KXEMU_ISA64 100 | _INST(lr_d) 101 | _INST(sc_d) 102 | _INST(amoswap_d) 103 | _INST(amoadd_d) 104 | _INST(amoxor_d) 105 | _INST(amoand_d) 106 | _INST(amoor_d) 107 | _INST(amomin_d) 108 | _INST(amomax_d) 109 | _INST(amominu_d) 110 | _INST(amomaxu_d) 111 | #endif 112 | 113 | // Compressed extension 114 | _INST(c_lwsp) 115 | _INST(c_swsp) 116 | 117 | _INST(c_lw) 118 | _INST(c_sw) 119 | 120 | _INST(c_j) 121 | _INST(c_jal) 122 | _INST(c_jr) 123 | _INST(c_jalr) 124 | 125 | _INST(c_beqz) 126 | _INST(c_bnez) 127 | 128 | _INST(c_li) 129 | _INST(c_lui) 130 | 131 | _INST(c_addi) 132 | _INST(c_addi16sp) 133 | _INST(c_addi4spn) 134 | 135 | _INST(c_slli) 136 | _INST(c_srli) 137 | _INST(c_srai) 138 | _INST(c_andi) 139 | 140 | _INST(c_mv) 141 | _INST(c_add) 142 | _INST(c_sub) 143 | _INST(c_xor) 144 | _INST(c_or) 145 | _INST(c_and) 146 | _INST(c_nop) 147 | 148 | #ifdef KXEMU_ISA64 149 | _INST(c_ldsp) 150 | _INST(c_sdsp) 151 | 152 | _INST(c_ld) 153 | _INST(c_sd) 154 | 155 | _INST(c_addiw) 156 | _INST(c_addw) 157 | _INST(c_subw) 158 | #endif 159 | 160 | // Privileged mode 161 | _INST(ecall) 162 | _INST(mret) 163 | _INST(sret) 164 | _INST(ebreak) 165 | _INST(wfi) 166 | 167 | _INST(sfence_vma) 168 | 169 | // Zicsr exntension 170 | _INST(csrrw) 171 | _INST(csrrs) 172 | _INST(csrrc) 173 | _INST(csrrwi) 174 | _INST(csrrsi) 175 | _INST(csrrci) 176 | 177 | #undef _INST 178 | -------------------------------------------------------------------------------- /include/cpu/word.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_CPU_WORD_H__ 2 | #define __KXEMU_CPU_WORD_H__ 3 | 4 | #include "config/config.h" 5 | 6 | #include 7 | 8 | namespace kxemu::cpu { 9 | #ifdef KXEMU_ISA64 10 | using word_t = uint64_t; 11 | using sword_t = int64_t; 12 | using dword_t = unsigned __int128; 13 | using sdword_t = __int128; 14 | #else 15 | using word_t = uint32_t; 16 | using sword_t = int32_t; 17 | using dword_t = uint64_t; 18 | using sdword_t = int64_t; 19 | #endif 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_DEBUG_H__ 2 | #define __KXEMU_DEBUG_H__ 3 | 4 | #include "macro.h" 5 | #include 6 | #include 7 | 8 | #define SELF_PROTECT(cond, ...) \ 9 | do { \ 10 | if (unlikely(!(cond))) { \ 11 | fprintf(stderr, FMT_FG_RED "[SELF-PROTECT][%s:%d %s]\nASSERT FAILED: %s\nThere must be wrong in your implemention. Please check.\n",\ 12 | __FILE__, __LINE__, __func__, #cond); \ 13 | fprintf(stderr, __VA_ARGS__); \ 14 | fprintf(stderr, FMT_FG_RESET "\n"); \ 15 | exit(1); \ 16 | } \ 17 | } while(0); 18 | 19 | #define NOT_IMPLEMENTED() \ 20 | do { \ 21 | printf(FMT_FG_RED "[NOT-IMPLEMENTED][%s:%d %s]\nNot implemented yet.\n" FMT_FG_RESET, __FILE__, __LINE__, __func__); \ 22 | exit(1); \ 23 | } while(0); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/device/bus.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_DEVICE_BUS_H__ 2 | #define __KXEMU_DEVICE_BUS_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace kxemu::device { 9 | 10 | using word_t = uint64_t; 11 | 12 | enum AMO { 13 | AMO_SWAP, 14 | AMO_ADD, 15 | AMO_AND, 16 | AMO_OR, 17 | AMO_XOR, 18 | AMO_MIN, 19 | AMO_MAX, 20 | AMO_MINU, 21 | AMO_MAXU 22 | }; 23 | 24 | class MemoryMap { 25 | public: 26 | virtual ~MemoryMap() {}; 27 | 28 | virtual word_t read(word_t offset, int size) = 0; 29 | virtual bool write(word_t offset, word_t data, int size) = 0; 30 | 31 | virtual word_t do_atomic(word_t addr, word_t data, int size, AMO amo, bool &valid) { 32 | valid = false; 33 | return 0; 34 | } 35 | 36 | virtual uint8_t *get_ptr(word_t offset) = 0; 37 | 38 | virtual const char *get_type_name() const = 0; 39 | }; 40 | 41 | class Bus { 42 | public: 43 | ~Bus(); 44 | 45 | struct MapBlock { 46 | std::string name; 47 | word_t start; 48 | word_t length; 49 | MemoryMap *map; 50 | }; 51 | std::vector memoryMaps; 52 | MapBlock *match_map(word_t addr, word_t size = 0) const; 53 | bool add_memory_map(const std::string &name, word_t start, word_t length, MemoryMap *map); 54 | void free_all(); 55 | 56 | word_t read(word_t addr, unsigned int size, bool &valid) const; 57 | bool write(word_t addr, word_t data, int size); 58 | 59 | word_t do_atomic(word_t addr, word_t data, int size, AMO amo, bool &valid); 60 | 61 | bool load_from_stream(std::istream &stream, word_t addr); 62 | bool load_from_stream(std::istream &stream, word_t addr, word_t length); 63 | bool load_from_memory(const uint8_t *src, word_t addr, word_t length); 64 | 65 | bool dump(std::ostream &stream, word_t addr, word_t length) const; 66 | 67 | bool memset(word_t addr, word_t length, uint8_t byte); 68 | uint8_t *get_ptr(word_t addr) const; 69 | word_t get_ptr_length(word_t addr) const; 70 | }; 71 | 72 | } // namespace kxemu::device 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /include/device/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_DEVICE_MEMORY_H__ 2 | #define __KXEMU_DEVICE_MEMORY_H__ 3 | 4 | #include "device/bus.h" 5 | 6 | namespace kxemu::device { 7 | 8 | class Memory : public MemoryMap { 9 | private: 10 | uint64_t length; 11 | public: 12 | Memory(uint64_t length); 13 | ~Memory(); 14 | 15 | word_t read(word_t addr, int size) override; 16 | bool write(word_t addr, word_t data, int size) override; 17 | uint8_t *get_ptr(word_t addr) override; 18 | const char *get_type_name() const override; 19 | 20 | uint8_t *data; 21 | 22 | word_t do_atomic(word_t addr, word_t data, int size, AMO amo, bool &valid) override; 23 | }; 24 | 25 | } // namespace kxemu::device 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/device/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_DEVICE_UART_H__ 2 | #define __KXEMU_DEVICE_UART_H__ 3 | 4 | #include "device/bus.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define UART_LENGTH 8 14 | 15 | namespace kxemu::device { 16 | 17 | class Uart16650: public MemoryMap { 18 | public: 19 | word_t read(word_t offset, int size) override; 20 | bool write(word_t offset, word_t data, int size) override; 21 | uint8_t *get_ptr(word_t offset) override; 22 | const char *get_type_name() const override; 23 | 24 | bool putch(uint8_t data); 25 | void set_output_stream(std::ostream &os); // send data to stream 26 | bool open_socket(const std::string &ip, int port); // open socket to send and receive data 27 | 28 | enum Mode { 29 | NONE, 30 | STREAM, 31 | SOCKET, 32 | }; 33 | 34 | ~Uart16650(); 35 | 36 | private: 37 | int mode = Mode::NONE; 38 | std::ostream *stream = nullptr; 39 | 40 | int sendSocket = -1; 41 | int recvSocket = -1; 42 | 43 | std::atomic uartSocketRunning; 44 | std::thread *recvThread; 45 | void recv_thread_loop(); 46 | 47 | std::mutex mtx; 48 | std::queue queue; // FIFO buffer 49 | 50 | void send_byte(uint8_t c); 51 | 52 | // Divisor Latch Register 53 | // [0-7] RW - Divisor Latch 54 | uint8_t lsb = 0x00; // Divisor Latch Low 55 | uint8_t msb = 0x00; // Divisor Latch High 56 | 57 | // Interrupt Enable Register 58 | // [0] RW - Enable Received Data Available Interrupt 59 | // [1] RW - Enable Transmitter Holding Register Empty Interrupt 60 | // [2] RW - Enable Receiver Line Status Interrupt 61 | // [3] RW - Enable Modem Status Interrupt 62 | // [4-7] RW - Reserved 63 | uint8_t ier = 0b00000000; // reset value 64 | 65 | // Interrupt Identification Register 66 | // [0-3] R - Interrupt Identification 67 | // [4-5] R - 0 68 | // [6-7] R - 1 69 | uint8_t iir = 0b11000001; // reset value 70 | 71 | // FIFO Control Register 72 | // [0] W - Ignored 73 | // [1] W - Clear Receive FIFO 74 | // [2] W - Clear Transmit FIFO 75 | // [3-5] W - Ignored 76 | // [6-7] R - Receiver FIFO Trigger Level 77 | uint8_t fcr = 0b11000000; // reset value 78 | 79 | // Line Control Register 80 | // [0-1] RW - Word Length 81 | // [2] RW - Stop Bit 82 | // [3] RW - Parity Enable 83 | // [4] RW - Parity Type 84 | // [5] RW - Strick Parity 85 | // [6] RW - Break Control 86 | // [7] RW - Divisor Latch Access Bit 87 | uint8_t lcr = 0b00000011; // reset value 88 | 89 | // Line Status Register 90 | // [0] R - Data Ready 91 | // [1] R - Overrun Error 92 | // [2] R - Parity Error 93 | // [3] R - Framing Error 94 | // [4] R - Break Interrupt 95 | // [5] R - Transmitter Holding Register Empty 96 | // [6] R - Transmitter Empty 97 | // [7] R - FIFO Data Error 98 | uint8_t lsr; 99 | 100 | // NOTE: The following registers are not implemented 101 | // Modem Status Register 102 | // [0] R - Delta Clear to Send 103 | // [1] R - Delta Data Set Ready 104 | // [2] R - Trailing Edge Ring Indicator 105 | // [3] R - Delta Data Carrier Detect 106 | uint8_t msr; 107 | }; 108 | 109 | } // namespace kxemu::device 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /include/isa/isa.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_ISA_ISA_H__ 2 | #define __KXEMU_ISA_ISA_H__ 3 | 4 | #include "config/config.h" 5 | #include "cpu/cpu.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace kxemu::isa { 11 | #if defined(KXEMU_ISA32) 12 | using word_t = uint32_t; 13 | #elif defined(KXEMU_ISA64) 14 | using word_t = uint64_t; 15 | #endif 16 | 17 | void init(); 18 | 19 | // ISA information 20 | const char *get_isa_name(); 21 | int get_elf_expected_machine(); 22 | 23 | cpu::CPU *new_cpu(); 24 | 25 | // Register 26 | unsigned int get_gpr_count(); 27 | const char *get_gpr_name(int idx); 28 | 29 | // Disassemble 30 | unsigned int get_max_inst_len(); 31 | std::string disassemble(const uint8_t *data, const uint64_t dataSize, const word_t pc, uint64_t &instLen); 32 | } // namespace kxemu::isa 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/isa/riscv/isa.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_ISA_RISCV32_ISA_H__ 2 | #define __KXEMU_ISA_RISCV32_ISA_H__ 3 | 4 | enum RVFlag { 5 | E = 1 << 0, 6 | C = 1 << 1, 7 | M = 1 << 2, 8 | A = 1 << 3, 9 | Zicsr = 1 << 4, 10 | Priv = 1 << 5, 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/isa/word.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_ISA_WORD_H__ 2 | #define __KXEMU_ISA_WORD_H__ 3 | 4 | // DO NOT include this file in the other header files 5 | 6 | #include "config/config.h" 7 | #include 8 | #include 9 | 10 | #define FMT_WORD32 "0x%08" PRIx32 11 | #define FMT_WORD64 "0x%016" PRIx64 12 | #define FMT_HEX32 "0x%" PRIx32 13 | #define FMT_HEX64 "0x%" PRIx64 14 | #define FMT_VARU32 "%" PRIu32 15 | #define FMT_VARU64 "%" PRIu64 16 | 17 | #ifdef KXEMU_ISA64 18 | #define FMT_WORD FMT_WORD64 19 | #define FMT_VARU FMT_VARU64 20 | #define WORD_BITS 64 21 | #define WORD_WIDTH 16 22 | #else 23 | #define FMT_WORD FMT_WORD32 24 | #define FMT_VARU FMT_VARU32 25 | #define WORD_BITS 32 26 | #define WORD_WIDTH 8 27 | #endif // KXEMU_ISA64 28 | 29 | #define FMT_STREAM_WORD(word) "0x" << std::hex << std::setfill('0') << word << std::dec 30 | 31 | #endif -------------------------------------------------------------------------------- /include/kdb/cmd.h: -------------------------------------------------------------------------------- 1 | /*************************************************************** 2 | * Project Name: KXemu 3 | * File Name: include/kdb/cmd.h 4 | * Description: Define functions for kdb command line interface. 5 | Command line is just an interface for user to interact with the system and the KDB. 6 | Only do such things in the cmd::* functions: 7 | 1. Parse the arguments from the user input. 8 | 2. Open file stream and pass the stream to the corresponding functions. 9 | 3. Call the corresponding functions to do the real work. 10 | 4. Return the result code to the caller. 11 | ***************************************************************/ 12 | 13 | #ifndef __KXEMU_KDB_CMD_H__ 14 | #define __KXEMU_KDB_CMD_H__ 15 | 16 | #include "kdb/kdb.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace kxemu::kdb::cmd { 24 | using args_t = std::vector; 25 | using func_t = int (*)(const args_t &); 26 | using cmd_map_t = std::unordered_map; 27 | int find_and_run(const args_t &args, const cmd_map_t &cmdMap, std::size_t startArgs = 0); 28 | 29 | // do command function 30 | int log (const args_t &); 31 | int mem (const args_t &); 32 | int help (const args_t &); 33 | int quit (const args_t &); 34 | int source (const args_t &); 35 | int reset (const args_t &); 36 | int step (const args_t &); 37 | int run (const args_t &); 38 | int symbol (const args_t &); // print symbol table from ELF 39 | int load (const args_t &); // load image from filename given by args 40 | int info (const args_t &); // print info of cpu 41 | int uart (const args_t &); // uart command 42 | int breakpoint(const args_t &); // set breakpoint 43 | int show_mem(const args_t &); 44 | 45 | // do command return code 46 | enum Code { 47 | EmptyArgs = -2, 48 | CmdNotFound = -1, 49 | Success = 0, 50 | InvalidArgs = 1, 51 | MissingPrevOp = 2, 52 | CmdError = 3 53 | }; 54 | 55 | // load source from args 56 | extern std::string elfFileName; 57 | 58 | extern cpu::Core *currentCore; // command line current cpu core 59 | extern int coreCount; // core count of cpu 60 | 61 | using completion_func_t = std::vector (*)(); // return possible list 62 | void init_completion(); 63 | namespace completion { 64 | std::vector log_off(); 65 | }; 66 | } // cmd 67 | 68 | #endif // __KDB_CMD_H__ 69 | -------------------------------------------------------------------------------- /include/kdb/kdb.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_KDB_KDB_H__ 2 | #define __KXEMU_KDB_KDB_H__ 3 | 4 | #include "device/uart.h" 5 | #include "cpu/cpu.h" 6 | #include "device/bus.h" 7 | #include "isa/isa.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace kxemu::kdb { 18 | using word_t = isa::word_t; 19 | 20 | void init(); 21 | void deinit(); 22 | 23 | // kdb command line 24 | void cmd_init(); 25 | int run_cmd_mainloop(); 26 | int run_command(const std::string &cmd); 27 | int run_source_file(const std::string &filename); 28 | 29 | // CPU execution 30 | extern cpu::CPU *cpu; 31 | extern int returnCode; // set when a core halt 32 | void reset_cpu(); 33 | int run_cpu(); 34 | int step_core(cpu::Core *core); 35 | 36 | // Bus 37 | extern device::Bus *bus; 38 | void init_bus(); 39 | void deinit_bus(); 40 | 41 | // Uart 42 | namespace uart{ 43 | extern std::vector list; 44 | bool add(word_t base, std::ostream &os); 45 | bool add(word_t base, const std::string &ip, int port); 46 | bool puts(std::size_t index, const std::string &s); 47 | }; 48 | 49 | // Breakpoint 50 | extern std::unordered_set breakpointSet; 51 | extern bool brkTriggered; 52 | void add_breakpoint(word_t addr); 53 | 54 | // ELF format 55 | // Load ELF to memory, return 0 if failed and entry else. 56 | word_t load_elf(const std::string &filename); 57 | std::optional addr_match_symbol(word_t addr, word_t &offset); 58 | extern std::map symbolTable; 59 | extern word_t programEntry; 60 | 61 | word_t string_to_addr(const std::string &s, bool &success); 62 | 63 | // GDB connection 64 | bool start_rsp(int port); 65 | 66 | } // kdb 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/kdb/rsp.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_KDB_RSP_H__ 2 | #define __KXEMU_KDB_RSP_H__ 3 | 4 | namespace rsp { 5 | void rsp_mainloop(int port); 6 | } // rsp 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_LOG_H__ 2 | #define __KXEMU_LOG_H__ 3 | 4 | #include "macro.h" 5 | #include "config/autoconf.h" 6 | 7 | #include 8 | 9 | #include "isa/word.h" 10 | 11 | #define PUTLOG(FG, TYPE, ...) \ 12 | do { \ 13 | if (!(kxemu::logFlag & kxemu::TYPE)) break; \ 14 | printf(FG "[" #TYPE "][%s:%d %s] " FMT_FG_RESET, __FILE__, __LINE__, __func__); \ 15 | printf(__VA_ARGS__); \ 16 | printf(FMT_FG_RESET "\n"); \ 17 | } while(0); 18 | 19 | #ifdef CONFIG_LOG 20 | 21 | #define DEBUG(...) \ 22 | PUTLOG(FMT_FG_YELLOW_BLOD, DEBUG, __VA_ARGS__) \ 23 | 24 | #define INFO(...) \ 25 | PUTLOG(FMT_FG_BLUE_BLOD, INFO, __VA_ARGS__) 26 | 27 | #define WARN(...) \ 28 | PUTLOG(FMT_FG_MAGENTA_BLOD, WARN, __VA_ARGS__) 29 | 30 | #else 31 | 32 | #define DEBUG(...) 33 | #define INFO(...) 34 | #define WARN(...) 35 | 36 | #endif 37 | 38 | #define PANIC(...) \ 39 | do { \ 40 | PUTLOG(FMT_FG_RED_BLOD, PANIC, __VA_ARGS__); \ 41 | exit(1); \ 42 | } while(0); 43 | 44 | #ifdef CONFIG_HINT 45 | #define HINT(...) \ 46 | PUTLOG(FMT_FG_YELLOW_BLOD, HINT, __VA_ARGS__); 47 | #else 48 | #define HINT(...) 49 | #endif 50 | 51 | namespace kxemu { 52 | enum LogFlag { 53 | DEBUG = 1, 54 | INFO = 2, 55 | WARN = 4, 56 | PANIC = 8, 57 | HINT = 16, 58 | }; 59 | 60 | extern int logFlag; 61 | } // namespace kxemu 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /include/macro.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_MACRO_H__ 2 | #define __KXEMU_MACRO_H__ 3 | 4 | #define likely(x) __builtin_expect(!!(x), 1) 5 | #define unlikely(x) __builtin_expect(!!(x), 0) 6 | 7 | #define MACRO_TO_STRING(x) #x 8 | #define EMPTY_MACRO(x) x 9 | 10 | #define ARRLEN(array) (sizeof(array) / sizeof(array[0])) 11 | 12 | #define FMT_FG_RED "\x1b[31m" 13 | #define FMT_FG_GREEN "\x1b[32m" 14 | #define FMT_FG_YELLOW "\x1b[33m" 15 | #define FMT_FG_BLUE "\x1b[34m" 16 | #define FMT_FG_MAGENTA "\x1b[35m" 17 | #define FMT_FG_CYAN "\x1b[36m" 18 | 19 | #define FMT_FG_RED_BLOD "\x1b[1;31m" 20 | #define FMT_FG_GREEN_BLOD "\x1b[1;32m" 21 | #define FMT_FG_YELLOW_BLOD "\x1b[1;33m" 22 | #define FMT_FG_BLUE_BLOD "\x1b[1;34m" 23 | #define FMT_FG_MAGENTA_BLOD "\x1b[1;35m" 24 | #define FMT_FG_CYAN_BLOD "\x1b[1;36m" 25 | 26 | #define FMT_FG_RESET "\x1b[0m" 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/utils/decoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_UTILS_DECODER_H__ 2 | #define __KXEMU_UTILS_DECODER_H__ 3 | 4 | #include "log.h" 5 | #include "macro.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace kxemu::utils { 12 | 13 | class BitPat { 14 | private: 15 | uint64_t bits; 16 | uint64_t mask; 17 | unsigned int length; 18 | public: 19 | BitPat(const std::string &s); 20 | BitPat(BitPat &other); 21 | BitPat(BitPat &&other); 22 | unsigned int get_length() const; 23 | bool match(uint64_t data) const { // To inline this function 24 | return (data & this->mask) == this->bits; 25 | } 26 | }; 27 | 28 | template 29 | class Decoder { 30 | private: 31 | struct Entry { 32 | BitPat pattern; 33 | void (T::*action)(); 34 | }; 35 | std::vector patterns; 36 | // Entry patterns[N]; 37 | T *obj; 38 | public: 39 | unsigned int fixedLength = 0; 40 | 41 | void init(T *obj) { 42 | this->obj = obj; 43 | } 44 | 45 | void add(const std::string &pattern, void (T::*action)()) { 46 | if (patterns.empty()) { 47 | fixedLength = BitPat(pattern).get_length(); 48 | } else { 49 | if (fixedLength != BitPat(pattern).get_length()) { 50 | PANIC("Pattern length mismatch"); 51 | } 52 | } 53 | patterns.push_back({BitPat(pattern), action}); 54 | } 55 | 56 | bool decode_and_exec(uint64_t bits) const { 57 | for (const Entry &p : patterns) { 58 | if (unlikely(p.pattern.match(bits))) { 59 | (this->obj->*(p.action))(); 60 | return true; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | unsigned int count() const { 67 | return patterns.size(); 68 | } 69 | }; 70 | 71 | } // namespace kxemu::utils 72 | 73 | #endif -------------------------------------------------------------------------------- /include/utils/gdb-rsp.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_UTILS_RSP_H__ 2 | #define __KXEMU_UTILS_RSP_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // This is an implementation of GDB Remote Serial Protocol (RSP) 10 | class RSP { 11 | private: 12 | int server; 13 | int client; 14 | struct sockaddr_in address; 15 | std::queue commandQueue; 16 | public: 17 | enum CommandType { 18 | Empty, // The command queue is empty. 19 | Error, // Meet error when call next_command(). 20 | Continue, // c and C - Continue the target. 21 | Step, // s and S - Step the target. 22 | GeneralReg, // g and G - Read or write general registers. 23 | Kill, // k - Kill the target. The semantics of this are not clearly defined. Most targets should probably ignore it. 24 | SpecificReg, // p and P - Read or write a specific register. 25 | Memory, // m and M - Read or write main memory. 26 | Offset, // qOffset - Report the offsets to use when relocating downloaded code. 27 | Symbol, // qSymbol - Request any symbol table data. A minimal implementation should request no data. 28 | LoadData, // X - Load binary data. 29 | Breakpoint, // z and Z - Clear or set breakpoints or watchpoints. 30 | Report, // ? - Report why the target halted. 31 | }; 32 | 33 | RSP(); 34 | ~RSP(); 35 | 36 | bool open_rsp(int port); 37 | bool wait_client(); 38 | int next_command(std::vector &args, bool blocked); 39 | int send_reply(int cmd); 40 | int send_reply(const std::string &reply); 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /include/utils/task-timer.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_UTILS_TIMER_H__ 2 | #define __KXEMU_UTILS_TIMER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace kxemu::utils { 12 | 13 | class TaskTimer { 14 | private: 15 | std::condition_variable cv; 16 | std::mutex mtx; 17 | std::thread *timerThread; 18 | 19 | using task_t = std::function; 20 | struct Task { 21 | std::chrono::time_point timepoint; 22 | task_t task; 23 | unsigned int id; 24 | 25 | bool operator<(const Task &other) const { 26 | return this->timepoint > other.timepoint; 27 | } 28 | }; 29 | std::priority_queue tasks; 30 | unsigned int counter; 31 | std::unordered_set removedTasks; 32 | 33 | bool running; 34 | void timer_thread(); 35 | 36 | public: 37 | TaskTimer(); 38 | ~TaskTimer(); 39 | 40 | void start_thread(); 41 | 42 | unsigned int add_task(uint64_t delay, task_t task); 43 | void remove_task(unsigned int id); 44 | }; 45 | 46 | } // namespace kxemu::utils 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/utils/tcp-server.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_UTILS_TCP_SERVER_H__ 2 | #define __KXEMU_UTILS_TCP_SERVER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class TCPServer { 14 | public: 15 | TCPServer(); 16 | ~TCPServer(); 17 | 18 | bool start(const std::string &ip, int port); 19 | int accept_connection(); 20 | ssize_t receive(int client, uint8_t *buffer, std::size_t bufferSize); 21 | ssize_t send(int client, const uint8_t *buffer, std::size_t bufferSize); 22 | void close_connection(int client); 23 | void stop(); 24 | private: 25 | int serverSocket; 26 | struct sockaddr_in address; 27 | bool isRunning; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/utils/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __KXEMU_UTILS_UTILS_H__ 2 | #define __KXEMU_UTILS_UTILS_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace kxemu::utils { 11 | uint64_t string_to_unsigned(const std::string &s); 12 | uint64_t string_to_unsigned(const std::string &s, bool &success); 13 | std::vector string_split(const std::string &s, const char delim); 14 | 15 | using timepoint_t = std::chrono::time_point; 16 | timepoint_t get_current_timepoint(); 17 | uint64_t timepoint_to_ns(const timepoint_t &tp); 18 | } 19 | 20 | #endif // __UTILS_UTILS_H__ 21 | -------------------------------------------------------------------------------- /scripts/config.mk: -------------------------------------------------------------------------------- 1 | CONFIG_FILE = ./configs/include/config/auto.conf 2 | CONFIG_DIR = ./configs 3 | CONF = ./tools/kconfig/build/conf 4 | MCONF = ./tools/kconfig/build/mconf 5 | 6 | ifeq ($(wildcard $(CONFIG_FILE)),) 7 | $(warning $(CONFIG_FILE) not found, run make menuconfig first) 8 | endif 9 | 10 | -include $(CONFIG_FILE) 11 | 12 | $(CONF): 13 | @$(MAKE) -C ./tools/kconfig NAME=conf 14 | 15 | $(MCONF): 16 | @$(MAKE) -C ./tools/kconfig NAME=mconf 17 | 18 | menuconfig: $(CONF) $(MCONF) 19 | @ cd $(CONFIG_DIR) && ../$(MCONF) -s Kconfig 20 | @ cd $(CONFIG_DIR) && ../$(CONF) -s --syncconfig Kconfig 21 | @ mkdir -p ./include/generated 22 | @ cp $(CONFIG_DIR)/include/generated/autoconf.h ./include/config 23 | -------------------------------------------------------------------------------- /scripts/decoder-gen/gen.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import List, Dict 3 | from instpat import * 4 | 5 | INST_PATTERN = re.compile(r'INSTPAT\s*\(\s*"([^"]+)"\s*,\s*([^,\s]+)\s*(?:,\s*(\d+))?\s*\)\s*;') 6 | 7 | def read_file(filename: str) -> List[List[InstPattern]]: 8 | f = open(filename, 'r') 9 | 10 | groups = [] 11 | group = [] 12 | 13 | for line in f: 14 | line = line.strip() 15 | if not line: 16 | continue 17 | 18 | if line == "---": 19 | groups.append(group) 20 | group = [] 21 | continue 22 | 23 | m = INST_PATTERN.match(line) 24 | if not m: 25 | raise ValueError(f"Invalid line '{line}'") 26 | 27 | pattern = m.group(1) 28 | name = m.group(2) 29 | t = m.group(3) 30 | 31 | if t is None: 32 | t = InstType.Both 33 | else: 34 | if t == "32": 35 | t = InstType.Only32 36 | elif t == "64": 37 | t = InstType.Only64 38 | else: 39 | raise ValueError(f"Invalid type '{t}'") 40 | group.append(InstPattern(pattern, name, t)) 41 | groups.append(group) 42 | 43 | f.close() 44 | return groups 45 | 46 | 47 | def build_decode_table(groups: List[List[InstPattern]]) -> List[Dict[int, List[InstPattern]]]: 48 | cases = [] 49 | for group in groups: 50 | case = {} 51 | for inst in group: 52 | mask = inst.mask 53 | if mask not in case: 54 | case[mask] = [] 55 | case[mask].append(inst) 56 | cases.append(case) 57 | return cases 58 | 59 | 60 | def gen_code(tables: List[Dict[int, List[InstPattern]]], functionPrefix: str, instType: str = "uint32_t") -> str: 61 | code = """#ifdef KXEMU_ISA32 62 | #define __INST32(x) x 63 | #define __INST64(x) 64 | #else 65 | #define __INST32(x) 66 | #define __INST64(x) x 67 | #endif 68 | """ 69 | for table in tables: 70 | for key in table: 71 | code += "switch (inst & " + hex(key) + ") {\n" 72 | for inst in table[key]: 73 | if inst.t == InstType.Only32: 74 | code += f" __INST32(case {hex(inst.key)}: {functionPrefix}{inst.name}(); return true;)\n" 75 | elif inst.t == InstType.Only64: 76 | code += f" __INST64(case {hex(inst.key)}: {functionPrefix}{inst.name}(); return true;)\n" 77 | else: 78 | code += f" case {hex(inst.key)}: {functionPrefix}{inst.name}(); return true;\n" 79 | code += "}\n" 80 | 81 | code += """ 82 | return false; 83 | 84 | #undef __INST32 85 | #undef __INST64 86 | """ 87 | 88 | return code 89 | -------------------------------------------------------------------------------- /scripts/decoder-gen/instpat.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class InstType(Enum): 4 | Only32 = 0 5 | Only64 = 1 6 | Both = 2 7 | 8 | class InstPattern(): 9 | def __init__(self, pattern: str, name: str, t: InstType): 10 | self.name = name 11 | self.t = t 12 | 13 | mask = 0 14 | length = 0 15 | key = 0 16 | for c in pattern: 17 | if c == ' ': 18 | continue 19 | 20 | length += 1 21 | mask <<= 1 22 | key <<= 1 23 | if c == '0': 24 | mask |= 1 25 | elif c == '1': 26 | mask |= 1 27 | key |= 1 28 | elif c in '?xX': 29 | pass 30 | else: 31 | raise ValueError(f"Invalid character '{c}' in pattern '{pattern}'") 32 | 33 | self.mask = mask 34 | self.length = length 35 | self.key = key 36 | -------------------------------------------------------------------------------- /scripts/decoder-gen/main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from gen import * 4 | 5 | def main(): 6 | parser = argparse.ArgumentParser(description="Generate instruction decode switch-case table from .instpat file") 7 | parser.add_argument("-i", "--input", type=str, required=True, help="Input .instpat file") 8 | parser.add_argument("-o", "--output", type=str, required=True) 9 | parser.add_argument("-p", "--prefix", type=str, default="") 10 | 11 | args = parser.parse_args() 12 | inputFile = args.input 13 | outputFile = args.output 14 | prefix = args.prefix 15 | 16 | groups = read_file(inputFile) 17 | tables = build_decode_table(groups) 18 | code = gen_code(tables, prefix) 19 | 20 | with open(outputFile, 'w') as f: 21 | f.write(code) 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /scripts/filelist.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HD-CSKX/KXemu/0d7589c98924b1100e634359a3b84fff8809f54b/scripts/filelist.mk -------------------------------------------------------------------------------- /scripts/isa/riscv.mk: -------------------------------------------------------------------------------- 1 | INSTPAT_SRC_DIR = ./src/cpu/riscv/instpat 2 | INSTPAT_DST_DIR = ./src/cpu/riscv/autogen 3 | INSTPAT_NAMES = base compressed 4 | 5 | INSTPAT_SRC_FILES = $(addprefix $(INSTPAT_SRC_DIR)/, $(addsuffix .instpat, $(INSTPAT_NAMES))) 6 | 7 | INSTPAT_DST_FILES = $(addprefix $(INSTPAT_DST_DIR)/, $(addsuffix -decoder.h, $(INSTPAT_NAMES))) 8 | 9 | DECODE_GEN_SCRIPT = ./scripts/decoder-gen/main.py 10 | 11 | $(INSTPAT_DST_DIR)/%-decoder.h: $(INSTPAT_SRC_DIR)/%.instpat 12 | mkdir -p $(INSTPAT_DST_DIR) 13 | python3 $(DECODE_GEN_SCRIPT) --input $< --output $@ --prefix "this->do_" 14 | 15 | instpat: $(INSTPAT_DST_FILES) 16 | 17 | kxemu: instpat 18 | -------------------------------------------------------------------------------- /scripts/kdb/dummy.kdb: -------------------------------------------------------------------------------- 1 | mem create mem 0x80000000 0x1000000 2 | reset 3 | load elf 4 | run 5 | -------------------------------------------------------------------------------- /src/cpu/riscv/cpu.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/riscv/cpu.h" 2 | #include "device/bus.h" 3 | #include "log.h" 4 | 5 | using namespace kxemu::cpu; 6 | using namespace kxemu::device; 7 | 8 | void RVCPU::init(Bus *memory, int flags, unsigned int coreCount) { 9 | if (coreCount != 1) { 10 | PANIC("RV32CPU does not support multi-core"); 11 | } 12 | this->aclint.init(coreCount); 13 | this->cores = new RVCore[coreCount]; 14 | for (unsigned int i = 0; i < coreCount; i++) { 15 | this->cores[i].init(i, memory, flags, &aclint, &taskTimer); 16 | } 17 | this->coreCount = coreCount; 18 | } 19 | 20 | void RVCPU::reset(word_t pc) { 21 | for (unsigned int i = 0; i < coreCount; i++) { 22 | cores[i].reset(pc); 23 | } 24 | aclint.reset(); 25 | } 26 | 27 | void RVCPU::step() { 28 | for (unsigned int i = 0; i < coreCount; i++) { 29 | cores[i].step(); 30 | } 31 | } 32 | 33 | bool RVCPU::is_running() { 34 | return cores[0].is_running(); 35 | } 36 | 37 | unsigned int RVCPU::core_count() { 38 | return coreCount; 39 | } 40 | 41 | RVCore *RVCPU::get_core(unsigned int coreID) { 42 | if (coreID >= coreCount) { 43 | PANIC("Core ID %d is out of range", coreID); 44 | } 45 | return &cores[coreID]; 46 | } 47 | 48 | RVCPU::~RVCPU() { 49 | delete []cores; 50 | } 51 | -------------------------------------------------------------------------------- /src/cpu/riscv/inst/local-decoder.h: -------------------------------------------------------------------------------- 1 | #define BITS(hi, lo) ((uint32_t)((this->inst >> lo) & ((1 << (hi - lo + 1)) - 1))) // Extract bits from hi to lo 2 | #define SEXT(bits, from) ((int32_t)((bits) << (32 - (from))) >> (32 - (from))) // Signed extend 3 | -------------------------------------------------------------------------------- /src/cpu/riscv/inst/mul.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/riscv/core.h" 2 | #include "./local-decoder.h" 3 | #include "cpu/word.h" 4 | #include "isa/word.h" 5 | 6 | #define RD unsigned int rd = BITS(11, 7); 7 | #define RS1 unsigned int rs1 = BITS(19, 15); 8 | #define RS2 unsigned int rs2 = BITS(24, 20); 9 | 10 | using namespace kxemu::cpu; 11 | 12 | void RVCore::do_mul() { 13 | RD; RS1; RS2; 14 | this->set_gpr(rd, this->gpr[rs1] * this->gpr[rs2]); 15 | } 16 | 17 | void RVCore::do_mulh() { 18 | RD; RS1; RS2; 19 | sdword_t res = (sdword_t)(sword_t)this->gpr[rs1] * (sword_t)this->gpr[rs2]; 20 | this->set_gpr(rd, res >> WORD_BITS); 21 | } 22 | 23 | void RVCore::do_mulhsu() { 24 | RD; RS1; RS2; 25 | dword_t res = (sdword_t)this->gpr[rs1] * (dword_t)this->gpr[rs2]; 26 | this->set_gpr(rd, res >> WORD_BITS); 27 | } 28 | 29 | void RVCore::do_mulhu() { 30 | RD; RS1; RS2; 31 | dword_t res = (dword_t)this->gpr[rs1] * (dword_t)this->gpr[rs2]; 32 | this->set_gpr(rd, res >> WORD_BITS); 33 | } 34 | 35 | void RVCore::do_div() { 36 | RD; RS1; RS2; 37 | if (this->gpr[rs2] == 0) { 38 | this->set_gpr(rd, -1); 39 | } else { 40 | this->set_gpr(rd, (sword_t)this->gpr[rs1] / (sword_t)this->gpr[rs2]); 41 | } 42 | } 43 | 44 | void RVCore::do_divu() { 45 | RD; RS1; RS2; 46 | if (this->gpr[rs2] == 0) { 47 | this->set_gpr(rd, -1); 48 | } else { 49 | this->set_gpr(rd, this->gpr[rs1] / this->gpr[rs2]); 50 | } 51 | } 52 | 53 | void RVCore::do_rem() { 54 | RD; RS1; RS2; 55 | if (this->gpr[rs2] == 0) { 56 | this->set_gpr(rd, rs1); 57 | } else { 58 | this->set_gpr(rd, (sword_t)this->gpr[rs1] % (sword_t)this->gpr[rs2]); 59 | } 60 | } 61 | 62 | void RVCore::do_remu() { 63 | RD; RS1; RS2; 64 | if (this->gpr[rs2] == 0) { 65 | this->set_gpr(rd, rs2); 66 | } else { 67 | this->set_gpr(rd, this->gpr[rs1] % this->gpr[rs2]); 68 | } 69 | } 70 | 71 | #ifdef KXEMU_ISA64 72 | 73 | void RVCore::do_mulw() { 74 | RD; RS1; RS2; 75 | int32_t res = (int32_t)this->gpr[rs1] * (int32_t)this->gpr[rs2]; 76 | this->set_gpr(rd, res); 77 | } 78 | 79 | void RVCore::do_divw() { 80 | RD; RS1; RS2; 81 | if (this->gpr[rs2] == 0) { 82 | this->set_gpr(rd, -1); 83 | } else { 84 | this->set_gpr(rd, (int32_t)this->gpr[rs1] / (int32_t)this->gpr[rs2]); 85 | } 86 | } 87 | 88 | void RVCore::do_divuw() { 89 | RD; RS1; RS2; 90 | if (this->gpr[rs2] == 0) { 91 | this->set_gpr(rd, -1); 92 | } else { 93 | this->set_gpr(rd, (uint32_t)this->gpr[rs1] / (uint32_t)this->gpr[rs2]); 94 | } 95 | } 96 | 97 | void RVCore::do_remw() { 98 | RD; RS1; RS2; 99 | if (this->gpr[rs2] == 0) { 100 | this->set_gpr(rd, rs1); 101 | } else { 102 | this->set_gpr(rd, (int32_t)this->gpr[rs1] % (int32_t)this->gpr[rs2]); 103 | } 104 | } 105 | 106 | void RVCore::do_remuw() { 107 | RD; RS1; RS2; 108 | if (this->gpr[rs2] == 0) { 109 | this->set_gpr(rd, rs2); 110 | } else { 111 | this->set_gpr(rd, (uint32_t)this->gpr[rs1] % (uint32_t)this->gpr[rs2]); 112 | } 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/cpu/riscv/inst/privileged.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/riscv/core.h" 2 | #include "cpu/riscv/def.h" 3 | #include "cpu/word.h" 4 | #include "isa/word.h" 5 | #include "log.h" 6 | 7 | #include 8 | 9 | using namespace kxemu::cpu; 10 | 11 | void RVCore::do_ecall() { 12 | word_t code; 13 | switch (this->privMode) { 14 | case PrivMode::MACHINE: code = TRAP_ECALL_M; break; 15 | case PrivMode::SUPERVISOR: code = TRAP_ECALL_S; break; 16 | case PrivMode::USER: code = TRAP_ECALL_U; break; 17 | default: PANIC("Invalid current privileged mode."); return; 18 | } 19 | trap(code); 20 | } 21 | 22 | // An MRET or SRET instruction is used to return from a trap in M-mode or S-mode respectively. When 23 | // executing an xRET instruction, supposing xPP holds the value y, xIE is set to xPIE; the privilege mode is 24 | // changed to y; xPIE is set to 1; and xPP is set to the least-privileged supported mode (U if U-mode is 25 | // implemented, else M). If y≠M, xRET also sets MPRV=0. 26 | void RVCore::do_mret() { 27 | word_t mstatus = *this->mstatus; 28 | 29 | // change to previous privilege mode 30 | word_t mpp = ((mstatus) >> STATUS_MPP_OFF) & 0x3; 31 | this->privMode = mpp; 32 | 33 | // set mstatus.MIE to mstatus.MPIE 34 | word_t mpie = (mstatus >> STATUS_MPIE_OFF) & 0x1; 35 | mstatus = (mstatus & ~STATUS_MIE_MASK) | (mpie << STATUS_MIE_OFF); 36 | 37 | // set mstatus.MPIE to 1 38 | mstatus |= STATUS_MPIE_MASK; 39 | 40 | // set mstatus.MPP to the lowest privilege mode 41 | mstatus = (mstatus & ~STATUS_MPP_MASK) | (PrivMode::USER << STATUS_MPP_OFF); 42 | 43 | *this->mstatus = mstatus; 44 | 45 | this->npc = this->csr.read_csr(CSR_MEPC); 46 | } 47 | 48 | void RVCore::do_sret() { 49 | word_t mstatus = *this->mstatus; 50 | 51 | // change to previous privilege mode 52 | word_t spp = (mstatus >> STATUS_SPP_OFF) & 0x1; 53 | this->privMode = spp ? PrivMode::SUPERVISOR : PrivMode::USER; 54 | 55 | // set mstatus.SIE to mstatus.SPIE 56 | word_t spie = (mstatus >> STATUS_SPIE_OFF) & 0x1; 57 | mstatus = (mstatus & ~STATUS_SIE_MASK) | (spie << STATUS_SIE_OFF); 58 | 59 | // set mstatus.SPIE to 1 60 | mstatus |= STATUS_SPIE_MASK; 61 | 62 | // set mstatus.SPP to the lowest privilege mode 63 | mstatus &= ~(1 << STATUS_SPP_OFF); 64 | 65 | // set mstatus.MPRV to 0 66 | mstatus &= ~STATUS_MPRV_OFF; 67 | 68 | *this->mstatus = mstatus; 69 | this->npc = this->csr.read_csr(CSR_SEPC); 70 | } 71 | 72 | void RVCore::do_ebreak() { 73 | INFO("EBREAK at pc=" FMT_WORD, this->pc); 74 | this->state = HALT; 75 | this->haltCode = this->gpr[10]; 76 | this->haltPC = this->pc; 77 | 78 | // breakpoint trap 79 | this->trap(TRAP_BREAKPOINT); 80 | } 81 | 82 | void RVCore::do_wfi() { 83 | while (!this->scan_interrupt()) { 84 | // wait for interrupt 85 | } 86 | } 87 | 88 | void RVCore::do_sfence_vma() { 89 | if (this->privMode == PrivMode::USER) { 90 | do_invalid_inst(); 91 | return; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/cpu/riscv/inst/zicsr.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/riscv/core.h" 2 | #include "cpu/riscv/def.h" 3 | 4 | #include "./local-decoder.h" 5 | 6 | #include 7 | 8 | #define CSR unsigned int csrAddr = BITS(31, 20) 9 | #define RD unsigned int rd = BITS(11, 7) 10 | #define RS1 unsigned int rs1 = BITS(19, 15) 11 | #define IMM word_t imm = BITS(19, 15) 12 | 13 | #define REQUIRE_WRITABLE do {if (IS_CSR_READ_ONLY(csrAddr)) {do_invalid_inst(); return;}} while(0); 14 | #define CHECK_SUCCESS do{if (!s) {do_invalid_inst(); return;}} while(0); 15 | 16 | using namespace kxemu::cpu; 17 | 18 | void RVCore::init_csr() { 19 | this->csr.init(this->coreID, std::bind(&RVCore::get_uptime, this)); 20 | this->csr.init_callbacks(std::bind(&RVCore::update_stimecmp, this)); 21 | } 22 | 23 | word_t RVCore::read_csr(unsigned int addr, bool &valid) { 24 | return this->csr.read_csr(addr, valid); 25 | } 26 | 27 | bool RVCore::write_csr(unsigned int addr, word_t value) { 28 | return this->csr.write_csr(addr, value); 29 | } 30 | 31 | void RVCore::do_csrrw() { 32 | CSR; RD; RS1; 33 | 34 | REQUIRE_WRITABLE; 35 | 36 | bool s; 37 | if (rd != 0) { 38 | word_t value = this->read_csr(csrAddr, s); 39 | CHECK_SUCCESS; 40 | this->set_gpr(rd, value); 41 | } 42 | 43 | s = this->write_csr(csrAddr, this->get_gpr(rs1)); 44 | CHECK_SUCCESS; 45 | } 46 | 47 | void RVCore::do_csrrs() { 48 | CSR; RD; RS1; 49 | bool s; 50 | word_t value = this->read_csr(csrAddr, s); 51 | CHECK_SUCCESS; 52 | this->set_gpr(rd, value); 53 | if (rs1 != 0) { 54 | REQUIRE_WRITABLE; 55 | s = this->write_csr(csrAddr, value | this->get_gpr(rs1)); 56 | CHECK_SUCCESS; 57 | } 58 | } 59 | 60 | void RVCore::do_csrrc() { 61 | CSR; RD; RS1; 62 | bool s; 63 | word_t value = this->read_csr(csrAddr, s); 64 | CHECK_SUCCESS; 65 | this->set_gpr(rd, value); 66 | if (rs1 != 0) { 67 | REQUIRE_WRITABLE; 68 | s = this->write_csr(csrAddr, value & (~this->get_gpr(rs1))); 69 | CHECK_SUCCESS; 70 | } 71 | } 72 | 73 | void RVCore::do_csrrwi() { 74 | CSR; RD; IMM; 75 | 76 | REQUIRE_WRITABLE; 77 | 78 | bool s; 79 | if (rd != 0) { 80 | word_t value = this->read_csr(csrAddr, s); 81 | CHECK_SUCCESS; 82 | this->set_gpr(rd, value); 83 | } 84 | 85 | s = this->write_csr(csrAddr, imm); 86 | CHECK_SUCCESS; 87 | } 88 | 89 | void RVCore::do_csrrsi() { 90 | CSR; RD; IMM; 91 | bool s; 92 | word_t value = this->read_csr(csrAddr, s); 93 | CHECK_SUCCESS; 94 | this->set_gpr(rd, value); 95 | if (imm != 0) { 96 | REQUIRE_WRITABLE; 97 | s = this->write_csr(csrAddr, value | imm); 98 | CHECK_SUCCESS; 99 | } 100 | } 101 | 102 | void RVCore::do_csrrci() { 103 | CSR; RD; IMM; 104 | bool s; 105 | word_t value = this->read_csr(csrAddr, s); 106 | CHECK_SUCCESS; 107 | this->set_gpr(rd, value); 108 | if (imm != 0) { 109 | REQUIRE_WRITABLE; 110 | s = this->write_csr(csrAddr, value & (~imm)); 111 | CHECK_SUCCESS; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/cpu/riscv/instpat/compressed.instpat: -------------------------------------------------------------------------------- 1 | INSTPAT("0000 0000 0000 0000", invalid_inst); 2 | INSTPAT("100 1 00000 00000 10", ebreak); 3 | INSTPAT("0000 0000 0000 0001", c_nop); 4 | 5 | INSTPAT("010 ? ????? ????? 10", c_lwsp); 6 | INSTPAT("110 ? ????? ????? 10", c_swsp); 7 | 8 | INSTPAT("010 ??? ??? ?? ??? 00", c_lw); 9 | INSTPAT("110 ??? ??? ?? ??? 00", c_sw); 10 | 11 | --- 12 | 13 | INSTPAT("101 ??????????? 01" , c_j ); 14 | INSTPAT("100 0 ????? 00000 10", c_jr ); 15 | INSTPAT("100 1 ????? 00000 10", c_jalr); 16 | 17 | INSTPAT("110 ??? ??? ?? ??? 01", c_beqz); 18 | INSTPAT("111 ??? ??? ?? ??? 01", c_bnez); 19 | 20 | INSTPAT("000 ? ????? ????? 01", c_addi); 21 | INSTPAT("011 ? 00010 ????? 01", c_addi16sp); 22 | INSTPAT("000 ???????? ??? 00" , c_addi4spn); 23 | 24 | --- 25 | 26 | INSTPAT("010 ? ????? ????? 01", c_li ); 27 | INSTPAT("011 ? ????? ????? 01", c_lui); 28 | 29 | INSTPAT("000 ? ????? ????? 10", c_slli, 64); 30 | INSTPAT("100 ? 00 ??? ????? 01", c_srli, 64); 31 | INSTPAT("100 ? 01 ??? ????? 01", c_srai, 64); 32 | INSTPAT("000 0 ????? ????? 10", c_slli, 32); 33 | INSTPAT("100 0 00 ??? ????? 01", c_srli, 32); 34 | INSTPAT("100 0 01 ??? ????? 01", c_srai, 32); 35 | INSTPAT("100 ? 10 ??? ????? 01", c_andi); 36 | 37 | INSTPAT("100 0 ????? ????? 10", c_mv ); 38 | INSTPAT("100 1 ????? ????? 10", c_add); 39 | 40 | INSTPAT("100 0 11 ??? 00 ??? 01", c_sub); 41 | INSTPAT("100 0 11 ??? 01 ??? 01", c_xor); 42 | INSTPAT("100 0 11 ??? 10 ??? 01", c_or ); 43 | INSTPAT("100 0 11 ??? 11 ??? 01", c_and); 44 | 45 | INSTPAT("011 ? ????? ????? 10", c_ldsp, 64); 46 | INSTPAT("111 ? ????? ????? 10", c_sdsp, 64); 47 | 48 | INSTPAT("011 ??? ??? ?? ??? 00", c_ld, 64); 49 | INSTPAT("111 ??? ??? ?? ??? 00", c_sd, 64); 50 | 51 | INSTPAT("100 1 11 ??? 01 ??? 01", c_addw, 64); 52 | INSTPAT("100 1 11 ??? 00 ??? 01", c_subw, 64); 53 | 54 | INSTPAT("001 ??????????? 01", c_addiw, 64); 55 | INSTPAT("001 ??????????? 01", c_jal, 32); 56 | -------------------------------------------------------------------------------- /src/cpu/riscv/privileged/trap.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/riscv/core.h" 2 | #include "cpu/riscv/def.h" 3 | 4 | using namespace kxemu::cpu; 5 | 6 | // To support nested traps, each privilege mode x that can respond to interrupts has a two-level stack of 7 | // interrupt-enable bits and privilege modes. xPIE holds the value of the interrupt-enable bit active prior 8 | // to the trap, and xPP holds the previous privilege mode. The xPP fields can only hold privilege modes 9 | // up to x, so MPP is two bits wide and SPP is one bit wide. When a trap is taken from privilege mode y 10 | // into privilege mode x, xPIE is set to the value of xIE; xIE is set to 0; and xPP is set to y. 11 | void RVCore::trap(word_t code, word_t value) { 12 | this->trapFlag = true; 13 | bool deleg; 14 | #ifdef KXEMU_ISA64 15 | deleg = *this->medeleg & (1 << code); 16 | #else 17 | if (code >= 32) { 18 | deleg = *this->medelegh & (1 << (code - 32)); 19 | } else { 20 | deleg = *this->medeleg & (1 << code); 21 | } 22 | #endif 23 | 24 | word_t cause = code & ~CAUSE_INTERRUPT_MASK; 25 | word_t vec; 26 | if (deleg) { 27 | this->csr.write_csr(CSR_SEPC, this->pc); 28 | this->csr.write_csr(CSR_SCAUSE, cause); 29 | this->csr.write_csr(CSR_STVAL, value); 30 | vec = this->csr.read_csr(CSR_STVEC); 31 | 32 | word_t mstatus = this->csr.read_csr(CSR_MSTATUS); 33 | mstatus = (mstatus & ~STATUS_SPP_MASK) | ((this->privMode != PrivMode::USER) << STATUS_SPP_OFF); 34 | mstatus = (mstatus & ~STATUS_SPIE_MASK) | ((mstatus & STATUS_SIE_MASK) << (STATUS_SPIE_OFF - STATUS_SIE_OFF)); 35 | mstatus = (mstatus & ~STATUS_SIE_MASK); 36 | this->csr.write_csr(CSR_MSTATUS, mstatus); 37 | 38 | this->privMode = PrivMode::SUPERVISOR; 39 | } else { 40 | this->csr.write_csr(CSR_MEPC, this->pc); 41 | this->csr.write_csr(CSR_MCAUSE, cause); 42 | this->csr.write_csr(CSR_MTVAL, value); 43 | vec = this->csr.read_csr(CSR_MTVEC); 44 | 45 | word_t mstatus = this->csr.read_csr(CSR_MSTATUS); 46 | mstatus = (mstatus & ~STATUS_MPP_MASK) | (this->privMode << STATUS_MPP_OFF); 47 | mstatus = (mstatus & ~STATUS_MPIE_MASK) | ((mstatus & STATUS_MIE_MASK) << (STATUS_MPIE_OFF - STATUS_MIE_OFF)); 48 | mstatus = (mstatus & ~STATUS_MIE_MASK); 49 | this->csr.write_csr(CSR_MSTATUS, mstatus); 50 | 51 | this->privMode = PrivMode::MACHINE; 52 | } 53 | 54 | word_t vecMode = vec & TVEC_MODE_MASK; 55 | if (vecMode == TVEC_MODE_VECTORED) { 56 | this->npc = (vec & ~TVEC_MODE_MASK) + (code << 2); 57 | } else { 58 | this->npc = vec & ~TVEC_MODE_MASK; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/device/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "device/memory.h" 2 | #include "device/bus.h" 3 | #include "log.h" 4 | #include 5 | 6 | using namespace kxemu::device; 7 | 8 | Memory::Memory(uint64_t length) { 9 | this->length = length; 10 | data = new uint8_t[length]; 11 | } 12 | 13 | Memory::~Memory() { 14 | delete[] data; 15 | } 16 | 17 | word_t Memory::read(word_t offset, int size) { 18 | switch (size) { 19 | case 1: return *(uint8_t *)(data + offset); 20 | case 2: return *(uint16_t *)(data + offset); 21 | case 4: return *(uint32_t *)(data + offset); 22 | case 8: return *(uint64_t *)(data + offset); 23 | default: PANIC("Invalid size=%d", size); return -1; 24 | } 25 | } 26 | 27 | bool Memory::write(word_t offset, word_t data, int size) { 28 | switch (size) { 29 | case 1: *(uint8_t *)(this->data + offset) = (uint8_t )data; break; 30 | case 2: *(uint16_t *)(this->data + offset) = (uint16_t)data; break; 31 | case 4: *(uint32_t *)(this->data + offset) = (uint32_t)data; break; 32 | case 8: *(uint64_t *)(this->data + offset) = (uint64_t)data; break; 33 | default: PANIC("Invalid size=%d", size); return false; 34 | } 35 | return true; 36 | } 37 | 38 | uint8_t *Memory::get_ptr(word_t offset) { 39 | return data + offset; 40 | } 41 | 42 | const char *Memory::get_type_name() const { 43 | return "memory"; 44 | } 45 | 46 | #define AMO_FUNC(name, op) \ 47 | switch (size) { \ 48 | case 1: return op(( uint8_t *)p, data, __ATOMIC_SEQ_CST); \ 49 | case 2: return op((uint16_t *)p, data, __ATOMIC_SEQ_CST); \ 50 | case 4: return op((uint32_t *)p, data, __ATOMIC_SEQ_CST); \ 51 | case 8: return op((uint64_t *)p, data, __ATOMIC_SEQ_CST); \ 52 | default: valid = false; return -1; \ 53 | } \ 54 | 55 | #define AMO_FUNCS(name, op) \ 56 | switch (size) { \ 57 | case 1: return op(( int8_t *)p, ( int8_t)data, __ATOMIC_SEQ_CST); \ 58 | case 2: return op((int16_t *)p, (int16_t)data, __ATOMIC_SEQ_CST); \ 59 | case 4: return op((int32_t *)p, (int32_t)data, __ATOMIC_SEQ_CST); \ 60 | case 8: return op((int64_t *)p, (int64_t)data, __ATOMIC_SEQ_CST); \ 61 | default: valid = false; return -1; \ 62 | } \ 63 | 64 | word_t Memory::do_atomic(word_t offset, word_t data, int size, AMO amo, bool &valid) { 65 | void *p = this->data + offset; 66 | valid = true; 67 | switch (amo) { 68 | case AMO_SWAP: AMO_FUNC (swap, __atomic_exchange_n); 69 | case AMO_ADD: AMO_FUNC (add, __atomic_fetch_add ); 70 | case AMO_AND: AMO_FUNC (and, __atomic_fetch_and ); 71 | case AMO_OR: AMO_FUNC (or, __atomic_fetch_or ); 72 | case AMO_XOR: AMO_FUNC (xor, __atomic_fetch_xor ); 73 | case AMO_MINU: AMO_FUNC (minu, __atomic_fetch_min ); 74 | case AMO_MAXU: AMO_FUNC (maxu, __atomic_fetch_max ); 75 | case AMO_MIN: AMO_FUNCS(min, __atomic_fetch_min ); 76 | case AMO_MAX: AMO_FUNCS(max, __atomic_fetch_max ); 77 | default: valid = false; return 0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/isa/riscv/disassemble.cpp: -------------------------------------------------------------------------------- 1 | #include "isa/isa.h" 2 | 3 | #include "llvm/MC/MCAsmInfo.h" 4 | #include "llvm/MC/MCContext.h" 5 | #include "llvm/MC/MCDisassembler/MCDisassembler.h" 6 | #include "llvm/MC/MCInstPrinter.h" 7 | #include 8 | #if LLVM_VERSION_MAJOR >= 14 9 | #include "llvm/MC/TargetRegistry.h" 10 | #if LLVM_VERSION_MAJOR >= 15 11 | #include "llvm/MC/MCSubtargetInfo.h" 12 | #endif 13 | #else 14 | #include "llvm/Support/TargetRegistry.h" 15 | #endif 16 | #include "llvm/Support/TargetSelect.h" 17 | 18 | using namespace llvm; 19 | 20 | static MCDisassembler *disassembler; 21 | static MCInstPrinter *gIP = nullptr; 22 | static MCSubtargetInfo *gSTI = nullptr; 23 | 24 | void init_disasm() { 25 | InitializeAllTargetInfos(); 26 | InitializeAllTargetMCs(); 27 | InitializeAllDisassemblers(); 28 | 29 | #ifdef KXEMU_ISA64 30 | std::string targetTriple = "riscv64-unknown-elf"; 31 | #else 32 | std::string targetTriple = "riscv32-unknown-elf"; 33 | #endif 34 | std::string error; 35 | const Target *target = TargetRegistry::lookupTarget(targetTriple, error); 36 | 37 | MCTargetOptions MCOptions; 38 | gSTI = target->createMCSubtargetInfo(targetTriple, "", ""); 39 | gSTI->ApplyFeatureFlag("+c"); 40 | auto gMII = target->createMCInstrInfo(); 41 | auto gMRI = target->createMCRegInfo(targetTriple); 42 | auto asmInfo = target->createMCAsmInfo(*gMRI, targetTriple, MCOptions); 43 | auto llvmTripleTwine = Twine(targetTriple); 44 | auto llvmtriple = llvm::Triple(llvmTripleTwine); 45 | auto ctx = new llvm::MCContext(llvmtriple, asmInfo, gMRI, nullptr); 46 | 47 | disassembler = target->createMCDisassembler(*gSTI, *ctx); 48 | gIP = target->createMCInstPrinter(llvm::Triple(targetTriple), 49 | asmInfo->getAssemblerDialect(), *asmInfo, *gMII, *gMRI); 50 | gIP->setPrintImmHex(true); 51 | gIP->setPrintBranchImmAsAddress(true); 52 | gIP->applyTargetSpecificCLOption("no-aliases"); 53 | } 54 | 55 | std::string kxemu::isa::disassemble(const uint8_t *code, const uint64_t dataSize, const word_t pc, uint64_t &instLen) { 56 | MCInst inst; 57 | ArrayRef arr(code, 4); 58 | disassembler->getInstruction(inst, instLen, arr, pc, nulls()); 59 | 60 | std::string s; 61 | raw_string_ostream os(s); 62 | gIP->printInst(&inst, pc, "", *gSTI, os); 63 | 64 | return s; 65 | } 66 | -------------------------------------------------------------------------------- /src/isa/riscv/riscv.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/cpu.h" 2 | #include "isa/isa.h" 3 | #include "cpu/riscv/cpu.h" 4 | 5 | #include 6 | 7 | using namespace kxemu; 8 | 9 | static const char* gprNames[] = { 10 | "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", 11 | "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", 12 | "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", 13 | "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" 14 | }; 15 | 16 | unsigned int isa::get_gpr_count() { 17 | return 32; 18 | } 19 | 20 | const char *kxemu::isa::get_gpr_name(int idx) { 21 | return gprNames[idx]; 22 | } 23 | 24 | const char *kxemu::isa::get_isa_name() { 25 | #ifdef KXEMU_ISA64 26 | return "riscv64"; 27 | #else 28 | return "riscv32"; 29 | #endif 30 | } 31 | 32 | int kxemu::isa::get_elf_expected_machine() { 33 | return EM_RISCV; 34 | } 35 | 36 | extern void init_disasm(); 37 | 38 | void kxemu::isa::init() { 39 | init_disasm(); 40 | } 41 | 42 | cpu::CPU *kxemu::isa::new_cpu() { 43 | return new cpu::RVCPU(); 44 | } 45 | -------------------------------------------------------------------------------- /src/isa/riscv32/disassemble.cpp: -------------------------------------------------------------------------------- 1 | #include "isa/isa.h" 2 | 3 | #include "llvm/MC/MCAsmInfo.h" 4 | #include "llvm/MC/MCContext.h" 5 | #include "llvm/MC/MCDisassembler/MCDisassembler.h" 6 | #include "llvm/MC/MCInstPrinter.h" 7 | #if LLVM_VERSION_MAJOR >= 14 8 | #include "llvm/MC/TargetRegistry.h" 9 | #if LLVM_VERSION_MAJOR >= 15 10 | #include "llvm/MC/MCSubtargetInfo.h" 11 | #endif 12 | #else 13 | #include "llvm/Support/TargetRegistry.h" 14 | #endif 15 | #include "llvm/Support/TargetSelect.h" 16 | 17 | using namespace llvm; 18 | 19 | static MCDisassembler *disassembler; 20 | static MCInstPrinter *gIP = nullptr; 21 | static MCSubtargetInfo *gSTI = nullptr; 22 | 23 | void init_disasm() { 24 | InitializeAllTargetInfos(); 25 | InitializeAllTargetMCs(); 26 | InitializeAllDisassemblers(); 27 | 28 | std::string targetTriple = "riscv32-unknown-elf"; 29 | std::string error; 30 | const Target *target = TargetRegistry::lookupTarget(targetTriple, error); 31 | 32 | if (!target) { 33 | PANIC("Failed to lookup target: %s", error.c_str()); 34 | } 35 | 36 | MCTargetOptions MCOptions; 37 | gSTI = target->createMCSubtargetInfo(targetTriple, "", ""); 38 | gSTI->ApplyFeatureFlag("+c"); 39 | auto gMII = target->createMCInstrInfo(); 40 | auto gMRI = target->createMCRegInfo(targetTriple); 41 | auto asmInfo = target->createMCAsmInfo(*gMRI, targetTriple, MCOptions); 42 | auto llvmTripleTwine = Twine(targetTriple); 43 | auto llvmtriple = llvm::Triple(llvmTripleTwine); 44 | auto ctx = new llvm::MCContext(llvmtriple, asmInfo, gMRI, nullptr); 45 | 46 | disassembler = target->createMCDisassembler(*gSTI, *ctx); 47 | gIP = target->createMCInstPrinter(llvm::Triple(targetTriple), 48 | asmInfo->getAssemblerDialect(), *asmInfo, *gMII, *gMRI); 49 | gIP->setPrintImmHex(true); 50 | gIP->setPrintBranchImmAsAddress(true); 51 | gIP->applyTargetSpecificCLOption("no-aliases"); 52 | } 53 | 54 | std::string kxemu::isa::disassemble(const uint8_t *code, const size_t length, word_t pc, unsigned int &instLen) { 55 | MCInst inst; 56 | ArrayRef arr(code, length); 57 | uint64_t dummy_size = 0; 58 | disassembler->getInstruction(inst, dummy_size, arr, pc, nulls()); 59 | 60 | std::string s; 61 | raw_string_ostream os(s); 62 | gIP->printInst(&inst, pc, "", *gSTI, os); 63 | 64 | instLen = dummy_size; 65 | return s; 66 | } 67 | -------------------------------------------------------------------------------- /src/isa/riscv32/riscv32.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/riscv32/cpu.h" 2 | #include "isa/isa.h" 3 | 4 | using namespace kxemu; 5 | 6 | cpu::CPU *isa::new_cpu() { 7 | return new cpu::RV32CPU; 8 | } 9 | -------------------------------------------------------------------------------- /src/kdb/cmd/completion.cpp: -------------------------------------------------------------------------------- 1 | #include "kdb/cmd.h" 2 | #include "utils/utils.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace kxemu; 10 | using namespace kxemu::kdb; 11 | 12 | struct Node { 13 | explicit Node(const std::unordered_map &children) : children(children) , func(nullptr){}; 14 | explicit Node(const cmd::completion_func_t &func) : func(func) {} 15 | explicit Node(const std::string &name) { 16 | children.insert({name, {}}); 17 | } 18 | Node() = default; 19 | 20 | std::unordered_map children; 21 | cmd::completion_func_t func; 22 | }; 23 | 24 | static const Node *tree = new Node({ 25 | { 26 | "log", 27 | new Node({ 28 | {"on", new Node({{"DEBUG", nullptr}, {"INFO", nullptr}, {"WARN", nullptr}, {"PANIC", nullptr}})}, 29 | {"off", new Node({{"DEBUG", nullptr}, {"INFO", nullptr}, {"WARN", nullptr}, {"PANIC", nullptr}})} 30 | }) 31 | }, 32 | {"info", new Node({ 33 | {"gpr", nullptr} 34 | })}, 35 | {"load", new Node({ 36 | {"elf", nullptr} 37 | })}, 38 | {"mem", new Node({ 39 | {"create", nullptr}, 40 | {"img", nullptr}, 41 | {"elf", nullptr}, 42 | {"map", nullptr}, 43 | {"save", nullptr} 44 | })}, 45 | {"step", nullptr}, 46 | {"exit", nullptr}, 47 | {"run" , nullptr}, 48 | {"symbol", nullptr}, 49 | {"breakpoint", nullptr}, 50 | {"reset", nullptr}, 51 | {"uart", new Node({ 52 | {"add", nullptr} 53 | })} 54 | }); 55 | 56 | static char *command_completion_generator(const char *, int state) { 57 | static std::vector matches; 58 | static std::size_t current = 0; 59 | 60 | if (state == 0) { 61 | const std::string line = rl_line_buffer; 62 | const cmd::args_t args = utils::string_split(rl_line_buffer, ' '); 63 | const std::size_t argsLength = args.size(); 64 | 65 | if (argsLength == 0) return NULL; 66 | 67 | const Node *node = tree; 68 | std::size_t index = 0; 69 | while (index < argsLength - 1) { 70 | auto iter = node->children.find(args[index]); 71 | if (iter == node->children.end()) { 72 | return NULL; // Unknown command 73 | } 74 | if (iter->second == nullptr) { 75 | return NULL; // End of a command 76 | } 77 | node = iter->second; 78 | index ++; 79 | } 80 | // Init matches 81 | current = 0; 82 | matches.clear(); 83 | 84 | if (line.back() == ' ') { 85 | auto iter = node->children.find(args[argsLength - 1]); 86 | if (iter == node->children.end()) { 87 | return NULL; // Unknown command 88 | } 89 | node = iter->second; 90 | if (node == nullptr) return NULL; // End of a command 91 | 92 | for (const auto &pair : node->children) { 93 | matches.push_back(pair.first); 94 | } 95 | 96 | } else { 97 | std::string prefix = args[argsLength - 1]; 98 | for (const auto &pair : node->children) { 99 | if (pair.first.rfind(prefix, 0) == 0 && pair.first != prefix) { 100 | matches.push_back(pair.first); 101 | } 102 | } 103 | } 104 | } 105 | 106 | if (current < matches.size()) { 107 | return strdup(matches[current++].c_str()); 108 | } else { 109 | return NULL; 110 | } 111 | } 112 | 113 | static char **cmd_completion(const char *text, int, int) { 114 | return rl_completion_matches(text, command_completion_generator); 115 | } 116 | 117 | void cmd::init_completion() { 118 | rl_attempted_completion_function = cmd_completion; 119 | } 120 | -------------------------------------------------------------------------------- /src/kdb/cmd/info.cpp: -------------------------------------------------------------------------------- 1 | #include "isa/isa.h" 2 | #include "isa/word.h" 3 | #include "kdb/cmd.h" 4 | 5 | #include 6 | 7 | using namespace kxemu; 8 | using namespace kxemu::kdb; 9 | 10 | static int cmd_info_gpr(const cmd::args_t &args); 11 | static int cmd_info_pc(const cmd::args_t &args); 12 | 13 | static const cmd::cmd_map_t cmdMap = { 14 | {"gpr", cmd_info_gpr}, 15 | {"pc", cmd_info_pc}, 16 | }; 17 | 18 | static int cmd_info_gpr(const cmd::args_t &) { 19 | for (unsigned int i = 0; i < isa::get_gpr_count(); i++) { 20 | word_t value = cmd::currentCore->get_gpr(i); 21 | std::cout << isa::get_gpr_name(i) << " = " 22 | << FMT_STREAM_WORD(value) 23 | << "(" << value<< ")" << std::endl; 24 | } 25 | return cmd::Success; 26 | } 27 | 28 | static int cmd_info_pc(const cmd::args_t &) { 29 | word_t pc = cmd::currentCore->get_pc(); 30 | std::cout << "pc = " << FMT_STREAM_WORD(pc) << std::endl; 31 | return cmd::Success; 32 | } 33 | 34 | int cmd::info(const args_t &args) { 35 | return cmd::find_and_run(args, cmdMap, 1); 36 | } 37 | -------------------------------------------------------------------------------- /src/kdb/cmd/load.cpp: -------------------------------------------------------------------------------- 1 | #include "kdb/kdb.h" 2 | #include "kdb/cmd.h" 3 | #include "isa/word.h" 4 | 5 | #include 6 | 7 | using namespace kxemu; 8 | using namespace kxemu::kdb; 9 | 10 | std::string cmd::elfFileName; 11 | 12 | static int cmd_load_elf(const cmd::args_t &); 13 | 14 | static const cmd::cmd_map_t cmdMap = { 15 | {"elf", cmd_load_elf}, 16 | }; 17 | 18 | static int cmd_load_elf(const cmd::args_t &args) { 19 | if (args.size() > 2) { 20 | std::cout << "WARN: load elf command only get source from kdb exec arguments." << std::endl; 21 | } 22 | if (cmd::elfFileName.empty()) { 23 | std::cout << "Failed to load ELF file, because file name is empty." << std::endl; 24 | return cmd::MissingPrevOp; 25 | } 26 | 27 | word_t entry = kdb::load_elf(cmd::elfFileName); 28 | if (entry == 0) { 29 | std::cout << "Failed to load ELF file " << cmd::elfFileName << std::endl; 30 | return cmd::CmdError; 31 | } 32 | std::cout << "Load ELF file success." << std::endl; 33 | std::cout << "Switch entry to " << FMT_STREAM_WORD(entry) << "." << std::endl; 34 | kdb::programEntry = entry; 35 | return cmd::Success; 36 | } 37 | 38 | int cmd::load(const cmd::args_t & args) { 39 | int r = cmd::find_and_run(args, cmdMap, 1); 40 | if (r == cmd::EmptyArgs) { 41 | std::cout << "Usage: load elf" << std::endl; 42 | } 43 | return r; 44 | } 45 | -------------------------------------------------------------------------------- /src/kdb/cmd/log.cpp: -------------------------------------------------------------------------------- 1 | #include "kdb/cmd.h" 2 | #include "log.h" 3 | 4 | #include 5 | 6 | using namespace kxemu; 7 | using namespace kxemu::kdb; 8 | 9 | static const struct { 10 | std::string name; 11 | int level; 12 | } pairs[] = { 13 | {"DEBUG", DEBUG}, 14 | {"debug", DEBUG}, 15 | {"INFO", INFO }, 16 | {"info", INFO }, 17 | {"WARN", WARN }, 18 | {"warn", WARN }, 19 | {"PANIC", PANIC}, 20 | {"panic", PANIC}, 21 | }; 22 | 23 | static const cmd::cmd_map_t cmdMap = { 24 | {"on" , cmd::log}, 25 | {"off", cmd::log}, 26 | }; 27 | 28 | static int cmd_log_on(const std::string &logLevel) { 29 | if (logLevel.empty()) { 30 | logFlag = DEBUG | INFO | WARN | PANIC; 31 | return 0; 32 | } 33 | 34 | for (auto &pair : pairs) { 35 | if (pair.name == logLevel) { 36 | logFlag |= pair.level; 37 | std::cout << "Turn on the log level " << logLevel << "." << std::endl; 38 | return 0; 39 | } 40 | } 41 | std::cout << "Unkonwn log level: " << logLevel << std::endl; 42 | return 1; 43 | } 44 | 45 | static int cmd_log_off(const std::string &logLevel) { 46 | if (logLevel.empty()) { 47 | logFlag = 0; 48 | return 0; 49 | } 50 | 51 | for (auto &pair : pairs) { 52 | if (pair.name == logLevel) { 53 | logFlag &= ~pair.level; 54 | std::cout << "Turn off the log level " << logLevel << "." << std::endl; 55 | return 0; 56 | } 57 | } 58 | std::cout << "Unkonwn log level: " << logLevel << std::endl; 59 | return 1; 60 | } 61 | 62 | int cmd::log(const std::vector &args) { 63 | if (args.size() < 2) { 64 | std::cout << "Usage: log on|off [DEBUG|INFO|WARN|PANIC]" << std::endl; 65 | return 1; 66 | } 67 | 68 | std::string arg; 69 | if (args.size() < 3) { 70 | arg = ""; 71 | } else { 72 | arg = args[2]; 73 | } 74 | 75 | if (args[1] == "on") { 76 | return cmd_log_on(arg); 77 | } else if (args[1] == "off") { 78 | return cmd_log_off(arg); 79 | } else { 80 | std::cout << "Usage: log on|off [DEBUG|INFO|WARN|PANIC]" << std::endl; 81 | return CmdNotFound; 82 | } 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/kdb/cmd/uart.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include "kdb/cmd.h" 3 | #include "kdb/kdb.h" 4 | #include "utils/utils.h" 5 | 6 | #include 7 | #include 8 | 9 | using namespace kxemu; 10 | using namespace kxemu::kdb; 11 | 12 | static int cmd_uart_add (const cmd::args_t &); 13 | static int cmd_uart_puts(const cmd::args_t &); 14 | 15 | static const cmd::cmd_map_t cmdMap = { 16 | {"add", cmd_uart_add}, 17 | {"puts", cmd_uart_puts}, 18 | }; 19 | 20 | static int cmd_uart_add(const cmd::args_t &args) { 21 | if (args.size() < 3) { 22 | std::cout << "Usage: uart add [port]" << std::endl; 23 | return cmd::InvalidArgs; 24 | } 25 | bool s; 26 | word_t base = kdb::string_to_addr(args[2], s); 27 | if (!s) { 28 | std::cout << "Invalid address: " << args[2] << std::endl; 29 | return cmd::InvalidArgs; 30 | } 31 | if (args.size() == 3) { 32 | if (kdb::uart::add(base, std::cout)) { 33 | std::cout << "UART added at base " << std::hex << base << std::dec << std::endl; 34 | return cmd::Success; 35 | } else { 36 | return cmd::CmdError; 37 | } 38 | } else { 39 | int port = 0; 40 | try { 41 | port = std::stoi(args[3]); 42 | } catch (std::exception &) { 43 | std::cout << "Invalid port: " << args[3] << std::endl; 44 | return cmd::InvalidArgs; 45 | } 46 | if (kdb::uart::add(base, "127.0.0.1", port)) { 47 | std::cout << "UART added at base " << FMT_STREAM_WORD(base) << " port " << port << std::endl; 48 | return cmd::Success; 49 | } else { 50 | std::cout << "Failed to add UART." << std::endl; 51 | return cmd::CmdError; 52 | } 53 | } 54 | } 55 | 56 | static int cmd_uart_puts(const cmd::args_t &args) { 57 | if (args.size() < 3) { 58 | std::cout << "Usage: uart putch " << std::endl; 59 | return cmd::InvalidArgs; 60 | } 61 | 62 | bool s; 63 | std::size_t index = utils::string_to_unsigned(args[2], s); 64 | if (!s) { 65 | std::cout << "Invalid Index: " << args[2] << std::endl; 66 | return cmd::InvalidArgs; 67 | } 68 | 69 | if (kdb::uart::puts(index, args[3])) { 70 | return cmd::Success; 71 | } else { 72 | std::cout << "Invalid Index: " << args[2] << std::endl; 73 | return cmd::CmdError; 74 | } 75 | } 76 | 77 | int cmd::uart(const args_t &args) { 78 | return cmd::find_and_run(args, cmdMap, 1); 79 | } 80 | -------------------------------------------------------------------------------- /src/kdb/cpu-exec.cpp: -------------------------------------------------------------------------------- 1 | #include "cpu/core.h" 2 | #include "kdb/kdb.h" 3 | #include "macro.h" 4 | #include "log.h" 5 | 6 | #include 7 | 8 | using namespace kxemu; 9 | using kxemu::kdb::word_t; 10 | using kxemu::cpu::Core; 11 | 12 | std::unordered_set kdb::breakpointSet; 13 | bool kdb::brkTriggered = false; 14 | 15 | void kdb::reset_cpu() { 16 | cpu->reset(kdb::programEntry); 17 | kdb::returnCode = 0; 18 | } 19 | 20 | int static output_and_set_trap(Core *core) { 21 | int r; 22 | if (core->is_error()) { 23 | std::cout << FMT_FG_RED_BLOD "Error" FMT_FG_BLUE_BLOD " at pc=" << FMT_STREAM_WORD(core->get_halt_pc()) << FMT_FG_RESET << std::endl; 24 | r = 1; 25 | kdb::returnCode = 1; 26 | } else if (core->is_halt()) { 27 | r = core->get_halt_code(); 28 | if (r == 0) { 29 | std::cout << FMT_FG_GREEN_BLOD "HIT GOOD TRAP" FMT_FG_BLUE_BLOD " at pc=" << FMT_STREAM_WORD(core->get_halt_pc()) << FMT_FG_RESET << std::endl; 30 | } else { 31 | std::cout << FMT_FG_RED_BLOD "HIT BAD TRAP" FMT_FG_BLUE_BLOD " at pc=" << FMT_STREAM_WORD(core->get_halt_pc()) << FMT_FG_RESET << std::endl; 32 | } 33 | kdb::returnCode = r; 34 | } else { 35 | r = 0; 36 | } 37 | return r; 38 | }; 39 | 40 | int kdb::step_core(Core *core) { 41 | word_t pc = core->get_pc(); 42 | core->step(); 43 | output_and_set_trap(core); 44 | if (breakpointSet.find(pc) != breakpointSet.end()) { 45 | brkTriggered = true; 46 | } 47 | return 0; 48 | } 49 | 50 | // NOTE: This function only support single core CPU 51 | int kdb::run_cpu() { 52 | auto core = cpu->get_core(0); 53 | 54 | unsigned int n = breakpointSet.size(); 55 | word_t *breakpoints = new word_t[n]; 56 | int i = 0; 57 | for (auto it = breakpointSet.begin(); it != breakpointSet.end(); it++) { 58 | breakpoints[i++] = *it; 59 | } 60 | core->run(breakpoints, n); 61 | delete[] breakpoints; 62 | 63 | brkTriggered = core->is_break(); 64 | 65 | return output_and_set_trap(core); 66 | } 67 | 68 | void kdb::add_breakpoint(word_t addr) { 69 | breakpointSet.insert(addr); 70 | } 71 | -------------------------------------------------------------------------------- /src/kdb/kdb.cpp: -------------------------------------------------------------------------------- 1 | #include "kdb/kdb.h" 2 | #include "cpu/cpu.h" 3 | #include "isa/isa.h" 4 | #include "kdb/rsp.h" 5 | #include "log.h" 6 | #include "isa/isa.h" 7 | #include "utils/utils.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace kxemu; 15 | using kxemu::cpu::CPU; 16 | using kxemu::kdb::word_t; 17 | 18 | CPU *kdb::cpu = nullptr; 19 | word_t kdb::programEntry; 20 | int kdb::returnCode = 0; 21 | 22 | void kdb::init() { 23 | init_bus(); 24 | 25 | logFlag = DEBUG | INFO | WARN | PANIC; 26 | 27 | cpu = isa::new_cpu(); 28 | cpu->init(bus, -1, 1); 29 | cpu->reset(0x80000000); 30 | kdb::programEntry = 0x80000000; 31 | 32 | INFO("Init %s CPU", isa::get_isa_name()); 33 | } 34 | 35 | void kdb::deinit() { 36 | delete cpu; 37 | cpu = nullptr; 38 | } 39 | 40 | // run .kdb source file to exec kdb command 41 | int kdb::run_source_file(const std::string &filename) { 42 | std::ifstream f; 43 | f.open(filename, std::ios::in); 44 | if (!f.is_open()) { 45 | std::cerr << "FileNotFound: No such file: " << filename << std::endl; 46 | return 1; 47 | } 48 | 49 | std::string cmdLine; 50 | while (std::getline(f, cmdLine)) { 51 | run_command(cmdLine); 52 | } 53 | return 0; 54 | } 55 | 56 | word_t kdb::string_to_addr(const std::string &s, bool &success) { 57 | success = false; 58 | word_t addr = -1; 59 | try { 60 | addr = utils::string_to_unsigned(s); 61 | success = true; 62 | return addr; 63 | } catch (std::exception &) {} 64 | 65 | for (auto iter : kdb::symbolTable) { 66 | if (iter.second == s) { 67 | addr = iter.first; 68 | success = true; 69 | } 70 | } 71 | return addr; 72 | } 73 | 74 | bool kdb::start_rsp(int port) { 75 | rsp::rsp_mainloop(port); 76 | return true; 77 | } 78 | -------------------------------------------------------------------------------- /src/kdb/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "kdb/kdb.h" 2 | #include "device/bus.h" 3 | 4 | using namespace kxemu; 5 | 6 | device::Bus *kdb::bus = nullptr; 7 | 8 | void kdb::init_bus() { 9 | bus = new device::Bus(); 10 | } 11 | 12 | void kdb::deinit_bus() { 13 | bus->free_all(); 14 | delete bus; 15 | } 16 | -------------------------------------------------------------------------------- /src/kdb/rsp/rsp.cpp: -------------------------------------------------------------------------------- 1 | #include "kdb/rsp.h" 2 | #include "debug.h" 3 | #include "log.h" 4 | #include "utils/gdb-rsp.h" 5 | #include 6 | #include 7 | 8 | void rsp::rsp_mainloop(int port) { 9 | NOT_IMPLEMENTED(); 10 | RSP server; 11 | if (!server.open_rsp(port)) { 12 | INFO("Failed to open RSP server"); 13 | return; 14 | } 15 | 16 | server.wait_client(); 17 | 18 | std::vector args; 19 | server.next_command(args, true); 20 | server.next_command(args, true); 21 | 22 | INFO("RSP server end"); 23 | } 24 | -------------------------------------------------------------------------------- /src/kdb/uart.cpp: -------------------------------------------------------------------------------- 1 | #include "device/uart.h" 2 | #include "kdb/kdb.h" 3 | #include 4 | 5 | using namespace kxemu; 6 | using kxemu::device::Uart16650; 7 | 8 | std::vector kdb::uart::list; 9 | 10 | static bool add_uart_map(kdb::word_t base, Uart16650 *uart) { 11 | if (!kdb::bus->add_memory_map("uart" + std::to_string(kdb::uart::list.size()), base, UART_LENGTH, uart)) { 12 | // Add memory map failed, free 13 | delete uart; 14 | return false; 15 | } 16 | kdb::uart::list.push_back(uart); 17 | return true; 18 | } 19 | 20 | bool kdb::uart::add(word_t base, std::ostream &os) { 21 | Uart16650 *uart = new Uart16650(); 22 | if (!add_uart_map(base, uart)) { 23 | return false; 24 | } 25 | uart->set_output_stream(os); 26 | return true; 27 | } 28 | 29 | bool kdb::uart::add(word_t base, const std::string &ip, int port) { 30 | Uart16650 *uart = new Uart16650(); 31 | if (!uart->open_socket(ip, port)) { 32 | delete uart; 33 | return false; 34 | } 35 | if (!add_uart_map(base, uart)) { 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | bool kdb::uart::puts(std::size_t index, const std::string &s) { 42 | if (index >= kdb::uart::list.size()) { 43 | return false; 44 | } 45 | for (char c : s) { 46 | kdb::uart::list[index]->putch(c); 47 | } 48 | return true; 49 | } 50 | -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | using namespace kxemu; 4 | 5 | int kxemu::logFlag = DEBUG | INFO | WARN | PANIC; 6 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * main.cpp 3 | * Copyright (C) 2024 Zhenrui Liu, Hangzhou Dianzi University 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "kdb/cmd.h" 20 | #include "kdb/kdb.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | using namespace kxemu; 27 | 28 | void parse_args(int argc, char **argv) { 29 | static struct option options[] = { 30 | {"source", required_argument, 0, 's'}, 31 | {"elf" , required_argument, 0, 'e'}, 32 | {0, 0, 0, 0} 33 | }; 34 | 35 | std::vector sourceFiles; 36 | 37 | int o; 38 | while((o = getopt_long(argc, argv, "s:", options, NULL)) != -1) { 39 | switch (o) { 40 | case 's': 41 | sourceFiles.push_back(optarg); 42 | break; 43 | case 'e': 44 | kdb::cmd::elfFileName = optarg; 45 | break; 46 | default: 47 | break; 48 | } 49 | } 50 | 51 | for (auto sourceFileName: sourceFiles) { 52 | kdb::run_source_file(sourceFileName); 53 | } 54 | } 55 | 56 | int main(int argc, char **argv) { 57 | kdb::init(); 58 | kdb::cmd_init(); 59 | parse_args(argc, argv); 60 | int r = kdb::run_cmd_mainloop(); 61 | return r; 62 | } -------------------------------------------------------------------------------- /src/utils/decoder/bitpat.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/decoder.h" 2 | #include "log.h" 3 | #include 4 | #include 5 | 6 | using namespace kxemu::utils; 7 | 8 | BitPat::BitPat(const std::string &s) { 9 | uint64_t bits = 0; 10 | uint64_t mask = 0; 11 | int length = 0; 12 | for (int i = s.length() - 1; i >= 0; i--) { 13 | switch (s[i]) { 14 | case '0': length++; break; 15 | case '1': bits |= 1 << length; length++; break; 16 | case 'x': mask |= 1 << length; length++; break; 17 | case '?': mask |= 1 << length; length++; break; 18 | case '_': break; 19 | case ' ': break; 20 | default: PANIC("Invalid BitPat %s", s.c_str()); 21 | } 22 | } 23 | if (length > 64) { 24 | PANIC("BitPat length %d is too long", length); 25 | } 26 | 27 | for (std::size_t i = length; i < 64; i++) { 28 | mask |= 1UL << i; 29 | } 30 | 31 | this->bits = bits; 32 | this->mask = ~mask; // mask is 0 for match any bit 33 | this->length = length; 34 | } 35 | 36 | BitPat::BitPat(BitPat &other) { 37 | this->bits = other.bits; 38 | this->mask = other.mask; 39 | this->length = other.length; 40 | } 41 | 42 | BitPat::BitPat(BitPat &&other) { 43 | this->bits = other.bits; 44 | this->mask = other.mask; 45 | this->length = other.length; 46 | } 47 | 48 | unsigned int BitPat::get_length() const { 49 | return this->length; 50 | } 51 | -------------------------------------------------------------------------------- /src/utils/gdb-rsp/gdb-rsp.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/gdb-rsp.h" 2 | #include "debug.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define BUFFER_SIZE 1024 14 | 15 | static unsigned int hexchar_to_digit(char c) { 16 | if (c >= '0' && c <= '9') { 17 | return c - '0'; 18 | } else if (c >= 'a' && c <= 'f') { 19 | return c - 'a' + 10; 20 | } else if (c >= 'A' && c <= 'F') { 21 | return c - 'A' + 10; 22 | } else { 23 | return 0; 24 | } 25 | } 26 | 27 | 28 | static bool check_valid(const char *buffer, size_t n) { 29 | if (n < 4) { 30 | return false; 31 | } 32 | 33 | if (buffer[0] != '$') { 34 | return false; 35 | } 36 | 37 | // check sum 38 | unsigned int checksum = (hexchar_to_digit(buffer[n - 2]) << 4) + hexchar_to_digit(buffer[n - 1]); 39 | unsigned int sum = 0; 40 | std::cout << "payload: "; 41 | for (size_t i = 1; i < n - 3; i++) { 42 | sum += buffer[i]; 43 | } 44 | 45 | return (sum & 0xff) == checksum; 46 | } 47 | 48 | RSP::RSP() { 49 | this->server = -1; 50 | this->client = -1; 51 | } 52 | 53 | bool RSP::open_rsp(int port) { 54 | if ((server = socket(AF_INET, SOCK_STREAM, 0)) == 0) { 55 | return false; 56 | } 57 | 58 | address.sin_family = AF_INET; 59 | address.sin_addr.s_addr = INADDR_ANY; 60 | address.sin_port = htons(port); 61 | 62 | if (bind(server, (struct sockaddr *)&address, sizeof(address)) < 0) { 63 | return false; 64 | } 65 | 66 | if (listen(server, 1) < 0) { 67 | return false; 68 | } 69 | 70 | return true; 71 | } 72 | 73 | bool RSP::wait_client() { 74 | SELF_PROTECT(this->server != -1, "Server is not initialized.") 75 | 76 | int addrlen = sizeof(address); 77 | if ((client = accept(server, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { 78 | std::cout << strerror(errno) << std::endl; 79 | close(server); 80 | return false; 81 | } 82 | char c; 83 | int n = read(client, &c, 1); 84 | if (n < 0) { 85 | close(client); 86 | client = -1; 87 | return false; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | int RSP::next_command(std::vector &, bool blocked) { 94 | SELF_PROTECT(this->client != -1, "Client is not initialized.") 95 | 96 | if (commandQueue.empty()) { 97 | if (!blocked) { 98 | return CommandType::Empty; 99 | } 100 | 101 | char buffer[BUFFER_SIZE]; 102 | int n = read(client, buffer, BUFFER_SIZE); 103 | if (n <= 0) { 104 | return CommandType::Error; 105 | } 106 | buffer[n] = '\0'; 107 | if (!check_valid(buffer, n)) { 108 | std::cout << "Invalid command: " << buffer << std::endl; 109 | return CommandType::Error; 110 | } 111 | std::cout << buffer << std::endl; 112 | } 113 | return 0; 114 | } 115 | 116 | RSP::~RSP() { 117 | if (client != -1) { 118 | close(client); 119 | } 120 | if (server != -1) { 121 | close(server); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/utils/task-timer/task-timer.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/task-timer.h" 2 | 3 | using namespace kxemu::utils; 4 | 5 | TaskTimer::TaskTimer() { 6 | this->running = false; 7 | this->timerThread = nullptr; 8 | this->counter = 0; 9 | } 10 | 11 | TaskTimer::~TaskTimer() { 12 | if (this->running) { 13 | this->running = false; 14 | this->cv.notify_all(); 15 | this->timerThread->join(); 16 | delete this->timerThread; 17 | } 18 | } 19 | 20 | void TaskTimer::timer_thread() { 21 | std::unique_lock lock(this->mtx); 22 | while (this->running) { 23 | if (this->tasks.empty()) { 24 | this->cv.wait(lock); 25 | continue; 26 | } 27 | 28 | auto &next = this->tasks.top(); 29 | 30 | if (this->removedTasks.find(next.id) != this->removedTasks.end()) { 31 | this->tasks.pop(); 32 | this->removedTasks.erase(next.id); 33 | continue; 34 | } 35 | 36 | auto now = std::chrono::high_resolution_clock::now(); 37 | if (next.timepoint <= now) { 38 | lock.unlock(); 39 | next.task(); 40 | lock.lock(); 41 | this->tasks.pop(); 42 | } else { 43 | this->cv.wait_until(lock, next.timepoint); 44 | } 45 | } 46 | } 47 | 48 | void TaskTimer::start_thread() { 49 | if (this->running) { 50 | return; 51 | } 52 | this->running = true; 53 | this->timerThread = new std::thread(&TaskTimer::timer_thread, this); 54 | } 55 | 56 | unsigned int TaskTimer::add_task(uint64_t delay, task_t task) { 57 | std::lock_guard lock(this->mtx); 58 | auto timepoint = std::chrono::high_resolution_clock::now() + std::chrono::nanoseconds(delay); 59 | unsigned int id = this->counter++; 60 | this->tasks.push({timepoint, task, id}); 61 | this->cv.notify_all(); 62 | return id; 63 | } 64 | 65 | void TaskTimer::remove_task(unsigned int id) { 66 | std::lock_guard lock(this->mtx); 67 | this->removedTasks.insert(id); 68 | this->cv.notify_all(); 69 | } 70 | -------------------------------------------------------------------------------- /src/utils/tcp-server/tcp-server.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/tcp-server.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | TCPServer::TCPServer() { 9 | std::memset(&address, 0, sizeof(address)); 10 | } 11 | 12 | bool TCPServer::start(const std::string &, int port) { 13 | serverSocket = socket(AF_INET, SOCK_STREAM, 0); 14 | if (serverSocket == -1) { 15 | return false; 16 | } 17 | 18 | int opt = 1; 19 | if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) < 0) { 20 | close(serverSocket); 21 | return false; 22 | } 23 | 24 | address.sin_family = AF_INET; 25 | address.sin_addr.s_addr = INADDR_ANY; // 监听所有接口 26 | address.sin_port = htons(port); 27 | 28 | if (bind(serverSocket, (struct sockaddr *)&address, sizeof(address)) < 0) { 29 | close(serverSocket); 30 | return false; 31 | } 32 | 33 | if (listen(serverSocket, 3) < 0) { 34 | close(serverSocket); 35 | return false; 36 | } 37 | 38 | isRunning = true; 39 | return true; 40 | } 41 | 42 | int TCPServer::accept_connection() { 43 | if (!isRunning) { 44 | return -1; 45 | } 46 | 47 | struct sockaddr_in clientAddress; 48 | socklen_t clientAddrLen = sizeof(clientAddress); 49 | int clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, &clientAddrLen); 50 | if (clientSocket < 0) { 51 | perror("接受连接失败"); 52 | return -1; 53 | } 54 | 55 | return clientSocket; 56 | } 57 | 58 | ssize_t TCPServer::receive(int client, uint8_t *buffer, std::size_t bufferSize) { 59 | ssize_t length = read(client, buffer, bufferSize); 60 | return length; 61 | } 62 | 63 | ssize_t TCPServer::send(int client, const uint8_t *buffer, std::size_t bufferSize) { 64 | ssize_t length = write(client, buffer, bufferSize); 65 | return length; 66 | } 67 | 68 | void TCPServer::close_connection(int client) { 69 | close(client); 70 | } 71 | 72 | void TCPServer::stop() { 73 | if (isRunning) { 74 | close(serverSocket); 75 | isRunning = false; 76 | } 77 | } 78 | 79 | TCPServer::~TCPServer() { 80 | stop(); 81 | } 82 | -------------------------------------------------------------------------------- /src/utils/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/utils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace kxemu; 9 | 10 | uint64_t utils::string_to_unsigned(const std::string &s) { 11 | uint64_t result; 12 | if (s.size() > 2 && s[0] == '0' && s[1] == 'x') { 13 | result = std::stoul(s, nullptr, 16); 14 | } else { 15 | result = std::stoul(s); 16 | } 17 | return result; 18 | } 19 | 20 | uint64_t utils::string_to_unsigned(const std::string &s, bool &success) { 21 | uint64_t result; 22 | try { 23 | if (s.size() > 2 && s[0] == '0' && s[1] == 'x') { 24 | result = std::stoul(s, nullptr, 16); 25 | } else { 26 | result = std::stoul(s); 27 | } 28 | } catch (std::exception &) { 29 | success = false; 30 | return 0; 31 | }; 32 | success = true; 33 | return result; 34 | } 35 | 36 | std::vector utils::string_split(const std::string &s, const char delim) { 37 | std::stringstream ss(s); 38 | std::vector result; 39 | std::string t; 40 | 41 | while (std::getline(ss, t, delim)) { 42 | if (!t.empty() && t.find_first_not_of(' ') != std::string::npos) { 43 | t.erase(t.begin(), std::find_if(t.begin(), t.end(), [](unsigned char c) { return !std::isspace(c); })); 44 | t.erase(std::find_if(t.rbegin(), t.rend(), [](unsigned char c) { return !std::isspace(c); }).base(), t.end()); 45 | result.push_back(t); 46 | } 47 | } 48 | return result; 49 | } 50 | 51 | utils::timepoint_t utils::get_current_timepoint() { 52 | return std::chrono::high_resolution_clock::now(); 53 | } 54 | -------------------------------------------------------------------------------- /tests/abstract-machine/Makefile: -------------------------------------------------------------------------------- 1 | AM_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) 2 | WORK_DIR := $(shell pwd) 3 | 4 | ifneq ($(findstring qemu,$(MAKECMDGOALS)),) 5 | PLATFORM ?= qemu 6 | else 7 | PLATFORM ?= kxemu 8 | endif 9 | 10 | ARCH = $(PLATFORM)-$(ISA) 11 | ARCH_NAME = $(ARCH)-$(NAME) 12 | 13 | ELF = $(BUILD_DIR)/$(ARCH_NAME).elf 14 | ARCHIVE = $(BUILD_DIR)/$(ARCH_NAME).a 15 | ASM = $(BUILD_DIR)/$(ARCH_NAME).txt 16 | 17 | BUILD_DIR := build 18 | OBJ_DIR := build/$(ARCH) 19 | 20 | OBJS += $(addprefix $(OBJ_DIR)/, $(addsuffix .o, $(basename $(SRCS)))) 21 | DEPS = $(OBJS:.o=.d) 22 | 23 | -include $(DEPS) 24 | 25 | # Libaries 26 | 27 | LIBS += am klib 28 | LIBS_ARCHIVE = $(foreach name, $(LIBS), $(AM_DIR)/$(name)/build/$(ARCH)-$(name).a) 29 | 30 | # Kxemu configuration 31 | KXEMU = $(AM_DIR)/../../build/$(ISA)-kxemu 32 | KXEMU_FLAGS += --elf $(abspath $(ELF)) --source $(AM_DIR)/scripts/kdb/init.kdb 33 | KXEMU_FLAGS += $(addprefix --source , $(KDB_INIT_SRC)) 34 | 35 | # Load ISA specific configuration 36 | -include $(AM_DIR)/scripts/$(ISA).mk 37 | 38 | CC = $(CROSS_COMPILE)-gcc 39 | CXX = $(CROSS_COMPILE)-g++ 40 | AS = $(CROSS_COMPILE)-gcc 41 | LD = $(CROSS_COMPILE)-ld 42 | AR = $(CROSS_COMPILE)-ar 43 | OBJDUMP = $(CROSS_COMPILE)-objdump 44 | 45 | INC_PATH += $(foreach name, $(LIBS), $(AM_DIR)/$(name)/include) 46 | 47 | CFLAGS += -nostdlib -fno-stack-protector -O3 -fno-builtin -fno-asynchronous-unwind-tables 48 | CFLAGS += $(addprefix -I, $(INC_PATH)) 49 | CXXFLAGS += $(CFLAGS) -fno-exceptions 50 | 51 | ifeq ($(PLATFORM),qemu) 52 | COMPILE_FLAGS += -D__QEMU__ 53 | else ifeq ($(PLATFORM),kxemu) 54 | COMPILE_FLAGS += -D__KXEMU__ 55 | else 56 | $(error "Unknown platform $(PLATFORM)") 57 | endif 58 | 59 | CFLAGS += -D__PLATFORM__=$(PLATFORM) 60 | ASFLAGS += -D__PLATFORM__=$(PLATFORM) 61 | 62 | all: elf archive 63 | 64 | $(OBJ_DIR)/%.o: %.c 65 | $(info + CC $<) 66 | @ mkdir -p $(dir $@) 67 | @ $(CC) $(CFLAGS) $(COMPILE_FLAGS) -MMD -MP -c $< -o $@ 68 | 69 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 70 | $(info + CC $<) 71 | @ mkdir -p $(dir $@) 72 | @ $(CC) $(CFLAGS) $(COMPILE_FLAGS) -MMD -MP -c $< -o $@ 73 | 74 | $(OBJ_DIR)/%.o: %.S 75 | $(info + AS $<) 76 | @ mkdir -p $(dir $@) 77 | @ $(AS) $(ASFLAGS) $(COMPILE_FLAGS) -c $< -o $@ 78 | 79 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.S 80 | $(info + AS $<) 81 | @ mkdir -p $(dir $@) 82 | @ $(AS) $(COMPILE_FLAGS) -c $< -o $@ 83 | 84 | $(OBJ_DIR)/%.o: %.cc 85 | $(info + CC $<) 86 | @ mkdir -p $(dir $@) 87 | @ $(CXX) $(ASFLAGS) $(CXXFLAGS) $(COMPILE_FLAGS) -MMD -MP -c $< -o $@ 88 | 89 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cc 90 | $(info + CC $<) 91 | @ mkdir -p $(dir $@) 92 | @ $(CXX) $(CXXFLAGS) $(COMPILE_FLAGS) -MMD -MP -c $< -o $@ 93 | 94 | $(ELF): $(OBJS) $(LIBS) 95 | $(info + LD $@) 96 | @ $(LD) $(OBJS) $(LIBS_ARCHIVE) -o $@ $(LDFLAGS) 97 | @ $(OBJDUMP) -d $@ > $(ASM) 98 | 99 | $(LIBS): %: 100 | $(info + MAKELIB $@) 101 | @ make -s -C $(AM_DIR)/$* NAME=$* ISA=$(ISA) PLATFORM=$(PLATFORM) archive 102 | 103 | $(ARCHIVE): $(OBJS) 104 | $(info + AR $@) 105 | @ $(AR) rcs $@ $(OBJS) 106 | 107 | elf: $(ELF) 108 | archive: $(ARCHIVE) 109 | 110 | run: $(ELF) 111 | $(info + RUNNING KXEMU) 112 | @ $(KXEMU) $(KXEMU_FLAGS) --source $(AM_DIR)/scripts/kdb/run.kdb 113 | 114 | debug: $(ELF) 115 | $(info + DEBUGGING KXEMU) 116 | @ $(KXEMU) $(KXEMU_FLAGS) --source $(AM_DIR)/scripts/kdb/debug.kdb 117 | 118 | qemu: $(ELF) 119 | $(info + RUNNING QEMU) 120 | @ $(QEMU) $(QEMU_FLAGS) -kernel $(ELF) 121 | 122 | qemu-gdb: $(ELF) 123 | $(info + DEBUGGING QEMU) 124 | @ $(QEMU) $(QEMU_FLAGS) -kernel $(ELF) -s -S 125 | 126 | perf: $(ELF) 127 | $(info + PROFILING KXEMU) 128 | perf record -g $(KXEMU) $(KXEMU_FLAGS) --source $(AM_DIR)/scripts/kdb/run.kdb 129 | 130 | clean: 131 | rm -rf $(BUILD_DIR) 132 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/Makefile: -------------------------------------------------------------------------------- 1 | SRC_DIR = ./src 2 | 3 | NAME := am 4 | 5 | SRCS += $(shell find $(SRC_DIR) -path $(SRC_DIR)/isa -prune -o \( -name "*.cpp" -o -name "*.c" \) -print) \ 6 | $(shell find $(SRC_DIR)/isa/$(AM_ISA_NAME) -name "*.cpp" -o -name "*.c" -o -name "*.S") 7 | 8 | include ../Makefile # Path: tests/cpu-tests/abstract-machine/Makefile 9 | 10 | .DEFAULT_GOAL := archive 11 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/include/am-dev.h: -------------------------------------------------------------------------------- 1 | #ifndef __AM_DEV_H__ 2 | #define __AM_DEV_H__ 3 | 4 | #include 5 | 6 | // uart 7 | uint8_t __uart_rx(); 8 | void __am_uart_tx(uint8_t ch); 9 | 10 | // timer 11 | struct _RTC { 12 | unsigned int year; 13 | unsigned short month; 14 | unsigned short day; 15 | unsigned short hour; 16 | unsigned short minute; 17 | unsigned short second; 18 | }; 19 | uint64_t __am_timer_uptime(); 20 | void __am_timer_rtc(struct _RTC *rtc); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/include/am.h: -------------------------------------------------------------------------------- 1 | #ifndef __AM_H__ 2 | #define __AM_H__ 3 | 4 | #include "isa/riscv.h" 5 | 6 | #include 7 | #include 8 | 9 | typedef struct { 10 | void *start; 11 | void *end; 12 | } Area; 13 | 14 | extern Area heap; 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | // TRM 21 | void halt(int code) __attribute__((noreturn)); 22 | int putchar(int c); 23 | 24 | #ifdef __cplusplus 25 | }; 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/include/isa/riscv.h: -------------------------------------------------------------------------------- 1 | #ifndef __ISA_H__ 2 | #define __ISA_H__ 3 | 4 | #include 5 | 6 | #define NR_REGS 32 7 | 8 | #ifdef __riscv_e 9 | #define GPR1 gpr[15] // a5 10 | #else 11 | #define GPR1 gpr[17] // a7 12 | #endif 13 | 14 | #define GPR2 gpr[10] 15 | #define GPR3 gpr[11] 16 | #define GPR4 gpr[12] 17 | #define GPRx gpr[10] 18 | 19 | #define IRQ_TIMER 0x80000007 // timer interrupt 20 | 21 | #define PTE_V (1 << 0) 22 | #define PTE_R (1 << 1) 23 | #define PTE_W (1 << 2) 24 | #define PTE_X (1 << 3) 25 | #define PTE_U (1 << 4) 26 | 27 | #define VM_SV32 (1 << 31) 28 | #define VM_SV39 (8ULL << 60) 29 | #define VM_SV48 (9ULL << 60) 30 | #define VM_SV57 (10ULL << 60) 31 | 32 | #define MSTATUS_MPP_MASK 0x00001800 33 | #define MSTATUS_MPP_U 0 34 | 35 | typedef uintptr_t word_t; 36 | 37 | word_t csrr_time(); 38 | 39 | word_t csrr_mstatus(); 40 | word_t csrr_mcause (); 41 | word_t csrr_mepc (); 42 | word_t csrr_medeleg(); 43 | 44 | void csrw_pmpcfg0(word_t value); 45 | void csrw_pmpaddr0(word_t value); 46 | 47 | void csrw_mstatus(word_t value); 48 | void csrw_mcause (word_t value); 49 | void csrw_mepc (word_t value); 50 | void csrw_mtvec (word_t value); 51 | void csrw_medeleg(word_t value); 52 | void csrw_mscratch(word_t value); 53 | 54 | void csrw_sepc (word_t value); 55 | void csrw_stvec (word_t value); 56 | void csrw_satp (word_t value); 57 | 58 | void mret(); 59 | void sret(); 60 | void ecall(); 61 | void wfi(); 62 | 63 | void sfence_vma(); 64 | 65 | // CTE 66 | typedef struct Context { 67 | uintptr_t gpr[32]; 68 | } Context; 69 | 70 | typedef struct { 71 | enum { 72 | EVENT_NULL = 0, 73 | EVENT_SYSCALL, EVENT_PAGEFAULT, EVENT_ERROR, EVENT_IRQ_TIMER, EVENT_IRQ_IODEV, 74 | } event; 75 | uintptr_t cause; 76 | } Event; 77 | 78 | void cte_init(Context*(*handler)(Event, Context*)); 79 | void yield(); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/csr.c: -------------------------------------------------------------------------------- 1 | #include "isa/riscv.h" 2 | #include 3 | 4 | word_t csrr_mstatus() { 5 | uint32_t x; 6 | asm volatile("csrr %0, mstatus" : "=r"(x)); 7 | return x; 8 | } 9 | 10 | word_t csrr_mcause() { 11 | uint32_t x; 12 | asm volatile("csrr %0, mcause" : "=r"(x)); 13 | return x; 14 | } 15 | 16 | word_t csrr_mepc() { 17 | uint32_t x; 18 | asm volatile("csrr %0, mepc" : "=r"(x)); 19 | return x; 20 | } 21 | 22 | word_t csrr_medeleg() { 23 | uint32_t x; 24 | asm volatile("csrr %0, medeleg" : "=r"(x)); 25 | return x; 26 | } 27 | 28 | void csrw_pmpcfg0(word_t value) { 29 | asm volatile("csrw pmpcfg0, %0" : : "r"(value)); 30 | } 31 | 32 | void csrw_pmpaddr0(word_t value) { 33 | asm volatile("csrw pmpaddr0, %0" : : "r"(value)); 34 | } 35 | 36 | void csrw_mcause(word_t value) { 37 | asm volatile("csrw mcause, %0" : : "r"(value)); 38 | } 39 | 40 | void csrw_mstatus(word_t value) { 41 | asm volatile("csrw mstatus, %0" : : "r"(value)); 42 | } 43 | 44 | void csrw_mepc(word_t value) { 45 | asm volatile("csrw mepc, %0" : : "r"(value)); 46 | } 47 | 48 | void csrw_mtvec(word_t value) { 49 | asm volatile("csrw mtvec, %0" : : "r"(value)); 50 | } 51 | 52 | void csrw_medeleg(word_t value) { 53 | asm volatile("csrw medeleg, %0" : : "r"(value)); 54 | } 55 | 56 | void csrw_mscratch(word_t value) { 57 | asm volatile("csrw mscratch, %0" : : "r"(value)); 58 | } 59 | 60 | void csrw_sepc(word_t value) { 61 | asm volatile("csrw sepc, %0" : : "r"(value)); 62 | } 63 | 64 | void csrw_stvec(word_t value) { 65 | asm volatile("csrw stvec, %0" : : "r"(value)); 66 | } 67 | 68 | void csrw_satp(word_t value) { 69 | asm volatile("csrw satp, %0" : : "r"(value)); 70 | } 71 | 72 | word_t csrr_time() { 73 | uint32_t x; 74 | asm volatile("csrr %0, time" : "=r"(x)); 75 | return x; 76 | } 77 | 78 | void mret() { 79 | asm volatile("mret"); 80 | } 81 | 82 | void sret() { 83 | asm volatile("sret"); 84 | } 85 | 86 | void ecall() { 87 | asm volatile("ecall"); 88 | } 89 | 90 | void wfi() { 91 | asm volatile("wfi"); 92 | } 93 | 94 | void sfence_vma() { 95 | asm volatile("sfence.vma zero, zero"); 96 | } 97 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/cte.c: -------------------------------------------------------------------------------- 1 | #include "am.h" 2 | #include "klib.h" 3 | #include "isa/riscv.h" 4 | 5 | #include 6 | #include 7 | 8 | void __am_get_cur_as(Context *c); 9 | void __am_switch(Context *c); 10 | 11 | static Context* (*user_handler)(Event, Context*) = NULL; 12 | 13 | Context* __am_irq_handle(Context *c) { 14 | return c; 15 | } 16 | 17 | extern void __am_asm_trap(void); 18 | 19 | void cte_init(Context*(*handler)(Event, Context*)) { 20 | // initialize exception entry 21 | asm volatile("csrw mtvec, %0" : : "r"(__am_asm_trap)); 22 | // register event handler 23 | user_handler = handler; 24 | } 25 | 26 | Context *kcontext(Area kstack, void (*entry)(void *), void *arg) { 27 | Context *context = (Context *)(kstack.end - sizeof(Context)); 28 | // context->mepc = (uintptr_t)entry - 4; 29 | // context->mstatus = 0x1800 | (1 << 3) | (1 << 7); 30 | // context->gpr[10] = (uintptr_t)arg; 31 | // context->gpr[2] = (uintptr_t)kstack.end; 32 | return context; 33 | } 34 | 35 | void yield() { 36 | #ifdef __riscv_e 37 | asm volatile("li a5, -1; ecall"); 38 | #else 39 | asm volatile("li a7, -1; ecall"); 40 | #endif 41 | } 42 | 43 | bool ienabled() { 44 | return false; 45 | } 46 | 47 | void iset(bool enable) { 48 | } 49 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/halt.S: -------------------------------------------------------------------------------- 1 | .globl __halt 2 | 3 | __halt: 4 | #ifdef __QEMU__ 5 | mv a1, a0 6 | la a0, ebreakMessage 7 | call printf 8 | j . 9 | #else 10 | ebreak 11 | #endif 12 | j . 13 | 14 | .section .data 15 | ebreakMessage: 16 | .asciz "HALT with code %d.\n" 17 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/ioe/timer.c: -------------------------------------------------------------------------------- 1 | #include "am-dev.h" 2 | #include 3 | 4 | #define MTIME_BASE 0x0200BFF8 5 | #define MTIME (volatile uint64_t *)MTIME_BASE 6 | #define MTIME_LOW (*(volatile uint32_t *)MTIME_BASE) 7 | #define MTIME_HIGH (*(volatile uint32_t *)(MTIME_BASE + 4)) 8 | 9 | uint64_t __am_timer_uptime() { 10 | #if __riscv_xlen == 32 11 | uint64_t uptime = MTIME_LOW; 12 | uptime |= (uint64_t)MTIME_HIGH << 32; 13 | return uptime / 10; 14 | #else 15 | return *MTIME / 10; 16 | #endif 17 | } 18 | 19 | void __am_timer_rtc(struct _RTC *rtc) { 20 | rtc->year = 2020; 21 | rtc->month = 1; 22 | rtc->day = 1; 23 | rtc->hour = 0; 24 | rtc->minute = 0; 25 | rtc->second = 0; 26 | } 27 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/ioe/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define UART_BASE 0x10000000 4 | #define UART_THR (*(volatile uint8_t *)(UART_BASE + 0)) 5 | #define UART_RBR (*(volatile uint8_t *)(UART_BASE + 0)) 6 | #define UART_LSR (*(volatile uint8_t *)(UART_BASE + 5)) 7 | 8 | static int __am_uart_tx_ready() { 9 | return UART_LSR & (1 << 5); 10 | } 11 | 12 | void __am_uart_tx(uint8_t ch) { 13 | while (!__am_uart_tx_ready()); 14 | UART_THR = ch; 15 | } 16 | 17 | static int __am_uart_rx_ready() { 18 | return UART_LSR & (1 << 0); 19 | } 20 | 21 | uint8_t __uart_rx() { 22 | if (__am_uart_rx_ready()) { 23 | return UART_RBR; 24 | } else { 25 | return 0xff; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/start.S: -------------------------------------------------------------------------------- 1 | .section entry, "ax" 2 | .globl _start 3 | 4 | _start: 5 | la sp, __stack_pointer # load stack pointer 6 | call __run_main 7 | 8 | j . 9 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/trap.S: -------------------------------------------------------------------------------- 1 | #define concat_temp(x, y) x ## y 2 | #define concat(x, y) concat_temp(x, y) 3 | #define MAP(c, f) c(f) 4 | 5 | #if __riscv_xlen == 32 6 | #define LOAD lw 7 | #define STORE sw 8 | #define XLEN 4 9 | #else 10 | #define LOAD ld 11 | #define STORE sd 12 | #define XLEN 8 13 | #endif 14 | 15 | #define REGS_LO16(f) \ 16 | f( 1) f( 4) f( 5) f( 6) f( 7) f( 8) f( 9) \ 17 | f(10) f(11) f(12) f(13) f(14) f(15) 18 | #ifndef __riscv_e 19 | #define REGS_HI16(f) \ 20 | f(16) f(17) f(18) f(19) \ 21 | f(20) f(21) f(22) f(23) f(24) f(25) f(26) f(27) f(28) f(29) \ 22 | f(30) f(31) 23 | #define NR_REGS 32 24 | #else 25 | #define REGS_HI16(f) 26 | #define NR_REGS 16 27 | #endif 28 | 29 | #define REGS(f) REGS_LO16(f) REGS_HI16(f) f(3) 30 | 31 | #define PUSH(n) STORE concat(x, n), (n * XLEN)(sp); 32 | #define POP(n) LOAD concat(x, n), (n * XLEN)(sp); 33 | 34 | #define OFFSET_STATUS ((NR_REGS + 0) * XLEN) 35 | #define OFFSET_EPC ((NR_REGS + 1) * XLEN) 36 | 37 | #define CONTEXT_SIZE ((NR_REGS + 3) * XLEN) 38 | 39 | .align 3 40 | .globl __am_asm_trap 41 | __am_asm_trap: 42 | addi sp, sp, -CONTEXT_SIZE 43 | 44 | MAP(REGS, PUSH) 45 | 46 | csrr t0, mstatus 47 | csrr t1, mepc 48 | 49 | STORE t0, OFFSET_STATUS(sp) 50 | STORE t1, OFFSET_EPC(sp) 51 | 52 | # set mstatus.MPRV to pass difftest 53 | li a0, (1 << 17) 54 | or t1, t1, a0 55 | csrw mstatus, t1 56 | 57 | mv a0, sp 58 | jal __am_irq_handle 59 | mv sp, a0 60 | 61 | LOAD t1, OFFSET_STATUS(sp) 62 | LOAD t2, OFFSET_EPC(sp) 63 | csrw mstatus, t1 64 | csrw mepc, t2 65 | 66 | MAP(REGS, POP) 67 | addi sp, sp, CONTEXT_SIZE 68 | 69 | mret 70 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/isa/riscv/vme.c: -------------------------------------------------------------------------------- 1 | #include "am.h" 2 | 3 | void __am_get_cur_as(Context *c) {} 4 | 5 | void __am_switch(Context *c) {} 6 | -------------------------------------------------------------------------------- /tests/abstract-machine/am/src/trm.c: -------------------------------------------------------------------------------- 1 | #include "am.h" 2 | #include 3 | 4 | #ifdef MAINARGS 5 | static const char *mainargs = "" MAINARGS ""; 6 | #else 7 | static const char *mainargs = "ref"; 8 | #endif 9 | 10 | Area heap; 11 | 12 | extern int main(const char *mainargs); 13 | 14 | extern void __halt(int code); 15 | 16 | __attribute__((noreturn)) 17 | void halt(int code) { 18 | __halt(code); 19 | while(1); 20 | } 21 | 22 | extern char __heap_start; 23 | extern char __mem_end; 24 | 25 | static void __trm_init() { 26 | heap.start = &__heap_start; 27 | heap.end = heap.start + 0x8000000; 28 | } 29 | 30 | void __run_main() { 31 | __trm_init(); 32 | int r = main(mainargs); 33 | halt(r); 34 | } 35 | 36 | extern void __am_uart_tx(unsigned char); 37 | 38 | int putchar(int c) { 39 | __am_uart_tx(c); 40 | return 0; 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/abstract-machine/klib/Makefile: -------------------------------------------------------------------------------- 1 | NAME = klib 2 | SRCS += $(shell find $(SRC_DIR) -name "*.cpp" -o -name "*.c") 3 | 4 | include ../Makefile # Path: tests/cpu-tests/abstract-machine/makefile 5 | -------------------------------------------------------------------------------- /tests/abstract-machine/klib/include/klib-macros.h: -------------------------------------------------------------------------------- 1 | #ifndef __KLIB_MACROS_H__ 2 | #define __KLIB_MACROS_H__ 3 | 4 | #include "am.h" 5 | 6 | #define ROUNDUP(a, sz) ((((uintptr_t)a) + (sz) - 1) & ~((sz) - 1)) 7 | #define ROUNDDOWN(a, sz) ((((uintptr_t)a)) & ~((sz) - 1)) 8 | #define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0])) 9 | #define RANGE(st, ed) (Area) { .start = (void *)(st), .end = (void *)(ed) } 10 | #define IN_RANGE(ptr, area) ((area).start <= (ptr) && (ptr) < (area).end) 11 | 12 | #define STRINGIFY(s) #s 13 | #define TOSTRING(s) STRINGIFY(s) 14 | #define _CONCAT(x, y) x ## y 15 | #define CONCAT(x, y) _CONCAT(x, y) 16 | 17 | #define putstr(s) \ 18 | ({ for (const char *p = s; *p; p++) putchar(*p); }) 19 | 20 | #define static_assert(const_cond) \ 21 | static char CONCAT(_static_assert_, __LINE__) [(const_cond) ? 1 : -1] __attribute__((unused)) 22 | 23 | #define panic_on(cond, s) \ 24 | ({ if (cond) { \ 25 | putstr("AM Panic: "); putstr(s); \ 26 | putstr(" @ " __FILE__ ":" TOSTRING(__LINE__) " \n"); \ 27 | halt(1); \ 28 | } }) 29 | 30 | #define panic(s) panic_on(1, s) 31 | 32 | #define assert(cond) \ 33 | ({ if (!(cond)) { \ 34 | putstr("AM Panic: Assertion failed: " #cond " @ " __FILE__ ":" TOSTRING(__LINE__) " \n"); \ 35 | halt(1); \ 36 | } }) 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /tests/abstract-machine/klib/include/klib.h: -------------------------------------------------------------------------------- 1 | #ifndef __KLIB_H__ 2 | #define __KLIB_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "klib-macros.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | // stdio.h 15 | int printf (const char *fmt, ...); 16 | int sprintf (char *out, const char *fmt, ...); 17 | int snprintf (char *out, size_t n, const char *fmt, ...); 18 | int vsprintf (char *str, const char *format, va_list ap); 19 | int vsnprintf (char *out, size_t n, const char *fmt, va_list ap); 20 | 21 | int puts(const char *s); 22 | 23 | // ctype.h 24 | int isalpha (int c); 25 | int isdigit (int c); 26 | 27 | // string.h 28 | size_t strlen (const char *s); 29 | char *strcpy (char *dst, const char *src); 30 | char *strncpy (char *dst, const char *src, size_t n); 31 | char *strcat (char *dst, const char *src); 32 | int strcmp (const char *s1, const char *s2); 33 | int strncmp (const char *s1, const char *s2, size_t n); 34 | void *memset (void *s, int c, size_t n); 35 | void *memmove (void *dst, const void *src, size_t n); 36 | void *memcpy (void *dst, const void *src, size_t n); 37 | int memcmp (const void *s1, const void *s2, size_t n); 38 | 39 | #ifdef __cplusplus 40 | }; 41 | #endif 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /tests/abstract-machine/klib/src/ctype.c: -------------------------------------------------------------------------------- 1 | int isalpha(int c) { 2 | return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); 3 | } 4 | 5 | int isdigit(int c) { 6 | return (c >= '0' && c <= '9'); 7 | } -------------------------------------------------------------------------------- /tests/abstract-machine/klib/src/stdlib.c: -------------------------------------------------------------------------------- 1 | int abs(int x) { 2 | return (x < 0 ? -x : x); 3 | } 4 | 5 | int atoi(const char* nptr) { 6 | int x = 0; 7 | while (*nptr == ' ') { nptr ++; } 8 | while (*nptr >= '0' && *nptr <= '9') { 9 | x = x * 10 + *nptr - '0'; 10 | nptr ++; 11 | } 12 | return x; 13 | } -------------------------------------------------------------------------------- /tests/abstract-machine/klib/src/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | size_t strlen(const char *s) { 5 | size_t i = 0; 6 | while (*s++) i++; 7 | return i; 8 | } 9 | 10 | char *strcpy(char *dst, const char *src) { 11 | char *p = (char*)src, *d = dst; 12 | while (*p) *(d++) = *(p++); 13 | *d = 0; 14 | return dst; 15 | } 16 | 17 | char *strncpy(char *dst, const char *src, size_t n) { 18 | size_t i; 19 | for (i = 0; i < n && src[i]; i++) { 20 | dst[i] = src[i]; 21 | } 22 | while (i < n) dst[i++] = 0; 23 | return dst; 24 | } 25 | 26 | char *strcat(char *dst, const char *src) { 27 | char *d = dst; 28 | while (*d) d++; 29 | for (char *p = (char *)src; *p; p++, d++) { 30 | *d = *p; 31 | } 32 | *d = 0; 33 | return dst; 34 | } 35 | 36 | int strcmp(const char *s1, const char *s2) { 37 | unsigned char *p1 = (unsigned char *)s1, *p2 = (unsigned char *)s2; 38 | for (; *p1 && *p2; p1++, p2++) { 39 | if (*p1 != *p2) return *p1 - *p2; 40 | } 41 | if (*p1) return 1; 42 | if (*p2) return -1; 43 | return 0; 44 | } 45 | 46 | int strncmp(const char *s1, const char *s2, size_t n) { 47 | unsigned char *p1 = (unsigned char *)s1, *p2 = (unsigned char *)s2; 48 | size_t i; 49 | for (i = 0; i < n && *p1 && *p2; i++, p1++, p2++) { 50 | if (*p1 != *p2) return *p1 - *p2; 51 | } 52 | if (i == n) return 0; 53 | if (*p1) return 1; 54 | if (*p2) return -1; 55 | return 0; 56 | } 57 | 58 | void *memset(void *s, int c, size_t n) { 59 | unsigned char *p = s; 60 | for (size_t i = 0; i < n; i++, p++) *p = (unsigned char) c; 61 | return s; 62 | } 63 | 64 | void *memmove(void *dst, const void *src, size_t n) { 65 | unsigned char p[n]; 66 | unsigned char *t = p, *d = dst, *s = (unsigned char *)src; 67 | for (int i = 0; i < n; i++, t++, s++) *t = *s; 68 | t = p; 69 | for (int i = 0; i < n; i++, d++, t++) *d = *t; 70 | return dst; 71 | } 72 | 73 | void *memcpy(void *out, const void *in, size_t n) { 74 | unsigned char *o = out, *i = (unsigned char *)in; 75 | for (size_t j = 0; j < n; j++, o++, i++) *o = *i; 76 | return out; 77 | } 78 | 79 | int memcmp(const void *s1, const void *s2, size_t n) { 80 | unsigned char *p1 = (unsigned char *)s1, *p2 = (unsigned char *)s2; 81 | for (size_t i = 0; i < n; i++, p1++, p2++) { 82 | if (*p1 != *p2) return *p1 - *p2; 83 | } 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /tests/abstract-machine/scripts/kdb/debug.kdb: -------------------------------------------------------------------------------- 1 | load elf 2 | reset -------------------------------------------------------------------------------- /tests/abstract-machine/scripts/kdb/init.kdb: -------------------------------------------------------------------------------- 1 | mem create mem 0x80000000 0x1000000 2 | log off DEBUG 3 | -------------------------------------------------------------------------------- /tests/abstract-machine/scripts/kdb/run.kdb: -------------------------------------------------------------------------------- 1 | load elf 2 | reset 3 | run 4 | exit 5 | -------------------------------------------------------------------------------- /tests/abstract-machine/scripts/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS { 4 | . = 0x80000000; 5 | .text : { 6 | *(entry) 7 | *(.text*) 8 | } 9 | .rodata : { 10 | *(.rodata*) 11 | } 12 | .data : { 13 | *(.data*) 14 | } 15 | .bss : { 16 | *(.bss*) 17 | *(.sbss*) 18 | *(.scommon) 19 | } 20 | __stack_top = ALIGN(0x1000); 21 | . = __stack_top + 0x8000; /* 32KB stack */ 22 | __stack_pointer = .; 23 | __heap_start = ALIGN(0x1000); 24 | } 25 | -------------------------------------------------------------------------------- /tests/abstract-machine/scripts/riscv32.mk: -------------------------------------------------------------------------------- 1 | AM_ISA_NAME = riscv 2 | 3 | CROSS_COMPILE ?= riscv64-linux-gnu 4 | 5 | COMPILE_FLAGS += -march=rv32imc_zicsr -mabi=ilp32 6 | 7 | LDFLAGS += -T $(AM_DIR)/scripts/linker.ld \ 8 | --defsym=_pmem_start=0x80000000 --defsym=_entry_offset=0x0 \ 9 | -melf32lriscv 10 | 11 | QEMU = qemu-system-riscv32 12 | QEMU_FLAGS += -machine virt -nographic -bios none -semihosting 13 | -------------------------------------------------------------------------------- /tests/abstract-machine/scripts/riscv64.mk: -------------------------------------------------------------------------------- 1 | AM_ISA_NAME = riscv 2 | 3 | CROSS_COMPILE ?= riscv64-linux-gnu 4 | 5 | COMPILE_FLAGS += -march=rv64im_zicsr -mabi=lp64 6 | 7 | LDFLAGS += -T $(AM_DIR)/scripts/linker.ld \ 8 | --defsym=_pmem_start=0x80000000 --defsym=_entry_offset=0x0 \ 9 | -melf64lriscv 10 | 11 | QEMU = qemu-system-riscv64 12 | QEMU_FLAGS = -machine virt -nographic -bios none -semihosting 13 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/Makefile: -------------------------------------------------------------------------------- 1 | NAME = microbench 2 | SRCS = $(shell find src/ -name "*.c" -o -name "*.cc") 3 | INC_PATH += $(abspath ./include) 4 | 5 | KDB_INIT_SRC = $(abspath ./init.kdb) 6 | 7 | include ../../abstract-machine/Makefile 8 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/README.md: -------------------------------------------------------------------------------- 1 | # MicroBench 2 | 3 | CPU正确性和性能测试用基准程序。对AbstractMachine的要求: 4 | 5 | 1. 需要实现TRM和IOE的API。 6 | 2. 在IOE的全部实现均留空的情况下仍可运行。如果有正确实现的`AM_TIMER_UPTIME`,可以输出正确的统计时间。若这个功能没有实现(返回`0`),仍可进行正确性测试。 7 | 3. 使用`putch(ch)`输出。 8 | 4. 堆区`heap`必须初始化(堆区可为空)。如果`heap.start == heap.end`,即分配了空的堆区,只能运行不使用堆区的测试程序。每个基准程序会预先指定堆区的大小,堆区不足的基准程序将被忽略。 9 | 10 | ## 使用方法 11 | 12 | 同一组程序分成四组:test,train,ref和huge。 13 | 14 | | 名称 | 动态指令数 | 计时 | 计分 | 建议使用场景 | 15 | | ----- | ----------- | ---- | ---- | ----- | 16 | | test | 约300K | X | X | 正确性测试 | 17 | | train | 约60M | O | X | 在RTL仿真环境中研究微结构行为 | 18 | | ref | 约2B | O | O | 在模拟器或FPGA环境中评估处理器性能 | 19 | | huge | 约50B | O | O | 衡量高性能处理器(如真机)的性能 | 20 | 21 | 默认运行ref数据规模,可通过`mainargs`选择其它的数据规模, 如: 22 | ```bash 23 | make ARCH=native run mainargs=huge 24 | ``` 25 | 26 | ## 评分根据 27 | 28 | 每个benchmark都记录以`REF_CPU`为基础测得的运行时间微秒数。每个benchmark的评分是相对于`REF_CPU`的运行速度,与基准处理器一样快的得分为`REF_SCORE=100000`。 29 | 30 | 所有benchmark的平均得分是整体得分。 31 | 32 | ## 已有的基准程序 33 | 34 | | 名称 | 描述 | ref堆区使用 | huge堆区使用 | 35 | | ----- | -------------------------------------------- | ----- | ----- | 36 | | qsort | 快速排序随机整数数组 | 640KB | 16MB | 37 | | queen | 位运算实现的n皇后问题 | 0 | 0 | 38 | | bf | Brainf**k解释器,快速排序输入的字符串 | 32KB | 32KB | 39 | | fib | Fibonacci数列f(n)=f(n-1)+…+f(n-m)的矩阵求解 | 256KB | 2MB | 40 | | sieve | Eratosthenes筛法求素数 | 2MB | 10MB | 41 | | 15pz | A*算法求解4x4数码问题 | 2MB | 64MB | 42 | | dinic | Dinic算法求解二分图最大流 | 680KB | 2MB | 43 | | lzip | Lzip数据压缩 | 4MB | 64MB | 44 | | ssort | Skew算法后缀排序 | 4MB | 64MB | 45 | | md5 | 计算长随机字符串的MD5校验和 | 10MB | 64MB | 46 | 47 | ## 增加一个基准程序`foo` 48 | 49 | 在`src/`目录下建立名为`foo`的目录,将源代码文件放入。 50 | 51 | 每个基准程序需要实现三个函数: 52 | 53 | * `void bench_foo_prepare();`:进行准备工作,如初始化随机数种子、为数组分配内存等。运行时环境不保证全局变量和堆区的初始值,因此基准程序使用的全局数据必须全部初始化。 54 | * `void bench_foo_run();`:实际运行基准程序。只有这个函数会被计时。 55 | * `int bench_foo_validate();`:验证基准程序运行结果。正确返回1,错误返回0。 56 | 57 | 在`benchmark.h`的`BENCHMARK_LIST`中增加相应的`def`项,格式参考已有的benchmark。 58 | 59 | ## 基准程序可以使用的库函数 60 | 61 | 虽然klib中提供了一些函数,但不同的klib实现会导致性能测试结果有差异。 62 | 因此MicroBench中内置一些简单的库函数: 63 | 64 | * `bench_memcpy(void *dst, const void *src, size_t n)`: 内存复制。 65 | * `bench_srand(uint seed)`:用seed初始化随机数种子。 66 | * `bench_rand()`:返回一个0..32767之间的随机数。 67 | * `bench_alloc`/`bench_free`:内存分配/回收。目前回收是空操作。 68 | 69 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/init.kdb: -------------------------------------------------------------------------------- 1 | uart add 0x10000000 -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/15pz/15pz.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "puzzle.h" 3 | #include "heap.h" 4 | 5 | const int N = 4; 6 | 7 | static int PUZZLE_S[N*N] = { 8 | 1, 2, 3, 4, 9 | 5, 6, 7, 8, 10 | 9, 10, 0, 11, 11 | 13, 14, 15, 12, 12 | }; 13 | 14 | static int PUZZLE_M[N*N] = { 15 | 1, 2, 3, 4, 16 | 5, 6, 7, 8, 17 | 12, 0, 14, 13, 18 | 11, 15, 10, 9, 19 | }; 20 | 21 | static int PUZZLE_L[N*N] = { 22 | 0, 2, 3, 4, 23 | 9, 6, 7, 8, 24 | 5, 11, 10, 12, 25 | 1, 15, 13, 14, 26 | }; 27 | 28 | static int PUZZLE_H[N*N] = { 29 | 2, 6, 8, 0, 30 | 9, 15, 4, 12, 31 | 5, 13, 11,14, 32 | 1, 7, 3, 10, 33 | }; 34 | 35 | static int ans; 36 | 37 | extern "C" { 38 | 39 | void bench_15pz_prepare() { 40 | } 41 | 42 | void bench_15pz_run() { 43 | N_puzzle puzzle; 44 | int MAXN; 45 | 46 | switch (setting->size) { 47 | case 0: puzzle = N_puzzle(PUZZLE_S); MAXN = 10; break; 48 | case 1: puzzle = N_puzzle(PUZZLE_M); MAXN = 2048; break; 49 | case 2: puzzle = N_puzzle(PUZZLE_L); MAXN = 16384; break; 50 | case 3: puzzle = N_puzzle(PUZZLE_H); MAXN = 786432; break; 51 | default: assert(0); 52 | } 53 | assert(puzzle.solvable()); 54 | 55 | auto *heap = (Updatable_heap> *) bench_alloc(sizeof(Updatable_heap>)); 56 | heap->init(MAXN); 57 | heap->push( puzzle, 0 ); 58 | 59 | int n = 0; 60 | ans = -1; 61 | 62 | while( heap->size() != 0 && n != MAXN ) { 63 | N_puzzle top = heap->pop(); 64 | ++n; 65 | 66 | if ( top == N_puzzle::solution() ) { 67 | // We are done 68 | ans = heap->length(top) * n; 69 | return; 70 | } 71 | 72 | if ( top.tile_left_possible() ) { 73 | heap->push( top.tile_left(), heap->length( top ) + 1 ); 74 | } 75 | 76 | if ( top.tile_right_possible() ) { 77 | heap->push( top.tile_right(), heap->length( top ) + 1 ); 78 | } 79 | 80 | if ( top.tile_up_possible() ) { 81 | heap->push( top.tile_up(), heap->length( top ) + 1 ); 82 | } 83 | 84 | if ( top.tile_down_possible() ) { 85 | heap->push( top.tile_down(), heap->length( top ) + 1 ); 86 | } 87 | } 88 | } 89 | 90 | 91 | int bench_15pz_validate() { 92 | return (uint32_t)ans == setting->checksum; 93 | } 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/dinic/dinic.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int N; 4 | const int INF = 0x3f3f3f; 5 | 6 | struct Edge { 7 | int from, to, cap, flow; 8 | Edge(){} 9 | Edge(int from, int to, int cap, int flow) { 10 | this->from = from; 11 | this->to = to; 12 | this->cap = cap; 13 | this->flow = flow; 14 | } 15 | }; 16 | 17 | template 18 | static inline T min(T x, T y) { 19 | return x < y ? x : y; 20 | } 21 | 22 | struct Dinic { 23 | int n, m, s, t; 24 | Edge *edges; 25 | int *head, *nxt, *d, *cur, *queue; 26 | bool *vis; 27 | 28 | void init(int n) { 29 | int nold = (n - 2) / 2; 30 | int maxm = (nold * nold + nold * 2) * 2; 31 | 32 | edges = (Edge *)bench_alloc(sizeof(Edge) * maxm); 33 | head = (int *)bench_alloc(sizeof(int) * n); 34 | nxt = (int *)bench_alloc(sizeof(int) * maxm); 35 | vis = (bool *)bench_alloc(sizeof(bool) * n); 36 | d = (int *)bench_alloc(sizeof(int) * n); 37 | cur = (int *)bench_alloc(sizeof(int) * n); 38 | queue = (int *)bench_alloc(sizeof(int) * n); 39 | 40 | this->n = n; 41 | for (int i = 0; i < n; i ++) { 42 | head[i] = -1; 43 | } 44 | m = 0; 45 | } 46 | 47 | void AddEdge(int u, int v, int c) { 48 | if (c == 0) return; 49 | edges[m] = Edge(u, v, c, 0); 50 | nxt[m] = head[u]; 51 | head[u] = m++; 52 | edges[m] = Edge(v, u, 0, 0); 53 | nxt[m] = head[v]; 54 | head[v] = m++; 55 | } 56 | 57 | bool BFS() { 58 | for (int i = 0; i < n; i ++) vis[i] = 0; 59 | int qf = 0, qr = 0; 60 | queue[qr ++] = s; 61 | d[s] = 0; 62 | vis[s] = 1; 63 | while (qf != qr) { 64 | int x = queue[qf ++]; 65 | for (int i = head[x]; i != -1; i = nxt[i]) { 66 | Edge& e = edges[i]; 67 | if (!vis[e.to] && e.cap > e.flow) { 68 | vis[e.to] = 1; 69 | d[e.to] = d[x] + 1; 70 | queue[qr ++] = e.to; 71 | } 72 | } 73 | } 74 | return vis[t]; 75 | } 76 | 77 | int DFS(int x, int a) { 78 | if (x == t || a == 0) return a; 79 | int flow = 0, f; 80 | for (int i = cur[x]; i != -1; i = nxt[i]) { 81 | Edge& e = edges[i]; 82 | if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) { 83 | e.flow += f; 84 | edges[i^1].flow -= f; 85 | flow += f; 86 | a -= f; 87 | if (a == 0) break; 88 | } 89 | } 90 | return flow; 91 | } 92 | 93 | int Maxflow(int s, int t) { 94 | this -> s = s; this -> t = t; 95 | int flow = 0; 96 | while (BFS()) { 97 | for (int i = 0; i < n; i++) 98 | cur[i] = head[i]; 99 | flow += DFS(s, INF); 100 | } 101 | return flow; 102 | } 103 | }; 104 | 105 | 106 | extern "C" { 107 | 108 | 109 | static Dinic *G; 110 | static int ans; 111 | 112 | void bench_dinic_prepare() { 113 | N = setting->size; 114 | bench_srand(1); 115 | int s = 2 * N, t = 2 * N + 1; 116 | G = (Dinic*)bench_alloc(sizeof(Dinic)); 117 | G->init(2 * N + 2); 118 | for (int i = 0; i < N; i ++) 119 | for (int j = 0; j < N; j ++) { 120 | G->AddEdge(i, N + j, bench_rand() % 10); 121 | } 122 | 123 | for (int i = 0; i < N; i ++) { 124 | G->AddEdge(s, i, bench_rand() % 1000); 125 | G->AddEdge(N + i, t, bench_rand() % 1000); 126 | } 127 | } 128 | 129 | void bench_dinic_run() { 130 | ans = G->Maxflow(2 * N, 2 * N + 1); 131 | } 132 | 133 | int bench_dinic_validate() { 134 | return (uint32_t)ans == setting->checksum; 135 | } 136 | } 137 | 138 | 139 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/fib/fib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // f(n) = (f(n-1) + f(n-2) + .. f(n-m)) mod 2^32 4 | 5 | #define N 2147483603 6 | static int M; 7 | 8 | static void put(uint32_t *m, int i, int j, uint32_t data) { 9 | m[i * M + j] = data; 10 | } 11 | 12 | static uint32_t get(uint32_t *m, int i, int j) { 13 | return m[i * M + j]; 14 | } 15 | 16 | static inline void mult(uint32_t *c, uint32_t *a, uint32_t *b) { 17 | for (int i = 0; i < M; i ++) 18 | for (int j = 0; j < M; j ++) { 19 | put(c, i, j, 0); 20 | for (int k = 0; k < M; k ++) { 21 | put(c, i, j, get(c, i, j) + get(a, i, k) * get(b, k, j)); 22 | } 23 | } 24 | } 25 | 26 | static inline void assign(uint32_t *a, uint32_t *b) { 27 | for (int i = 0; i < M; i ++) 28 | for (int j = 0; j < M; j ++) 29 | put(a, i, j, get(b, i, j)); 30 | } 31 | 32 | static uint32_t *A, *ans, *T, *tmp; 33 | 34 | void bench_fib_prepare() { 35 | M = setting->size; 36 | int sz = sizeof(uint32_t) * M * M; 37 | A = bench_alloc(sz); 38 | T = bench_alloc(sz); 39 | ans = bench_alloc(sz); 40 | tmp = bench_alloc(sz); 41 | } 42 | 43 | void bench_fib_run() { 44 | for (int i = 0; i < M; i ++) 45 | for (int j = 0; j < M; j ++) { 46 | uint32_t x = (i == M - 1 || j == i + 1); 47 | put(A, i, j, x); 48 | put(T, i, j, x); 49 | put(ans, i, j, i == j); 50 | } 51 | 52 | for (int n = N; n > 0; n >>= 1) { 53 | if (n & 1) { 54 | mult(tmp, ans, T); 55 | assign(ans, tmp); 56 | } 57 | mult(tmp, T, T); 58 | assign(T, tmp); 59 | } 60 | } 61 | 62 | int bench_fib_validate() { 63 | return get(ans, M-1, M-1) == setting->checksum; 64 | } 65 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/lzip/lzip.c: -------------------------------------------------------------------------------- 1 | #include "quicklz.h" 2 | #include 3 | 4 | static int SIZE; 5 | 6 | static qlz_state_compress *state; 7 | static char *blk; 8 | static char *compress; 9 | static int len; 10 | 11 | void bench_lzip_prepare() { 12 | SIZE = setting->size; 13 | bench_srand(1); 14 | state = bench_alloc(sizeof(qlz_state_compress)); 15 | blk = bench_alloc(SIZE); 16 | compress = bench_alloc(SIZE + 400); 17 | for (int i = 0; i < SIZE; i ++) { 18 | blk[i] = 'a' + bench_rand() % 26; 19 | } 20 | } 21 | 22 | void bench_lzip_run() { 23 | len = qlz_compress(blk, compress, SIZE, state); 24 | } 25 | 26 | int bench_lzip_validate() { 27 | return checksum(compress, compress + len) == setting->checksum; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/qsort/qsort.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int N, *data; 4 | 5 | void bench_qsort_prepare() { 6 | bench_srand(1); 7 | 8 | N = setting->size; 9 | 10 | data = bench_alloc(N * sizeof(int)); 11 | for (int i = 0; i < N; i ++) { 12 | int a = bench_rand(); 13 | int b = bench_rand(); 14 | data[i] = (a << 16) | b; 15 | } 16 | } 17 | 18 | static void swap(int *a, int *b) { 19 | int t = *a; 20 | *a = *b; 21 | *b = t; 22 | } 23 | 24 | static void myqsort(int *a, int l, int r) { 25 | if (l < r) { 26 | int p = a[l], pivot = l, j; 27 | for (j = l + 1; j < r; j ++) { 28 | if (a[j] < p) { 29 | swap(&a[++pivot], &a[j]); 30 | } 31 | } 32 | swap(&a[pivot], &a[l]); 33 | myqsort(a, l, pivot); 34 | myqsort(a, pivot + 1, r); 35 | } 36 | } 37 | 38 | void bench_qsort_run() { 39 | myqsort(data, 0, N); 40 | } 41 | 42 | int bench_qsort_validate() { 43 | return checksum(data, data + N) == setting->checksum; 44 | } 45 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/queen/queen.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static unsigned int FULL; 4 | 5 | static unsigned int dfs(unsigned int row, unsigned int ld, unsigned int rd) { 6 | if (row == FULL) { 7 | return 1; 8 | } else { 9 | unsigned int pos = FULL & (~(row | ld | rd)), ans = 0; 10 | while (pos) { 11 | unsigned int p = (pos & (~pos + 1)); 12 | pos -= p; 13 | ans += dfs(row | p, (ld | p) << 1, (rd | p) >> 1); 14 | } 15 | return ans; 16 | } 17 | } 18 | 19 | static unsigned int ans; 20 | 21 | void bench_queen_prepare() { 22 | ans = 0; 23 | FULL = (1 << setting->size) - 1; 24 | } 25 | 26 | void bench_queen_run() { 27 | ans = dfs(0, 0, 0); 28 | } 29 | 30 | int bench_queen_validate() { 31 | return ans == setting->checksum; 32 | } 33 | -------------------------------------------------------------------------------- /tests/benchmarks/microbench/src/sieve/sieve.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int N; 4 | 5 | static int ans; 6 | static uint32_t *primes; 7 | 8 | static inline int get(int n) { 9 | return (primes[n >> 5] >> (n & 31)) & 1; 10 | } 11 | 12 | static inline void clear(int n) { 13 | primes[n >> 5] &= ~(1ul << (n & 31)); 14 | } 15 | 16 | void bench_sieve_prepare() { 17 | N = setting->size; 18 | primes = (uint32_t*)bench_alloc(N / 8 + 128); 19 | for (int i = 0; i <= N / 32; i ++) { 20 | primes[i] = 0xffffffff; 21 | } 22 | } 23 | 24 | void bench_sieve_run() { 25 | for (int i = 1; i <= N; i ++) 26 | if (!get(i)) return; 27 | for (int i = 2; i * i <= N; i ++) { 28 | if (get(i)) { 29 | for (int j = i + i; j <= N; j += i) 30 | clear(j); 31 | } 32 | } 33 | ans = 0; 34 | for (int i = 2; i <= N; i ++) 35 | if (get(i)) { 36 | ans ++; 37 | } 38 | } 39 | 40 | int bench_sieve_validate() { 41 | return ans == setting->checksum; 42 | } 43 | -------------------------------------------------------------------------------- /tests/cpu-tests/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all run clean $(ALL) 2 | 3 | RESULT = .result 4 | $(shell > $(RESULT)) 5 | 6 | COLOR_RED = \033[1;31m 7 | COLOR_GREEN = \033[1;32m 8 | COLOR_NONE = \033[0m 9 | 10 | ALL = $(basename $(notdir $(shell find tests/. -name "*.c"))) 11 | 12 | all: $(addprefix Makefile., $(ALL)) 13 | @echo "test list [$(words $(ALL)) item(s)]:" $(ALL) 14 | 15 | $(ALL): %: Makefile.% 16 | 17 | Makefile.%: tests/%.c latest 18 | @/bin/echo -e "INC_PATH += $(abspath ./include)\nSRCS += $<\nNAME=$*\ninclude ../abstract-machine/Makefile" > $@ 19 | @if make -s -f $@ ISA=$(ISA) $(MAKECMDGOALS); then \ 20 | printf "[%14s] $(COLOR_GREEN)PASS$(COLOR_NONE)\n" $* >> $(RESULT); \ 21 | else \ 22 | printf "[%14s] $(COLOR_RED)***FAIL***$(COLOR_NONE)\n" $* >> $(RESULT); \ 23 | fi 24 | -@rm -f Makefile.$* 25 | 26 | run: all 27 | @cat $(RESULT) 28 | @rm $(RESULT) 29 | 30 | debug: all 31 | @cat $(RESULT) 32 | @rm $(RESULT) 33 | 34 | qemu: all 35 | @rm $(RESULT) 36 | 37 | clean: 38 | rm -rf Makefile.* build/ 39 | 40 | latest: 41 | -------------------------------------------------------------------------------- /tests/cpu-tests/include/test.h: -------------------------------------------------------------------------------- 1 | #ifndef __TRAP_H__ 2 | #define __TRAP_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0])) 8 | 9 | __attribute__((noinline)) 10 | void check(bool cond) { 11 | if (!cond) halt(1); 12 | } 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/add-longlong.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | long long add(long long a, long long b) { 4 | long long c = a + b; 5 | return c; 6 | } 7 | 8 | long long test_data[] = {0, 1, 2, 0x7fffffffffffffffLL, 0x8000000000000000LL, 0x8000000000000001LL, 0xfffffffffffffffeLL, 0xffffffffffffffffLL}; 9 | long long ans[] = {0LL, 0x1LL, 0x2LL, 0x7fffffffffffffffLL, 0x8000000000000000LL, 0x8000000000000001LL, 0xfffffffffffffffeLL, 0xffffffffffffffffLL, 0x1LL, 0x2LL, 0x3LL, 0x8000000000000000LL, 0x8000000000000001LL, 0x8000000000000002LL, 0xffffffffffffffffLL, 0LL, 0x2LL, 0x3LL, 0x4LL, 0x8000000000000001LL, 0x8000000000000002LL, 0x8000000000000003LL, 0LL, 0x1LL, 0x7fffffffffffffffLL, 0x8000000000000000LL, 0x8000000000000001LL, 0xfffffffffffffffeLL, 0xffffffffffffffffLL, 0LL, 0x7ffffffffffffffdLL, 0x7ffffffffffffffeLL, 0x8000000000000000LL, 0x8000000000000001LL, 0x8000000000000002LL, 0xffffffffffffffffLL, 0LL, 0x1LL, 0x7ffffffffffffffeLL, 0x7fffffffffffffffLL, 0x8000000000000001LL, 0x8000000000000002LL, 0x8000000000000003LL, 0LL, 0x1LL, 0x2LL, 0x7fffffffffffffffLL, 0x8000000000000000LL, 0xfffffffffffffffeLL, 0xffffffffffffffffLL, 0LL, 0x7ffffffffffffffdLL, 0x7ffffffffffffffeLL, 0x7fffffffffffffffLL, 0xfffffffffffffffcLL, 0xfffffffffffffffdLL, 0xffffffffffffffffLL, 0LL, 0x1LL, 0x7ffffffffffffffeLL, 0x7fffffffffffffffLL, 0x8000000000000000LL, 0xfffffffffffffffdLL, 0xfffffffffffffffeLL}; 10 | 11 | #define NR_DATA LENGTH(test_data) 12 | 13 | int main() { 14 | int i, j, ans_idx = 0; 15 | for(i = 0; i < NR_DATA; i ++) { 16 | for(j = 0; j < NR_DATA; j ++) { 17 | check(add(test_data[i], test_data[j]) == ans[ans_idx ++]); 18 | } 19 | } 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/add.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int add(int a, int b) { 4 | int c = a + b; 5 | return c; 6 | } 7 | 8 | int test_data[] = {0, 1, 2, 0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff}; 9 | int ans[] = {0, 0x1, 0x2, 0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff, 0x1, 0x2, 0x3, 0x80000000, 0x80000001, 0x80000002, 0xffffffff, 0, 0x2, 0x3, 0x4, 0x80000001, 0x80000002, 0x80000003, 0, 0x1, 0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff, 0, 0x7ffffffd, 0x7ffffffe, 0x80000000, 0x80000001, 0x80000002, 0xffffffff, 0, 0x1, 0x7ffffffe, 0x7fffffff, 0x80000001, 0x80000002, 0x80000003, 0, 0x1, 0x2, 0x7fffffff, 0x80000000, 0xfffffffe, 0xffffffff, 0, 0x7ffffffd, 0x7ffffffe, 0x7fffffff, 0xfffffffc, 0xfffffffd, 0xffffffff, 0, 0x1, 0x7ffffffe, 0x7fffffff, 0x80000000, 0xfffffffd, 0xfffffffe}; 10 | 11 | #define NR_DATA LENGTH(test_data) 12 | 13 | int main() { 14 | int i, j, ans_idx = 0; 15 | for(i = 0; i < NR_DATA; i ++) { 16 | for(j = 0; j < NR_DATA; j ++) { 17 | check(add(test_data[i], test_data[j]) == ans[ans_idx ++]); 18 | } 19 | check(j == NR_DATA); 20 | } 21 | 22 | check(i == NR_DATA); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/bit.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | typedef unsigned char uint8_t; 4 | __attribute__((noinline)) 5 | bool getbit(void *buf, int offset){ 6 | int byte = offset >> 3; 7 | offset &= 7; 8 | uint8_t mask = 1 << offset; 9 | return (((uint8_t *)buf)[byte] & mask) != 0; 10 | } 11 | __attribute__((noinline)) 12 | void setbit(void *buf, int offset, bool bit){ 13 | int byte = offset >> 3; 14 | offset &= 7; 15 | uint8_t mask = 1 << offset; 16 | 17 | uint8_t * volatile p = buf + byte; 18 | *p = (bit == 0 ? (*p & ~mask) : (*p | mask)); 19 | } 20 | 21 | int main() { 22 | uint8_t buf[2]; 23 | 24 | buf[0] = 0xaa; 25 | buf[1] = 0x0; 26 | check(getbit(buf, 0) == 0); 27 | // check(getbit(buf, 1) == 1); 28 | // check(getbit(buf, 2) == 0); 29 | // check(getbit(buf, 3) == 1); 30 | // check(getbit(buf, 4) == 0); 31 | // check(getbit(buf, 5) == 1); 32 | // check(getbit(buf, 6) == 0); 33 | // check(getbit(buf, 7) == 1); 34 | 35 | // setbit(buf, 8, 1); 36 | // setbit(buf, 9, 0); 37 | // setbit(buf, 10, 1); 38 | // setbit(buf, 11, 0); 39 | // setbit(buf, 12, 1); 40 | // setbit(buf, 13, 0); 41 | // setbit(buf, 14, 1); 42 | // setbit(buf, 15, 0); 43 | // check(buf[1] == 0x55); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/bubble-sort.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define N 20 4 | 5 | int a[N] = {2, 12, 14, 6, 13, 15, 16, 10, 0, 18, 11, 19, 9, 1, 7, 5, 4, 3, 8, 17}; 6 | 7 | void bubble_sort() { 8 | int i, j, t; 9 | for(j = 0; j < N; j ++) { 10 | for(i = 0; i < N - 1 - j; i ++) { 11 | if(a[i] > a[i + 1]) { 12 | t = a[i]; 13 | a[i] = a[i + 1]; 14 | a[i + 1] = t; 15 | } 16 | } 17 | } 18 | } 19 | 20 | int main() { 21 | bubble_sort(); 22 | 23 | int i; 24 | for(i = 0; i < N; i ++) { 25 | check(a[i] == i); 26 | } 27 | 28 | check(i == N); 29 | 30 | bubble_sort(); 31 | 32 | for(i = 0; i < N; i ++) { 33 | check(a[i] == i); 34 | } 35 | 36 | check(i == N); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/crc32.c: -------------------------------------------------------------------------------- 1 | /* from http://rosettacode.org/wiki/CRC-32#C */ 2 | 3 | #include 4 | #include 5 | #include "test.h" 6 | 7 | #define STR "The quick brown fox jumps over the lazy dog" 8 | #define STRLEN (sizeof(STR) - 1) 9 | 10 | uint32_t rc_crc32(uint32_t crc, const char *buf, size_t len) { 11 | static uint32_t table[256]; 12 | static int have_table = 0; 13 | uint32_t rem; 14 | uint8_t octet; 15 | int i, j; 16 | const char *p, *q; 17 | 18 | /* This check is not thread safe; there is no mutex. */ 19 | if (have_table == 0) { 20 | /* Calculate CRC table. */ 21 | for (i = 0; i < 256; i++) { 22 | rem = i; /* remainder from polynomial division */ 23 | for (j = 0; j < 8; j++) { 24 | if (rem & 1) { 25 | rem >>= 1; 26 | rem ^= 0xedb88320; 27 | } else 28 | rem >>= 1; 29 | } 30 | table[i] = rem; 31 | } 32 | have_table = 1; 33 | } 34 | 35 | crc = ~crc; 36 | q = buf + len; 37 | for (p = buf; p < q; p++) { 38 | octet = *p; /* Cast to unsigned octet. */ 39 | crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet]; 40 | } 41 | return ~crc; 42 | } 43 | 44 | int main() { 45 | uint32_t res = rc_crc32(0, STR, STRLEN); 46 | check(res == 0x414FA339); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/div.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define N 10 4 | int a[N]; 5 | 6 | int main() { 7 | int i, j; 8 | for(i = 0; i < N; i ++) 9 | a[i] = i; 10 | for(i = 0; i < N; i ++) 11 | for(j = 1; j < N + 1; j ++) 12 | a[i] *= j; 13 | for(i = 0; i < N; i ++) 14 | for(j = 1; j < N + 1; j ++) 15 | a[i] /= j; 16 | 17 | for(i = 0; i < N; i ++) 18 | check(a[i] == i); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/dummy.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/fact.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int f[15]; 4 | int ans[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600}; 5 | 6 | int fact(int n) { 7 | if(n == 0 || n == 1) return 1; 8 | else return fact(n - 1) * n; 9 | } 10 | 11 | int main() { 12 | int i; 13 | for(i = 0; i < 13; i ++) { 14 | f[i] = fact(i); 15 | check(f[i] == ans[i]); 16 | } 17 | 18 | return 0; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/fib.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int fib[40] = {1, 1}; 4 | int ans[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155}; 5 | 6 | int main() { 7 | int i; 8 | for(i = 2; i < 40; i ++) { 9 | fib[i] = fib[i - 1] + fib[i - 2]; 10 | check(fib[i] == ans[i]); 11 | } 12 | 13 | check(i == 40); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/goldbach.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int is_prime(int n) { 4 | if(n < 2) return 0; 5 | 6 | int i; 7 | for(i = 2; i < n; i ++) { 8 | if(n % i == 0) { 9 | return 0; 10 | } 11 | } 12 | 13 | return 1; 14 | } 15 | 16 | int goldbach(int n) { 17 | int i; 18 | for(i = 2; i < n; i ++) { 19 | if(is_prime(i) && is_prime(n - i)) { 20 | return 1; 21 | } 22 | } 23 | 24 | return 0; 25 | } 26 | 27 | int main() { 28 | int n; 29 | for(n = 4; n <= 30; n += 2) { 30 | check(goldbach(n) == 1); 31 | } 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/hello-str.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | char buf[128]; 4 | 5 | int main() { 6 | sprintf(buf, "%s", "Hello world!\n"); 7 | check(strcmp(buf, "Hello world!\n") == 0); 8 | 9 | sprintf(buf, "%d + %d = %d\n", 1, 1, 2); 10 | check(strcmp(buf, "1 + 1 = 2\n") == 0); 11 | 12 | sprintf(buf, "%d + %d = %d\n", 2, 10, 12); 13 | check(strcmp(buf, "2 + 10 = 12\n") == 0); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/if-else.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int if_else(int n) { 4 | int cost; 5 | if(n > 500) cost = 150; 6 | else if(n > 300) cost = 100; 7 | else if(n > 100) cost = 75; 8 | else if(n > 50) cost = 50; 9 | else cost = 0; 10 | 11 | return cost; 12 | } 13 | 14 | int test_data[] = {-1, 0, 49, 50, 51, 99, 100, 101, 299, 300, 301, 499, 500, 501}; 15 | int ans[] = {0, 0, 0, 0, 50, 50, 50, 75, 75, 75, 100, 100, 100, 150}; 16 | 17 | #define NR_DATA LENGTH(test_data) 18 | 19 | int main() { 20 | int i, ans_idx = 0; 21 | for(i = 0; i < NR_DATA; i ++) { 22 | check(if_else(test_data[i]) == ans[ans_idx ++]); 23 | } 24 | 25 | check(i == NR_DATA); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/leap-year.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int is_leap_year(int n) { 4 | return (n % 4 == 0 && n % 100 != 0) || (n % 400 == 0); 5 | } 6 | 7 | int ans[] = {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}; 8 | 9 | int main() { 10 | int i; 11 | for(i = 0; i < 125; i ++) { 12 | check(is_leap_year(i + 1890) == ans[i]); 13 | } 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/load-store.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | unsigned short mem[] = { 4 | 0x0, 0x0258, 0x4abc, 0x7fff, 0x8000, 0x8100, 0xabcd, 0xffff 5 | }; 6 | 7 | unsigned lh_ans[] = { 8 | 0x00000000, 0x00000258, 0x00004abc, 0x00007fff, 0xffff8000, 0xffff8100, 0xffffabcd, 0xffffffff 9 | }; 10 | 11 | unsigned lhu_ans[] = { 12 | 0x00000000, 0x00000258, 0x00004abc, 0x00007fff, 0x00008000, 0x00008100, 0x0000abcd, 0x0000ffff 13 | }; 14 | 15 | unsigned sh_ans[] = { 16 | 0x0000fffd, 0x0000fff7, 0x0000ffdf, 0x0000ff7f, 0x0000fdff, 0x0000f7ff, 0x0000dfff, 0x00007fff 17 | }; 18 | 19 | unsigned lwlr_ans[] = { 20 | 0xbc025800, 0x7fff4a, 0xcd810080, 0xffffab 21 | }; 22 | 23 | int main() { 24 | unsigned i; 25 | 26 | for(i = 0; i < LENGTH(mem); i ++) { 27 | check((short)mem[i] == lh_ans[i]); 28 | } 29 | 30 | for(i = 0; i < LENGTH(mem); i ++) { 31 | check(mem[i] == lhu_ans[i]); 32 | } 33 | 34 | for(i = 0; i < ((LENGTH(mem) / 2) - 1); i ++) { 35 | unsigned x = ((unsigned*)((void*)mem + 1))[i]; 36 | check(x == lwlr_ans[i]); 37 | } 38 | 39 | for(i = 0; i < LENGTH(mem); i ++) { 40 | mem[i] = ~(1 << (2 * i + 1)); 41 | check(mem[i] == sh_ans[i]); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/matrix-mul.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define N 10 4 | int a[N][N] = { 5 | {31, -73, -67, -28, 87, -17, -15, -35, -53, -54}, 6 | {52, 36, 9, -91, -27, -78, 42, 82, 19, -6}, 7 | {41, -56, 31, 32, -52, 74, 28, 20, 55, -72}, 8 | {-59, 2, -79, -8, 44, 55, -83, -95, -45, 50}, 9 | {-95, 61, -63, 62, -16, 52, 40, 92, -32, -26}, 10 | {-99, 52, 96, 63, -75, -74, -82, 82, -95, 42}, 11 | {11, -22, 27, -27, -27, -76, -71, 58, -40, -65}, 12 | {91, -53, -67, 72, 36, -77, -3, 93, -24, 97}, 13 | {-52, -11, -77, -93, -92, -24, 70, 18, 56, 88}, 14 | {-43, -41, -26, 11, -84, -14, -41, 83, 27, -11} 15 | }; 16 | int b[N][N] = { 17 | {-48, -70, -40, -82, -74, -63, -59, -72, -100, -72}, 18 | {5, -84, 28, 56, 60, -33, -42, -50, -83, -83}, 19 | {-5, 5, 48, 75, -78, -9, 9, 2, 88, 70}, 20 | {69, 23, 66, 66, -11, 50, 67, 18, -58, 76}, 21 | {30, 45, 32, 25, -73, 57, -67, -14, 53, -33}, 22 | {98, -86, -63, 80, -45, -88, 80, -64, 58, -84}, 23 | {-55, -39, -13, -27, -37, 8, -96, 84, -89, 31}, 24 | {-82, 58, 81, -41, -58, 36, 76, -79, -29, 23}, 25 | {86, -46, 16, -18, 81, 90, 35, -90, 43, 55}, 26 | {-38, -19, -40, 82, -76, 57, -29, -2, 79, -48}, 27 | }; 28 | 29 | int ans[N][N] = { 30 | {-1317, 10379, -5821, -14322, -4330, -3114, -9940, 7033, -1883, -6027}, 31 | {-24266, -861, 4044, -19824, -223, 886, -11988, -6442, -13846, -1054}, 32 | {9783, -7073, -918, -5911, -967, -7100, 14605, -7556, -3439, 9607}, 33 | {15980, -520, -13297, 15043, 6185, -3654, 1325, 4193, 16925, -17761}, 34 | {2566, 3187, 10248, 7925, 6318, 1421, 14648, 700, -12193, 1083}, 35 | {-12603, 19006, 20952, 18599, -1539, 5184, 17408, 6740, 6264, 15114}, 36 | {-12715, 15121, 9963, -13717, 2411, -2196, 6147, -1698, -3389, 8200}, 37 | {-19007, 12417, 5723, -11309, -19242, 15740, -3791, -3949, -13130, -21}, 38 | {-12557, -5970, -11570, -8905, 12227, 7814, -5094, 4532, 1071, -1309}, 39 | {-2955, 9381, 6372, -6898, 9117, 5753, 20778, -5045, 1047, 12114}}; 40 | 41 | int c[N][N]; 42 | 43 | int main() { 44 | int i, j, k; 45 | for(i = 0; i < N; i ++) { 46 | for(j = 0; j < N; j ++) { 47 | c[i][j] = 0; 48 | for(k = 0; k < N; k ++) { 49 | c[i][j] += a[i][k] * b[k][j]; 50 | } 51 | check(c[i][j] == ans[i][j]); 52 | check(k == N); 53 | } 54 | check(j == N); 55 | } 56 | 57 | check(i == N); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/max.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int max(int x, int y) { 4 | int z; 5 | if(x > y) { z = x; } 6 | else { z = y; } 7 | return z; 8 | } 9 | 10 | int test_data[] = {0, 1, 2, 0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff}; 11 | int ans[] = {0, 0x1, 0x2, 0x7fffffff, 0, 0, 0, 0, 0x1, 0x1, 0x2, 0x7fffffff, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x7fffffff, 0x2, 0x2, 0x2, 0x2, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff, 0, 0x1, 0x2, 0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff, 0, 0x1, 0x2, 0x7fffffff, 0x80000001, 0x80000001, 0xfffffffe, 0xffffffff, 0, 0x1, 0x2, 0x7fffffff, 0xfffffffe, 0xfffffffe, 0xfffffffe, 0xffffffff, 0, 0x1, 0x2, 0x7fffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; 12 | 13 | #define NR_DATA LENGTH(test_data) 14 | 15 | int main() { 16 | int i, j, ans_idx = 0; 17 | for(i = 0; i < NR_DATA; i ++) { 18 | for(j = 0; j < NR_DATA; j ++) { 19 | check(max(test_data[i], test_data[j]) == ans[ans_idx ++]); 20 | } 21 | check(j == NR_DATA); 22 | } 23 | 24 | check(i == NR_DATA); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/mersenne.c: -------------------------------------------------------------------------------- 1 | /* adapted from http://rosettacode.org/wiki/Factors_of_a_Mersenne_number#C */ 2 | #include "test.h" 3 | 4 | int isPrime(int n) { 5 | int d = 5; 6 | if (n % 2 == 0) return n==2; 7 | if (n % 3 == 0) return n==3; 8 | while (d * d <= n) { 9 | if (n % d == 0) return 0; 10 | d += 2; 11 | if (n % d == 0) return 0; 12 | d += 4; 13 | } 14 | return 1; 15 | } 16 | 17 | int main() { 18 | int i, d, p, r, q = 929; 19 | if (!isPrime(q)) return 1; 20 | r = q; 21 | while (r > 0) r <<= 1; 22 | d = 2 * q + 1; 23 | do { 24 | for (p = r, i = 1; p; p <<= 1) { 25 | i = ((long long)i * i) % d; 26 | if (p < 0) i *= 2; 27 | if (i > d) i -= d; 28 | } 29 | if (i != 1) d += 2 * q; 30 | else break; 31 | } while(1); 32 | check(d == 13007); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/min3.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int min3(int x, int y, int z) { 4 | int m; 5 | if(x < y) { m = x; } 6 | else { m = y; } 7 | if(z < m) m = z; 8 | return m; 9 | } 10 | 11 | int test_data[] = {0, 0x7fffffff, 0x80000000, 0xffffffff}; 12 | int ans [] = {0, 0, -2147483648, -1, 0, 0, -2147483648, -1, -2147483648, -2147483648, -2147483648, -2147483648, -1, -1, -2147483648, -1, 0, 0, -2147483648, -1, 0, 2147483647, -2147483648, -1, -2147483648, -2147483648, -2147483648, -2147483648, -1, -1, -2147483648, -1, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -2147483648, -1, -1, -2147483648, -1, -1, -1, -2147483648, -1, -2147483648, -2147483648, -2147483648, -2147483648, -1, -1, -2147483648, -1}; 13 | 14 | #define NR_DATA LENGTH(test_data) 15 | 16 | int main() { 17 | int i, j, k, ans_idx = 0; 18 | for(i = 0; i < NR_DATA; i ++) { 19 | for(j = 0; j < NR_DATA; j ++) { 20 | for(k = 0; k < NR_DATA; k ++) { 21 | check(min3(test_data[i], test_data[j], test_data[k]) == ans[ans_idx ++]); 22 | } 23 | check(k == NR_DATA); 24 | } 25 | check(j == NR_DATA); 26 | } 27 | 28 | check(i == NR_DATA); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/mov-c.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | volatile int A[10]; 4 | volatile int b; 5 | 6 | int main() { 7 | A[0] = 0; 8 | A[1] = 1; 9 | A[2] = 2; 10 | A[3] = 3; 11 | A[4] = 4; 12 | 13 | b = A[3]; 14 | A[5] = b; 15 | 16 | check(A[0] == 0); 17 | check(A[1] == 1); 18 | check(A[2] == 2); 19 | check(A[3] == 3); 20 | check(A[4] == 4); 21 | check(b == 3); 22 | check(A[5] == 3); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/movsx.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | volatile int A[10]; 4 | volatile int b; 5 | volatile signed char C[10]; 6 | int main() { 7 | A[0] = 0; 8 | A[1] = 1; 9 | A[2] = 2; 10 | A[3] = 3; 11 | A[4] = 4; 12 | 13 | b = A[3]; 14 | A[5] = b; 15 | C[0] = 'a'; 16 | check(C[0] == 'a'); 17 | C[1] = C[0]; 18 | check(C[1] == 'a'); 19 | A[0] = (int)C[0]; 20 | check(A[0] == 'a'); 21 | C[1] = 0x80; 22 | A[0] = (int)C[1]; 23 | check(A[1] == 1); 24 | check(A[2] == 2); 25 | check(A[3] == 3); 26 | check(A[4] == 4); 27 | check(b == 3); 28 | check(A[5] == 3); 29 | check(C[1] == 0xffffff80); 30 | check(A[0] == 0xffffff80); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/mul-longlong.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | long long mul(long long a,long long b) { 4 | long long ans = a*b; 5 | return ans; 6 | } 7 | 8 | int test_data[] = { 0xaeb1c2aa, 0x4500ff2b, 0x877190af, 0x11f42438}; 9 | long long ans[] = { 0x19d29ab9db1a18e4LL, 0xea15986d3ac3088eLL, 0x2649e980fc0db236LL, 0xfa4c43da0a4a7d30LL, 0x1299898e2c56b139LL, 0xdf8123d50a319e65LL, 0x4d6dfa84c15dd68LL, 0x38c5d79b9e4357a1LL, 0xf78b91cb1efc4248LL, 0x14255a47fdfcc40LL}; 10 | 11 | #define NR_DATA LENGTH(test_data) 12 | 13 | int main() { 14 | int i,j,ans_idx = 0; 15 | for (i = 0;i < NR_DATA;i++) { 16 | for (j = i;j < NR_DATA;j++) { 17 | check(ans[ans_idx++] == mul(test_data[i],test_data[j])); 18 | } 19 | check(j == NR_DATA); 20 | } 21 | 22 | check(i == NR_DATA); 23 | 24 | return 0; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/pascal.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define N 31 4 | 5 | int a[N]; 6 | int ans[] = {1, 30, 435, 4060, 27405, 142506, 593775, 2035800, 5852925, 14307150, 30045015, 54627300, 86493225, 119759850, 145422675, 155117520, 145422675, 119759850, 86493225, 54627300, 30045015, 14307150, 5852925, 2035800, 593775, 142506, 27405, 4060, 435, 30, 1}; 7 | 8 | int main() { 9 | int i, j; 10 | int t0, t1; 11 | a[0] = a[1] = 1; 12 | 13 | for(i = 2; i < N; i ++) { 14 | t0 = 1; 15 | for(j = 1; j < i; j ++) { 16 | t1 = a[j]; 17 | a[j] = t0 + t1; 18 | t0 = t1; 19 | } 20 | a[i] = 1; 21 | } 22 | 23 | for(j = 0; j < N; j ++) { 24 | check(a[j] == ans[j]); 25 | } 26 | 27 | check(j == N); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/prime.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int ans[] = {101, 103, 107, 109, 113, 127, 131, 137, 139, 149}; 4 | 5 | int main() { 6 | int m, i, n = 0; 7 | int prime; 8 | for(m = 101; m <= 150; m += 2) { 9 | prime = 1; 10 | for(i = 2; i < m; i ++) { 11 | if(m % i == 0) { 12 | prime = 0; 13 | break; 14 | } 15 | } 16 | if(prime) { 17 | check(i == ans[n]); 18 | n ++; 19 | } 20 | } 21 | 22 | check(n == 10); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/quick-sort.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define N 20 4 | 5 | int a[N] = {2, 12, 14, 6, 13, 15, 16, 10, 0, 18, 11, 19, 9, 1, 7, 5, 4, 3, 8, 17}; 6 | 7 | int partition(int *a, int p, int q) { 8 | int pivot = a[p]; 9 | int i = p, j = q; 10 | while(i < j) { 11 | while(i < j && a[j] > pivot) j --; 12 | a[i] = a[j]; 13 | 14 | while(i < j && a[i] <= pivot) i ++; 15 | a[j] = a[i]; 16 | } 17 | 18 | a[i] = pivot; 19 | return i; 20 | } 21 | 22 | void quick_sort(int *a, int p, int q) { 23 | if(p >= q) return; 24 | 25 | int m = partition(a, p, q); 26 | quick_sort(a, p, m - 1); 27 | quick_sort(a, m + 1, q); 28 | } 29 | 30 | int main() { 31 | quick_sort(a, 0, N - 1); 32 | 33 | int i; 34 | for(i = 0; i < N; i ++) { 35 | check(a[i] == i); 36 | } 37 | 38 | check(i == N); 39 | 40 | quick_sort(a, 0, N - 1); 41 | 42 | for(i = 0; i < N; i ++) { 43 | check(a[i] == i); 44 | } 45 | 46 | check(i == N); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/recursion.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int f0(int, int); 4 | int f1(int, int); 5 | int f2(int, int); 6 | int f3(int, int); 7 | 8 | int (*func[])(int, int) = { 9 | f0, f1, f2, f3, 10 | }; 11 | 12 | int rec = 0, lvl = 0; 13 | 14 | int f0(int n, int l) { 15 | if (l > lvl) lvl = l; 16 | rec ++; 17 | return n <= 0 ? 1 : func[3](n / 3, l + 1); 18 | }; 19 | 20 | int f1(int n, int l) { 21 | if (l > lvl) lvl = l; 22 | rec ++; 23 | return n <= 0 ? 1 : func[0](n - 1, l + 1); 24 | }; 25 | 26 | int f2(int n, int l) { 27 | if (l > lvl) lvl = l; 28 | rec ++; 29 | return n <= 0 ? 1 : func[1](n, l + 1) + 9; 30 | }; 31 | 32 | int f3(int n, int l) { 33 | if (l > lvl) lvl = l; 34 | rec ++; 35 | return n <= 0 ? 1 : func[2](n / 2, l + 1) * 3 + func[2](n / 2, l + 1) * 2; 36 | }; 37 | 38 | int ans[] = {38270, 218, 20}; 39 | 40 | int main() { 41 | int x = func[0](14371, 0); 42 | check(x == ans[0]); // answer 43 | check(rec == ans[1]); // # recursions 44 | check(lvl == ans[2]); // max depth 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/select-sort.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #define N 20 4 | 5 | int a[N] = {2, 12, 14, 6, 13, 15, 16, 10, 0, 18, 11, 19, 9, 1, 7, 5, 4, 3, 8, 17}; 6 | 7 | void select_sort() { 8 | int i, j, k, t; 9 | for(i = 0; i < N - 1; i ++) { 10 | k = i; 11 | for(j = i + 1; j < N; j ++) { 12 | if(a[j] < a[k]) { 13 | k = j; 14 | } 15 | } 16 | 17 | t = a[i]; 18 | a[i] = a[k]; 19 | a[k] = t; 20 | } 21 | } 22 | 23 | int main() { 24 | select_sort(); 25 | 26 | int i; 27 | for(i = 0; i < N; i ++) { 28 | check(a[i] == i); 29 | } 30 | 31 | check(i == N); 32 | 33 | select_sort(); 34 | 35 | for(i = 0; i < N; i ++) { 36 | check(a[i] == i); 37 | } 38 | 39 | check(i == N); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/shift.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | unsigned test[] = { 4 | 0x12345678, 0x98765432, 0x0, 0xeffa1000, 0x7fffffff, 0x80000000, 0x33, 0xffffffff 5 | }; 6 | 7 | unsigned srl_ans[] = { 8 | 0x2468ac, 0x130eca8, 0x0, 0x1dff420, 0xffffff, 0x1000000, 0x0, 0x1ffffff 9 | }; 10 | 11 | unsigned srlv_ans[] = { 12 | 0x1234567, 0x4c3b2a1, 0x0, 0x1dff420, 0x7fffff, 0x400000, 0x0, 0x1fffff 13 | }; 14 | 15 | unsigned srav_ans[] = { 16 | 0x1234567, 0xfcc3b2a1, 0x0, 0xffdff420, 0x7fffff, 0xffc00000, 0x0, 0xffffffff 17 | }; 18 | 19 | 20 | int main() { 21 | unsigned i; 22 | 23 | for(i = 0; i < LENGTH(test); i ++) { 24 | check((test[i] >> 7) == srl_ans[i]); 25 | } 26 | 27 | for(i = 0; i < LENGTH(test); i ++) { 28 | check((unsigned)((int)test[i] >> (i + 4)) == srav_ans[i]); 29 | } 30 | 31 | for(i = 0; i < LENGTH(test); i ++) { 32 | check((test[i] >> (i + 4)) == srlv_ans[i]); 33 | } 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/shuixianhua.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int ans[] = {153, 370, 371, 407}; 4 | 5 | int cube(int n) { 6 | return n * n * n; 7 | } 8 | 9 | int main() { 10 | int n, n2, n1, n0; 11 | int k = 0; 12 | for(n = 100; n < 500; n ++) { 13 | n2 = n / 100; 14 | n1 = (n / 10) % 10; 15 | n0 = n % 10; 16 | 17 | if(n == cube(n2) + cube(n1) + cube(n0)) { 18 | check(n == ans[k]); 19 | k ++; 20 | } 21 | } 22 | 23 | check(k == 4); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/string.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | char *s[] = { 4 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 5 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", 6 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 7 | ", World!\n", 8 | "Hello, World!\n", 9 | "#####" 10 | }; 11 | 12 | char str1[] = "Hello"; 13 | char str[20]; 14 | 15 | int main() { 16 | check(strcmp(s[0], s[2]) == 0); 17 | check(strcmp(s[0], s[1]) < 0); 18 | check(strcmp(s[0] + 1, s[1] + 1) < 0); 19 | check(strcmp(s[0] + 2, s[1] + 2) < 0); 20 | check(strcmp(s[0] + 3, s[1] + 3) < 0); 21 | 22 | check(strcmp( strcat(strcpy(str, str1), s[3]), s[4]) == 0); 23 | 24 | check(memcmp(memset(str, '#', 5), s[5], 5) == 0); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/sub-longlong.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | long long sub(long long a, long long b) { 4 | long long c = a - b; 5 | return c; 6 | } 7 | 8 | long long test_data[] = {0, 1, 2, 0x7fffffffffffffffLL, 0x8000000000000000LL, 0x8000000000000001LL, 0xfffffffffffffffeLL, 0xffffffffffffffffLL}; 9 | long long ans[] = {0LL, 0xffffffffffffffffLL, 0xfffffffffffffffeLL, 0x8000000000000001LL, 0x8000000000000000LL, 0x7fffffffffffffffLL, 0x2LL, 0x1LL, 0x1LL, 0LL, 0xffffffffffffffffLL, 0x8000000000000002LL, 0x8000000000000001LL, 0x8000000000000000LL, 0x3LL, 0x2LL, 0x2LL, 0x1LL, 0LL, 0x8000000000000003LL, 0x8000000000000002LL, 0x8000000000000001LL, 0x4LL, 0x3LL, 0x7fffffffffffffffLL, 0x7ffffffffffffffeLL, 0x7ffffffffffffffdLL, 0LL, 0xffffffffffffffffLL, 0xfffffffffffffffeLL, 0x8000000000000001LL, 0x8000000000000000LL, 0x8000000000000000LL, 0x7fffffffffffffffLL, 0x7ffffffffffffffeLL, 0x1LL, 0LL, 0xffffffffffffffffLL, 0x8000000000000002LL, 0x8000000000000001LL, 0x8000000000000001LL, 0x8000000000000000LL, 0x7fffffffffffffffLL, 0x2LL, 0x1LL, 0LL, 0x8000000000000003LL, 0x8000000000000002LL, 0xfffffffffffffffeLL, 0xfffffffffffffffdLL, 0xfffffffffffffffcLL, 0x7fffffffffffffffLL, 0x7ffffffffffffffeLL, 0x7ffffffffffffffdLL, 0LL, 0xffffffffffffffffLL, 0xffffffffffffffffLL, 0xfffffffffffffffeLL, 0xfffffffffffffffdLL, 0x8000000000000000LL, 0x7fffffffffffffffLL, 0x7ffffffffffffffeLL, 0x1LL, 0LL}; 10 | 11 | #define NR_DATA LENGTH(test_data) 12 | 13 | int main() { 14 | int i, j, ans_idx = 0; 15 | for(i = 0; i < NR_DATA; i ++) { 16 | for(j = 0; j < NR_DATA; j ++) { 17 | check(sub(test_data[i], test_data[j]) == ans[ans_idx ++]); 18 | } 19 | } 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/sum.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int main() { 4 | int i = 1; 5 | volatile int sum = 0; 6 | while(i <= 100) { 7 | sum += i; 8 | i ++; 9 | } 10 | 11 | check(sum == 5050); 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/switch.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int switch_case(int n) { 4 | int ret; 5 | switch(n) { 6 | case 0: ret = 0; break; 7 | case 1: ret = 2; break; 8 | case 2: case 3: ret = 5; break; 9 | case 4: case 5: case 6: case 7: ret = 8; break; 10 | case 8: case 9: case 10: case 11: ret = 10; break; 11 | case 12: ret = 15; break; 12 | default: ret = -1; break; 13 | } 14 | 15 | return ret; 16 | } 17 | 18 | int ans[] = {-1, 0, 2, 5, 5, 8, 8, 8, 8, 10, 10, 10, 10, 15, -1}; 19 | 20 | int main() { 21 | int i; 22 | for(i = 0; i < 15; i ++) { 23 | check(switch_case(i - 1) == ans[i]); 24 | } 25 | 26 | check(i == 15); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/to-lower-case.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | char to_lower_case(char c) { 4 | return (c >= 'A' && c <= 'Z' ? (c + 32) : c); 5 | } 6 | 7 | volatile char ans [] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 8 | 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 9 | 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; 10 | 11 | int main() { 12 | int i; 13 | for(i = 0; i < 128; i ++) { 14 | check(to_lower_case(i) == ans[i]); 15 | } 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/unalign.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | volatile unsigned x = 0xffffffff; 4 | volatile unsigned char buf[16]; 5 | 6 | int main() { 7 | 8 | for(int i = 0; i < 4; i++) { 9 | *((volatile unsigned*)(buf + 3)) = 0xaabbccdd; 10 | 11 | x = *((volatile unsigned*)(buf + 3)); 12 | check(x == 0xaabbccdd); 13 | 14 | buf[0] = buf[1] = 0; 15 | } 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/cpu-tests/tests/wanshu.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | int ans[] = {6, 28}; 4 | 5 | int main() { 6 | int n, sum, i, k = 0; 7 | for(n = 1; n < 30; n ++) { 8 | sum = 0; 9 | for(i = 1; i < n; i ++) { 10 | if(n % i == 0) { 11 | sum += i; 12 | } 13 | } 14 | 15 | if(sum == n) { 16 | check(n == ans[k]); 17 | k ++; 18 | } 19 | } 20 | 21 | check(k == 2); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/ioe-tests/makefile: -------------------------------------------------------------------------------- 1 | SRCS += ./$(NAME).c 2 | SRC_DIR = . 3 | ELF = build/$(NAME)-$(ISA).elf 4 | 5 | KDB_FILE = $(NAME).kdb 6 | 7 | ifneq ($(wildcard $(KDB_FILE)),) 8 | KDB_INIT_SRC += $(abspath $(KDB_FILE)) 9 | endif 10 | 11 | include ../abstract-machine/Makefile 12 | -------------------------------------------------------------------------------- /tests/ioe-tests/uart.c: -------------------------------------------------------------------------------- 1 | #include "klib.h" 2 | 3 | int main() { 4 | printf("Hello, World! This is KXemu!\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/ioe-tests/uart.kdb: -------------------------------------------------------------------------------- 1 | uart add 0x10000000 -------------------------------------------------------------------------------- /tests/privileged-tests/intr.c: -------------------------------------------------------------------------------- 1 | #include "am.h" 2 | #include "klib-macros.h" 3 | 4 | Context *handler(Event ev, Context *ctx) { 5 | switch(ev.event) { 6 | case EVENT_IRQ_TIMER: putchar('t'); break; 7 | case EVENT_IRQ_IODEV: putchar('d'); break; 8 | case EVENT_SYSCALL: putchar('s'); break; 9 | default: putstr("Unknown event\n"); 10 | } 11 | ctx->mepc += 4; 12 | return ctx; 13 | } 14 | 15 | int main() { 16 | cte_init(handler); 17 | while (1) { 18 | yield(); 19 | for (volatile int i = 0; i < 1000000; i++); 20 | } 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /tests/privileged-tests/makefile: -------------------------------------------------------------------------------- 1 | SRCS += ./$(NAME).c 2 | SRC_DIR = . 3 | ELF = build/$(NAME)-$(ISA).elf 4 | 5 | KDB_FILE = $(NAME).kdb 6 | 7 | ifneq ($(wildcard $(KDB_FILE)),) 8 | KDB_INIT_SRC += $(abspath $(KDB_FILE)) 9 | endif 10 | KDB_INIT_SRC += $(abspath ./uart.kdb) 11 | 12 | include ../abstract-machine/makefile 13 | -------------------------------------------------------------------------------- /tests/privileged-tests/uart.kdb: -------------------------------------------------------------------------------- 1 | uart add 0x10000000 -------------------------------------------------------------------------------- /tests/riscv/Makefile: -------------------------------------------------------------------------------- 1 | ISA = riscv32 2 | SRCS += $(shell find ./$(NAME)/src -name '*.S' -or -name '*.c') 3 | 4 | KDB_FILE = $(NAME).kdb 5 | 6 | ifneq ($(wildcard $(KDB_FILE)),) 7 | KDB_INIT_SRC += $(abspath $(KDB_FILE)) 8 | endif 9 | KDB_INIT_SRC += $(abspath ./uart.kdb) 10 | 11 | include ../abstract-machine/Makefile 12 | -------------------------------------------------------------------------------- /tests/riscv/README.md: -------------------------------------------------------------------------------- 1 | # Test for RISCV 2 | 3 | ## deleg 4 | 5 | 这个测试用于测试riscv的trap代理。代码将U模式下的`ecall`内陷代理给了S模式,U模式下通过`ecall`进入S模式,再在S模式下通过`sret`返回到U模式并退出。正确的输出应该如下: 6 | 7 | ```text 8 | Enter to S-mode 9 | Call from U-mode 10 | Enter to U-mode 11 | U-mode ECALL 12 | ``` 13 | 14 | ## smode 15 | 16 | 这个测试用于测试KXemu能否进入到S模式。代码将`mstatus`寄存器的`MPP`字段设置为了`S`,并通过`mret`进入到`S`模式,在模式下再次使用`ecall`指令进入到M模式后返回,最后退出程序。正确的输出应该如下: 17 | 18 | ```text 19 | Enter to S-mode 20 | Call from U-mode 21 | Enter to U-mode 22 | U-mode ECALL 23 | ``` 24 | 25 | 26 | ## mtimer 27 | 28 | 这个测试用于测试KXemu的M模式下的定时功能,即设置mtime和mtimecmp寄存器。正确的输出应该每隔约1秒输出一行字符串。 29 | 30 | ## vm32 vm64 31 | 32 | 这两个测试用于测试虚拟内存的实现。M模式设置好页表后,返回到U模式,U模式在分页上运行,并尝试读写数据,每次读写结束返回至M模式验证读写结果正确性。 33 | -------------------------------------------------------------------------------- /tests/riscv/deleg/src/deleg.S: -------------------------------------------------------------------------------- 1 | .global main 2 | .global puts 3 | 4 | main: 5 | li t0, 0xffffffff 6 | csrw pmpaddr0, t0 7 | li t0, 0xf 8 | csrw pmpcfg0, t0 9 | 10 | // set the mstatues.mpp to S mode 11 | li t0, 3 << 11 12 | csrc mstatus, t0 13 | li t0, 1 << 11 14 | csrs mstatus, t0 15 | 16 | // set the mepc to smode_entry 17 | la t0, s_mode_entry 18 | csrw mepc, t0 19 | 20 | la t0, m_mode_handle 21 | csrw mtvec, t0 22 | 23 | // set the medeleg to 0xffff 24 | li t0, 0xffff 25 | csrw medeleg, t0 26 | 27 | mret 28 | 29 | .align 2 30 | m_mode_handle: 31 | // Should not reach here 32 | la a0, str0 33 | call puts 34 | li a0, 3 35 | ebreak 36 | 37 | .align 2 38 | s_mode_entry: 39 | la a0, str1 40 | call puts 41 | 42 | // set the mstatus.mpp to U mode 43 | li t0, 1 << 8 44 | csrc sstatus, t0 45 | 46 | la t0, s_mode_handle 47 | csrw stvec, t0 48 | 49 | la t0, u_mode_entry 50 | csrw sepc, t0 51 | 52 | sret 53 | 54 | .align 2 55 | s_mode_handle: 56 | mv s1, a0 57 | la a0, str2 58 | call puts 59 | mv a0, s1 60 | 61 | li t1, 0x123 62 | bne a0, t1, .fail 63 | la a0, str4 64 | call puts 65 | 66 | csrr t0, sepc 67 | addi t0, t0, 4 68 | csrw sepc, t0 69 | sret 70 | 71 | .fail: 72 | la a0, str0 73 | call puts 74 | 75 | li a0, 2 76 | ebreak 77 | 78 | .align 2 79 | u_mode_entry: 80 | la a0, str3 81 | call puts 82 | 83 | li a0, 0x123 84 | ecall 85 | li a0, 0 86 | ebreak 87 | 88 | .section .data 89 | str0: 90 | .string "Should not reach here!" 91 | str1: 92 | .string "Enter to S-mode" 93 | str2: 94 | .string "Enter to U-mode" 95 | str3: 96 | .string "Call from U-mode" 97 | str4: 98 | .string "U-mode ECALL" 99 | -------------------------------------------------------------------------------- /tests/riscv/mtimer/src/timer.S: -------------------------------------------------------------------------------- 1 | .global main 2 | 3 | #define MTIMECMP_BASE 0x02004000 4 | 5 | #define MSTATUS_MIE_MASK (1 << 3) 6 | 7 | main: 8 | la t0, m_mode_handle 9 | csrw mtvec, t0 10 | 11 | li t1, MTIMECMP_BASE 12 | #if __riscv_xlen == 32 13 | li t0, 10000000 14 | sw t0, 0(t1) 15 | sw zero, 4(t1) 16 | #else 17 | li t0, 10000000 18 | sd t0, 0(t1) 19 | #endif 20 | 21 | li t0, MSTATUS_MIE_MASK 22 | csrs mstatus, t0 23 | 24 | li t0, (1 << 7) 25 | csrs mie, t0 26 | 27 | li s0, 0 28 | 29 | .loop: 30 | wfi 31 | j .loop 32 | 33 | .align 2 34 | m_mode_handle: 35 | csrr t0, mcause 36 | #if __riscv_xlen == 32 37 | li t1, 0x80000007 38 | #else 39 | li t1, 0x8000000000000007 40 | #endif 41 | bne t0, t1, .fail 42 | 43 | la a0, str1 44 | call puts 45 | addi s0, s0, 1 46 | la a0, str2 47 | mv a1, s0 48 | call printf 49 | 50 | li t1, MTIMECMP_BASE 51 | li t2, 10000000 52 | #if __riscv_xlen == 32 53 | lw t0, 0(t1) 54 | add t0, t0, t2 55 | sw t0, 0(t1) 56 | sw zero, 4(t1) 57 | #else 58 | ld t0, 0(t1) 59 | li t2, 10000000 60 | add t0, t0, t2 61 | sd t0, 0(t1) 62 | #endif 63 | 64 | mret 65 | 66 | .fail: 67 | la a0, str0 68 | call puts 69 | 70 | li a0, 1 71 | ebreak 72 | j . 73 | 74 | .section .data 75 | str0: 76 | .string "Should not reach here." 77 | str1: 78 | .string "Machine Timer interrupt." 79 | str2: 80 | .string "%ds\n" 81 | -------------------------------------------------------------------------------- /tests/riscv/smode/src/smode.S: -------------------------------------------------------------------------------- 1 | .global main 2 | .global puts 3 | 4 | main: 5 | // configure Physical Memory Protection to give supervisor mode 6 | // access to all of physical memory. 7 | li t0, 0xffffffff 8 | csrw pmpaddr0, t0 9 | li t0, 0xf 10 | csrw pmpcfg0, t0 11 | 12 | // set the mstatues.mpp to S mode 13 | li t0, 3 << 11 14 | csrc mstatus, t0 15 | li t0, 1 << 11 16 | csrs mstatus, t0 17 | 18 | // set the mepc to smode_entry 19 | la t0, s_mode_entry 20 | csrw mepc, t0 21 | 22 | la t0, m_mode_handle 23 | csrw mtvec, t0 24 | 25 | mret 26 | 27 | .align 2 28 | m_mode_handle: 29 | csrr t0, mepc 30 | addi t0, t0, 4 31 | csrw mepc, t0 32 | 33 | addi sp, sp, -4 34 | sw ra, 0(sp) 35 | la a0, str2 36 | call puts 37 | lw ra, 0(sp) 38 | 39 | mret 40 | 41 | .align 2 42 | s_mode_entry: 43 | la a0, str1 44 | call puts 45 | ecall 46 | la a0, str3 47 | call puts 48 | 49 | li a0, 0 50 | ebreak 51 | 52 | .section .data 53 | str1: 54 | .string "Enter to S-mode" 55 | str2: 56 | .string "Enter to M-mode" 57 | str3: 58 | .string "Return to S-mode" 59 | -------------------------------------------------------------------------------- /tests/riscv/stimer/src/stimer.S: -------------------------------------------------------------------------------- 1 | .global main 2 | 3 | #define STATUS_SIE_MASK (1 << 1) 4 | #define STATUS_MPP_MASK (3 << 11) 5 | #define STATUS_SPIE_MASK (1 << 5) 6 | #define IE_STIE_MASK (1 << 5) 7 | #define IP_STIP_MASK (1 << 5) 8 | 9 | main: 10 | // Configure Physical Memory Protection to give supervisor and 11 | // user mode access to all of physical memory. 12 | li t0, 0xffffffff 13 | csrw pmpaddr0, t0 14 | li t0, 0xf 15 | csrw pmpcfg0, t0 16 | 17 | la t0, s_mode_handle 18 | csrw stvec, t0 19 | 20 | // Enable machine timer interrupt 21 | li t0, STATUS_SIE_MASK 22 | csrs mstatus, t0 23 | li t0, IE_STIE_MASK 24 | csrs mie, t0 25 | 26 | // Set MPP to user mode 27 | li t0, STATUS_MPP_MASK 28 | csrc mstatus, t0 29 | 30 | // Delegate interrupts to supervisor mode 31 | li t0, 0xffff 32 | csrs mideleg, t0 33 | 34 | // Set supervisor timer interrupt 35 | csrr t0, time 36 | li t1, 10000000 37 | add t0, t0, t1 38 | csrw stimecmp, t0 39 | 40 | # li t0, (1 << 1) 41 | # csrs mcounteren, t0 42 | # li t0, (1 << 63) 43 | # csrs menvcfg, t0 44 | 45 | la t0, m_mode_handle 46 | csrw mtvec, t0 47 | 48 | // Enter to user mode 49 | la t0, u_mode 50 | csrw mepc, t0 51 | mret 52 | 53 | .align 2 54 | u_mode: 55 | li s0, 0 56 | .loop: 57 | j .loop 58 | 59 | .align 2 60 | s_mode_handle: 61 | csrr t0, scause 62 | #if __riscv_xlen == 32 63 | li t1, 0x80000005 64 | #else 65 | li t1, 0x8000000000000005 66 | #endif 67 | bne t0, t1, .fail 68 | 69 | la a0, str1 70 | call puts 71 | addi s0, s0, 1 72 | la a0, str2 73 | mv a1, s0 74 | call printf 75 | 76 | csrr t0, time 77 | li t1, 10000000 78 | add t0, t0, t1 79 | csrw stimecmp, t0 80 | 81 | li t0, IP_STIP_MASK 82 | csrc sip, t0 83 | 84 | sret 85 | 86 | .fail: 87 | la a0, str0 88 | call puts 89 | 90 | li a0, 1 91 | call __halt 92 | 93 | m_mode_handle: 94 | csrr a1, mcause 95 | la a0, str3 96 | call printf 97 | 98 | csrr a1, mtval 99 | la a0, str4 100 | call printf 101 | 102 | li a0, 1 103 | call __halt 104 | 105 | .section .rodata 106 | str0: 107 | .string "Should not reach here." 108 | str1: 109 | .string "Supervisor Timer interrupt." 110 | str2: 111 | .string "%ds\n" 112 | str3: 113 | .string "mcause=%x\n" 114 | str4: 115 | .string "mtval=%x\n" 116 | -------------------------------------------------------------------------------- /tests/riscv/uart.kdb: -------------------------------------------------------------------------------- 1 | uart add 0x10000000 -------------------------------------------------------------------------------- /tests/riscv/vm32/src/inc.S: -------------------------------------------------------------------------------- 1 | .globl user_img_start 2 | .globl user_img_end 3 | 4 | .section .data 5 | user_img_start: 6 | .incbin "./vm32/user-img/build/user.bin" 7 | user_img_end: 8 | -------------------------------------------------------------------------------- /tests/riscv/vm32/src/main.c: -------------------------------------------------------------------------------- 1 | #include "klib.h" 2 | #include "am.h" 3 | #include "isa/riscv.h" 4 | #include 5 | #include 6 | 7 | // page[0] -> user code 8 | // page[1] -> user space for test 9 | // page[2] -> fisrt stage page table 10 | // page[3] -> second stage page table 11 | __attribute__((aligned(4 << 20)))uint8_t page[4][4096]; 12 | 13 | // VM Mapping 14 | // 0x20000000 ~ 0x20000fff -> page[0] 15 | // 0x20001000 ~ 0x20001fff -> page[1] 16 | static void init_vm() { 17 | word_t satp = 0; 18 | satp |= (1 << 31); // Sv32 mode 19 | satp |= ((word_t)(&page[2]) >> 12); // Set the root page table 20 | csrw_satp(satp); 21 | 22 | // Set fisrt stage page table 23 | ((word_t *)page[2])[(0x20000000 >> 22) & 0x3ff] = (((word_t)&page[3] >> 12) << 10) | PTE_V; 24 | // Map page[0] to 0x20000000 25 | ((word_t *)page[3])[(0x20000000 >> 12) & 0x3ff] = (((word_t)&page[0] >> 12) << 10) | PTE_V | PTE_U | PTE_X; 26 | // Map page[1] to 0x20001000 27 | ((word_t *)page[3])[(0x20001000 >> 12) & 0x3ff] = (((word_t)&page[1] >> 12) << 10) | PTE_V | PTE_U | PTE_R | PTE_W; 28 | 29 | sfence_vma(); 30 | } 31 | 32 | static void init_vm_superpage() { 33 | word_t satp = 0; 34 | satp |= (1 << 31); // Sv32 mode 35 | satp |= ((word_t)(&page[2]) >> 12); // Set the root page table 36 | csrw_satp(satp); 37 | 38 | // Set fisrt stage page table 39 | ((word_t *)page[2])[(0x20000000 >> 22) & 0x3ff] = (((word_t)&page[0] >> 12) << 10) | PTE_V | PTE_R | PTE_W | PTE_X | PTE_U; 40 | 41 | sfence_vma(); 42 | } 43 | 44 | // User code 45 | extern char user_img_start[]; 46 | extern char user_img_end[]; 47 | 48 | void load_user_img() { 49 | char *src = user_img_start; 50 | char *dst = (char *)page[0]; 51 | while (src < user_img_end) { 52 | *dst = *src; 53 | src++; 54 | dst++; 55 | } 56 | } 57 | 58 | 59 | // Trap handler 60 | extern void m_trap_entry(); 61 | extern void user_entry(Context *context); 62 | 63 | static int count = 0; 64 | Context *m_trap_handler(Context *context) { 65 | uintptr_t mcause = csrr_mcause(); 66 | 67 | printf("mcause = %d\n", mcause); 68 | if (mcause != 8) { 69 | halt(1); 70 | } 71 | 72 | count ++; 73 | if (count == 1 || count == 3) { 74 | if (context->gpr[6] != 0x10ADDA7A) { 75 | printf("Test%d FAIL\n", count); 76 | halt(2); 77 | } 78 | } else if (count == 2 || count == 4) { 79 | if (*(uint32_t *)(page[1] + 0xa + 0x4) != 0x52CAFFEE) { 80 | printf("Test%d FAIL\n", count); 81 | halt(3); 82 | } 83 | } 84 | printf("Test%d PASS\n", count); 85 | 86 | if (count == 2) { 87 | init_vm_superpage(); 88 | memset(page[1], 0, sizeof(page[1])); 89 | *(uint32_t *)(page[1] + 0xa) = 0x10ADDA7A; // load data 90 | } 91 | 92 | if (count == 4) { 93 | printf("All test PASS\n"); 94 | halt(0); 95 | } 96 | 97 | uintptr_t mepc = csrr_mepc(); 98 | csrw_mepc(mepc + 4); 99 | 100 | return context; 101 | } 102 | 103 | int main() { 104 | // Let umode access the memory 105 | csrw_pmpcfg0(0xf); 106 | csrw_pmpaddr0(0xffffffff); 107 | 108 | // set the mstatues.mpp to U mode 109 | word_t mstatus = csrr_mstatus(); 110 | mstatus &= ~MSTATUS_MPP_MASK; 111 | mstatus |= MSTATUS_MPP_U; 112 | csrw_mstatus(mstatus); 113 | 114 | // Load user code 115 | load_user_img(); 116 | 117 | // Init VM 118 | init_vm(); 119 | 120 | // set the mtvec to the address of the trap handler 121 | csrw_mtvec((word_t)m_trap_entry & ~0b11); 122 | csrw_mepc(0x20000000); 123 | 124 | Context context = {}; 125 | context.gpr[2] = 0x20002000; 126 | 127 | *(uint32_t *)(page[1] + 0xa) = 0x10ADDA7A; // load data 128 | 129 | user_entry(&context); 130 | 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /tests/riscv/vm32/src/trap.S: -------------------------------------------------------------------------------- 1 | .globl user_entry 2 | .globl m_trap_entry 3 | 4 | .global m_trap_handler 5 | 6 | #if __riscv_xlen == 64 7 | #define STORE sd 8 | #define LOAD ld 9 | #define XLEN 8 10 | #else 11 | #define STORE sw 12 | #define LOAD lw 13 | #define XLEN 4 14 | #endif 15 | 16 | #define CONTEXT_SIZE 32 * XLEN 17 | 18 | .align 2 19 | m_trap_entry: 20 | // Save stack pointer of the u-mode 21 | csrrw sp, mscratch, sp 22 | 23 | addi sp, sp, -CONTEXT_SIZE 24 | 25 | STORE x1, (1 * XLEN)(sp) 26 | STORE x3, (3 * XLEN)(sp) 27 | STORE x4, (4 * XLEN)(sp) 28 | STORE x5, (5 * XLEN)(sp) 29 | STORE x6, (6 * XLEN)(sp) 30 | STORE x7, (7 * XLEN)(sp) 31 | STORE x8, (8 * XLEN)(sp) 32 | STORE x9, (9 * XLEN)(sp) 33 | STORE x10, (10 * XLEN)(sp) 34 | STORE x11, (11 * XLEN)(sp) 35 | STORE x12, (12 * XLEN)(sp) 36 | STORE x13, (13 * XLEN)(sp) 37 | STORE x14, (14 * XLEN)(sp) 38 | STORE x15, (15 * XLEN)(sp) 39 | STORE x16, (16 * XLEN)(sp) 40 | STORE x17, (17 * XLEN)(sp) 41 | STORE x18, (18 * XLEN)(sp) 42 | STORE x19, (19 * XLEN)(sp) 43 | STORE x20, (20 * XLEN)(sp) 44 | STORE x21, (21 * XLEN)(sp) 45 | STORE x22, (22 * XLEN)(sp) 46 | STORE x23, (23 * XLEN)(sp) 47 | STORE x24, (24 * XLEN)(sp) 48 | STORE x25, (25 * XLEN)(sp) 49 | STORE x26, (26 * XLEN)(sp) 50 | STORE x27, (27 * XLEN)(sp) 51 | STORE x28, (28 * XLEN)(sp) 52 | STORE x29, (29 * XLEN)(sp) 53 | STORE x30, (30 * XLEN)(sp) 54 | STORE x31, (31 * XLEN)(sp) 55 | 56 | mv a0, sp 57 | call m_trap_handler 58 | 59 | addi sp, sp, CONTEXT_SIZE 60 | 61 | .align 2 62 | user_entry: 63 | // Save stack pointer of the m-mode 64 | csrw mscratch, sp 65 | 66 | mv sp, a0 67 | 68 | LOAD x1, (1 * XLEN)(sp) 69 | LOAD x3, (3 * XLEN)(sp) 70 | LOAD x4, (4 * XLEN)(sp) 71 | LOAD x5, (5 * XLEN)(sp) 72 | LOAD x6, (6 * XLEN)(sp) 73 | LOAD x7, (7 * XLEN)(sp) 74 | LOAD x8, (8 * XLEN)(sp) 75 | LOAD x9, (9 * XLEN)(sp) 76 | LOAD x10, (10 * XLEN)(sp) 77 | LOAD x11, (11 * XLEN)(sp) 78 | LOAD x12, (12 * XLEN)(sp) 79 | LOAD x13, (13 * XLEN)(sp) 80 | LOAD x14, (14 * XLEN)(sp) 81 | LOAD x15, (15 * XLEN)(sp) 82 | LOAD x16, (16 * XLEN)(sp) 83 | LOAD x17, (17 * XLEN)(sp) 84 | LOAD x18, (18 * XLEN)(sp) 85 | LOAD x19, (19 * XLEN)(sp) 86 | LOAD x20, (20 * XLEN)(sp) 87 | LOAD x21, (21 * XLEN)(sp) 88 | LOAD x22, (22 * XLEN)(sp) 89 | LOAD x23, (23 * XLEN)(sp) 90 | LOAD x24, (24 * XLEN)(sp) 91 | LOAD x25, (25 * XLEN)(sp) 92 | LOAD x26, (26 * XLEN)(sp) 93 | LOAD x27, (27 * XLEN)(sp) 94 | LOAD x28, (28 * XLEN)(sp) 95 | LOAD x29, (29 * XLEN)(sp) 96 | LOAD x30, (30 * XLEN)(sp) 97 | LOAD x31, (31 * XLEN)(sp) 98 | 99 | // Load the stack pointer of the u-mode 100 | LOAD sp, (2 * XLEN)(sp) 101 | 102 | mret 103 | -------------------------------------------------------------------------------- /tests/riscv/vm32/user-img/Makefile: -------------------------------------------------------------------------------- 1 | img: 2 | mkdir -p build 3 | riscv64-linux-gnu-gcc -march=rv32i -mabi=ilp32 -static -nostdlib -nostartfiles ./user.S -o ./build/user.elf 4 | riscv64-linux-gnu-objcopy -O binary -j .text ./build/user.elf ./build/user.bin -------------------------------------------------------------------------------- /tests/riscv/vm32/user-img/user.S: -------------------------------------------------------------------------------- 1 | #define ADDR 0x2000100a 2 | 3 | _entry: 4 | li t0, ADDR 5 | lw t1, 0(t0) 6 | 7 | li a0, 1 8 | mv a1, t1 9 | ecall 10 | 11 | li t1, 0x52CAFFEE 12 | sw t1, 4(t0) 13 | 14 | li a0, 2 15 | ecall 16 | 17 | li t0, ADDR 18 | lw t1, 0(t0) 19 | 20 | li a0, 1 21 | mv a1, t1 22 | ecall 23 | 24 | li t1, 0x52CAFFEE 25 | sw t1, 4(t0) 26 | 27 | li a0, 2 28 | ecall 29 | 30 | j . 31 | -------------------------------------------------------------------------------- /tests/riscv/vm64/src/inc.S: -------------------------------------------------------------------------------- 1 | .globl user_img_start 2 | .globl user_img_end 3 | 4 | .section .data 5 | user_img_start: 6 | .incbin "./vm64/user-img/build/user.bin" 7 | user_img_end: 8 | -------------------------------------------------------------------------------- /tests/riscv/vm64/src/main.c: -------------------------------------------------------------------------------- 1 | #include "isa/riscv.h" 2 | #include "klib.h" 3 | 4 | extern uint8_t page[8][1 << 12]; 5 | void init_sv39(); 6 | void init_sv48(); 7 | void init_sv57(); 8 | 9 | // Trap handler 10 | extern void m_trap_entry(); 11 | extern void user_entry(Context *context); 12 | 13 | static int count = 0; 14 | Context *m_trap_handler(Context *context) { 15 | uintptr_t mcause = csrr_mcause(); 16 | 17 | printf("mcause = %d\n", mcause); 18 | if (mcause != 8) { 19 | halt(1); 20 | } 21 | 22 | count ++; 23 | if (count % 2 == 1) { 24 | if (context->gpr[6] != 0x10ADDA7A) { 25 | printf("Test%d FAIL\n", count); 26 | halt(2); 27 | } 28 | } else { 29 | if (*(word_t *)(page[1] + 0xa + 0x8) != 0x52CAFFEE) { 30 | printf("Test%d FAIL\n", count); 31 | halt(3); 32 | } 33 | } 34 | printf("Test%d PASS\n", count); 35 | 36 | if (count == 2) { 37 | init_sv48(); 38 | memset(page[1], 0, sizeof(page[1])); 39 | *(uint32_t *)(page[1] + 0xa) = 0x10ADDA7A; 40 | } else if (count == 4) { 41 | init_sv57(); 42 | memset(page[1], 0, sizeof(page[1])); 43 | *(uint32_t *)(page[1] + 0xa) = 0x10ADDA7A; 44 | } else if (count == 6) { 45 | printf("All test PASS\n"); 46 | halt(0); 47 | } 48 | 49 | uintptr_t mepc = csrr_mepc(); 50 | csrw_mepc(mepc + 4); 51 | 52 | return context; 53 | } 54 | 55 | void load_user_img(); 56 | 57 | int main() { 58 | // Let umode access the memory 59 | csrw_pmpcfg0(0xf); 60 | csrw_pmpaddr0(0xffffffff); 61 | 62 | // set the mstatues.mpp to U mode 63 | word_t mstatus = csrr_mstatus(); 64 | mstatus &= ~MSTATUS_MPP_MASK; 65 | mstatus |= MSTATUS_MPP_U; 66 | csrw_mstatus(mstatus); 67 | 68 | // Load user code 69 | load_user_img(); 70 | 71 | // Init VM 72 | init_sv39(); 73 | 74 | // set the mtvec to the address of the trap handler 75 | csrw_mtvec((word_t)m_trap_entry & ~0b11); 76 | csrw_mepc(0x20000000); 77 | 78 | Context context = {}; 79 | context.gpr[2] = 0x20002000; 80 | 81 | *(uint32_t *)(page[1] + 0xa) = 0x10ADDA7A; // load data 82 | 83 | user_entry(&context); 84 | 85 | return 0; 86 | } -------------------------------------------------------------------------------- /tests/riscv/vm64/src/trap.S: -------------------------------------------------------------------------------- 1 | .globl user_entry 2 | .globl m_trap_entry 3 | 4 | .global m_trap_handler 5 | 6 | #if __riscv_xlen == 64 7 | #define STORE sd 8 | #define LOAD ld 9 | #define XLEN 8 10 | #else 11 | #define STORE sw 12 | #define LOAD lw 13 | #define XLEN 4 14 | #endif 15 | 16 | #define CONTEXT_SIZE 32 * XLEN 17 | 18 | .align 2 19 | m_trap_entry: 20 | // Save stack pointer of the u-mode 21 | csrrw sp, mscratch, sp 22 | 23 | addi sp, sp, -CONTEXT_SIZE 24 | 25 | STORE x1, (1 * XLEN)(sp) 26 | STORE x3, (3 * XLEN)(sp) 27 | STORE x4, (4 * XLEN)(sp) 28 | STORE x5, (5 * XLEN)(sp) 29 | STORE x6, (6 * XLEN)(sp) 30 | STORE x7, (7 * XLEN)(sp) 31 | STORE x8, (8 * XLEN)(sp) 32 | STORE x9, (9 * XLEN)(sp) 33 | STORE x10, (10 * XLEN)(sp) 34 | STORE x11, (11 * XLEN)(sp) 35 | STORE x12, (12 * XLEN)(sp) 36 | STORE x13, (13 * XLEN)(sp) 37 | STORE x14, (14 * XLEN)(sp) 38 | STORE x15, (15 * XLEN)(sp) 39 | STORE x16, (16 * XLEN)(sp) 40 | STORE x17, (17 * XLEN)(sp) 41 | STORE x18, (18 * XLEN)(sp) 42 | STORE x19, (19 * XLEN)(sp) 43 | STORE x20, (20 * XLEN)(sp) 44 | STORE x21, (21 * XLEN)(sp) 45 | STORE x22, (22 * XLEN)(sp) 46 | STORE x23, (23 * XLEN)(sp) 47 | STORE x24, (24 * XLEN)(sp) 48 | STORE x25, (25 * XLEN)(sp) 49 | STORE x26, (26 * XLEN)(sp) 50 | STORE x27, (27 * XLEN)(sp) 51 | STORE x28, (28 * XLEN)(sp) 52 | STORE x29, (29 * XLEN)(sp) 53 | STORE x30, (30 * XLEN)(sp) 54 | STORE x31, (31 * XLEN)(sp) 55 | 56 | mv a0, sp 57 | call m_trap_handler 58 | 59 | addi sp, sp, CONTEXT_SIZE 60 | 61 | .align 2 62 | user_entry: 63 | // Save stack pointer of the m-mode 64 | csrw mscratch, sp 65 | 66 | mv sp, a0 67 | 68 | LOAD x1, (1 * XLEN)(sp) 69 | LOAD x3, (3 * XLEN)(sp) 70 | LOAD x4, (4 * XLEN)(sp) 71 | LOAD x5, (5 * XLEN)(sp) 72 | LOAD x6, (6 * XLEN)(sp) 73 | LOAD x7, (7 * XLEN)(sp) 74 | LOAD x8, (8 * XLEN)(sp) 75 | LOAD x9, (9 * XLEN)(sp) 76 | LOAD x10, (10 * XLEN)(sp) 77 | LOAD x11, (11 * XLEN)(sp) 78 | LOAD x12, (12 * XLEN)(sp) 79 | LOAD x13, (13 * XLEN)(sp) 80 | LOAD x14, (14 * XLEN)(sp) 81 | LOAD x15, (15 * XLEN)(sp) 82 | LOAD x16, (16 * XLEN)(sp) 83 | LOAD x17, (17 * XLEN)(sp) 84 | LOAD x18, (18 * XLEN)(sp) 85 | LOAD x19, (19 * XLEN)(sp) 86 | LOAD x20, (20 * XLEN)(sp) 87 | LOAD x21, (21 * XLEN)(sp) 88 | LOAD x22, (22 * XLEN)(sp) 89 | LOAD x23, (23 * XLEN)(sp) 90 | LOAD x24, (24 * XLEN)(sp) 91 | LOAD x25, (25 * XLEN)(sp) 92 | LOAD x26, (26 * XLEN)(sp) 93 | LOAD x27, (27 * XLEN)(sp) 94 | LOAD x28, (28 * XLEN)(sp) 95 | LOAD x29, (29 * XLEN)(sp) 96 | LOAD x30, (30 * XLEN)(sp) 97 | LOAD x31, (31 * XLEN)(sp) 98 | 99 | // Load the stack pointer of the u-mode 100 | LOAD sp, (2 * XLEN)(sp) 101 | 102 | mret 103 | -------------------------------------------------------------------------------- /tests/riscv/vm64/src/vm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "isa/riscv.h" 3 | 4 | __attribute__((aligned(4 << 20))) uint8_t page[8][1 << 12]; 5 | 6 | void init_sv39() { 7 | word_t satp = 0; 8 | satp |= VM_SV39; 9 | satp |= ((word_t)(&page[2]) >> 12); // Set the root page table 10 | csrw_satp(satp); 11 | 12 | ((word_t *)page[2])[(0x20000000UL >> 30) & 0x1ff] = (((word_t)&page[3] >> 12) << 10) | PTE_V; 13 | ((word_t *)page[3])[(0x20000000UL >> 21) & 0x1ff] = (((word_t)&page[4] >> 12) << 10) | PTE_V; 14 | ((word_t *)page[4])[(0x20000000UL >> 12) & 0x1ff] = (((word_t)&page[0] >> 12) << 10) | PTE_V | PTE_U | PTE_X; 15 | ((word_t *)page[4])[(0x20001000UL >> 12) & 0x1ff] = (((word_t)&page[1] >> 12) << 10) | PTE_V | PTE_U | PTE_R | PTE_W; 16 | 17 | sfence_vma(); 18 | } 19 | 20 | void init_sv48() { 21 | word_t satp = 0; 22 | satp |= VM_SV48; 23 | satp |= ((word_t)(&page[2]) >> 12); // Set the root page table 24 | csrw_satp(satp); 25 | 26 | ((word_t *)page[2])[(0x20000000UL >> 39) & 0x1ff] = (((word_t)&page[3] >> 12) << 10) | PTE_V; 27 | ((word_t *)page[3])[(0x20000000UL >> 30) & 0x1ff] = (((word_t)&page[4] >> 12) << 10) | PTE_V; 28 | ((word_t *)page[4])[(0x20000000UL >> 21) & 0x1ff] = (((word_t)&page[5] >> 12) << 10) | PTE_V; 29 | ((word_t *)page[5])[(0x20000000UL >> 12) & 0x1ff] = (((word_t)&page[0] >> 12) << 10) | PTE_V | PTE_U | PTE_X; 30 | ((word_t *)page[5])[(0x20001000UL >> 12) & 0x1ff] = (((word_t)&page[1] >> 12) << 10) | PTE_V | PTE_U | PTE_R | PTE_W; 31 | 32 | sfence_vma(); 33 | } 34 | 35 | void init_sv57() { 36 | word_t satp = 0; 37 | satp |= VM_SV57; 38 | satp |= ((word_t)(&page[2]) >> 12); 39 | csrw_satp(satp); 40 | 41 | ((word_t *)page[2])[(0x20000000UL >> 48) & 0x1ff] = (((word_t)&page[3] >> 12) << 10) | PTE_V; 42 | ((word_t *)page[3])[(0x20000000UL >> 39) & 0x1ff] = (((word_t)&page[4] >> 12) << 10) | PTE_V; 43 | ((word_t *)page[4])[(0x20000000UL >> 30) & 0x1ff] = (((word_t)&page[5] >> 12) << 10) | PTE_V; 44 | ((word_t *)page[5])[(0x20000000UL >> 21) & 0x1ff] = (((word_t)&page[6] >> 12) << 10) | PTE_V; 45 | ((word_t *)page[6])[(0x20000000UL >> 12) & 0x1ff] = (((word_t)&page[0] >> 12) << 10) | PTE_V | PTE_U | PTE_X; 46 | ((word_t *)page[6])[(0x20001000UL >> 12) & 0x1ff] = (((word_t)&page[1] >> 12) << 10) | PTE_V | PTE_U | PTE_R | PTE_W; 47 | 48 | sfence_vma(); 49 | } 50 | 51 | extern char user_img_start[]; 52 | extern char user_img_end[]; 53 | 54 | void load_user_img() { 55 | char *src = user_img_start; 56 | char *dst = (char *)page[0]; 57 | while (src < user_img_end) { 58 | *dst = *src; 59 | src++; 60 | dst++; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/riscv/vm64/user-img/Makefile: -------------------------------------------------------------------------------- 1 | img: 2 | mkdir -p build 3 | riscv64-linux-gnu-gcc -march=rv64i -mabi=lp64 -static -nostdlib -nostartfiles ./user.S -o ./build/user.elf 4 | riscv64-linux-gnu-objcopy -O binary -j .text ./build/user.elf ./build/user.bin 5 | -------------------------------------------------------------------------------- /tests/riscv/vm64/user-img/user.S: -------------------------------------------------------------------------------- 1 | #define ADDR 0x2000100a 2 | 3 | _entry: 4 | .loop: 5 | li t0, ADDR 6 | ld t1, 0(t0) 7 | 8 | li a0, 1 9 | mv a1, t1 10 | ecall 11 | 12 | li t1, 0x52CAFFEE 13 | sd t1, 8(t0) 14 | 15 | li a0, 2 16 | ecall 17 | 18 | j .loop 19 | -------------------------------------------------------------------------------- /tools/kconfig/.config: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Main menu 4 | # 5 | 6 | # 7 | # Example Configurations 8 | # 9 | CONFIG_FEATURE_A=y 10 | CONFIG_FEATURE_B=20 11 | # end of Example Configurations 12 | -------------------------------------------------------------------------------- /tools/kconfig/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | !lexer.l 3 | !parser.y 4 | -------------------------------------------------------------------------------- /tools/kconfig/Makefile: -------------------------------------------------------------------------------- 1 | NAME = conf 2 | obj := build 3 | SRCS += confdata.c expr.c preprocess.c symbol.c util.c 4 | SRCS += $(obj)/lexer.lex.c $(obj)/parser.tab.c 5 | CC = gcc 6 | LD = g++ 7 | CFLAGS += -DYYDEBUG 8 | INC_PATH += . 9 | DISTRO = $(shell cat /etc/os-release | grep PRETTY_NAME | sed 's/PRETTY_NAME=//') 10 | 11 | WORK_DIR = $(shell pwd) 12 | BUILD_DIR = $(WORK_DIR)/build 13 | 14 | INC_PATH := $(WORK_DIR)/include $(INC_PATH) 15 | OBJ_DIR = $(BUILD_DIR)/obj-$(NAME)$(SO) 16 | BINARY = $(BUILD_DIR)/$(NAME)$(SO) 17 | 18 | ifeq ($(DISTRO),"Gentoo Linux") 19 | LIBS += -ltinfo 20 | endif 21 | 22 | ifeq ($(NAME),conf) 23 | SRCS += conf.c 24 | else ifeq ($(NAME),mconf) 25 | SRCS += mconf.c $(shell find lxdialog/ -name "*.c") 26 | LIBS += -lncurses 27 | else 28 | $(error bad target=$(NAME)) 29 | endif 30 | 31 | $(obj)/lexer.lex.o: $(obj)/parser.tab.h 32 | $(obj)/lexer.lex.c: lexer.l $(obj)/parser.tab.h 33 | @echo + LEX $@ 34 | @flex -o $@ $< 35 | 36 | $(obj)/parser.tab.c $(obj)/parser.tab.h: parser.y 37 | @echo + YACC $@ 38 | @bison -v $< --defines=$(obj)/parser.tab.h -o $(obj)/parser.tab.c 39 | 40 | conf: 41 | @$(MAKE) -s 42 | 43 | mconf: 44 | @$(MAKE) -s NAME=mconf 45 | 46 | # Compilation flags 47 | INCLUDES = $(addprefix -I, $(INC_PATH)) 48 | CFLAGS := -O2 -MMD -Wall -Werror $(INCLUDES) $(CFLAGS) 49 | LDFLAGS := -O2 $(LDFLAGS) 50 | 51 | OBJS = $(SRCS:%.c=$(OBJ_DIR)/%.o) $(CXXSRC:%.cc=$(OBJ_DIR)/%.o) 52 | 53 | # Compilation patterns 54 | $(OBJ_DIR)/%.o: %.c 55 | @echo + CC $< 56 | @mkdir -p $(dir $@) 57 | @$(CC) $(CFLAGS) -c -o $@ $< 58 | $(call call_fixdep, $(@:.o=.d), $@) 59 | 60 | $(OBJ_DIR)/%.o: %.cc 61 | @echo + CXX $< 62 | @mkdir -p $(dir $@) 63 | @$(CXX) $(CFLAGS) $(CXXFLAGS) -c -o $@ $< 64 | $(call call_fixdep, $(@:.o=.d), $@) 65 | 66 | # Depencies 67 | -include $(OBJS:.o=.d) 68 | 69 | # Some convenient rules 70 | 71 | app: $(BINARY) 72 | 73 | $(BINARY):: $(OBJS) $(ARCHIVES) 74 | @echo + LD $@ 75 | @$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS) 76 | 77 | clean: 78 | -rm -rf $(BUILD_DIR) 79 | 80 | .PHONY: conf mconf 81 | 82 | .DEFAULT_GOAL := app 83 | -------------------------------------------------------------------------------- /tools/kconfig/lkc_proto.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include 3 | 4 | /* confdata.c */ 5 | void conf_parse(const char *name); 6 | int conf_read(const char *name); 7 | int conf_read_simple(const char *name, int); 8 | int conf_write_defconfig(const char *name); 9 | int conf_write(const char *name); 10 | int conf_write_autoconf(int overwrite); 11 | bool conf_get_changed(void); 12 | void conf_set_changed_callback(void (*fn)(void)); 13 | void conf_set_message_callback(void (*fn)(const char *s)); 14 | 15 | /* symbol.c */ 16 | extern struct symbol * symbol_hash[SYMBOL_HASHSIZE]; 17 | 18 | struct symbol * sym_lookup(const char *name, int flags); 19 | struct symbol * sym_find(const char *name); 20 | const char * sym_escape_string_value(const char *in); 21 | struct symbol ** sym_re_search(const char *pattern); 22 | const char * sym_type_name(enum symbol_type type); 23 | void sym_calc_value(struct symbol *sym); 24 | enum symbol_type sym_get_type(struct symbol *sym); 25 | bool sym_tristate_within_range(struct symbol *sym,tristate tri); 26 | bool sym_set_tristate_value(struct symbol *sym,tristate tri); 27 | tristate sym_toggle_tristate_value(struct symbol *sym); 28 | bool sym_string_valid(struct symbol *sym, const char *newval); 29 | bool sym_string_within_range(struct symbol *sym, const char *str); 30 | bool sym_set_string_value(struct symbol *sym, const char *newval); 31 | bool sym_is_changeable(struct symbol *sym); 32 | struct property * sym_get_choice_prop(struct symbol *sym); 33 | const char * sym_get_string_value(struct symbol *sym); 34 | 35 | const char * prop_get_type_name(enum prop_type type); 36 | 37 | /* preprocess.c */ 38 | enum variable_flavor { 39 | VAR_SIMPLE, 40 | VAR_RECURSIVE, 41 | VAR_APPEND, 42 | }; 43 | void env_write_dep(FILE *f, const char *auto_conf_name); 44 | void variable_add(const char *name, const char *value, 45 | enum variable_flavor flavor); 46 | void variable_all_del(void); 47 | char *expand_dollar(const char **str); 48 | char *expand_one_token(const char **str); 49 | 50 | /* expr.c */ 51 | void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken); 52 | -------------------------------------------------------------------------------- /tools/kconfig/lxdialog/yesno.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0+ 2 | /* 3 | * yesno.c -- implements the yes/no box 4 | * 5 | * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) 6 | * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) 7 | */ 8 | 9 | #include "dialog.h" 10 | 11 | /* 12 | * Display termination buttons 13 | */ 14 | static void print_buttons(WINDOW * dialog, int height, int width, int selected) 15 | { 16 | int x = width / 2 - 10; 17 | int y = height - 2; 18 | 19 | print_button(dialog, " Yes ", y, x, selected == 0); 20 | print_button(dialog, " No ", y, x + 13, selected == 1); 21 | 22 | wmove(dialog, y, x + 1 + 13 * selected); 23 | wrefresh(dialog); 24 | } 25 | 26 | /* 27 | * Display a dialog box with two buttons - Yes and No 28 | */ 29 | int dialog_yesno(const char *title, const char *prompt, int height, int width) 30 | { 31 | int i, x, y, key = 0, button = 0; 32 | WINDOW *dialog; 33 | 34 | do_resize: 35 | if (getmaxy(stdscr) < (height + YESNO_HEIGTH_MIN)) 36 | return -ERRDISPLAYTOOSMALL; 37 | if (getmaxx(stdscr) < (width + YESNO_WIDTH_MIN)) 38 | return -ERRDISPLAYTOOSMALL; 39 | 40 | /* center dialog box on screen */ 41 | x = (getmaxx(stdscr) - width) / 2; 42 | y = (getmaxy(stdscr) - height) / 2; 43 | 44 | draw_shadow(stdscr, y, x, height, width); 45 | 46 | dialog = newwin(height, width, y, x); 47 | keypad(dialog, TRUE); 48 | 49 | draw_box(dialog, 0, 0, height, width, 50 | dlg.dialog.atr, dlg.border.atr); 51 | wattrset(dialog, dlg.border.atr); 52 | mvwaddch(dialog, height - 3, 0, ACS_LTEE); 53 | for (i = 0; i < width - 2; i++) 54 | waddch(dialog, ACS_HLINE); 55 | wattrset(dialog, dlg.dialog.atr); 56 | waddch(dialog, ACS_RTEE); 57 | 58 | print_title(dialog, title, width); 59 | 60 | wattrset(dialog, dlg.dialog.atr); 61 | print_autowrap(dialog, prompt, width - 2, 1, 3); 62 | 63 | print_buttons(dialog, height, width, 0); 64 | 65 | while (key != KEY_ESC) { 66 | key = wgetch(dialog); 67 | switch (key) { 68 | case 'Y': 69 | case 'y': 70 | delwin(dialog); 71 | return 0; 72 | case 'N': 73 | case 'n': 74 | delwin(dialog); 75 | return 1; 76 | 77 | case TAB: 78 | case KEY_LEFT: 79 | case KEY_RIGHT: 80 | button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button); 81 | 82 | print_buttons(dialog, height, width, button); 83 | wrefresh(dialog); 84 | break; 85 | case ' ': 86 | case '\n': 87 | delwin(dialog); 88 | return button; 89 | case KEY_ESC: 90 | key = on_key_esc(dialog); 91 | break; 92 | case KEY_RESIZE: 93 | delwin(dialog); 94 | on_key_resize(); 95 | goto do_resize; 96 | } 97 | } 98 | 99 | delwin(dialog); 100 | return key; /* ESC pressed */ 101 | } 102 | -------------------------------------------------------------------------------- /tools/kconfig/util.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2002-2005 Roman Zippel 4 | * Copyright (C) 2002-2005 Sam Ravnborg 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include "lkc.h" 11 | 12 | /* file already present in list? If not add it */ 13 | struct file *file_lookup(const char *name) 14 | { 15 | struct file *file; 16 | 17 | for (file = file_list; file; file = file->next) { 18 | if (!strcmp(name, file->name)) { 19 | return file; 20 | } 21 | } 22 | 23 | file = xmalloc(sizeof(*file)); 24 | memset(file, 0, sizeof(*file)); 25 | file->name = xstrdup(name); 26 | file->next = file_list; 27 | file_list = file; 28 | return file; 29 | } 30 | 31 | /* Allocate initial growable string */ 32 | struct gstr str_new(void) 33 | { 34 | struct gstr gs; 35 | gs.s = xmalloc(sizeof(char) * 64); 36 | gs.len = 64; 37 | gs.max_width = 0; 38 | strcpy(gs.s, "\0"); 39 | return gs; 40 | } 41 | 42 | /* Free storage for growable string */ 43 | void str_free(struct gstr *gs) 44 | { 45 | if (gs->s) 46 | free(gs->s); 47 | gs->s = NULL; 48 | gs->len = 0; 49 | } 50 | 51 | /* Append to growable string */ 52 | void str_append(struct gstr *gs, const char *s) 53 | { 54 | size_t l; 55 | if (s) { 56 | l = strlen(gs->s) + strlen(s) + 1; 57 | if (l > gs->len) { 58 | gs->s = xrealloc(gs->s, l); 59 | gs->len = l; 60 | } 61 | strcat(gs->s, s); 62 | } 63 | } 64 | 65 | /* Append printf formatted string to growable string */ 66 | void str_printf(struct gstr *gs, const char *fmt, ...) 67 | { 68 | va_list ap; 69 | char s[10000]; /* big enough... */ 70 | va_start(ap, fmt); 71 | vsnprintf(s, sizeof(s), fmt, ap); 72 | str_append(gs, s); 73 | va_end(ap); 74 | } 75 | 76 | /* Retrieve value of growable string */ 77 | const char *str_get(struct gstr *gs) 78 | { 79 | return gs->s; 80 | } 81 | 82 | void *xmalloc(size_t size) 83 | { 84 | void *p = malloc(size); 85 | if (p) 86 | return p; 87 | fprintf(stderr, "Out of memory.\n"); 88 | exit(1); 89 | } 90 | 91 | void *xcalloc(size_t nmemb, size_t size) 92 | { 93 | void *p = calloc(nmemb, size); 94 | if (p) 95 | return p; 96 | fprintf(stderr, "Out of memory.\n"); 97 | exit(1); 98 | } 99 | 100 | void *xrealloc(void *p, size_t size) 101 | { 102 | p = realloc(p, size); 103 | if (p) 104 | return p; 105 | fprintf(stderr, "Out of memory.\n"); 106 | exit(1); 107 | } 108 | 109 | char *xstrdup(const char *s) 110 | { 111 | char *p; 112 | 113 | p = strdup(s); 114 | if (p) 115 | return p; 116 | fprintf(stderr, "Out of memory.\n"); 117 | exit(1); 118 | } 119 | 120 | char *xstrndup(const char *s, size_t n) 121 | { 122 | char *p; 123 | 124 | p = strndup(s, n); 125 | if (p) 126 | return p; 127 | fprintf(stderr, "Out of memory.\n"); 128 | exit(1); 129 | } 130 | -------------------------------------------------------------------------------- /utils/uart/uart.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import termios 3 | import tty 4 | import socket 5 | import select 6 | 7 | 8 | def main(): 9 | host = "127.0.0.1" 10 | port = 8192 11 | 12 | sendServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 | recvServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 14 | 15 | sendServer.bind((host, port)) 16 | recvServer.bind((host, port + 1)) 17 | sendServer.listen(1) 18 | recvServer.listen(1) 19 | 20 | sendClient, sendAddr = sendServer.accept() 21 | print(f"Send server connected to {sendAddr}") 22 | recvClient, recvAddr = recvServer.accept() 23 | print(f"Recv server connected to {recvAddr}") 24 | 25 | fd = sys.stdin.fileno() 26 | old_settings = termios.tcgetattr(fd) 27 | tty.setraw(fd) 28 | 29 | running = True 30 | while running: 31 | readable, _, _ = select.select([sys.stdin, recvClient], [], []) 32 | for r in readable: 33 | if r == sys.stdin: 34 | char = sys.stdin.read(1) 35 | if char == '\x03': 36 | running = False 37 | break 38 | if char == '\r': 39 | char = '\n' 40 | sendClient.send(char.encode()) 41 | elif r == recvClient: 42 | data = recvClient.recv(1024) 43 | s = data.decode() 44 | s = s.replace("\n", "\r\n").replace("\x7f", "\b \b") 45 | sys.stdout.write(s) 46 | sys.stdout.flush() 47 | 48 | termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) 49 | sendServer.close() 50 | recvServer.close() 51 | print("Close server") 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | --------------------------------------------------------------------------------