diff --git a/SUMMARY.md b/SUMMARY.md index ddc316d63b247292c85f90e1a499006ec1b8d207..d24012c48047bff6a0f6fc473a0ccfdb8bd6ec9e 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -12,7 +12,7 @@ - [启动流程](programing-manual/base/start.md) - [内存管理](programing-manual/mm/mm.md) - [buddy伙伴算法](programing-manual/mm/buddy.md) - - [heapCache虚拟内存管理](programing-manual/mm/heap-cache.md) + - [heapCache虚拟内存管理](programing-manual/mm/heap.md) - [vmspace虚拟地址空间](programing-manual/mm/vmspace.md) - [调度管理](programing-manual/sched/sched.md) - [线程管理](programing-manual/sched/thread.md) @@ -22,9 +22,10 @@ - [中断系统](programing-manual/interrupt/irq.md) - [延迟队列](programing-manual/interrupt/delay_queue.md) - [定时器](programing-manual/timer/timer.md) - - [驱动框架](programing-manual/driver/framework.md) - - [单元测试](programing-manual/test/utest.md) + - 内核移植 + - [riscv架构移植](programing-manual/port/riscv/riscv.md) + - 用户接口 - [API设计](programing-manual/user/api.md) @@ -34,4 +35,3 @@ - [菜单配置](tutorial/menuconfig.md) - [工具链](tutorial/toolchains.md) - [单元测试](tutorial/utest.md) - \ No newline at end of file diff --git a/book.json b/book.json new file mode 100644 index 0000000000000000000000000000000000000000..07b3917352bc452fef9a94295d37e49e127820bd --- /dev/null +++ b/book.json @@ -0,0 +1,22 @@ +{ + "title": "book for nxos", + "author": "nxos development team", + "description": "document for nxos kernel", + "language": "zh-hans", + "gitbook": "3.2.3", + "links": { + "sidebar": { + "bookos": "http://book-os.org" + } + }, + "plugins": [ + "tbfed-pagefooter" + ], + "pluginsConfig": { + "tbfed-pagefooter": { + "copyright":"Copyright © BookOS-developers 2022", + "modify_label": "修订时间:", + "modify_format": "YYYY-MM-DD" + } + } +} \ No newline at end of file diff --git a/programing-manual/port/riscv/figures/call.png b/programing-manual/port/riscv/figures/call.png new file mode 100644 index 0000000000000000000000000000000000000000..e2c4e4b00443d858f97bf98b20cdf1414ccbf42a Binary files /dev/null and b/programing-manual/port/riscv/figures/call.png differ diff --git a/programing-manual/port/riscv/figures/extension.png b/programing-manual/port/riscv/figures/extension.png new file mode 100644 index 0000000000000000000000000000000000000000..c8066515c213786f25dd118285312745ae587dfa Binary files /dev/null and b/programing-manual/port/riscv/figures/extension.png differ diff --git a/programing-manual/port/riscv/figures/interrupt_class.png b/programing-manual/port/riscv/figures/interrupt_class.png new file mode 100644 index 0000000000000000000000000000000000000000..ba270c0cb1ec4c690026182d76c3cab9857a7797 Binary files /dev/null and b/programing-manual/port/riscv/figures/interrupt_class.png differ diff --git a/programing-manual/port/riscv/figures/mstatus(rv32).jpg b/programing-manual/port/riscv/figures/mstatus(rv32).jpg new file mode 100644 index 0000000000000000000000000000000000000000..f2f2b7d28cc49cf33525180f58a78be235d165d2 Binary files /dev/null and b/programing-manual/port/riscv/figures/mstatus(rv32).jpg differ diff --git a/programing-manual/port/riscv/figures/mstatus(rv64).jpg b/programing-manual/port/riscv/figures/mstatus(rv64).jpg new file mode 100644 index 0000000000000000000000000000000000000000..59e2b0df9aa7bbd38c6d4913c04758bd63cf0e2d Binary files /dev/null and b/programing-manual/port/riscv/figures/mstatus(rv64).jpg differ diff --git a/programing-manual/port/riscv/figures/privilege_level.png b/programing-manual/port/riscv/figures/privilege_level.png new file mode 100644 index 0000000000000000000000000000000000000000..c06dae4dd975e692c5f29c367de64815dcb3b91e Binary files /dev/null and b/programing-manual/port/riscv/figures/privilege_level.png differ diff --git a/programing-manual/port/riscv/figures/register.png b/programing-manual/port/riscv/figures/register.png new file mode 100644 index 0000000000000000000000000000000000000000..1ef4dfaf5d7e8191f278c04a1ce61d9ae8dce30f Binary files /dev/null and b/programing-manual/port/riscv/figures/register.png differ diff --git a/programing-manual/port/riscv/figures/satp.png b/programing-manual/port/riscv/figures/satp.png new file mode 100644 index 0000000000000000000000000000000000000000..74e11c99d375dc2e5c74c121c357b67d5060dcdc Binary files /dev/null and b/programing-manual/port/riscv/figures/satp.png differ diff --git a/programing-manual/port/riscv/riscv.md b/programing-manual/port/riscv/riscv.md new file mode 100644 index 0000000000000000000000000000000000000000..54979fb938ffadd246a2b29492d2b10f84418aaa --- /dev/null +++ b/programing-manual/port/riscv/riscv.md @@ -0,0 +1,1219 @@ +# RISC-V架构移植 + +## 一、描述 + +RISC-V(发音为“risk-five”)是一个基于精简指令集(RISC)原则的开源指令集架构(ISA)。 + +与大多数指令集相比,RISC-V指令集可以自由地用于任何目的,允许任何人设计、制造和销售RISC-V芯片和软件。虽然这不是第一个开源指令集,但它具有重要意义,因为其设计使其适用于现代计算设备(如仓库规模云计算机、高端移动电话和微小嵌入式系统)。设计者考虑到了这些用途中的性能与功率效率。该指令集还具有众多支持的软件,这解决了新指令集通常的弱点。 + +该项目2010年始于加州大学伯克利分校,但许多贡献者是该大学以外的志愿者和行业工作者。 +RISC-V指令集的设计考虑了小型、快速、低功耗的现实情况来实做,但并没有对特定的微架构做过度的设计。 + +截至2017年5月,RISC-V已经确立了版本2.22的用户空间的指令集(userspace ISA),而特权指令集(privileged ISA)也处在草案版本1.10。 + +(以上摘自百度百科) + +## 二、架构简述 + +### 1. 指令集 + +RISC-V被设计成可扩展的,可以配置的,根据不同的应用场景可以生产带有不同功能扩展的处理器。 + +![特权级](figures/extension.png) + +### 2. 特权模式 + +RISC-V目前有4种特权模式(`Machine, Hypervisor,Supervisor, User`)。 + +机器模式用于固件程序代码执行,例如 `OpenSBI`, `RustSBI` 等,但是在 `rtos` 和裸机开发种,很多时候都是直接使用机器模式。 + +`Hypervisor` 虚拟化模式用于使用虚拟化的场景,目前还在开发阶段。 + +操作系统更多运行在 `Supervisor` 监管者模式,这样可以和 `User` 更贴近,也不用管太多机器模式需要做的事情。通常, `OpenSBI` 和 `RustSBI` 初始化完成后,会跳转到 `Supervisor` 模式,进入内核。内核只需要链接到 `SBI` 指定的地址就行了。使用 `SBI` 的好处是,可以直接使用串口的输入输出,很方便调试,不用自己从机器模式跳转到 `Supervisor` 模式,降低开发的复杂度。 + +`User` 用户模式就是应用程序执行的模式,需要通过系统调用进入到 `Supervisor` 模式去调用内核提供的服务。 + +![特权级](figures/privilege_level.png) + +### 3. 寄存器 + +RISCV(RV32)具有32个整数寄存器组(取名为:x0~x31),其中31个是通用寄存器(x1~x31),它们存储整数数值,寄存器x0是硬件连线的常数0。当你设计的RISCV架构处理器实现了浮点扩展时,还必须有32个浮点寄存器f0~f31。对于RV32,其x寄存器是32位宽度的,XLEN=32,对于RV64,它们是64位宽度的,XLEN=64。 + +![寄存器](figures/register.png) +![调用约定](figures/call.png) + +特权级寄存器的命名规则,特权模式+寄存器名字,表示只能在该特权级使用。比如status寄存器,在机器模式有mstatus,监管模式有sstatus。 + +![机器模式状态寄存器(rv64)](figures/mstatus(rv64).jpg) +上图为机器模式状态寄存器(rv64) + +![机器模式状态寄存器(rv32)](figures/mstatus(rv32).jpg) +上图为机器模式状态寄存器(rv32) + +### 3. 中断管理 + +RISC-V中断分为两种类型,一种是同步中断,即ECALL、EBREAK等指令所产生的中断,另一种是异步中断,即GPIO、UART等外设产生的中断。 + +RISC-V的中断管理由处理器核局部中断CLINT(CoreLocalInterrupt)和平台级中断控制器PLIC(PlatformLevelInterruptController)组成。CLINT分为软件中断核计时器中断,负责响应处理器的异常核和计时器中断。PLIC负责处理外设的中断。 + +![中断类型](figures/interrupt_class.png) + +中断表地址需要写入管理者模式的向量基址寄存器 `STVEC` ,产生中断后,就去该寄存器保存的中断向量表地址找中断号对应的中断服务。 + +`Supervisor`模式的中断的打开与关闭是由状态寄存器 `SSTATUS` 中的 `SIE` 控制的,置为1则表示使能中断,置为0则表示禁用中断。 + +### 4. MMU内存管理单元 + +RISC-V的MMU支持多种模式,有Sv32/Sv39/Sv48/Sv57/Sv64等。不同的模式映射的页面等级,页面大小是有差异的。在64位处理器种最常用的是Sv39,它是3级4KB页面大小映射。 + +监管者模式的页表的地址是由地址转换寄存器 `SATP` 保存的,其格式如下: +![地址转换](figures/satp.png) + +具体字段的解析如下: + +* Mode - MMU 地址翻译模式 + +|Value |Name |Description| +| ------- | ---------- | ---------- | +|0 |Bare |No translation or protection| +|1-7| - |Reserved| +|8 |Sv39 |Page-based 39-bit virtual addressing| +|9 |Sv48 |Page-based 48-bit virtual addressing| +|10 |Sv57 |Reserved for page-based 57-bit virtual addressing| +|11 |Sv64 |Reserved for page-based 64-bit virtual addressing| +|12-15| - |Reserved| + +当 Mode 为 0 时,MMU 关闭。 + +* ASID – 当前 ASID。表示当前程序的 ASID 号。 +* PPN – 硬件回填根 PPN。第一级硬件回填使用的 PPN (Phsical Page Number)。 + +## 三、代码移植 + +移植一个新的平台需要实现如下内容: +|内容 |描述 | +| ------- | ---------- | +| 内核入口 | 不同的架构内核入口代码有差异,需要根据架构来实现 | +| 平台初始化 | 调用平台的初始化代码 | +| 原子操作 | 原子操作用于对数据的加减运算不会被打断,以及一些原子级别的数据交换等 | +| 内存屏障 | 内存屏障前的所有读写操作完成后才能执行屏障后的读写操作 | +| 计时器时钟 | 计时器时钟用于驱动多线程的切换和内核定时器 | +| 线程上下文 | 上下文切换是多线程实现的根基 | +| 中断管理 | 中断管理接口对内核以及驱动都是至关重要的 | +| MMU虚拟内存管理 | 虚拟地址的实现和访问,MMU的切换,映射和解除页面映射 | +| 进程管理 | 进程管理相关操作是实现进程的必要条件 | +| SMP多核 | 对处理器多核的启动和初始化 | + +所有需要对接的接口都存放在 `src/arch/riscv64/port` 目录下面,新的平台只需要支持这些接口的功能即可。 + +### 1. 内核入口 + +在 `nxos` 中,使用了 `OpenSBI` 和 `RustSBI` 作为机器模式的固件,`qemu_riscv64`平台选择了 `OpenSBI` ,其内核的入口地址为 `0x80200000`, +`k210` 选择了 `RustSBI`,其内核的入口地址为 `0x80020000`, `RustSBI` 针对 `k210` 做了一些兼容,使用起来比较简单。 + +在 `SBI` 执行结束后,会从机器模式跳转到位于监护者模式的内核中,并且寄存器 `a0` 记录了当前处理器核心的 `id` ,可以根据寄存器的值来判断处理器 `id`。 + +入口地址在链接脚本中写的是 `_Start` 这个符号,因此会进入 `sbi_entry.S` 的 `_Start` 执行。 + +* 文件:src/arch/riscv64/kernel/sbi_entry.S +```c +/** + * Copyright (c) 2018-2022, NXOS Development Team + * SPDX-License-Identifier: Apache-2.0 + * + * Contains: Riscv64 entry + * + * Change Logs: + * Date Author Notes + * 2021-10-1 JasonHu Init + */ + + .section .text.start + .extern NX_Main + + .globl CPU_StackTop0 + .globl CPU_StackTop1 + .globl CPU_StackTop2 + .globl CPU_StackTop3 + + .global _Start +_Start: + li t0, 0 + beq a0, t0, _SetSP0 + li t0, 1 + beq a0, t0, _SetSP1 + li t0, 2 + beq a0, t0, _SetSP2 + li t0, 3 + beq a0, t0, _SetSP3 + + j _EnterMain + +_SetSP0: + la sp, CPU_StackTop0 + j _EnterMain + +_SetSP1: + la sp, CPU_StackTop1 + j _EnterMain + +_SetSP2: + la sp, CPU_StackTop2 + j _EnterMain + +_SetSP3: + la sp, CPU_StackTop3 + j _EnterMain + +_EnterMain: + csrw sscratch, sp /* first set sscrach as cpu stack here */ + csrc sstatus, 0x2 /* disable interrupt */ + call NX_Main + +loop: + j loop + + /* set in data seciton, avoid clear bss to clean stack */ + .section .data.stack + .align 12 + +CPU_Stack0: + .space 8192 +CPU_StackTop0: + +CPU_Stack1: + .space 8192 +CPU_StackTop1: + +CoreStack2: + .space 8192 +CPU_StackTop2: + +CoreStack3: + .space 8192 +CPU_StackTop3: +``` + +该代码简单地设置了栈就进入了 `NX_Main` 执行,那么入口程序的移植就完成了。 + +### 2. 平台初始化 + +在平台初始化 `NX_HalPlatformInit` 中,进行初始化,初始化完串口后,就能使用打印功能了。 +然后再初始化中断,初始化物理内存管理。 + +在 `NX_HalPlatformStage2`中,可以使用内核的功能,内存分配,中断注册等。 +因此 `NX_HalDirectUartStage2` 就注册了串口中断,可以接受输入。 + +* 文件:src/platform/qemu_riscv64/hal/init.c +```c +/** + * Copyright (c) 2018-2022, NXOS Development Team + * SPDX-License-Identifier: Apache-2.0 + * + * Contains: Init Riscv64 Qemu platfrom + * + * Change Logs: + * Date Author Notes + * 2021-10-1 JasonHu Init + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NX_LOG_LEVEL NX_LOG_INFO +#define NX_LOG_NAME "INIT" +#include + +NX_INTERFACE NX_Error NX_HalPlatformInit(NX_UArch coreId) +{ + NX_HalClearBSS(); + + /* NOTE: init trap first before do anything */ + CPU_InitTrap(coreId); + + NX_HalDirectUartInit(); + + sbi_init(); + sbi_print_version(); + + NX_LOG_I("Hello, QEMU Riscv64!"); + + PLIC_Init(NX_True); + + NX_HalPageZoneInit(); + + return NX_EOK; +} + +NX_INTERFACE NX_Error NX_HalPlatformStage2(void) +{ + NX_LOG_I("stage2!"); + + NX_HalDirectUartStage2(); + + return NX_EOK; +} +``` + +串口可以配置为调用 `SBI` 实现输出,也可以通过自己操作寄存器实现。在早期,调用 `SBI` 串口来输出是非常方便的。 +实现 `NX_HalConsoleOutChar` 函数后,就可以使用 `NX_Printf` 和 `NX_LOG_*` 函数来打印消息。 + +* 文件:src/platform/qemu_riscv64/drivers/direct_uart.c +```c +/** + * Copyright (c) 2018-2022, NXOS Development Team + * SPDX-License-Identifier: Apache-2.0 + * + * Contains: Direct uart driver + * low-level driver routines for 16550a UART. + * + * Change Logs: + * Date Author Notes + * 2021-10-1 JasonHu Init + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_NX_UART0_FROM_SBI +#include +#endif + +#include + +// the UART control registers. +// some have different meanings for +// read vs write. +// see http://byterunner.com/16550.html +#define RHR 0 // receive holding register (for input bytes) +#define THR 0 // transmit holding register (for output bytes) +#define IER 1 // interrupt enable register +#define IER_RX_ENABLE (1 << 0) // receiver ready interrupt. +#define IER_TX_ENABLE (1 << 1) // transmitter empty interrupt. +#define FCR 2 // FIFO control register +#define FCR_FIFO_ENABLE (1 << 0) +#define FCR_FIFO_CLEAR (3 << 1) // clear the content of the two FIFOs +#define ISR 2 // interrupt status register +#define LCR 3 // line control register +#define LCR_EIGHT_BITS (3 << 0) +#define LCR_BAUD_LATCH (1 << 7) // special mode to set baud rate +#define LSR 5 // line status register +#define LSR_RX_READY (1 << 0) // input is waiting to be read from RHR +#define LSR_TX_IDLE (1 << 5) // THR can accept another character to send + +void NX_HalDirectUartPutc(char ch) +{ +#ifdef CONFIG_NX_UART0_FROM_SBI + sbi_console_putchar(ch); +#else + if ((Read8(UART0_PHY_ADDR + LSR) & LSR_TX_IDLE) == 0) + { + // the UART transmit holding register is full, + return; + } + Write8(UART0_PHY_ADDR + THR, ch); +#endif +} + +int NX_HalDirectUartGetc(void) +{ +#ifdef CONFIG_NX_UART0_FROM_SBI + return sbi_console_getchar(); +#else + if (Read8(UART0_PHY_ADDR + LSR) & 0x01) + { + // input data is ready. + return Read8(UART0_PHY_ADDR + RHR); + } + else + { + return -1; + } +#endif +} + +NX_INTERFACE void NX_HalConsoleOutChar(char ch) +{ + NX_HalDirectUartPutc(ch); +} + +void NX_HalDirectUartInit(void) +{ + // disable interrupts. + Write8(UART0_PHY_ADDR + IER, 0x00); + // special mode to set baud rate. + Write8(UART0_PHY_ADDR + LCR, LCR_BAUD_LATCH); + // LSB for baud rate of 115.2K. + Write8(UART0_PHY_ADDR + 0, 0x01); + // MSB for baud rate of 115.2K. + Write8(UART0_PHY_ADDR + 1, 0x00); + // leave set-baud mode, + // and set word length to 8 bits, no parity. + Write8(UART0_PHY_ADDR + LCR, LCR_EIGHT_BITS); + // reset and enable FIFOs. + Write8(UART0_PHY_ADDR + FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); +} + +/** + * default handler +*/ +NX_WEAK_SYM void NX_HalDirectUartGetcHandler(char data) +{ + NX_LOG_I("Deafult uart handler:%x/%c\n", data, data); +} + +NX_PRIVATE NX_Error UartIrqHandler(NX_IRQ_Number irqno, void *arg) +{ + int data = NX_HalDirectUartGetc(); + if (data != -1) + { + if (NX_HalDirectUartGetcHandler != NX_NULL) + { + NX_HalDirectUartGetcHandler(data); + } + } + return data != -1 ? NX_EOK : NX_EIO; +} + +void NX_HalDirectUartStage2(void) +{ + /* enable receive interrupts. */ + Write8(UART0_PHY_ADDR + IER, IER_RX_ENABLE); + + NX_ASSERT(NX_IRQ_Bind(UART0_IRQ, UartIrqHandler, NX_NULL, "Uart", 0) == NX_EOK); + NX_ASSERT(NX_IRQ_Unmask(UART0_IRQ) == NX_EOK); +} +``` + +### 3. 原子操作 + +原子操作需要实现对数据的原子设置,获取,加法,减法,数据交换等。由于 `gcc` 内置了原子操作的函数, +这里只需要调用 `gcc` 内置的原子操作即可。 + +* 文件:src/arch/riscv64/port/atomic.c +```c +/** + * Copyright (c) 2018-2022, NXOS Development Team + * SPDX-License-Identifier: Apache-2.0 + * + * Contains: HAL NX_Atomic + * + * Change Logs: + * Date Author Notes + * 2021-12-1 JasonHu Init + */ + +#include + +NX_PRIVATE void NX_HalAtomicSet(NX_Atomic *atomic, long value) +{ + atomic->value = value; +} + +NX_PRIVATE long NX_HalAtomicGet(NX_Atomic *atomic) +{ + return atomic->value; +} + +NX_PRIVATE void NX_HalAtomicAdd(NX_Atomic *atomic, long value) +{ + /* gcc build-in functions */ + __sync_fetch_and_add(&atomic->value, value); +} + +NX_PRIVATE void NX_HalAtomicSub(NX_Atomic *atomic, long value) +{ + __sync_fetch_and_sub(&atomic->value, value); +} + +NX_PRIVATE void NX_HalAtomicInc(NX_Atomic *atomic) +{ + __sync_fetch_and_add(&atomic->value, 1); +} + +NX_PRIVATE void NX_HalAtomicDec(NX_Atomic *atomic) +{ + __sync_fetch_and_sub(&atomic->value, 1); +} + +NX_PRIVATE void NX_HalAtomicSetMask(NX_Atomic *atomic, long mask) +{ + __sync_fetch_and_or(&atomic->value, mask); +} + +NX_PRIVATE void NX_HalAtomicClearMask(NX_Atomic *atomic, long mask) +{ + __sync_fetch_and_and(&atomic->value, ~mask); +} + +NX_PRIVATE long NX_HalAtomicSwap(NX_Atomic *atomic, long newValue) +{ + return __sync_lock_test_and_set(&((atomic)->value), newValue); +} + +NX_PRIVATE long NX_HalAtomicCAS(NX_Atomic *atomic, long old, long newValue) +{ + return __sync_val_compare_and_swap(&atomic->value, old, newValue); +} + +NX_INTERFACE struct NX_AtomicOps NX_AtomicOpsInterface = +{ + .set = NX_HalAtomicSet, + .get = NX_HalAtomicGet, + .add = NX_HalAtomicAdd, + .sub = NX_HalAtomicSub, + .inc = NX_HalAtomicInc, + .dec = NX_HalAtomicDec, + .setMask = NX_HalAtomicSetMask, + .clearMask = NX_HalAtomicClearMask, + .swap = NX_HalAtomicSwap, + .cas = NX_HalAtomicCAS, +}; +``` + +### 4. 内存屏障 + +在 `risc-v` 架构中,内存全(读和写)屏障使用 `gcc` 内置函数 `__sync_synchronize` 实现, +内存读/写屏障使用 `fence` 指令去实现。内存指令屏障使用 `fence.i` 指令实现。 + +* 文件:src/arch/riscv64/port/barrier.c +```c +/** + * Copyright (c) 2018-2022, NXOS Development Team + * SPDX-License-Identifier: Apache-2.0 + * + * Contains: HAL Memory Barrier + * + * Change Logs: + * Date Author Notes + * 2021-12-9 JasonHu Init + */ + +#include + +NX_PRIVATE void NX_HalMemBarrier(void) +{ + __sync_synchronize(); +} + +NX_PRIVATE void NX_HalMemBarrierRead(void) +{ + NX_CASM("fence":::"memory"); +} + +NX_PRIVATE void NX_HalMemBarrierWrite(void) +{ + NX_CASM("fence":::"memory"); +} + +NX_PRIVATE void NX_HalMemBarrierInstruction(void) +{ + NX_CASM("fence.i":::"memory"); +} + +NX_INTERFACE struct NX_MemBarrierOps NX_MemBarrierOpsInterface = +{ + .barrier = NX_HalMemBarrier, + .barrierRead = NX_HalMemBarrierRead, + .barrierWrite = NX_HalMemBarrierWrite, + .barrierInstruction = NX_HalMemBarrierInstruction, +}; +``` + +### 5. 计时器时钟 + +硬件时钟会按照一定的频率去增长当前定时器寄存器的值,在 `qemu` 中这个频率是 `10MHZ` 。 +如果当前定时器的值超过了超时定时器寄存器的值的时候,就会触发一个中断。注意:前提是打开了定时器中断。 + +在做时钟定时器初始化的时候,需要先关闭 `Supervisor` 模式的定时器中断,然后再设置超时定时器的值。 +这是操作需要通过 `SBI` 去实现,而获取操作可以直接通过 `rdtime` 指令获取。 + +由于我们想实现1秒钟产生 `NX_TICKS_PER_SECOND` (一般在100~1000)次定时器中断的效果, +所以需要计算出产生一次中断,需要间隔多少个`tick`即可。 + +当超时后,就会产生一个中断,于是就设置下一个中断产生时的超时值。 + +* 文件:src/arch/riscv64/port/clock.c +```c +/** + * Copyright (c) 2018-2022, NXOS Development Team + * SPDX-License-Identifier: Apache-2.0 + * + * Contains: Clock for system + * + * Change Logs: + * Date Author Notes + * 2021-10-16 JasonHu Init + */ + +#include