diff --git a/docs/source/pages/api/libs/stdbigos/clock.rst b/docs/source/pages/api/libs/stdbigos/clock.rst new file mode 100644 index 00000000..9342f674 --- /dev/null +++ b/docs/source/pages/api/libs/stdbigos/clock.rst @@ -0,0 +1,5 @@ +===== +Clock +===== + +.. doxygengroup:: clock diff --git a/docs/source/pages/api/libs/stdbigos/index.rst b/docs/source/pages/api/libs/stdbigos/index.rst index 94ba6004..8d51e59e 100644 --- a/docs/source/pages/api/libs/stdbigos/index.rst +++ b/docs/source/pages/api/libs/stdbigos/index.rst @@ -10,3 +10,4 @@ Stdbigos types string bitutils + clock diff --git a/include/hal/timer.h b/include/hal/timer.h new file mode 100644 index 00000000..bc790984 --- /dev/null +++ b/include/hal/timer.h @@ -0,0 +1,52 @@ +#ifndef HAL_TIMER_H +#define HAL_TIMER_H + +#include +#include + +/** + * @addtogroup hal + * @{ + */ + +/** + * @brief Read current monotonic hardware timer value. + * @return Current hardware timer tick value. + */ +u64 hal_timer_now(void); + +/** + * @brief Program the next timer interrupt deadline. + * + * @param deadline Absolute timer value when the interrupt should fire. + * @retval ERR_NONE Success + * @retval ERR_BAD_ARG Invalid deadline argument + * @retval ERR_NOT_IMPLEMENTED Timer SBI extension unsupported + * @retval ERR_NOT_VALID Other platform/firmware failure + */ +error_t hal_timer_arm_absolute(u64 deadline); + +/** + * @brief Program next timer interrupt relative to current time. + * + * @param delta Timer ticks from now + * @retval ERR_NONE Success + * @retval ERR_BAD_ARG Invalid delta argument + * @retval ERR_NOT_IMPLEMENTED Timer SBI extension unsupported + * @retval ERR_NOT_VALID Other platform/firmware failure + */ +error_t hal_timer_arm_relative(u64 delta); + +/** + * @brief Enable supervisor timer interrupts. + */ +void hal_timer_enable_interrupts(void); + +/** + * @brief Disable supervisor timer interrupts. + */ +void hal_timer_disable_interrupts(void); + +/// @} + +#endif // !HAL_TIMER_H diff --git a/include/stdbigos/clock.h b/include/stdbigos/clock.h new file mode 100644 index 00000000..87fdd536 --- /dev/null +++ b/include/stdbigos/clock.h @@ -0,0 +1,91 @@ +#ifndef STDBIGOS_CLOCK +#define STDBIGOS_CLOCK + +#include "error.h" +#include "types.h" + +/** + * @file include/stdbigos/clock.h + * @brief Public periodic clock API used by the scheduler and examples. + * + * The periodic clock provides a monotonic time source measured in + * architecture-specific ticks and supports scheduling a "next switch" + * tick for the scheduler to observe. + * + * @addtogroup stdbigos + * @{ + * @addtogroup clock + * @{ + */ + +/** + * @brief Initialize the periodic clock subsystem. + * + * Initialize internal state and program the first timer deadline using + * the provided tick quantum. + * + * @param tick_quantum Number of hardware timer ticks per software tick. + * @return error_t E_OK on success, otherwise an error code. + */ +error_t clock_init(u64 tick_quantum); + +/** + * @brief Return the current time in hardware timer ticks. + * + * This returns a monotonic timestamp suitable for computing deadlines. + * + * @return Current time in hardware ticks. + */ +u64 clock_now(void); + +/** + * @brief Return the number of software ticks since boot. + * + * Software ticks are incremented by the timer IRQ handler and advance + * at a rate of one per `tick_quantum` hardware ticks. + * + * @return Number of elapsed software ticks. + */ +u64 clock_ticks_now(void); + +/** + * @brief Request the scheduler be woken in a number of software ticks. + * + * Programs the next switch tick to be `ticks_from_now` software ticks + * in the future. This does not block; use `clock_next_switch_tick` + * or `clock_ticks_to_next_switch` to query the scheduled value. + * + * @param ticks_from_now Number of software ticks from now to schedule. + * @return error_t E_OK on success, otherwise an error code. + */ +error_t clock_set_next_switch_in(u64 ticks_from_now); + +/** + * @brief Obtain the currently scheduled next-switch absolute tick. + * + * Writes the absolute software tick value at which the scheduler has + * requested a switch into `out_tick`. + * + * @param out_tick Pointer to u64 to receive the absolute tick value. + * @return error_t E_OK on success, EINVAL if `out_tick` is NULL, or + * other error codes on failure. + */ +error_t clock_next_switch_tick(u64* out_tick); + +/** + * @brief Obtain the number of software ticks until the next switch. + * + * Writes into `out_ticks` the number of ticks remaining until the + * scheduled next-switch tick. If a next-switch is not scheduled, + * behavior is implementation-defined. + * + * @param out_ticks Pointer to u64 to receive remaining ticks. + * @return error_t E_OK on success, EINVAL if `out_ticks` is NULL, or + * other error codes on failure. + */ +error_t clock_ticks_to_next_switch(u64* out_ticks); + +/** @} */ /* end group clock */ +/** @} */ /* end group stdbigos */ + +#endif // !STDBIGOS_CLOCK diff --git a/include/stdbigos/sbi_utils.h b/include/stdbigos/sbi_utils.h new file mode 100644 index 00000000..9b8ef191 --- /dev/null +++ b/include/stdbigos/sbi_utils.h @@ -0,0 +1,16 @@ +#ifndef _STDBIGOS_SBI_UTILS_H_ +#define _STDBIGOS_SBI_UTILS_H_ + +#include "error.h" +#include "sbi.h" + +static inline error_t sbi_map_error(sbi_error_t err) { + switch (err) { + case SBI_SUCCESS: return ERR_NONE; + case SBI_ERR_INVALID_PARAM: return ERR_BAD_ARG; + case SBI_ERR_NOT_SUPPORTED: return ERR_NOT_IMPLEMENTED; + default: return ERR_NOT_VALID; + } +} + +#endif // !_STDBIGOS_SBI_UTILS_H_ diff --git a/src/example_sbi/CMakeLists.txt b/src/example_sbi/CMakeLists.txt index 5612d64e..b26491e6 100644 --- a/src/example_sbi/CMakeLists.txt +++ b/src/example_sbi/CMakeLists.txt @@ -1,5 +1,5 @@ SETUP_EXECUTABLE(example_sbi) -target_link_libraries(example_sbi PRIVATE startup stdbigos) +target_link_libraries(example_sbi PRIVATE startup stdbigos hal) ADD_QEMU_TARGET(example_sbi) diff --git a/src/example_sbi/entry.c b/src/example_sbi/entry.c index 53fe6f0e..7d9c95c3 100644 --- a/src/example_sbi/entry.c +++ b/src/example_sbi/entry.c @@ -1,3 +1,6 @@ +#include +#include +#include #include #include @@ -6,5 +9,31 @@ static void sbi_puts(const char* str) { } void main([[maybe_unused]] u32 hartid, [[maybe_unused]] const void* fdt) { - sbi_puts("Hello world\n"); + if (hal_trap_init() != ERR_NONE) { + sbi_puts("trap init failed\n"); + return; + } + + error_t err = clock_init(50000llu); + if (err != ERR_NONE) { + sbi_puts("clock init failed\n"); + return; + } + + hal_timer_enable_interrupts(); + + sbi_puts("clock started\n"); + + u64 last_tick = clock_ticks_now(); + while (true) { + hal_wait_for_interrupt(); + + u64 now_tick = clock_ticks_now(); + if (now_tick != last_tick) { + if ((now_tick % 100) == 0) { + sbi_puts("tick periodic\n"); + } + last_tick = now_tick; + } + } } diff --git a/src/lib/hal/arch/riscv/timer.c b/src/lib/hal/arch/riscv/timer.c new file mode 100644 index 00000000..befc0cac --- /dev/null +++ b/src/lib/hal/arch/riscv/timer.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "csr.h" +#include "trap.h" + +u64 hal_timer_now(void) { + u64 now; + __asm__ volatile("rdtime %0" : "=r"(now)::"memory"); + return now; +} + +error_t hal_timer_arm_absolute(u64 deadline) { + if (deadline == 0) { + return ERR_BAD_ARG; + } + + struct sbiret ret = sbi_set_timer(deadline); + if (ret.error == SBI_ERR_NOT_SUPPORTED) { + ret = sbi_legacy_set_timer(deadline); + } + + return sbi_map_error(ret.error); +} + +error_t hal_timer_arm_relative(u64 delta) { + if (delta == 0) { + return ERR_BAD_ARG; + } + + return hal_timer_arm_absolute(hal_timer_now() + delta); +} + +void hal_timer_enable_interrupts(void) { + CSR_SET(sie, 1ul << HAL_RISCV_TRAP_INT_S_TIMER); +} + +void hal_timer_disable_interrupts(void) { + CSR_CLEAR(sie, 1ul << HAL_RISCV_TRAP_INT_S_TIMER); +} diff --git a/src/lib/stdbigos/clock.c b/src/lib/stdbigos/clock.c new file mode 100644 index 00000000..a64273957d --- /dev/null +++ b/src/lib/stdbigos/clock.c @@ -0,0 +1,107 @@ +#include +#include +#include + +static u64 g_tick_quantum; +static u64 g_next_deadline; +static u64 g_ticks; +static u64 g_next_switch_tick; + +static error_t clock_program_timer(u64 deadline) { + error_t err = hal_timer_arm_absolute(deadline); + if (err != ERR_NONE) { + return err; + } + + return ERR_NONE; +} + +static error_t clock_schedule_next_deadline(void) { + if (g_tick_quantum == 0) { + return ERR_NOT_INITIALIZED; + } + + g_next_deadline = clock_now() + g_tick_quantum; + return clock_program_timer(g_next_deadline); +} + +static void clock_timer_irq_handler(void) { + ++g_ticks; + (void)clock_schedule_next_deadline(); +} + +u64 clock_now(void) { + return hal_timer_now(); +} + +u64 clock_ticks_now(void) { + return g_ticks; +} + +error_t clock_init(u64 tick_quantum) { + if (tick_quantum == 0) { + return ERR_BAD_ARG; + } + + error_t err = hal_trap_register_timer_handler(clock_timer_irq_handler); + if (err != ERR_NONE) { + return err; + } + + g_ticks = 0; + g_tick_quantum = tick_quantum; + g_next_switch_tick = 0; + return clock_schedule_next_deadline(); +} + +error_t clock_set_next_switch_in(u64 ticks_from_now) { + if (g_tick_quantum == 0) { + return ERR_NOT_INITIALIZED; + } + + if (ticks_from_now == 0) { + return ERR_BAD_ARG; + } + + g_next_switch_tick = g_ticks + ticks_from_now; + return ERR_NONE; +} + +error_t clock_next_switch_tick(u64* out_tick) { + if (out_tick == NULL) { + return ERR_BAD_ARG; + } + + if (g_tick_quantum == 0) { + return ERR_NOT_INITIALIZED; + } + + if (g_next_switch_tick == 0) { + return ERR_NOT_FOUND; + } + + *out_tick = g_next_switch_tick; + return ERR_NONE; +} + +error_t clock_ticks_to_next_switch(u64* out_ticks) { + if (out_ticks == NULL) { + return ERR_BAD_ARG; + } + + if (g_tick_quantum == 0) { + return ERR_NOT_INITIALIZED; + } + + if (g_next_switch_tick == 0) { + return ERR_NOT_FOUND; + } + + if (g_ticks >= g_next_switch_tick) { + *out_ticks = 0; + return ERR_NONE; + } + + *out_ticks = g_next_switch_tick - g_ticks; + return ERR_NONE; +}