diff --git a/articles/20220327-riscv-specs.md b/articles/20220327-riscv-specs.md index f809bc0447d0b1bad5af209838c754b75f579f65..cb930699f313ab6b232b00b4d2ed5ebecbdba95c 100644 --- a/articles/20220327-riscv-specs.md +++ b/articles/20220327-riscv-specs.md @@ -1,4 +1,3 @@ - > Author: iosdevlog
> Date: 2022/03/27
> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) @@ -17,13 +16,13 @@ ![设计 ISA 的 7 种衡量标准](images/riscv_specs/isa.png) -* 成本: 集成电路 -* 简洁性: 缩小芯片面积 -* 性能: `𝑖𝑛𝑠𝑡𝑟𝑢𝑐𝑡𝑖𝑜𝑛𝑠 * 𝑐𝑦𝑐𝑙𝑒𝑠 * 𝑡𝑖𝑚𝑒` -* 架构/实现分离: 延迟分支 -* 提升空间: 添加自定义指令 -* 程序大小: 嵌入式 -* 于编程/编译/链接: 寄存器 +* 成本: 集成电路 +* 简洁性: 缩小芯片面积 +* 性能: `𝑖𝑛𝑠𝑡𝑟𝑢𝑐𝑡𝑖𝑜𝑛𝑠 * 𝑐𝑦𝑐𝑙𝑒𝑠 * 𝑡𝑖𝑚𝑒` +* 架构/实现分离: 延迟分支 +* 提升空间: 添加自定义指令 +* 程序大小: 嵌入式 +* 于编程/编译/链接: 寄存器 ## RISC-V @@ -65,7 +64,7 @@ RISC-V 指令集的设计考虑了小型、快速、低功耗的现实情况来 * 核心是一个名为 RV32I 的基础 ISA,运行一个完整的软件栈 * RV32I 是固定的,永远不会改变 * 惯例是把代表扩展的字母附加到指令集名称之后作为指示 - * 例如,RV32IMFD 将乘法(RV32M),单精度浮点(RV32F)和双精度浮点 (RV32D)的扩展添加了基础指令集(RV32I)中 + * 例如,RV32IMFD 将乘法(RV32M),单精度浮点(RV32F)和双精度浮点(RV32D)的扩展添加了基础指令集(RV32I)中 ## RISC-V 指令格式 @@ -77,21 +76,371 @@ RISC-V 指令集的设计考虑了小型、快速、低功耗的现实情况来 1. 立即数寻址 * 操作数是操作本身的常量 -1. 寄存器寻址 +2. 寄存器寻址 * 操作数在寄存器 -1. 基址寻址 +3. 基址寻址 * 操作数于内存中,其地址是寄存 器和指令中的常量之和 -1. PC 相对寻址 +4. PC 相对寻址 * 分支地址是 PC 和指令中常量之和 ## RISC-V 基础整数指令集 -**寄存器** +### RV32I 指令 + +![RV32I](images/riscv_specs/RV32I.png) + +### 寄存器 ![register](images/riscv_specs/register.png) -## 参考文档 +### Control And Status Registers(CSRs) 控制与状态寄存器 + +![csr](images/riscv_specs/csr.png) + +`csr` 需要通过特殊的指令访问。 + +![csr_rw](images/riscv_specs/csr_rw.png) + +### 特点 + +![mistake](images/riscv_specs/mistake.png) + +* 32 位字节可寻址的地址空间 +* 所有指令均为 32 位长 +* 31 个寄存器,全部 32 位宽,寄存器 0 硬连线为零 +* 所有操作都在寄存器之间(没有寄存器到内存的操作 +* 加载/存储字加上有符号和无符号加载/存储字节和半字 +* 所有算术,逻辑和移位指令都有立即数版本的指令 +* 立即数总是符号扩展 +* 仅提供一种数据寻址模式(寄存器 + 立即数)和 PC 相对分支 +* 无乘法或除法指令 +* 一个指令,用于将大立即数加载到寄存器的高位,这样加载 32 位常量到寄存器只需要两条指令 + +## RV32M 乘法和除法指令 + +![RV32M](images/riscv_specs/RV32M.png) + +## RV32F 和 RV32D 单精度和双精度浮点数 + +![RV32FD](images/riscv_specs/RV32FD.png) + +RV32F 和 RV32D 使用 32 个独立的 f 寄存器而不是 x 寄存器。 + +## RV32A 原子指令 + +RV32A 有两种类型的原子操作: + +1. 内存原子操作(AMO) +2. 加载保留/条件存储(load reserved/store conditional) + +![RV32A](images/riscv_specs/RV32A.png) + +## RV32C 压缩指令 + +每条短指令必须和一条标准的 32 位 RISC-V 指令一一 对应。 + +为了能在一系列的程序上得到良好的代码压缩效果,RISC-V 架构师精心挑选了 RVC 扩展中的指令。 + +架构师们成功地将指令压缩到了 16 位。 + +1. 十个常用寄存器(a0-a5,s0-s1,sp 以及 ra)访问的频率远超过其他寄存器 +2. 许多指令的写入目标是它的源操作数之一 +3. 立即数往往很小,而且有些指令比较喜欢某些特定的立即数 + +![RV32C](images/riscv_specs/RV32C.png) + +## RV64:64 位地址指令 + +从 32 位切换到 64 位 ISA, ISA 只添加了少数指令。 + +指令集只添加了 32 位指令对应的字(word),双字(doubleword)和长整数(long)版本的指令,并将所有寄存器(包括 PC)扩展为 64 位。 + +因此,RV64I 中的 sub 操作的是两个 64 位数字而不是 RV32I 中的 32 位数字。 + +RV64 很接近 RV32 但实际上又有所不同;它添加了少量指令同时基础指令做的事情与 RV32 中稍有不同。 + +![RV64I](images/riscv_specs/RV64I.png) + +## Privileged ISA 特权 ISA + +![privileged](images/riscv_specs/privileged.png) + +![levels](images/riscv_specs/levels.png) + +![models](images/riscv_specs/models.png) + +### Control And Status Registers(CSRs) 控制与状态寄存器 + +**CSR Listing** + +* Unprivileged and User-Level CSRs + * Timers + * Counters + * floating-point CSRs +* Supervisor-Level CSRs +* Hypervisor and VS (Virtual S-mode) CSRs +* Machine-Level CSRs + +![CSR_Listing](images/riscv_specs/CSR_Listing.jpg) + +![CSR_Listing](images/riscv_specs/CSR_Listing_2.jpg) + +目前已分配的机器级 CSR 地址 + +timer、counter、float-point CSRs 都是标准的非特权 CSRs。 + +其它寄存器都用于特权代码。 + +> 需注意的是,并非所有寄存器都需要被实现。 + +**CSR Field Specifications CSR 字段规范** + +1. Reserved Writes Preserve Values, Reads Ignore Values (WPRI) 写时保护保留值,读时忽略值 +2. Write/Read Only Legal Values (WLRL) 只读写合法值 +3. Write Any Values, Reads Legal Values (WARL) 写任意值,读合法值 + +**CSR Width Modulation CSR 位宽调制** + +如果 CSR 的位宽被改变(例如,通过改变 MXLEN 或 UXLEN),则除非另有规定,新位宽的 CSR 的可写字段和位的值应根据以下算法,由旧位宽的 CSR 来决定: + +* 旧位宽 CSR 的值复制到相同位宽的临时寄存器中。 +* 对于旧位宽 CSR 的只读位,临时寄存器中相同位置的位设为零。 +* 临时寄存器的位宽变为新位宽。如果新位宽 W 小于旧位宽,则保留临时寄存器最低有效的 W 位,将更高的有效位丢弃。如果新位宽大于旧位宽,则临时寄存器通过零扩展(zero-extension)扩展新位宽。 +* 新位宽 CSR 的每个可写字段取自临时寄存器中相同位置的位。 +* 更改 CSR 的位宽这一操作并非对 CSR 的读取或写入,因此不触发任何副作用。 + +## Debug 调试 + +调试模式具体实现(implementation)中还可以包含一个 debug mode,以支持片外调试和/或制造测试。 + +debug mode(D-mode) 可被看作为一个额外的特权模式, + +它的权限甚至比 M-mode 还多。 + +debug specification 中描述了 debug mode 下 RISC-V hart 的操作。debug mode 保留了一些 CSR 地址,这些地址只能在 D-mode 下访问,此外也可以在平台上保留一些物理地址空间。 + +![debug](images/riscv_specs/debug.png) + +* Blocks shown in dotted lines (虚线) are optional +* The user interacts with the Debug Host (e.g. laptop), which is running a debugger (e.g. gdb). +* The debugger communicates with a Debug Translator (e.g. OpenOCD, which may include a hardware driver) to communicate with Debug Transport Hardware (e.g. Olimex USB-JTAG adapter). + +## Trace 跟踪 + +![trace](images/riscv_specs/trace.png) + +## Demo + +RISC-V Specs 演示代码。 + +**Machine-Level ISA 机器级 ISA** + +![misa](images/riscv_specs/misa.png) + +### 环境 + +#### Linux Lab + +``` +$ git clone https://gitee.com/tinylab/cloud-lab.git +$ cd cloud-lab/ +$ tools/docker/run linux-lab +$ tools/docker/bash +``` + +#### 下载源代码 + +``` +$ git clone https://gitee.com/tinylab/riscv-linux +$ cd code/misa/ +``` + +代码运行在 `M-Mode`,使用 `qemu` 测试。 + +### 汇编语言 + +查看一下汇编代码。 + +``` + .text # 定义 text 代码段 + .global _start # 定义全局入口符号 _start + +_start: + csrr t0, misa # 将 misa CSR 读取到 t0/x5 + +stop: + j stop # 无限循环 + + .end # 文件结束 +``` + +查看 `misa` 寄存器的值,需要通过 `debug` 命令。 + +`make debug` 开始调试。 + +``` +cd asm +make debug +``` + +![asm](images/riscv_specs/asm.png) + +> RV32I 寄存器长度是 32 位,RV64I 寄存器是 64 位,默认是 RV64I,可以修改 `common.mk` 设置成 32 位。 + +`misa.md` 有更详细的说明。 + +### C 语言 + +使用汇编语言还需要 `debug` 才能看到 `misa` 的二进制值,不能直接解析并查看具体信息,非常不方便。 + +接下来使用 C 语言,可以很容易展示 `misa` 里面的具体内容。 + +#### 首先,定义一个 `misa` 结构体 `b`。 + +```c +/** + * \brief Union type to access MISA register. + */ +typedef union { + struct { + rv_csr_t a:1; /*!< bit: 0 Atomic extension */ + rv_csr_t b:1; /*!< bit: 1 Tentatively reserved for Bit-Manipulation extension */ + rv_csr_t c:1; /*!< bit: 2 Compressed extension */ + rv_csr_t d:1; /*!< bit: 3 Double-precision floating-point extension */ + rv_csr_t e:1; /*!< bit: 4 RV32E base ISA */ + rv_csr_t f:1; /*!< bit: 5 Single-precision floating-point extension */ + rv_csr_t g:1; /*!< bit: 6 Additional standard extensions present */ + rv_csr_t h:1; /*!< bit: 7 Hypervisor extension */ + rv_csr_t i:1; /*!< bit: 8 RV32I/64I/128I base ISA */ + rv_csr_t j:1; /*!< bit: 9 Tentatively reserved for Dynamically Translated Languages extension */ + rv_csr_t _reserved1:1; /*!< bit: 10 Reserved */ + rv_csr_t l:1; /*!< bit: 11 Tentatively reserved for Decimal Floating-Point extension */ + rv_csr_t m:1; /*!< bit: 12 Integer Multiply/Divide extension */ + rv_csr_t n:1; /*!< bit: 13 User-level interrupts supported */ + rv_csr_t _reserved2:1; /*!< bit: 14 Reserved */ + rv_csr_t p:1; /*!< bit: 15 Tentatively reserved for Packed-SIMD extension */ + rv_csr_t q:1; /*!< bit: 16 Quad-precision floating-point extension */ + rv_csr_t _resreved3:1; /*!< bit: 17 Reserved */ + rv_csr_t s:1; /*!< bit: 18 Supervisor mode implemented */ + rv_csr_t t:1; /*!< bit: 19 Tentatively reserved for Transactional Memory extension */ + rv_csr_t u:1; /*!< bit: 20 User mode implemented */ + rv_csr_t v:1; /*!< bit: 21 Tentatively reserved for Vector extension */ + rv_csr_t _reserved4:1; /*!< bit: 22 Reserved */ + rv_csr_t x:1; /*!< bit: 23 Non-standard extensions present */ +#if defined(__RISCV_XLEN) && __RISCV_XLEN == 64 + rv_csr_t _reserved5:38; /*!< bit: 24..61 Reserved */ + rv_csr_t mxl:2; /*!< bit: 62..63 Machine XLEN */ +#else + rv_csr_t _reserved5:6; /*!< bit: 24..29 Reserved */ + rv_csr_t mxl:2; /*!< bit: 30..31 Machine XLEN */ +#endif + } b; /*!< Structure used for bit access */ + rv_csr_t d; /*!< Type used for csr data access */ +} CSR_MISA_Type; +``` + +#### 之后,定义 csr 读取的宏:`__RV_CSR_READ` + +``` +/* symbolic CSR names: */ +#define CSR_MISA 0x301 + +#define XSTR(x) #x +#define __STR(s) #s +#define STRINGIFY(s) __STR(s) + +/** + * \brief CSR operation Macro for csrr instruction. + * \details + * Read the content of csr register to __v and return it + * \param csr CSR macro definition defined in + * \ref NMSIS_Core_CSR_Registers, eg. \ref CSR_MSTATUS + * \return the CSR register value + */ +#define __RV_CSR_READ(csr) \ + ({ \ + register rv_csr_t __v; \ + asm volatile("csrr %0, " STRINGIFY(csr) \ + : "=r"(__v) \ + : \ + : "memory"); \ + __v; \ + }) + +#if 0 +// linux +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) +#endif +``` + +代码下面是 *linux kernel* 里面 `arch/riscv/include/asm/csr.h` 的实现方式,代码内容是一样的。 + +#### 接着,通过内联汇编获取 `misa` csr + +C 语言可以使用内联汇编获取 `misa` 的值。 + +#### 最后,使用 C 语言解析 `misa` csr + +```c +void print_misa(void) +{ + CSR_MISA_Type misa_bits = (CSR_MISA_Type) __RV_CSR_READ(CSR_MISA); + static char misa_chars[30]; + uint8_t index = 0; + if (misa_bits.b.mxl == 1) { + misa_chars[index++] = '3'; + misa_chars[index++] = '2'; + } else if (misa_bits.b.mxl == 2) { + misa_chars[index++] = '6'; + misa_chars[index++] = '4'; + } else if (misa_bits.b.mxl == 3) { + misa_chars[index++] = '1'; + misa_chars[index++] = '2'; + misa_chars[index++] = '8'; + } + + if (misa_bits.b.i) { + misa_chars[index++] = 'I'; + } + + ... + + misa_chars[index++] = '\0'; + + printf("MISA: RV%s\r\n", misa_chars); +} +``` + +#### 查看 `misa` 信息 + +``` +cd c +make run +``` + +![c](images/riscv_specs/c.png) + +可以一眼看出以下信息: + +1. `RV64I`: 64 位 +2. `M`: M-Mode +3. `A`: 原子扩展 +4. `C`: 压缩扩展 +5. `F`: 单精度扩展 +6. `D`: 双精度扩展 +7. `S`: S-Mode +8. `U`: U-Mode + +## 参考文档 1. [RISC-V Platform](https://github.com/riscv/riscv-platform-specs/blob/main/riscv-platform-spec.adoc/) 2. [D1_SDK_Howto](https://linux-sunxi.org/D1_SDK_Howto) 3. [ADB Download - Get the latest version of ADB and fastboot](https://adbdownload.com/) +4. [rvos-lab](https://gitee.com/tinylab/rvos-lab) diff --git a/articles/images/riscv_specs/CSR_Listing.jpg b/articles/images/riscv_specs/CSR_Listing.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b5c20191cb13c0d6f4b68f208975abfa7874413 Binary files /dev/null and b/articles/images/riscv_specs/CSR_Listing.jpg differ diff --git a/articles/images/riscv_specs/CSR_Listing_2.jpg b/articles/images/riscv_specs/CSR_Listing_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..969db714b45bc992a36dc6047043be99ec07ce9f Binary files /dev/null and b/articles/images/riscv_specs/CSR_Listing_2.jpg differ diff --git a/articles/images/riscv_specs/RV32A.png b/articles/images/riscv_specs/RV32A.png new file mode 100644 index 0000000000000000000000000000000000000000..381a549538df54900bdfa05b3d5db7ce859012cf Binary files /dev/null and b/articles/images/riscv_specs/RV32A.png differ diff --git a/articles/images/riscv_specs/RV32C.png b/articles/images/riscv_specs/RV32C.png new file mode 100644 index 0000000000000000000000000000000000000000..5bb0406304a7fc1042624f01e8bd8ad3e75d2062 Binary files /dev/null and b/articles/images/riscv_specs/RV32C.png differ diff --git a/articles/images/riscv_specs/RV32FD.png b/articles/images/riscv_specs/RV32FD.png new file mode 100644 index 0000000000000000000000000000000000000000..04948a883e0254f923ee96f7404484f057ab6741 Binary files /dev/null and b/articles/images/riscv_specs/RV32FD.png differ diff --git a/articles/images/riscv_specs/RV32I.png b/articles/images/riscv_specs/RV32I.png new file mode 100644 index 0000000000000000000000000000000000000000..19af05a53d82134bfc00d76f61bf8d42a51706ac Binary files /dev/null and b/articles/images/riscv_specs/RV32I.png differ diff --git a/articles/images/riscv_specs/RV32M.png b/articles/images/riscv_specs/RV32M.png new file mode 100644 index 0000000000000000000000000000000000000000..4baecec35359843f94cfabfe1f3dbb7c98c00d49 Binary files /dev/null and b/articles/images/riscv_specs/RV32M.png differ diff --git a/articles/images/riscv_specs/RV64I.png b/articles/images/riscv_specs/RV64I.png new file mode 100644 index 0000000000000000000000000000000000000000..3add18c8bd448f4c28d39173812acac7ebf9371d Binary files /dev/null and b/articles/images/riscv_specs/RV64I.png differ diff --git a/articles/images/riscv_specs/asm.png b/articles/images/riscv_specs/asm.png new file mode 100644 index 0000000000000000000000000000000000000000..75686ef71a8647198f7f7e88236e65a1eee8aff0 Binary files /dev/null and b/articles/images/riscv_specs/asm.png differ diff --git a/articles/images/riscv_specs/c.png b/articles/images/riscv_specs/c.png new file mode 100644 index 0000000000000000000000000000000000000000..f47fbdef4943533230610d0b9fd25ed8f9acdeda Binary files /dev/null and b/articles/images/riscv_specs/c.png differ diff --git a/articles/images/riscv_specs/csr.png b/articles/images/riscv_specs/csr.png new file mode 100644 index 0000000000000000000000000000000000000000..54b8ecd04286dcb152c851d5aa331ab2fcfa586c Binary files /dev/null and b/articles/images/riscv_specs/csr.png differ diff --git a/articles/images/riscv_specs/csr_rw.png b/articles/images/riscv_specs/csr_rw.png new file mode 100644 index 0000000000000000000000000000000000000000..57f2d51d010396f19b7f0822c58dd865523c779d Binary files /dev/null and b/articles/images/riscv_specs/csr_rw.png differ diff --git a/articles/images/riscv_specs/debug.png b/articles/images/riscv_specs/debug.png new file mode 100644 index 0000000000000000000000000000000000000000..51b1235fed276758ca496dbdd4f551ed76cdac22 Binary files /dev/null and b/articles/images/riscv_specs/debug.png differ diff --git a/articles/images/riscv_specs/levels.png b/articles/images/riscv_specs/levels.png new file mode 100644 index 0000000000000000000000000000000000000000..ae3039e59cf9b5b6943ffe0b33fcb9dfdddb3c7d Binary files /dev/null and b/articles/images/riscv_specs/levels.png differ diff --git a/articles/images/riscv_specs/misa.png b/articles/images/riscv_specs/misa.png new file mode 100644 index 0000000000000000000000000000000000000000..535e0bc5b08819cd11973e66a14f2c255d09458d Binary files /dev/null and b/articles/images/riscv_specs/misa.png differ diff --git a/articles/images/riscv_specs/mistake.png b/articles/images/riscv_specs/mistake.png new file mode 100644 index 0000000000000000000000000000000000000000..fab8f8157e2f32610fa085c760322405239aa0c5 Binary files /dev/null and b/articles/images/riscv_specs/mistake.png differ diff --git a/articles/images/riscv_specs/models.png b/articles/images/riscv_specs/models.png new file mode 100644 index 0000000000000000000000000000000000000000..d923f1f1cadb43807c8d8fa97e34d2c1f8b8f111 Binary files /dev/null and b/articles/images/riscv_specs/models.png differ diff --git a/articles/images/riscv_specs/privileged.png b/articles/images/riscv_specs/privileged.png new file mode 100644 index 0000000000000000000000000000000000000000..72fdaed7c4cd136adddfdef400813942b7366e50 Binary files /dev/null and b/articles/images/riscv_specs/privileged.png differ diff --git a/articles/images/riscv_specs/trace.png b/articles/images/riscv_specs/trace.png new file mode 100644 index 0000000000000000000000000000000000000000..822b2ef4778d273bcfd24d815ef4411483736a88 Binary files /dev/null and b/articles/images/riscv_specs/trace.png differ diff --git a/code/misa/.gitignore b/code/misa/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d4a07aa04bc5622381a08bdafe7a1ce511e4e5ef --- /dev/null +++ b/code/misa/.gitignore @@ -0,0 +1,4 @@ +*.o +*.bin +*.elf +core diff --git a/code/misa/README.md b/code/misa/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c502d75932413385f5c3b6c133801145dfc5fc2a --- /dev/null +++ b/code/misa/README.md @@ -0,0 +1,42 @@ +# RISC-V misa +--- + +RISC-V Specs 演示代码。 + +## 环境 + +### linux-lab + +``` +$ git clone https://gitee.com/tinylab/cloud-lab.git +$ cd cloud-lab/ +$ tools/docker/run linux-lab +$ tools/docker/bash +``` + +### source code + +``` +$ git clone https://gitee.com/tinylab/riscv-linux +$ cd code/misa/ +``` + +代码运行在 `M-Mode`,使用 `qemu` 测试。 + +## asm + +``` +cd asm +make debug +``` + +`misa.md` 有详细说明。 + +## c + +``` +cd c +make run +``` + +C 语言能查看更多的信息。 diff --git a/code/misa/asm/Makefile b/code/misa/asm/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b7dbaf5a4a2f6afe0542b3d6a06afc45a39af0b9 --- /dev/null +++ b/code/misa/asm/Makefile @@ -0,0 +1,7 @@ +EXEC = test + +SRC = ${EXEC}.s + +GDBINIT = ./gdbinit + +include ./rule.mk diff --git a/code/misa/asm/gdbinit b/code/misa/asm/gdbinit new file mode 100644 index 0000000000000000000000000000000000000000..c5182f34c271e88f9522f7ce0403c789d4bb926a --- /dev/null +++ b/code/misa/asm/gdbinit @@ -0,0 +1,9 @@ +set confirm off + +display/z $t0 +display/t $t0 + +set disassemble-next-line on +b _start +target remote : 1234 +c diff --git a/code/misa/asm/misa.md b/code/misa/asm/misa.md new file mode 100644 index 0000000000000000000000000000000000000000..44b0cf9721ef28fd583c6ef074caa4d0aafa6a6c --- /dev/null +++ b/code/misa/asm/misa.md @@ -0,0 +1,56 @@ +## RV32 + + 1000000000101000001000100101101: 31bit +01000000000101000001000100101101: 32bit + +01: 32bit + +0000 + +zyxwvutsrqponmlkgihgfedcba + * * * * * ** * +00000101000001000100101101 + * * * * * ** * +ZYXWVUTSRQPONMLKGIHGFEDCBA + + +## RV64 + +1000000000000000000000000000000000000000000101000001000100101101: 64bit +10: 64bit +000000000000000000000000000000000000 +zyxwvutsrqponmlkgihgfedcba + * * * * * ** * +00000101000001000100101101 + * * * * * ** * +ZYXWVUTSRQPONMLKGIHGFEDCBA + +10: 64bit + +Bit Character Description + 0*A Atomic extension + 1 B Tentatively reserved for Bit-Manipulation extension + 2*C Compressed extension + 3*D Double-precision floating-point extension + 4 E RV32E base ISA + 5 F Single-precision floating-point extension + 6 G Reserved + 7 H Hypervisor extension + 8*I RV32I/64I/128I base ISA + 9 J Tentatively reserved for Dynamically Translated Languages extension +10 K Reserved +11 L Reserved +12*M Integer Multiply/Divide extension +13 N Tentatively reserved for User-Level Interrupts extension +14 O Reserved +15 P Tentatively reserved for Packed-SIMD extension +16 Q Quad-precision floating-point extension +17 R Reserved +18*S Supervisor mode implemented +19 T Reserved +20*U User mode implemented +21 V Tentatively reserved for Vector extension +22 W Reserved +23 X Non-standard extensions present +24 Y Reserved +25 Z Reserved diff --git a/code/misa/asm/rule.mk b/code/misa/asm/rule.mk new file mode 100644 index 0000000000000000000000000000000000000000..6a7328d165b9fcb445bc1e808716e96d5310a0b7 --- /dev/null +++ b/code/misa/asm/rule.mk @@ -0,0 +1,32 @@ +include ../common.mk + +.DEFAULT_GOAL := all +all: + @${CC} ${CFLAGS} ${SRC} -Ttext=0x80000000 -o ${EXEC}.elf + @${OBJCOPY} -O binary ${EXEC}.elf ${EXEC}.bin + +.PHONY : run +run: all + @echo "Press Ctrl-A and then X to exit QEMU" + @echo "------------------------------------" + @echo "No output, please run 'make debug' to see details" + @${QEMU} ${QFLAGS} -kernel ./${EXEC}.elf + +.PHONY : debug +debug: all + @echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU" + @echo "-------------------------------------------------------" + @${QEMU} ${QFLAGS} -kernel ${EXEC}.elf -s -S & + @${GDB} ${EXEC}.elf -q -x ${GDBINIT} + +.PHONY : code +code: all + @${OBJDUMP} -S ${EXEC}.elf | less + +.PHONY : hex +hex: all + @hexdump -C ${EXEC}.bin + +.PHONY : clean +clean: + rm -rf *.o *.bin *.elf diff --git a/code/misa/asm/test.s b/code/misa/asm/test.s new file mode 100644 index 0000000000000000000000000000000000000000..5a8da8c7862da5846beaded37e58a0da349304ca --- /dev/null +++ b/code/misa/asm/test.s @@ -0,0 +1,10 @@ + .text # Define beginning of text section + .global _start # Define entry _start + +_start: + csrr t0, misa + +stop: + j stop # Infinite loop to stop execution + + .end # End of file diff --git a/code/misa/c/Makefile b/code/misa/c/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d238627750f8f37279ad23e4c4f1828cc53ce3dc --- /dev/null +++ b/code/misa/c/Makefile @@ -0,0 +1,49 @@ +include ../common.mk + +SRCS_ASM = \ + start.S \ + +SRCS_C = \ + kernel.c \ + uart.c \ + printf.c \ + misa.c \ + +OBJS = $(SRCS_ASM:.S=.o) +OBJS += $(SRCS_C:.c=.o) + +.DEFAULT_GOAL := all +all: os.elf + +# start.o must be the first in dependency! +os.elf: ${OBJS} + ${CC} ${CFLAGS} -T os.ld -o os.elf $^ + ${OBJCOPY} -O binary os.elf os.bin + +%.o : %.c + ${CC} ${CFLAGS} -c -o $@ $< + +%.o : %.S + ${CC} ${CFLAGS} -c -o $@ $< + +run: all + @${QEMU} -M ? | grep virt >/dev/null || exit + @echo "Press Ctrl-A and then X to exit QEMU" + @echo "------------------------------------" + @${QEMU} ${QFLAGS} -kernel os.elf + +.PHONY : debug +debug: all + @echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU" + @echo "-------------------------------------------------------" + @${QEMU} ${QFLAGS} -kernel os.elf -s -S & + @${GDB} os.elf -q -x ../gdbinit + +.PHONY : code +code: all + @${OBJDUMP} -S os.elf | less + +.PHONY : clean +clean: + rm -rf *.o *.bin *.elf + diff --git a/code/misa/c/csr.h b/code/misa/c/csr.h new file mode 100644 index 0000000000000000000000000000000000000000..c9fa9eedf07423697d2a5c843605165c4c8b5226 --- /dev/null +++ b/code/misa/c/csr.h @@ -0,0 +1,173 @@ +#ifndef __MISC_H__ +#define __MISC_H__ + +#include "types.h" + +/* symbolic CSR names: */ +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 + +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 +#define CSR_SATP 0x180 + +#define CSR_VSSTATUS 0x200 +#define CSR_VSIE 0x204 +#define CSR_VSTVEC 0x205 +#define CSR_VSSCRATCH 0x240 +#define CSR_VSEPC 0x241 +#define CSR_VSCAUSE 0x242 +#define CSR_VSTVAL 0x243 +#define CSR_VSIP 0x244 +#define CSR_VSATP 0x280 + +#define CSR_HSTATUS 0x600 +#define CSR_HEDELEG 0x602 +#define CSR_HIDELEG 0x603 +#define CSR_HIE 0x604 +#define CSR_HTIMEDELTA 0x605 +#define CSR_HCOUNTEREN 0x606 +#define CSR_HGEIE 0x607 +#define CSR_HTIMEDELTAH 0x615 +#define CSR_HTVAL 0x643 +#define CSR_HIP 0x644 +#define CSR_HVIP 0x645 +#define CSR_HTINST 0x64a +#define CSR_HGATP 0x680 +#define CSR_HGEIP 0xe12 + +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPADDR0 0x3b0 +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 + +#define XSTR(x) #x +#define __STR(s) #s +#define STRINGIFY(s) __STR(s) + +/** + * \brief CSR operation Macro for csrr instruction. + * \details + * Read the content of csr register to __v and return it + * \param csr CSR macro definition defined in + * \ref NMSIS_Core_CSR_Registers, eg. \ref CSR_MSTATUS + * \return the CSR register value + */ +#define __RV_CSR_READ(csr) \ + ({ \ + register rv_csr_t __v; \ + asm volatile("csrr %0, " STRINGIFY(csr) \ + : "=r"(__v) \ + : \ + : "memory"); \ + __v; \ + }) + +#if 0 +// linux +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) + +#endif +/** + * \defgroup NMSIS_Core_Registers Register Define and Type Definitions + * \brief Type definitions and defines for core registers. + * + * @{ + */ +#ifndef __RISCV_XLEN + /** \brief Refer to the width of an integer register in bits(either 32 or 64) */ + #ifndef __riscv_xlen + #define __RISCV_XLEN 32 + #else + #define __RISCV_XLEN __riscv_xlen + #endif +#endif /* __RISCV_XLEN */ + +/** \brief Type of Control and Status Register(CSR), depends on the XLEN defined in RISC-V */ +#if __RISCV_XLEN == 32 + typedef uint32_t rv_csr_t; +#elif __RISCV_XLEN == 64 + typedef uint64_t rv_csr_t; +#else + typedef uint32_t rv_csr_t; +#endif +/** @} */ /* End of Doxygen Group NMSIS_Core_Registers */ +/** + * \defgroup NMSIS_Core_Base_Registers Base Register Define and Type Definitions + * \ingroup NMSIS_Core_Registers + * \brief Type definitions and defines for base core registers. + * + * @{ + */ +/** + * \brief Union type to access MISA register. + */ +typedef union { + struct { + rv_csr_t a:1; /*!< bit: 0 Atomic extension */ + rv_csr_t b:1; /*!< bit: 1 Tentatively reserved for Bit-Manipulation extension */ + rv_csr_t c:1; /*!< bit: 2 Compressed extension */ + rv_csr_t d:1; /*!< bit: 3 Double-precision floating-point extension */ + rv_csr_t e:1; /*!< bit: 4 RV32E base ISA */ + rv_csr_t f:1; /*!< bit: 5 Single-precision floating-point extension */ + rv_csr_t g:1; /*!< bit: 6 Additional standard extensions present */ + rv_csr_t h:1; /*!< bit: 7 Hypervisor extension */ + rv_csr_t i:1; /*!< bit: 8 RV32I/64I/128I base ISA */ + rv_csr_t j:1; /*!< bit: 9 Tentatively reserved for Dynamically Translated Languages extension */ + rv_csr_t _reserved1:1; /*!< bit: 10 Reserved */ + rv_csr_t l:1; /*!< bit: 11 Tentatively reserved for Decimal Floating-Point extension */ + rv_csr_t m:1; /*!< bit: 12 Integer Multiply/Divide extension */ + rv_csr_t n:1; /*!< bit: 13 User-level interrupts supported */ + rv_csr_t _reserved2:1; /*!< bit: 14 Reserved */ + rv_csr_t p:1; /*!< bit: 15 Tentatively reserved for Packed-SIMD extension */ + rv_csr_t q:1; /*!< bit: 16 Quad-precision floating-point extension */ + rv_csr_t _resreved3:1; /*!< bit: 17 Reserved */ + rv_csr_t s:1; /*!< bit: 18 Supervisor mode implemented */ + rv_csr_t t:1; /*!< bit: 19 Tentatively reserved for Transactional Memory extension */ + rv_csr_t u:1; /*!< bit: 20 User mode implemented */ + rv_csr_t v:1; /*!< bit: 21 Tentatively reserved for Vector extension */ + rv_csr_t _reserved4:1; /*!< bit: 22 Reserved */ + rv_csr_t x:1; /*!< bit: 23 Non-standard extensions present */ +#if defined(__RISCV_XLEN) && __RISCV_XLEN == 64 + rv_csr_t _reserved5:38; /*!< bit: 24..61 Reserved */ + rv_csr_t mxl:2; /*!< bit: 62..63 Machine XLEN */ +#else + rv_csr_t _reserved5:6; /*!< bit: 24..29 Reserved */ + rv_csr_t mxl:2; /*!< bit: 30..31 Machine XLEN */ +#endif + } b; /*!< Structure used for bit access */ + rv_csr_t d; /*!< Type used for csr data access */ +} CSR_MISA_Type; + +void print_misa(void); + +#endif diff --git a/code/misa/c/kernel.c b/code/misa/c/kernel.c new file mode 100644 index 0000000000000000000000000000000000000000..bc61578fa3045c6ad854655dbc5a453f073f7f14 --- /dev/null +++ b/code/misa/c/kernel.c @@ -0,0 +1,18 @@ +#include "os.h" +#include "csr.h" + +/* + * Following functions SHOULD be called ONLY ONE time here, + * so just declared here ONCE and NOT included in file os.h. + */ +extern void uart_init(void); + +void start_kernel(void) +{ + uart_init(); + uart_puts("Hello, RVOS!\n"); + print_misa(); + + while (1) {}; // stop here! +} + diff --git a/code/misa/c/misa.c b/code/misa/c/misa.c new file mode 100644 index 0000000000000000000000000000000000000000..9464411a3fc8ca6d0e7aae7e29a277952de4971c --- /dev/null +++ b/code/misa/c/misa.c @@ -0,0 +1,78 @@ +#include "csr.h" +#include "os.h" + +void print_misa(void) +{ + CSR_MISA_Type misa_bits = (CSR_MISA_Type) __RV_CSR_READ(CSR_MISA); + static char misa_chars[30]; + uint8_t index = 0; + if (misa_bits.b.mxl == 1) { + misa_chars[index++] = '3'; + misa_chars[index++] = '2'; + } else if (misa_bits.b.mxl == 2) { + misa_chars[index++] = '6'; + misa_chars[index++] = '4'; + } else if (misa_bits.b.mxl == 3) { + misa_chars[index++] = '1'; + misa_chars[index++] = '2'; + misa_chars[index++] = '8'; + } + if (misa_bits.b.i) { + misa_chars[index++] = 'I'; + } + if (misa_bits.b.m) { + misa_chars[index++] = 'M'; + } + if (misa_bits.b.a) { + misa_chars[index++] = 'A'; + } + if (misa_bits.b.b) { + misa_chars[index++] = 'B'; + } + if (misa_bits.b.c) { + misa_chars[index++] = 'C'; + } + if (misa_bits.b.e) { + misa_chars[index++] = 'E'; + } + if (misa_bits.b.f) { + misa_chars[index++] = 'F'; + } + if (misa_bits.b.d) { + misa_chars[index++] = 'D'; + } + if (misa_bits.b.q) { + misa_chars[index++] = 'Q'; + } + if (misa_bits.b.h) { + misa_chars[index++] = 'H'; + } + if (misa_bits.b.j) { + misa_chars[index++] = 'J'; + } + if (misa_bits.b.l) { + misa_chars[index++] = 'L'; + } + if (misa_bits.b.n) { + misa_chars[index++] = 'N'; + } + if (misa_bits.b.s) { + misa_chars[index++] = 'S'; + } + if (misa_bits.b.p) { + misa_chars[index++] = 'P'; + } + if (misa_bits.b.t) { + misa_chars[index++] = 'T'; + } + if (misa_bits.b.u) { + misa_chars[index++] = 'U'; + } + if (misa_bits.b.x) { + misa_chars[index++] = 'X'; + } + + misa_chars[index++] = '\0'; + + printf("MISA: RV%s\r\n", misa_chars); +} diff --git a/code/misa/c/os.h b/code/misa/c/os.h new file mode 100644 index 0000000000000000000000000000000000000000..1908af4a6470d0f482fe660369b7548727cccc9e --- /dev/null +++ b/code/misa/c/os.h @@ -0,0 +1,22 @@ +#ifndef __OS_H__ +#define __OS_H__ + +#include "types.h" +#include "platform.h" + +#include +#include + +/* uart */ +extern int uart_putc(char ch); +extern void uart_puts(char *s); + +/* printf */ +extern int printf(const char* s, ...); +extern void panic(char *s); + +/* memory management */ +extern void *page_alloc(int npages); +extern void page_free(void *p); + +#endif /* __OS_H__ */ diff --git a/code/misa/c/os.ld b/code/misa/c/os.ld new file mode 100644 index 0000000000000000000000000000000000000000..a75cd930d128f56635905874269857b72406b934 --- /dev/null +++ b/code/misa/c/os.ld @@ -0,0 +1,136 @@ +/* + * rvos.ld + * Linker script for outputting to RVOS + */ + +/* + * https://sourceware.org/binutils/docs/ld/Miscellaneous-Commands.html + * OUTPUT_ARCH command specifies a particular output machine architecture. + * "riscv" is the name of the architecture for both 64-bit and 32-bit + * RISC-V target. We will further refine this by using -march=rv32ima + * and -mabi=ilp32 when calling gcc. + */ +OUTPUT_ARCH( "riscv" ) + +/* + * https://sourceware.org/binutils/docs/ld/Entry-Point.html + * ENTRY command is used to set the "entry point", which is the first instruction + * to execute in a program. + * The argument of ENTRY command is a symbol name, here is "_start" which is + * defined in start.S. + */ +ENTRY( _start ) + +/* + * https://sourceware.org/binutils/docs/ld/MEMORY.html + * The MEMORY command describes the location and size of blocks of memory in + * the target. + * The syntax for MEMORY is: + * MEMORY + * { + * name [(attr)] : ORIGIN = origin, LENGTH = len + * ...... + * } + * Each line defines a memory region. + * Each memory region must have a distinct name within the MEMORY command. Here + * we only define one region named as "ram". + * The "attr" string is an optional list of attributes that specify whether to + * use a particular memory region for an input section which is not explicitly + * mapped in the linker script. Here we assign 'w' (writeable), 'x' (executable), + * and 'a' (allocatable). We use '!' to invert 'r' (read-only) and + * 'i' (initialized). + * The "ORIGIN" is used to set the start address of the memory region. Here we + * place it right at the beginning of 0x8000_0000 because this is where the + * QEMU-virt machine will start executing. + * Finally LENGTH = 128M tells the linker that we have 128 megabyte of RAM. + * The linker will double check this to make sure everything can fit. + */ +MEMORY +{ + ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M +} + +/* + * https://sourceware.org/binutils/docs/ld/SECTIONS.html + * The SECTIONS command tells the linker how to map input sections into output + * sections, and how to place the output sections in memory. + * The format of the SECTIONS command is: + * SECTIONS + * { + * sections-command + * sections-command + * ...... + * } + * + * Each sections-command may of be one of the following: + * (1) an ENTRY command + * (2) a symbol assignment + * (3) an output section description + * (4) an overlay description + * We here only demo (2) & (3). + * + * We use PROVIDE command to define symbols. + * https://sourceware.org/binutils/docs/ld/PROVIDE.html + * The PROVIDE keyword may be used to define a symbol. + * The syntax is PROVIDE(symbol = expression). + * Such symbols as "_text_start", "_text_end" ... will be used in mem.S. + * Notice the period '.' tells the linker to set symbol(e.g. _text_start) to + * the CURRENT location ('.' = current memory location). This current memory + * location moves as we add things. + */ +SECTIONS +{ + /* + * We are going to layout all text sections in .text output section, + * starting with .text. The asterisk("*") in front of the + * parentheses means to match the .text section of ANY object file. + */ + .text : { + PROVIDE(_text_start = .); + *(.text .text.*) + PROVIDE(_text_end = .); + } >ram + + .rodata : { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + PROVIDE(_rodata_end = .); + } >ram + + .data : { + /* + * . = ALIGN(4096) tells the linker to align the current memory + * location to 4096 bytes. This will insert padding bytes until + * current location becomes aligned on 4096-byte boundary. + * This is because our paging system's resolution is 4,096 bytes. + */ + . = ALIGN(4096); + PROVIDE(_data_start = .); + /* + * sdata and data are essentially the same thing. We do not need + * to distinguish sdata from data. + */ + *(.sdata .sdata.*) + *(.data .data.*) + PROVIDE(_data_end = .); + } >ram + + .bss :{ + /* + * https://sourceware.org/binutils/docs/ld/Input-Section-Common.html + * In most cases, common symbols in input files will be placed + * in the ‘.bss’ section in the output file. + */ + PROVIDE(_bss_start = .); + *(.sbss .sbss.*) + *(.bss .bss.*) + *(COMMON) + PROVIDE(_bss_end = .); + } >ram + + PROVIDE(_memory_start = ORIGIN(ram)); + PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram)); + + PROVIDE(_heap_start = _bss_end); + PROVIDE(_heap_size = _memory_end - _heap_start); +} diff --git a/code/misa/c/platform.h b/code/misa/c/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..c5367c69942a280c8d5edbc87b128ad78711f8ca --- /dev/null +++ b/code/misa/c/platform.h @@ -0,0 +1,29 @@ +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +/* + * QEMU RISC-V Virt machine with 16550a UART and VirtIO MMIO + */ + +/* + * maximum number of CPUs + * see https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h + * #define VIRT_CPUS_MAX 8 + */ +#define MAXNUM_CPU 8 + +/* + * MemoryMap + * see https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c, virt_memmap[] + * 0x00001000 -- boot ROM, provided by qemu + * 0x02000000 -- CLINT + * 0x0C000000 -- PLIC + * 0x10000000 -- UART0 + * 0x10001000 -- virtio disk + * 0x80000000 -- boot ROM jumps here in machine mode, where we load our kernel + */ + +/* This machine puts UART registers here in physical memory. */ +#define UART0 0x10000000L + +#endif /* __PLATFORM_H__ */ diff --git a/code/misa/c/printf.c b/code/misa/c/printf.c new file mode 100644 index 0000000000000000000000000000000000000000..93ace5be9a6d6e4428d6b74cfa516bd9827dbc99 --- /dev/null +++ b/code/misa/c/printf.c @@ -0,0 +1,138 @@ +#include "os.h" + +/* + * ref: https://github.com/cccriscv/mini-riscv-os/blob/master/05-Preemptive/lib.c + */ + +static int _vsnprintf(char * out, size_t n, const char* s, va_list vl) +{ + int format = 0; + int longarg = 0; + size_t pos = 0; + for (; *s; s++) { + if (format) { + switch(*s) { + case 'l': { + longarg = 1; + break; + } + case 'p': { + longarg = 1; + if (out && pos < n) { + out[pos] = '0'; + } + pos++; + if (out && pos < n) { + out[pos] = 'x'; + } + pos++; + } + case 'x': { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + int hexdigits = 2*(longarg ? sizeof(long) : sizeof(int))-1; + for(int i = hexdigits; i >= 0; i--) { + int d = (num >> (4*i)) & 0xF; + if (out && pos < n) { + out[pos] = (d < 10 ? '0'+d : 'a'+d-10); + } + pos++; + } + longarg = 0; + format = 0; + break; + } + case 'd': { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + if (num < 0) { + num = -num; + if (out && pos < n) { + out[pos] = '-'; + } + pos++; + } + long digits = 1; + for (long nn = num; nn /= 10; digits++); + for (int i = digits-1; i >= 0; i--) { + if (out && pos + i < n) { + out[pos + i] = '0' + (num % 10); + } + num /= 10; + } + pos += digits; + longarg = 0; + format = 0; + break; + } + case 's': { + const char* s2 = va_arg(vl, const char*); + while (*s2) { + if (out && pos < n) { + out[pos] = *s2; + } + pos++; + s2++; + } + longarg = 0; + format = 0; + break; + } + case 'c': { + if (out && pos < n) { + out[pos] = (char)va_arg(vl,int); + } + pos++; + longarg = 0; + format = 0; + break; + } + default: + break; + } + } else if (*s == '%') { + format = 1; + } else { + if (out && pos < n) { + out[pos] = *s; + } + pos++; + } + } + if (out && pos < n) { + out[pos] = 0; + } else if (out && n) { + out[n-1] = 0; + } + return pos; +} + +static char out_buf[1000]; // buffer for _vprintf() + +static int _vprintf(const char* s, va_list vl) +{ + int res = _vsnprintf(NULL, -1, s, vl); + if (res+1 >= sizeof(out_buf)) { + uart_puts("error: output string size overflow\n"); + while(1) {} + } + _vsnprintf(out_buf, res + 1, s, vl); + uart_puts(out_buf); + return res; +} + +int printf(const char* s, ...) +{ + int res = 0; + va_list vl; + va_start(vl, s); + res = _vprintf(s, vl); + va_end(vl); + return res; +} + +void panic(char *s) +{ + printf("panic: "); + printf(s); + printf("\n"); + while(1){}; +} diff --git a/code/misa/c/start.S b/code/misa/c/start.S new file mode 100644 index 0000000000000000000000000000000000000000..c7ad5e4fa7cf5d5055508a12a385d5571dae3f96 --- /dev/null +++ b/code/misa/c/start.S @@ -0,0 +1,42 @@ +#include "platform.h" + + # size of each hart's stack is 1024 bytes + .equ STACK_SIZE, 1024 + + .global _start + + .text +_start: + # park harts with id != 0 + csrr t0, mhartid # read current hart id + mv tp, t0 # keep CPU's hartid in its tp for later usage. + bnez t0, park # if we're not on the hart 0 + # we park the hart + + # Set all bytes in the BSS section to zero. + la a0, _bss_start + la a1, _bss_end + bgeu a0, a1, 2f +1: + sw zero, (a0) + addi a0, a0, 4 + bltu a0, a1, 1b +2: + # Setup stacks, the stack grows from bottom to top, so we put the + # stack pointer to the very end of the stack range. + slli t0, t0, 10 # shift left the hart id by 1024 + la sp, stacks + STACK_SIZE # set the initial stack pointer + # to the end of the first stack space + add sp, sp, t0 # move the current hart stack pointer + # to its place in the stack space + + j start_kernel # hart 0 jump to c + +park: + wfi + j park + +stacks: + .skip STACK_SIZE * MAXNUM_CPU # allocate space for all the harts stacks + + .end # End of file diff --git a/code/misa/c/types.h b/code/misa/c/types.h new file mode 100644 index 0000000000000000000000000000000000000000..e2a8b8e0eb85cce235dd11802e485c0233a96282 --- /dev/null +++ b/code/misa/c/types.h @@ -0,0 +1,9 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif /* __TYPES_H__ */ \ No newline at end of file diff --git a/code/misa/c/uart.c b/code/misa/c/uart.c new file mode 100644 index 0000000000000000000000000000000000000000..ed22367a2ace76ed18ee0247264dccf11a97aa76 --- /dev/null +++ b/code/misa/c/uart.c @@ -0,0 +1,119 @@ +#include "os.h" + +/* + * The UART control registers are memory-mapped at address UART0. + * This macro returns the address of one of the registers. + */ +#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg)) + +/* + * Reference + * [1]: TECHNICAL DATA ON 16550, http://byterunner.com/16550.html + */ + +/* + * UART control registers map. see [1] "PROGRAMMING TABLE" + * note some are reused by multiple functions + * 0 (write mode): THR/DLL + * 1 (write mode): IER/DLM + */ +#define RHR 0 // Receive Holding Register (read mode) +#define THR 0 // Transmit Holding Register (write mode) +#define DLL 0 // LSB of Divisor Latch (write mode) +#define IER 1 // Interrupt Enable Register (write mode) +#define DLM 1 // MSB of Divisor Latch (write mode) +#define FCR 2 // FIFO Control Register (write mode) +#define ISR 2 // Interrupt Status Register (read mode) +#define LCR 3 // Line Control Register +#define MCR 4 // Modem Control Register +#define LSR 5 // Line Status Register +#define MSR 6 // Modem Status Register +#define SPR 7 // ScratchPad Register + +/* + * POWER UP DEFAULTS + * IER = 0: TX/RX holding register interrupts are both disabled + * ISR = 1: no interrupt penting + * LCR = 0 + * MCR = 0 + * LSR = 60 HEX + * MSR = BITS 0-3 = 0, BITS 4-7 = inputs + * FCR = 0 + * TX = High + * OP1 = High + * OP2 = High + * RTS = High + * DTR = High + * RXRDY = High + * TXRDY = Low + * INT = Low + */ + +/* + * LINE STATUS REGISTER (LSR) + * LSR BIT 0: + * 0 = no data in receive holding register or FIFO. + * 1 = data has been receive and saved in the receive holding register or FIFO. + * ...... + * LSR BIT 5: + * 0 = transmit holding register is full. 16550 will not accept any data for transmission. + * 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character. + * ...... + */ +#define LSR_RX_READY (1 << 0) +#define LSR_TX_IDLE (1 << 5) + +#define uart_read_reg(reg) (*(UART_REG(reg))) +#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v)) + +void uart_init() +{ + /* disable interrupts. */ + uart_write_reg(IER, 0x00); + + /* + * Setting baud rate. Just a demo here if we care about the divisor, + * but for our purpose [QEMU-virt], this doesn't really do anything. + * + * Notice that the divisor register DLL (divisor latch least) and DLM (divisor + * latch most) have the same base address as the receiver/transmitter and the + * interrupt enable register. To change what the base address points to, we + * open the "divisor latch" by writing 1 into the Divisor Latch Access Bit + * (DLAB), which is bit index 7 of the Line Control Register (LCR). + * + * Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE". + * We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3. + * And due to the divisor register is two bytes (16 bits), so we need to + * split the value of 3(0x0003) into two bytes, DLL stores the low byte, + * DLM stores the high byte. + */ + uint8_t lcr = uart_read_reg(LCR); + uart_write_reg(LCR, lcr | (1 << 7)); + uart_write_reg(DLL, 0x03); + uart_write_reg(DLM, 0x00); + + /* + * Continue setting the asynchronous data communication format. + * - number of the word length: 8 bits + * - number of stop bits:1 bit when word length is 8 bits + * - no parity + * - no break control + * - disabled baud latch + */ + lcr = 0; + uart_write_reg(LCR, lcr | (3 << 0)); +} + +int uart_putc(char ch) +{ + while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0); + return uart_write_reg(THR, ch); +} + +void uart_puts(char *s) +{ + while (*s) { + uart_putc(*s++); + } +} + diff --git a/code/misa/common.mk b/code/misa/common.mk new file mode 100644 index 0000000000000000000000000000000000000000..514215da0e0c9d7960bcc038e80d5133ed683e8b --- /dev/null +++ b/code/misa/common.mk @@ -0,0 +1,42 @@ +CFLAGS = -nostdlib -fno-builtin -march=rv64ima -mabi=lp64 -g -Wall + +QEMU = qemu-system-riscv64 +QFLAGS = -nographic -smp 1 -machine virt -bios none + +CC_LIST := riscv64-unknown-elf- riscv64-linux-gnu- +CROSS_COMPILE ?= $(shell for cc in $(CC_LIST); do which $${cc}gcc >/dev/null 2>&1 && echo $${cc} && break; done) + +ifneq ($(CROSS_COMPILE),) + GDB_LIST := $(CROSS_COMPILE)gdb gdb-multiarch + GDB ?= $(shell for gdb in $(GDB_LIST); do which $${gdb} >/dev/null 2>&1 && echo $${gdb} && break; done) + + GCC = ${CROSS_COMPILE}gcc +endif + +ifeq ($(GCC),) + $(error Please install one of $(addsuffix gcc,$(CC_LIST))) +else + ifeq ($(GDB),) + $(error Please install one of $(GDB_LIST)) + endif +endif + +ifneq ($(shell which $(QEMU) >/dev/null 2>&1; echo $$?),0) + $(error Please install $(QEMU)) +endif + +ifeq ($(CROSS_COMPILE),riscv64-linux-gnu-) + # make sure PIE disabled for kernel-like binaries + CFLAGS += -fno-PIE -mcmodel=medany + + # mem.S not work with riscv64-linux-gnu-, the addresses in mem.S can not be resolved, which breaks the whole mem management support + # use mem.h instead of mem.S in such case + CFLAGS += -DMEM_H +else + # mem.S work well with riscv64-unknown-elf- + CFLAGS += -DMEM_S +endif + +CC = ${GCC} +OBJCOPY = ${CROSS_COMPILE}objcopy +OBJDUMP = ${CROSS_COMPILE}objdump