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 @@

-* 成本: 集成电路
-* 简洁性: 缩小芯片面积
-* 性能: `𝑖𝑛𝑠𝑡𝑟𝑢𝑐𝑡𝑖𝑜𝑛𝑠 * 𝑐𝑦𝑐𝑙𝑒𝑠 * 𝑡𝑖𝑚𝑒`
-* 架构/实现分离: 延迟分支
-* 提升空间: 添加自定义指令
-* 程序大小: 嵌入式
-* 于编程/编译/链接: 寄存器
+* 成本: 集成电路
+* 简洁性: 缩小芯片面积
+* 性能: `𝑖𝑛𝑠𝑡𝑟𝑢𝑐𝑡𝑖𝑜𝑛𝑠 * 𝑐𝑦𝑐𝑙𝑒𝑠 * 𝑡𝑖𝑚𝑒`
+* 架构/实现分离: 延迟分支
+* 提升空间: 添加自定义指令
+* 程序大小: 嵌入式
+* 于编程/编译/链接: 寄存器
## 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 指令
+
+
+
+### 寄存器

-## 参考文档
+### Control And Status Registers(CSRs) 控制与状态寄存器
+
+
+
+`csr` 需要通过特殊的指令访问。
+
+
+
+### 特点
+
+
+
+* 32 位字节可寻址的地址空间
+* 所有指令均为 32 位长
+* 31 个寄存器,全部 32 位宽,寄存器 0 硬连线为零
+* 所有操作都在寄存器之间(没有寄存器到内存的操作
+* 加载/存储字加上有符号和无符号加载/存储字节和半字
+* 所有算术,逻辑和移位指令都有立即数版本的指令
+* 立即数总是符号扩展
+* 仅提供一种数据寻址模式(寄存器 + 立即数)和 PC 相对分支
+* 无乘法或除法指令
+* 一个指令,用于将大立即数加载到寄存器的高位,这样加载 32 位常量到寄存器只需要两条指令
+
+## RV32M 乘法和除法指令
+
+
+
+## RV32F 和 RV32D 单精度和双精度浮点数
+
+
+
+RV32F 和 RV32D 使用 32 个独立的 f 寄存器而不是 x 寄存器。
+
+## RV32A 原子指令
+
+RV32A 有两种类型的原子操作:
+
+1. 内存原子操作(AMO)
+2. 加载保留/条件存储(load reserved/store conditional)
+
+
+
+## RV32C 压缩指令
+
+每条短指令必须和一条标准的 32 位 RISC-V 指令一一 对应。
+
+为了能在一系列的程序上得到良好的代码压缩效果,RISC-V 架构师精心挑选了 RVC 扩展中的指令。
+
+架构师们成功地将指令压缩到了 16 位。
+
+1. 十个常用寄存器(a0-a5,s0-s1,sp 以及 ra)访问的频率远超过其他寄存器
+2. 许多指令的写入目标是它的源操作数之一
+3. 立即数往往很小,而且有些指令比较喜欢某些特定的立即数
+
+
+
+## RV64:64 位地址指令
+
+从 32 位切换到 64 位 ISA, ISA 只添加了少数指令。
+
+指令集只添加了 32 位指令对应的字(word),双字(doubleword)和长整数(long)版本的指令,并将所有寄存器(包括 PC)扩展为 64 位。
+
+因此,RV64I 中的 sub 操作的是两个 64 位数字而不是 RV32I 中的 32 位数字。
+
+RV64 很接近 RV32 但实际上又有所不同;它添加了少量指令同时基础指令做的事情与 RV32 中稍有不同。
+
+
+
+## Privileged ISA 特权 ISA
+
+
+
+
+
+
+
+### 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 地址
+
+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 下访问,此外也可以在平台上保留一些物理地址空间。
+
+
+
+* 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 跟踪
+
+
+
+## Demo
+
+RISC-V Specs 演示代码。
+
+**Machine-Level ISA 机器级 ISA**
+
+
+
+### 环境
+
+#### 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
+```
+
+
+
+> 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
+```
+
+
+
+可以一眼看出以下信息:
+
+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