Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,14 @@ fn addTimerDriver(
driver.addCSourceFile(.{
.file = b.path(source),
});
driver.addCSourceFile(.{
.file = b.path("drivers/timer/time_conv.c"),
});
driver.addCSourceFile(.{
.file = b.path("drivers/timer/timer_common.c"),
});
driver.addIncludePath(b.path("include"));
driver.addIncludePath(b.path("include/sddf/timer"));
driver.addIncludePath(b.path("include/sddf/util/custom_libc"));
driver.addIncludePath(b.path("include/microkit"));
driver.linkLibrary(util);
Expand Down
40 changes: 7 additions & 33 deletions drivers/timer/apb_timer/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <sddf/timer/timer_driver.h>
#include <sddf/resources/device.h>
#include <stdint.h>
#include <microkit.h>
#include <sddf/resources/device.h>
#include <sddf/timer/protocol.h>
#include <sddf/timer/timer_heap.h>
#include <sddf/util/printf.h>
#include <sddf/util/util.h>
#include <sddf/timer/protocol.h>
#include <sddf/timer/timer_driver.h>

// #define DEBUG_APBTIMER

Expand All @@ -25,7 +26,6 @@
// Minimum = 2 due to a bug in the HDL, assumed for rest of this driver.
#define APBTIMER_MAX_TICKS (UINT32_MAX)
#define APBTIMER_CLK_FREQ ((uint64_t)50000000) // 50MHz
#define NANO_INVERSE NS_IN_S

// The APB timer has an array of internal timers. Use one for long-running time measurements, use
// the other for generating interrupts at finer granularity using prescalers.
Expand Down Expand Up @@ -79,24 +79,6 @@ typedef struct apbtimer_timeout_conf {
uint8_t prescaler;
} apbtimer_timeout_conf_t;

/**
* Convert the tick count of a timer to nanoseconds, given the expected prescaler
* and overflow counter. Prescaler can be set to 0 to ignore.
*
* Prescaler should be given in same format as ctrl reg - i.e. 0 = disabled (multiply
* by 1), 1 = multiply by 2, etc.
*/
static inline uint64_t tick_to_ns(uint64_t ticks, uint64_t prescaler)
{
// seconds per tick = T_clk*prescaler = (f_clk^-1)*prescaler
// ns per period = T_clk / 1e-9 = 1e9/f_clk = T_clk_nano
// ns per tick = T_clk_nano * prescaler
// ticks in ns = ticks * (T_clk_nano*prescaler)
assert(prescaler <= APBTIMER_PRESCALER_MAX);
uint64_t prescaler_adjusted = (prescaler == 0) ? 1 : prescaler + 1;
return (ticks * NANO_INVERSE * prescaler_adjusted) / APBTIMER_CLK_FREQ;
}

/**
* Return number of ticks since driver startup using timekeeper timer.
* NOTE: one round of timer @ 50MHz with prescaler of (1<<3)=4 lasts for 171.8
Expand All @@ -108,7 +90,7 @@ static uint64_t get_time_ns(void)
uint64_t value_h = (uint64_t)timekeeper_overflow_count;
uint64_t value_ticks = (value_h << 32) | value_l;

return tick_to_ns(value_ticks, TIMEKEEPER_PRESCALER);
return tick_to_ns_cached(value_ticks, TIMEKEEPER_PRESCALER, APBTIMER_CLK_FREQ);
}

/**
Expand All @@ -119,22 +101,14 @@ static uint64_t get_time_ns(void)
static apbtimer_timeout_conf_t calculate_timeout_from_ns(uint64_t ns_delay)
{
// Convert nanoseconds to ticks with a prescaler of zero (x1)
// tick = 1 timer period = 1/f_clk = T_clk
// ticks = n. periods in delay = seconds_delay / T_clk
//
// To get ticks efficiently, precalculate 1/nano (10e-9).
// Hence, T_clk = NANO_INVERSE / F_clk
// and T_delay = seconds_delay / (NANO_INVERSE/F_clk)
// uint64_t divisor = NANO_INVERSE / APBTIMER_CLK_FREQ;
// uint64_t ticks = ns_delay / divisor;
uint64_t ticks = (ns_delay * APBTIMER_CLK_FREQ) / NANO_INVERSE;
uint64_t ticks = ns_to_tick_cached(ns_delay, 0, APBTIMER_CLK_FREQ);

uint32_t prescaler = 0;
uint32_t cmp = ticks;
// NOTE: at the time of writing, the APB timer's prescaler logic is completely
// broken. The prescaler calculator is disabled as a result.

// if (ticks <= UINT32_MAX) {
// if (ticks < UINT32_MAX - 1) {
// // No prescaler needed
// cmp = ticks;
// } else {
Expand Down
19 changes: 14 additions & 5 deletions drivers/timer/apb_timer/timer_driver.mk
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2025, UNSW
# Copyright 2026, UNSW
#
# SPDX-License-Identifier: BSD-2-Clause
#
Expand All @@ -12,11 +12,20 @@

TIMER_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

timer_driver.elf: timer/timer.o
$(LD) $(LDFLAGS) $< $(LIBS) -o $@
timer_driver.elf: timer/timer_driver.o timer/timer_common.o timer/time_conv.o
$(LD) $(LDFLAGS) $^ $(LIBS) -o $@

timer/timer.o: ${TIMER_DIR}/timer.c $(SDDF_LIBC_INCLUDE)|timer
${CC} ${CFLAGS} -o $@ -c $<
timer/timer_driver.o: CFLAGS+=-I${TIMER_DIR}
timer/timer_driver.o: ${TIMER_DIR}/timer.c ${CHECK_FLAGS_BOARD_MD5} $(SDDF_LIBC_INCLUDE)|timer
${CC} ${CFLAGS} -c -o $@ $<

timer/timer_common.o: CFLAGS+=-I${TIMER_DIR}
timer/timer_common.o: ${TIMER_DIR}/../timer_common.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -c -o $@ $<

timer/time_conv.o: CFLAGS+=-I${TIMER_DIR}
timer/time_conv.o: ${TIMER_DIR}/../time_conv.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -c -o $@ $<

timer:
mkdir -p timer
Expand Down
52 changes: 10 additions & 42 deletions drivers/timer/arm/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <os/sddf.h>
#include <sddf/timer/protocol.h>
#include <sddf/timer/config.h>
#include <sddf/timer/timer_driver.h>
#include <sddf/util/util.h>
#include <sddf/util/printf.h>
#include <sddf/util/udivmodti4.h>
Expand All @@ -26,10 +27,10 @@ static uint64_t timer_freq;
#define LOW_WORD(x) (x & 0xffffffffffffffff)
#define HIGH_WORD(x) (x >> 64)

#define COPROC_WRITE_WORD(R,W) asm volatile ("msr " R ", %0" :: "r"(W))
#define COPROC_READ_WORD(R,W) asm volatile ("mrs %0, " R : "=r" (W))
#define COPROC_WRITE_64(R,W) COPROC_WRITE_WORD(R,W)
#define COPROC_READ_64(R,W) COPROC_READ_WORD(R,W)
#define COPROC_WRITE_WORD(R, W) asm volatile ("msr " R ", %0" :: "r"(W))
#define COPROC_READ_WORD(R, W) asm volatile ("mrs %0, " R : "=r" (W))
#define COPROC_WRITE_64(R, W) COPROC_WRITE_WORD(R,W)
#define COPROC_READ_64(R, W) COPROC_READ_WORD(R,W)

/* control reigster for the el1 physical timer */
#define CNTP_CTL "cntp_ctl_el0"
Expand Down Expand Up @@ -58,7 +59,7 @@ static inline uint32_t generic_timer_get_freq(void)
{
uintptr_t freq;
COPROC_READ_WORD(CNTFRQ, freq);
return (uint32_t) freq;
return (uint32_t)freq;
}

static inline uint32_t generic_timer_read_ctrl(void)
Expand Down Expand Up @@ -89,41 +90,9 @@ static inline void generic_timer_disable(void)
generic_timer_or_ctrl(~GENERIC_TIMER_ENABLE);
}

