Skip to content

Commit e407733

Browse files
authored
Implement clock controller (#93)
1 parent d8c485c commit e407733

9 files changed

Lines changed: 344 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=====
2+
Clock
3+
=====
4+
5+
.. doxygengroup:: clock

docs/source/pages/api/libs/stdbigos/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ Stdbigos
1010
types
1111
string
1212
bitutils
13+
clock

include/hal/timer.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#ifndef HAL_TIMER_H
2+
#define HAL_TIMER_H
3+
4+
#include <stdbigos/error.h>
5+
#include <stdbigos/types.h>
6+
7+
/**
8+
* @addtogroup hal
9+
* @{
10+
*/
11+
12+
/**
13+
* @brief Read current monotonic hardware timer value.
14+
* @return Current hardware timer tick value.
15+
*/
16+
u64 hal_timer_now(void);
17+
18+
/**
19+
* @brief Program the next timer interrupt deadline.
20+
*
21+
* @param deadline Absolute timer value when the interrupt should fire.
22+
* @retval ERR_NONE Success
23+
* @retval ERR_BAD_ARG Invalid deadline argument
24+
* @retval ERR_NOT_IMPLEMENTED Timer SBI extension unsupported
25+
* @retval ERR_NOT_VALID Other platform/firmware failure
26+
*/
27+
error_t hal_timer_arm_absolute(u64 deadline);
28+
29+
/**
30+
* @brief Program next timer interrupt relative to current time.
31+
*
32+
* @param delta Timer ticks from now
33+
* @retval ERR_NONE Success
34+
* @retval ERR_BAD_ARG Invalid delta argument
35+
* @retval ERR_NOT_IMPLEMENTED Timer SBI extension unsupported
36+
* @retval ERR_NOT_VALID Other platform/firmware failure
37+
*/
38+
error_t hal_timer_arm_relative(u64 delta);
39+
40+
/**
41+
* @brief Enable supervisor timer interrupts.
42+
*/
43+
void hal_timer_enable_interrupts(void);
44+
45+
/**
46+
* @brief Disable supervisor timer interrupts.
47+
*/
48+
void hal_timer_disable_interrupts(void);
49+
50+
/// @}
51+
52+
#endif // !HAL_TIMER_H

include/stdbigos/clock.h

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#ifndef STDBIGOS_CLOCK
2+
#define STDBIGOS_CLOCK
3+
4+
#include "error.h"
5+
#include "types.h"
6+
7+
/**
8+
* @file include/stdbigos/clock.h
9+
* @brief Public periodic clock API used by the scheduler and examples.
10+
*
11+
* The periodic clock provides a monotonic time source measured in
12+
* architecture-specific ticks and supports scheduling a "next switch"
13+
* tick for the scheduler to observe.
14+
*
15+
* @addtogroup stdbigos
16+
* @{
17+
* @addtogroup clock
18+
* @{
19+
*/
20+
21+
/**
22+
* @brief Initialize the periodic clock subsystem.
23+
*
24+
* Initialize internal state and program the first timer deadline using
25+
* the provided tick quantum.
26+
*
27+
* @param tick_quantum Number of hardware timer ticks per software tick.
28+
* @return error_t E_OK on success, otherwise an error code.
29+
*/
30+
error_t clock_init(u64 tick_quantum);
31+
32+
/**
33+
* @brief Return the current time in hardware timer ticks.
34+
*
35+
* This returns a monotonic timestamp suitable for computing deadlines.
36+
*
37+
* @return Current time in hardware ticks.
38+
*/
39+
u64 clock_now(void);
40+
41+
/**
42+
* @brief Return the number of software ticks since boot.
43+
*
44+
* Software ticks are incremented by the timer IRQ handler and advance
45+
* at a rate of one per `tick_quantum` hardware ticks.
46+
*
47+
* @return Number of elapsed software ticks.
48+
*/
49+
u64 clock_ticks_now(void);
50+
51+
/**
52+
* @brief Request the scheduler be woken in a number of software ticks.
53+
*
54+
* Programs the next switch tick to be `ticks_from_now` software ticks
55+
* in the future. This does not block; use `clock_next_switch_tick`
56+
* or `clock_ticks_to_next_switch` to query the scheduled value.
57+
*
58+
* @param ticks_from_now Number of software ticks from now to schedule.
59+
* @return error_t E_OK on success, otherwise an error code.
60+
*/
61+
error_t clock_set_next_switch_in(u64 ticks_from_now);
62+
63+
/**
64+
* @brief Obtain the currently scheduled next-switch absolute tick.
65+
*
66+
* Writes the absolute software tick value at which the scheduler has
67+
* requested a switch into `out_tick`.
68+
*
69+
* @param out_tick Pointer to u64 to receive the absolute tick value.
70+
* @return error_t E_OK on success, EINVAL if `out_tick` is NULL, or
71+
* other error codes on failure.
72+
*/
73+
error_t clock_next_switch_tick(u64* out_tick);
74+
75+
/**
76+
* @brief Obtain the number of software ticks until the next switch.
77+
*
78+
* Writes into `out_ticks` the number of ticks remaining until the
79+
* scheduled next-switch tick. If a next-switch is not scheduled,
80+
* behavior is implementation-defined.
81+
*
82+
* @param out_ticks Pointer to u64 to receive remaining ticks.
83+
* @return error_t E_OK on success, EINVAL if `out_ticks` is NULL, or
84+
* other error codes on failure.
85+
*/
86+
error_t clock_ticks_to_next_switch(u64* out_ticks);
87+
88+
/** @} */ /* end group clock */
89+
/** @} */ /* end group stdbigos */
90+
91+
#endif // !STDBIGOS_CLOCK

include/stdbigos/sbi_utils.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef _STDBIGOS_SBI_UTILS_H_
2+
#define _STDBIGOS_SBI_UTILS_H_
3+
4+
#include "error.h"
5+
#include "sbi.h"
6+
7+
static inline error_t sbi_map_error(sbi_error_t err) {
8+
switch (err) {
9+
case SBI_SUCCESS: return ERR_NONE;
10+
case SBI_ERR_INVALID_PARAM: return ERR_BAD_ARG;
11+
case SBI_ERR_NOT_SUPPORTED: return ERR_NOT_IMPLEMENTED;
12+
default: return ERR_NOT_VALID;
13+
}
14+
}
15+
16+
#endif // !_STDBIGOS_SBI_UTILS_H_

src/example_sbi/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
SETUP_EXECUTABLE(example_sbi)
22

3-
target_link_libraries(example_sbi PRIVATE startup stdbigos)
3+
target_link_libraries(example_sbi PRIVATE startup stdbigos hal)
44

55
ADD_QEMU_TARGET(example_sbi)

src/example_sbi/entry.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#include <hal/timer.h>
2+
#include <hal/trap.h>
3+
#include <stdbigos/clock.h>
14
#include <stdbigos/sbi.h>
25
#include <stdbigos/types.h>
36

@@ -6,5 +9,31 @@ static void sbi_puts(const char* str) {
69
}
710

811
void main([[maybe_unused]] u32 hartid, [[maybe_unused]] const void* fdt) {
9-
sbi_puts("Hello world\n");
12+
if (hal_trap_init() != ERR_NONE) {
13+
sbi_puts("trap init failed\n");
14+
return;
15+
}
16+
17+
error_t err = clock_init(50000llu);
18+
if (err != ERR_NONE) {
19+
sbi_puts("clock init failed\n");
20+
return;
21+
}
22+
23+
hal_timer_enable_interrupts();
24+
25+
sbi_puts("clock started\n");
26+
27+
u64 last_tick = clock_ticks_now();
28+
while (true) {
29+
hal_wait_for_interrupt();
30+
31+
u64 now_tick = clock_ticks_now();
32+
if (now_tick != last_tick) {
33+
if ((now_tick % 100) == 0) {
34+
sbi_puts("tick periodic\n");
35+
}
36+
last_tick = now_tick;
37+
}
38+
}
1039
}

src/lib/hal/arch/riscv/timer.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include <hal/timer.h>
2+
#include <stdbigos/sbi.h>
3+
#include <stdbigos/sbi_utils.h>
4+
5+
#include "csr.h"
6+
#include "trap.h"
7+
8+
u64 hal_timer_now(void) {
9+
u64 now;
10+
__asm__ volatile("rdtime %0" : "=r"(now)::"memory");
11+
return now;
12+
}
13+
14+
error_t hal_timer_arm_absolute(u64 deadline) {
15+
if (deadline == 0) {
16+
return ERR_BAD_ARG;
17+
}
18+
19+
struct sbiret ret = sbi_set_timer(deadline);
20+
if (ret.error == SBI_ERR_NOT_SUPPORTED) {
21+
ret = sbi_legacy_set_timer(deadline);
22+
}
23+
24+
return sbi_map_error(ret.error);
25+
}
26+
27+
error_t hal_timer_arm_relative(u64 delta) {
28+
if (delta == 0) {
29+
return ERR_BAD_ARG;
30+
}
31+
32+
return hal_timer_arm_absolute(hal_timer_now() + delta);
33+
}
34+
35+
void hal_timer_enable_interrupts(void) {
36+
CSR_SET(sie, 1ul << HAL_RISCV_TRAP_INT_S_TIMER);
37+
}
38+
39+
void hal_timer_disable_interrupts(void) {
40+
CSR_CLEAR(sie, 1ul << HAL_RISCV_TRAP_INT_S_TIMER);
41+
}

src/lib/stdbigos/clock.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#include <hal/timer.h>
2+
#include <hal/trap.h>
3+
#include <stdbigos/clock.h>
4+
5+
static u64 g_tick_quantum;
6+
static u64 g_next_deadline;
7+
static u64 g_ticks;
8+
static u64 g_next_switch_tick;
9+
10+
static error_t clock_program_timer(u64 deadline) {
11+
error_t err = hal_timer_arm_absolute(deadline);
12+
if (err != ERR_NONE) {
13+
return err;
14+
}
15+
16+
return ERR_NONE;
17+
}
18+
19+
static error_t clock_schedule_next_deadline(void) {
20+
if (g_tick_quantum == 0) {
21+
return ERR_NOT_INITIALIZED;
22+
}
23+
24+
g_next_deadline = clock_now() + g_tick_quantum;
25+
return clock_program_timer(g_next_deadline);
26+
}
27+
28+
static void clock_timer_irq_handler(void) {
29+
++g_ticks;
30+
(void)clock_schedule_next_deadline();
31+
}
32+
33+
u64 clock_now(void) {
34+
return hal_timer_now();
35+
}
36+
37+
u64 clock_ticks_now(void) {
38+
return g_ticks;
39+
}
40+
41+
error_t clock_init(u64 tick_quantum) {
42+
if (tick_quantum == 0) {
43+
return ERR_BAD_ARG;
44+
}
45+
46+
error_t err = hal_trap_register_timer_handler(clock_timer_irq_handler);
47+
if (err != ERR_NONE) {
48+
return err;
49+
}
50+
51+
g_ticks = 0;
52+
g_tick_quantum = tick_quantum;
53+
g_next_switch_tick = 0;
54+
return clock_schedule_next_deadline();
55+
}
56+
57+
error_t clock_set_next_switch_in(u64 ticks_from_now) {
58+
if (g_tick_quantum == 0) {
59+
return ERR_NOT_INITIALIZED;
60+
}
61+
62+
if (ticks_from_now == 0) {
63+
return ERR_BAD_ARG;
64+
}
65+
66+
g_next_switch_tick = g_ticks + ticks_from_now;
67+
return ERR_NONE;
68+
}
69+
70+
error_t clock_next_switch_tick(u64* out_tick) {
71+
if (out_tick == NULL) {
72+
return ERR_BAD_ARG;
73+
}
74+
75+
if (g_tick_quantum == 0) {
76+
return ERR_NOT_INITIALIZED;
77+
}
78+
79+
if (g_next_switch_tick == 0) {
80+
return ERR_NOT_FOUND;
81+
}
82+
83+
*out_tick = g_next_switch_tick;
84+
return ERR_NONE;
85+
}
86+
87+
error_t clock_ticks_to_next_switch(u64* out_ticks) {
88+
if (out_ticks == NULL) {
89+
return ERR_BAD_ARG;
90+
}
91+
92+
if (g_tick_quantum == 0) {
93+
return ERR_NOT_INITIALIZED;
94+
}
95+
96+
if (g_next_switch_tick == 0) {
97+
return ERR_NOT_FOUND;
98+
}
99+
100+
if (g_ticks >= g_next_switch_tick) {
101+
*out_ticks = 0;
102+
return ERR_NONE;
103+
}
104+
105+
*out_ticks = g_next_switch_tick - g_ticks;
106+
return ERR_NONE;
107+
}

0 commit comments

Comments
 (0)