blob: 15adfd3ab535c6116f9779241a531d134478de58 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_TIMER_H_
#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_TIMER_H_
/**
* @file
* @brief <a href="/hw/ip/rv_timer/doc/">RV Timer</a> Device Interface Functions
*/
#include <stdint.h>
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_base.h"
#include "sw/device/lib/dif/autogen/dif_rv_timer_autogen.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Represents timekeeping parameters for a particular timer.
*/
typedef struct dif_rv_timer_tick_params {
/**
* The prescaler value is the period of the timer tick in clock cycles,
* minus one. That is,
*
* prescale = clock_freq * tick_period - 1
*
* with |clock_freq| and |tick_period| given in units of hertz and seconds,
* respectively.
*
* For example, if the clock frequency is 50 MHz, and the desired tick
* period is is 1 microsecond, i.e, a tick frequency of 1 MHz, then the
* prescaler should be:
*
* (50 * 10^6) * (1 * 10^-6) - 1 = 49
*
* However, since |tick_period| is very small, it is much more convenient to
* work with |tick_freq|, its inverse, which will be an integer number of
* hertz. In particular,
*
* prescale = (clock_freq / tick_freq) - 1
*
* This value is declared as a uint16_t, but only the lowest 12 bits are
* actually used.
*/
uint16_t prescale;
/**
* The amount to increment the timer counter at each tick.
*/
uint8_t tick_step;
} dif_rv_timer_tick_params_t;
/**
* Generates an aproximate `dif_rv_timer_tick_params_t` given the device
* clock frequency and desired counter frequency (both given in Hertz).
*
* For the purposes of this function, "counter frequency" is the frequency
* at which software would observe a timer counter to increase. If the
* clock has insufficient resolution, high counter frequencies may set a
* larger value for `tick_step`. For example, if the clock ticks at 50kHz,
* but we want a counter that seems to tick every microsecond (1MHz),
* we can achieve this with a prescale of 0 (so that there is a tick per
* clock cycle) and a tick step of 20 (since 20 * 50kHz = 1MHz).
*
* The return value of this function is only an approximation, and the
* actual counter frequency ultimately depends on the accuracy of the
* clock. The function will return an error if it cannot produce an acceptably
* accurate counter frequency using the given clock resolution.
*
* @param clock_freq The device clock frequency, in Hertz.
* @param counter_freq The desired counter frequency, in Hertz.
* @param[out] out Tick parameters that will approximately produce the desired
* counter frequency.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_approximate_tick_params(
uint64_t clock_freq, uint64_t counter_freq,
dif_rv_timer_tick_params_t *out);
/**
* Completely resets a timer device, disabling all IRQs, counters, and
* comparators.
*
* @param timer A timer device.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_reset(const dif_rv_timer_t *timer);
/**
* Configures the tick params for a particular hart's counter.
*
* This function should not be called when `hart_id`'s counter is enabled; it is
* the caller's responsibility to assert this precondition.
* The function `dif_rv_timer_approximate_tick_params()` can be used to generate
* tick parameter values.
*
* @param timer A timer device.
* @param hart_id The hart to configure.
* @param params The timing parameters.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_set_tick_params(const dif_rv_timer_t *timer,
uint32_t hart_id,
dif_rv_timer_tick_params_t params);
/**
* Starts or stops a particular hart's counter.
*
* While a counter is enabled, the counter value will increase each tick, but
* its timekeeping values cannot be reconfigured.
*
* @param timer A timer device.
* @param hart_id The hart counter to enable/disable.
* @param state The new enablement state.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_counter_set_enabled(const dif_rv_timer_t *timer,
uint32_t hart_id,
dif_toggle_t state);
/**
* Reads the current value on a particular hart's timer.
*
* @param timer A timer device.
* @param hart_id The hart counter to read.
* @param[out] out The counter value.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_counter_read(const dif_rv_timer_t *timer,
uint32_t hart_id, uint64_t *out);
/**
* Writes the given value to a particular hart's timer.
*
* @param timer A timer device.
* @param hart_id The hart counter to write.
* @param count The counter value to write.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_counter_write(const dif_rv_timer_t *timer,
uint32_t hart_id, uint64_t count);
/**
* Arms the timer to go off once the counter value is greater than
* or equal to `threshold`, by setting up the given comparator.
*
* Beware that the following naive implementation of setting an alarm
* contains a bug:
* uint64_t time;
* dif_rv_timer_counter_read(my_timer, kMyHart, &time);
* time += kSomeDuration; // (*)
* dif_rv_timer_arm(my_timer, kMyHart, kMyComp, time);
*
* If `time` wraps around when performing the addition, an interrupt will be
* fired immediately upon calling `dif_rv_timer_arm`. Care should be taken to
* perform saturating addition at (*), so that the interrupt is fired when the
* timer value wraps around; this way, the interrupt handler can re-arm the
* timer for the rest of the duration.
*
* This function makes no effort to protect the caller from setting alarms in
* the past that would immediately fire an interrupt. It is the caller's
* responsibility to read the current counter value and pick a reasonable alarm
* threshold.
*
* @param timer A timer device.
* @param hart_id The hart counter to arm against.
* @param comp_id The comparator to set up.
* @param threshold The value to go off at.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_rv_timer_arm(const dif_rv_timer_t *timer, uint32_t hart_id,
uint32_t comp_id, uint64_t threshold);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_TIMER_H_