#define KHZ (1000)
#define MHZ (1000 * KHZ)
#define GHZ (1000 * MHZ)

static inline uint64_t freq_cycles_and_hz_to_ns(uint64_t ncycles, uint64_t hz)
{
if (hz % GHZ == 0) {
return ncycles / (hz / GHZ);
} else if (hz % MHZ == 0) {
return ncycles * MS_IN_S / (hz / MHZ);
} else if (hz % KHZ == 0) {
return ncycles * US_IN_S / (hz / KHZ);
}

__uint128_t ncycles_in_s = (__uint128_t)ncycles * NS_IN_S;
/* We can discard the remainder. */
uint64_t rem = 0;
uint64_t res = udiv128by64to64(HIGH_WORD(ncycles_in_s), LOW_WORD(ncycles_in_s), NS_IN_S, &rem);

return res;
}

static inline uint64_t freq_ns_and_hz_to_cycles(uint64_t ns, uint64_t hz)
{
__uint128_t calc = ((__uint128_t)ns * (__uint128_t)hz);
/* We can discard the remainder. */
uint64_t rem = 0;
uint64_t res = udiv128by64to64(HIGH_WORD(calc), LOW_WORD(calc), NS_IN_S, &rem);

return res;
}

void set_timeout(uint64_t timeout)
{
generic_timer_set_compare(freq_ns_and_hz_to_cycles(timeout, timer_freq));
generic_timer_set_compare(ns_to_tick_cached(timeout, 0, timer_freq));
}

