Timer HWIP Technical Specification
Overview
This document specifies RISC-V Timer hardware IP functionality. This module conforms to the Comportable guideline for peripheral functionality. See that document for integration overview within the broader top level system.
Features
- 64-bit timer with 12-bit prescaler and 8-bit step register
- Compliant with RISC-V privileged specification v1.11
- Configurable number of timers per hart and number of harts
Description
The timer module provides a configurable number of 64-bit counters where each counter increments by a step value whenever the prescaler times out. Each timer generates an interrupt if the counter reaches (or is above) a programmed value. The timer is intended to be used by the processors to check the current time relative to the reset or the system power-on.
In this version, the timer doesn’t consider low-power modes and assumes the clock is neither turned off nor changed during runtime.
Compatibility
The timer IP provides memory-mapped registers mtime
and mtimecmp
which can
be used as the machine-mode timer registers defined in the RISC-V privileged
spec. Additional features such as prescaler, step, and a configurable number of
timers and harts have been added.
Theory of Operations
Block Diagram
The timer module is composed of tick generators, counters, and comparators.
A tick generator creates a tick every time its internal counter hits the
CFG0.prescaler value. The tick is used to increment mtime
by the
CFG0.step
value. The 64-bit mtime
value is compared with the 64-bit mtimecmp
. If
mtime
is greater than or equal to mtimecmp
, the timer raises an interrupt.
Hardware Interfaces
Referring to the Comportable guideline for peripheral device functionality, the module rv_timer
has the following hardware interfaces defined.
Primary Clock: clk_i
Other Clocks: none
Bus Device Interfaces (TL-UL): tl
Bus Host Interfaces (TL-UL): none
Peripheral Pins for Chip IO: none
Inter-Module Signals: Reference
Port Name | Package::Struct | Type | Act | Width | Description |
---|---|---|---|---|---|
tl | tlul_pkg::tl | req_rsp | rsp | 1 |
Interrupts:
Interrupt Name | Type | Description |
---|---|---|
timer_expired_hart0_timer0 | Event | raised if hart0's timer0 expired (mtimecmp >= mtime) |
Security Alerts:
Alert Name | Description |
---|---|
fatal_fault | This fatal alert is triggered when a fatal TL-UL bus integrity fault is detected inside the RV_TIMER unit. |
Security Countermeasures:
Countermeasure ID | Description |
---|---|
RV_TIMER.BUS.INTEGRITY | End-to-end bus integrity scheme. |
Design Details
Tick Generator
The tick module inside the timer IP is used to generate a fixed period of pulse
signal. This allows creation of a call-clock timer tick such as 1us or 10us
regardless of the system clock period. It is useful if the system has more than
one clock as a clock source. The firmware just needs to adjust the
CFG0.prescaler value and the actual timer interrupt handling routine does not
need a variable clock period to update mtimecmp
.
For instance, if a system switches between 48MHz and 200MHz clocks, a prescaler value of 47 for 48MHz and 199 for 200MHz will generate a 1us tick. In this version, the timer only supports a single fixed clock, so the firmware should change CFG0.prescaler appropriately.
Configurable number of timers and harts
The timer IP supports more than one HART and/or more than one timer per hart.
Each hart has a set of tick generator and counter. It means the timer IP has the
same number of prescalers, steps, and mtime
registers as the number of harts.
Each hart can have multiple sets of mtimecmp
, comparator logic, and expire
interrupt signals. This version of the IP is fixed to have one Hart and one
Timer per Hart.
Below is an example configuration file for N_TIMER
2 and N_HARTS
2.
It has separate interrupts per timer and a set of interrupt enable and state
registers per Hart.
{
// ...
interrupt_list: [
{ name: "timer_expired_hart0_timer0",
desc: "raised if hart0's timer0 expired (mtimecmp >= mtime)"
},
{ name: "timer_expired_hart0_timer1",
desc: "raised if hart0's timer1 expired (mtimecmp >= mtime)"
},
{ name: "timer_expired_hart1_timer0",
desc: "raised if hart1's timer0 expired (mtimecmp >= mtime)"
},
{ name: "timer_expired_hart1_timer1",
desc: "raised if hart1's timer1 expired (mtimecmp >= mtime)"
},
],
//...
registers: {
// ...
{ skipto: "0x100" },
{ name: "CFG0",
desc: "Configuration for Hart 0",
swaccess: "rw",
hwaccess: "hro",
fields: [
{ bits: "11:0", name: "prescale", desc: "Prescaler to generate tick" },
{ bits: "23:16", name: "step", resval: "0x1", desc: "Incremental value for each tick" },
],
},
// ...
{ multireg: {
name: "INTR_ENABLE0",
desc: "Interrupt Enable",
count: 2,
cname: "TIMER",
swaccess: "rw",
hwaccess: "hro",
fields: [
{ bits: "0", name: "IE", desc: "Interrupt Enable for timer" }
]
}
},
{ multireg: {
name: "INTR_STATE0",
desc: "Interrupt Status",
count: 2,
cname: "TIMER",
swaccess: "ro",
hwaccess: "hrw",
fields: [
{ bits: "0", name: "IS", desc: "Interrupt status for timer" }
],
}
},
// ...
{ skipto: "0x200" },
{ name: "CFG1",
desc: "Configuration for Hart 1",
swaccess: "rw",
hwaccess: "hro",
fields: [
{ bits: "11:0", name: "prescale", desc: "Prescaler to generate tick" },
{ bits: "23:16", name: "step", resval: "0x1", desc: "Incremental value for each tick" },
],
},
// ...
{ name: "TIMER_V_UPPER1",
desc: "Timer value Upper",
swaccess: "rw",
hwaccess: "hrw",
fields: [
{ bits: "31:0", name: "v", desc: "Timer value [63:32]" },
],
},
// ...
}
Programmers Guide
Initialization
Software is expected to configure prescaler
and step
before activating the
timer. These two fields need to be stable to correctly increment the timer
value. If software wants to change these fields, it should de-activate the
timer and then proceed.
Register Access
The timer IP has 64-bit timer value registers and 64-bit compare registers. The register interface, however, is set to 32-bit data width. The CPU cannot access 64-bit data in a single request. However, when split into two reads, it is possible the timer value can increment between the two requests.
The IP doesn’t have a latching or blocking mechanism to avoid this issue. It is
the programmer’s responsibility to ensure the correct value is read. For
instance, if the CPU reads 0xFFFF_FFFF
from lower 32-bit timer value (mtime
)
and 0x0000_0001
from upper 32-bit timer value (mtimeh
), there is a chance
that rather than having the value 0x1_FFFF_FFFF
the timer value has changed
from 0x0_FFFF_FFFF
to 0x1_0000_0000
between the two reads. If there is the
possibility of an interrupt between the two reads then the counter could have
advanced even more.
This condition can be detected in a standard way using a third read. Figure 10.1 in the RISC-V unprivileged specification explains how to avoid this.
again:
rdcycleh x3
rdcycle x2
rdcycleh x4
bne x3, x4, again
Updating mtimecmp
register also follows a similar approach to avoid a spurious
interrupt during the register update. Please refer to the mtimecmp
section in
the RISC-V privileged specification.
# New comparand is in a1:a0.
li t0, -1
sw t0, mtimecmp # No smaller than old value.
sw a1, mtimecmp+4 # No smaller than new value.
sw a0, mtimecmp # New value.
Timer behaviour close to 2^64
There are some peculiarities when mtime
and mtimecmp
get close to the end of
the 64-bit integer range. In particular, because an unsigned comparison is done
between mtime
and mtimecmp
care is needed. A couple of cases are:
-
mtimecmp
close to 0xFFFF_FFFF_FFFF_FFFF. In this case the time-out event will be signaled whenmtime
passes the comparison value, the interrupt will be raised and the source indicated in the corresponding bit of the interrupt status register. However, if there is a delay in servicing the interrupt themtime
value could wrap to zero (and continue to increment) so the value read by the interrupt service routine will be less than the comparison value. -
When the timer is setup to trigger a
timeout
some number of timer ticks into the future, the computation of the comparison valuemtime + timeout
may overflow. If this value is set inmtimecmp
it would makemtime
greater thanmtimecmp
and immediately signal an interrupt. A possible solution is to have an intermediate interrupt by setting themtimecmp
to 64-bit all-ones,0xFFFF_FFFF_FFFF_FFFF
. Then the service routine for that interrupt will need to pollmtime
until it wraps (which could take up to a timer clock tick) before scheduling the required interrupt using the originally computedmtimecmp
value.
Interrupt Handling
If mtime
is greater than or equal to the value of mtimecmp
, the interrupt is generated from the RV_TIMER module.
If the core enables the timer interrupt in MIE
CSR, it jumps into the timer interrupt service routine.
Clearing the interrupt can be done by writing 1 into the Interrupt Status register
INTR_STATE0.
The RV_TIMER module also follows RISC-V Privileged spec that requires the interrupt to be cleared by updating mtimecmp
memory-mapped CSRs.
In this case both
COMPARE_LOWER0_0 and
COMPARE_UPPER0_0 can clear the interrupt source.
Device Interface Functions (DIFs)
To use this DIF, include the following C header:
#include "sw/device/lib/dif/dif_rv_timer.h"
This header provides the following device interface functions:
dif_rv_timer_approximate_tick_params
Generates an aproximatedif_rv_timer_tick_params_t
given the device clock frequency and desired counter frequency (both given in Hertz).dif_rv_timer_arm
Arms the timer to go off once the counter value is greater than or equal tothreshold
, by setting up the given comparator.dif_rv_timer_counter_read
Reads the current value on a particular hart's timer.dif_rv_timer_counter_set_enabled
Starts or stops a particular hart's counter.dif_rv_timer_counter_write
Writes the given value to a particular hart's timer.dif_rv_timer_reset
Completely resets a timer device, disabling all IRQs, counters, and comparators.dif_rv_timer_set_tick_params
Configures the tick params for a particular hart's counter.
Register Table
Summary | |||
---|---|---|---|
Name | Offset | Length | Description |
rv_timer.ALERT_TEST | 0x0 | 4 | Alert Test Register |
rv_timer.CTRL | 0x4 | 4 | Control register |
rv_timer.INTR_ENABLE0 | 0x100 | 4 | Interrupt Enable |
rv_timer.INTR_STATE0 | 0x104 | 4 | Interrupt Status |
rv_timer.INTR_TEST0 | 0x108 | 4 | Interrupt test register |
rv_timer.CFG0 | 0x10c | 4 | Configuration for Hart 0 |
rv_timer.TIMER_V_LOWER0 | 0x110 | 4 | Timer value Lower |
rv_timer.TIMER_V_UPPER0 | 0x114 | 4 | Timer value Upper |
rv_timer.COMPARE_LOWER0_0 | 0x118 | 4 | Timer value Lower |
rv_timer.COMPARE_UPPER0_0 | 0x11c | 4 | Timer value Upper |
rv_timer.ALERT_TEST @ 0x0
Alert Test Register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | wo | 0x0 | fatal_fault | Write 1 to trigger one alert event of this kind. |
rv_timer.CTRL @ 0x4
Control register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | rw | 0x0 | active_0 | If 1, timer operates |
rv_timer.INTR_ENABLE0 @ 0x100
Interrupt Enable Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | rw | 0x0 | IE_0 | Interrupt Enable for timer |
rv_timer.INTR_STATE0 @ 0x104
Interrupt Status Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | rw1c | 0x0 | IS_0 | Interrupt status for timer |
rv_timer.INTR_TEST0 @ 0x108
Interrupt test register Reset default = 0x0, mask 0x1
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0 | wo | x | T_0 | Interrupt test for timer |
rv_timer.CFG0 @ 0x10c
Configuration for Hart 0 Reset default = 0x10000, mask 0xff0fff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
11:0 | rw | 0x0 | prescale | Prescaler to generate tick | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
15:12 | Reserved | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
23:16 | rw | 0x1 | step | Incremental value for each tick |
rv_timer.TIMER_V_LOWER0 @ 0x110
Timer value Lower Reset default = 0x0, mask 0xffffffff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
31:0 | rw | 0x0 | v | Timer value [31:0] |
rv_timer.TIMER_V_UPPER0 @ 0x114
Timer value Upper Reset default = 0x0, mask 0xffffffff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
31:0 | rw | 0x0 | v | Timer value [63:32] |
rv_timer.COMPARE_LOWER0_0 @ 0x118
Timer value Lower Reset default = 0xffffffff, mask 0xffffffff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
31:0 | rw | 0xffffffff | v | Timer compare value [31:0] |
rv_timer.COMPARE_UPPER0_0 @ 0x11c
Timer value Upper Reset default = 0xffffffff, mask 0xffffffff
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bits | Type | Reset | Name | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
31:0 | rw | 0xffffffff | v | Timer compare value [63:32] |