static uint64_t timeouts[MAX_TIMEOUTS];
Expand All @@ -147,7 +116,6 @@ static void process_timeouts(uint64_t curr_time)
if (next_timeout != UINT64_MAX) {
set_timeout(next_timeout);
}

}

void init()
Expand All @@ -171,20 +139,20 @@ void notified(sddf_channel ch)
sddf_deferred_irq_ack(ch);

generic_timer_set_compare(UINT64_MAX);
uint64_t curr_time = freq_cycles_and_hz_to_ns(get_ticks(), timer_freq);
uint64_t curr_time = tick_to_ns_cached(get_ticks(), 0, timer_freq);
process_timeouts(curr_time);
}

seL4_MessageInfo_t protected(sddf_channel ch, seL4_MessageInfo_t msginfo)
{
switch (seL4_MessageInfo_get_label(msginfo)) {
case SDDF_TIMER_GET_TIME: {
uint64_t time_ns = freq_cycles_and_hz_to_ns(get_ticks(), timer_freq);
uint64_t time_ns = tick_to_ns_cached(get_ticks(), 0, timer_freq);
sddf_set_mr(0, time_ns);
return seL4_MessageInfo_new(0, 0, 0, 1);
}
case SDDF_TIMER_SET_TIMEOUT: {
uint64_t curr_time = freq_cycles_and_hz_to_ns(get_ticks(), timer_freq);
uint64_t curr_time = tick_to_ns_cached(get_ticks(), 0, timer_freq);
uint64_t offset_us = (uint64_t)(sddf_get_mr(0));
timeouts[ch] = curr_time + offset_us;
process_timeouts(curr_time);
Expand Down
17 changes: 13 additions & 4 deletions drivers/timer/arm/timer_driver.mk
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@

TIMER_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

timer_driver.elf: timer/timer.o
$(LD) $(LDFLAGS) $< $(LIBS) -o $@
timer_driver.elf: timer/timer_driver.o timer/timer_common.o timer/time_conv.o
$(LD) $(LDFLAGS) $^ $(LIBS) -o $@

timer/timer.o: ${TIMER_DIR}/timer.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -o $@ -c $<
timer/timer_driver.o: CFLAGS+=-I${TIMER_DIR}
timer/timer_driver.o: ${TIMER_DIR}/timer.c ${CHECK_FLAGS_BOARD_MD5} $(SDDF_LIBC_INCLUDE)|timer
${CC} ${CFLAGS} -c -o $@ $<

timer/timer_common.o: CFLAGS+=-I${TIMER_DIR}
timer/timer_common.o: ${TIMER_DIR}/../timer_common.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -c -o $@ $<

timer/time_conv.o: CFLAGS+=-I${TIMER_DIR}
timer/time_conv.o: ${TIMER_DIR}/../time_conv.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -c -o $@ $<

timer:
mkdir -p timer
Expand Down
8 changes: 5 additions & 3 deletions drivers/timer/bcm2835/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stdint.h>
#include <os/sddf.h>
#include <sddf/timer/protocol.h>
#include <sddf/timer/timer_driver.h>
#include <sddf/timer/config.h>
#include <sddf/util/util.h>
#include <sddf/util/printf.h>
Expand All @@ -14,8 +15,9 @@
__attribute__((__section__(".device_resources"))) device_resources_t device_resources;

#if !defined(CONFIG_PLAT_BCM2711)
#error "Driver assumes 100MHz clock frequency, check if your platform supports that"
#error "Driver assumes 1MHz clock frequency, check if your platform supports that"
#endif
#define BCM2835_CLK_FREQ ((sddf_timer_freq_hz_t) 1*MEGA)

/*
* The system timer has four 32-bit compare registers available.
Expand Down Expand Up @@ -61,12 +63,12 @@ static inline uint64_t get_ticks_in_ns(void)
uint64_t value_h = (uint64_t)timer_regs->chi;
uint64_t value_l = (uint64_t)timer_regs->clo;
uint64_t value_us = (value_h << 32) | value_l;
return value_us * NS_IN_US;
return tick_to_ns_cached(value_us, 0, BCM2835_CLK_FREQ);
}

void set_timeout(uint64_t ns)
{
uint64_t value_us = ns / NS_IN_US;
uint64_t value_us = ns_to_tick_cached(ns, 0, BCM2835_CLK_FREQ);
if (value_us > BCM2835_TIMER_MAX_US) {
value_us = BCM2835_TIMER_MAX_US;
}
Expand Down
17 changes: 13 additions & 4 deletions drivers/timer/bcm2835/timer_driver.mk
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@

TIMER_DIR := $(dir $(lastword $(MAKEFILE_LIST)))

timer_driver.elf: timer/timer.o
$(LD) $(LDFLAGS) $< $(LIBS) -o $@
timer_driver.elf: timer/timer_driver.o timer/timer_common.o timer/time_conv.o
$(LD) $(LDFLAGS) $^ $(LIBS) -o $@

timer/timer.o: ${TIMER_DIR}/timer.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -o $@ -c $<
timer/timer_driver.o: CFLAGS+=-I${TIMER_DIR}
timer/timer_driver.o: ${TIMER_DIR}/timer.c ${CHECK_FLAGS_BOARD_MD5} $(SDDF_LIBC_INCLUDE)|timer
${CC} ${CFLAGS} -c -o $@ $<

timer/timer_common.o: CFLAGS+=-I${TIMER_DIR}
timer/timer_common.o: ${TIMER_DIR}/../timer_common.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -c -o $@ $<

timer/time_conv.o: CFLAGS+=-I${TIMER_DIR}
timer/time_conv.o: ${TIMER_DIR}/../time_conv.c |timer $(SDDF_LIBC_INCLUDE)
${CC} ${CFLAGS} -c -o $@ $<

timer:
mkdir -p timer
Expand Down
19 changes: 7 additions & 12 deletions drivers/timer/cdns/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <os/sddf.h>
#include <sddf/timer/protocol.h>
#include <sddf/timer/config.h>
#include <sddf/timer/timer_driver.h>
#include <sddf/util/util.h>
#include <sddf/util/printf.h>
#include <sddf/resources/device.h>
Expand All @@ -30,12 +31,13 @@ __attribute__((__section__(".device_resources"))) device_resources_t device_reso
* You can read the IOPLL clk value in u-boot using `clk dump` and view the source and divider value for
* LSBUS by reading LPD_LSBUS_CTRL register.
*/
#define CDNS_TIMER_REF_CLOCK_RATE (100UL * 1000UL * 1000UL)
#define CDNS_TIMER_REF_CLOCK_RATE (100*MEGA)
#else
#error "unknown LSBUS clock frequency (timer device reference clock)"
#endif
/* default source clock, scaled down by a factor of 2^(CDNS_TIMER_PRESCALE_VALUE+1) if enabled prescale */
#define CDNS_TIMER_TICKS_PER_SECOND (CDNS_TIMER_REF_CLOCK_RATE >> ((CDNS_TIMER_PRESCALE_VALUE & CDNS_TIMER_MAX_PRESCALE) + 1))
#define CDNS_TRUE_PRESCALE (CDNS_TIMER_PRESCALE_VALUE + 1)

#define TTC_COUNTER_TIMER 0
#define TTC_TIMEOUT_TIMER 1
Expand Down Expand Up @@ -111,21 +113,14 @@ static inline uint64_t get_ticks_in_ns(void)
uint64_t value_ticks = (value_h << 32) | value_l;

/* convert from ticks to nanoseconds */
uint64_t value_whole_seconds = value_ticks / CDNS_TIMER_TICKS_PER_SECOND;
uint64_t value_subsecond_ticks = value_ticks % CDNS_TIMER_TICKS_PER_SECOND;
uint64_t value_subsecond_ns = (value_subsecond_ticks * NS_IN_S) / CDNS_TIMER_TICKS_PER_SECOND;
uint64_t value_ns = value_whole_seconds * NS_IN_S + value_subsecond_ns;

return value_ns;
return tick_to_ns_cached(value_ticks, CDNS_TRUE_PRESCALE, CDNS_TIMER_REF_CLOCK_RATE);
}

void set_timeout(uint64_t ns)
{
/* stop the timeout timer */
timer_regs->cnt_ctrl[TTC_TIMEOUT_TIMER] |= CDNS_TIMER_DISABLE;
uint64_t ticks_whole_seconds = (ns / NS_IN_S) * CDNS_TIMER_TICKS_PER_SECOND;
uint64_t ticks_remainder = (ns % NS_IN_S) * CDNS_TIMER_TICKS_PER_SECOND / NS_IN_S;
uint64_t num_ticks = ticks_whole_seconds + ticks_remainder;
uint64_t num_ticks = ns_to_tick_cached(ns, CDNS_TRUE_PRESCALE, CDNS_TIMER_REF_CLOCK_RATE);

if (num_ticks > CDNS_TIMER_MAX_TICKS) {
/* truncate num_ticks to maximum timeout, will use multiple interrupts to process the requested timeout. */
Expand Down Expand Up @@ -253,8 +248,8 @@ seL4_MessageInfo_t protected(sddf_channel ch, seL4_MessageInfo_t msginfo)
}
case SDDF_TIMER_SET_TIMEOUT: {
uint64_t curr_time = get_ticks_in_ns();
uint64_t offset_us = (uint64_t)(sddf_get_mr(0));
timeouts[ch - CLIENT_CH_START] = curr_time + offset_us;
uint64_t offset_ns = (uint64_t)(sddf_get_mr(0));
timeouts[ch - CLIENT_CH_START] = curr_time + offset_ns;
process_timeouts(curr_time);
break;
}
Expand Down
Loading
Loading