(chap-timers)=
# Internal Timers

This chapter describes the internal timer feature of the VeeR EH2 core.

## Features

The VeeR EH2's internal timer features are:

- Two independently controlled 32-bit timers per thread
  - Dedicated counter
  - Dedicated bound
  - Dedicated control to enable/disable incrementing generally, during power management Sleep, and while executing PAUSE
  - Enable/disable local interrupts (in standard RISC-V `mie` register)
- Cascade mode to form a single 64-bit timer

## Description

The VeeR EH2 core implements two internal timers per thread.
The `mitcnt0` and `mitcnt1` registers (see Section 6.4.1) are 32-bit unsigned counters.
Each counter also has a corresponding 32-bit unsigned bound register (i.e., `mitb0` and `mitb1` , see Section 6.4.2) and control register (i.e., `mitctl0` and `mitctl1` , see Section 6.4.3).

All registers are cleared at reset unless otherwise noted.
After reset, the counters start incrementing the next clock cycle if the increment conditions are met.
All registers can be read as well as written at any time.
The `mitcnt0/1` and `mitb0/1` registers may be written to any 32-bit value.
If the conditions to increment are met, the corresponding counter `mitcnt0/1` increments every clock cycle.

Cascade mode (see Section 6.4.3) links the two counters together.
The `mitcnt1` register is only incremented when the conditions to increment `mitcnt1` are met and the `mitcnt0` register is greater than or equal to the bound in its `mitb0` register.

For each timer, a local interrupt (see Section 6.3) is triggered when that counter is at or above its bound.
When a counter is at or above its bound, it gets cleared the next clock cycle (i.e., the interrupt condition is not sticky).

:::{note}
If the thread is in Debug Mode and being single-stepped, it may take multiple clock cycles to execute a single instruction.
If the conditions to increment are met, the counter increments for every clock cycle it takes to execute a single instruction.
Therefore, every executed single-stepped instruction in Debug Mode may result in multiple counter increments.
:::

:::{note}
If the thread is in the Debug Mode's Halted (i.e., db-halt) state, an internal timer interrupt does not transition the thread back to the Active (i.e., Running) state.
:::

:::{note}
Hart T0 is enabled at powerup.
The timer registers of hart T1 cannot be programmed until hart T1 has exited the Idle state.
:::

## Internal Timer Local Interrupts

Local-to-the-thread interrupts for internal timer 0 and 1 have pending [^35] (*mitip0/1*) and enable (*mitie0/1*) bits in bit positions 29 (for internal timer 0) and 28 (for internal timer 1) of the standard RISC-V `mip` (see {ref}`Table 12-2 <table-12-2>`) and `mie` (see {ref}`Table 12-1 <table-12-1>`) registers, respectively.
The priority is lower than the RISC-V External, Software, and Timer interrupts (see {ref}`Table 14-1 <table-14-1>`).
The internal timer 0 and 1 local interrupts have an `mcause` value of 0x8000\_001D (for internal timer 0) and 0x8000\_001C (for internal timer 1) (see {ref}`Table 13-3 <table-13-3>`).

:::{note}
If both internal timer interrupts occur in the same cycle, internal timer 0's interrupt has higher priority than internal timer 1's interrupt.
:::

:::{note}
A common interrupt service routine may be used for both interrupts.
The `mcause` register value differentiates the two local interrupts.
:::

## Control/Status Registers

A summary of platform-specific internal timer control/status registers in CSR space:

- Internal Timer Counter 0/1 Register (`mitcnt0/1`) (see Section 6.4.1)
- Internal Timer Bound 0/1 Register (`mitb0/1`) (see Section 6.4.2)
- Internal Timer Control 0/1 Register (`mitctl0/1`) (see Section 6.4.3)

All reserved and unused bits in these control/status registers must be hardwired to `0`.
Unless otherwise noted, all read/write control/status registers must have WARL (Write Any value, Read Legal value) behavior.

### Internal Timer Counter 0/1 Register (`mitcnt0/1`)

The `mitcnt0` and `mitcnt1` registers are the counters of the internal timer 0 and 1, respectively.

The conditions to increment a counter are:

- The *enable* bit in the corresponding `mitctl0/1` register is `1`,
- if the thread is in Sleep (i.e., pmu/fw-halt) state, the *halt_en* bit in the corresponding `mitctl0/1` register is `1`,
- if the thread is paused, the *pause_en* bit in the corresponding `mitctl0/1` register is `1`, and
- the thread is not in Debug Mode, except while executing a single-stepped instruction.

A counter is cleared if its value is greater than or equal to its corresponding `mitb0/1` register.

:::{note}
If a write to the `mitcnt0/1` register is committed in the same clock cycle as the timer interrupt condition is met, the internal timer local interrupt is triggered, if enabled, but the counter is not cleared in this case.
Instead, the counter is set to the written value.
:::

These registers are mapped to the non-standard read/write CSR address space and hart-specific (i.e., separate registers per thread).

:::{list-table} Internal Timer Counter 0/1 Register (`mitcnt0/1`, at CSR 0x7D2 / 0x7D5)
:name: table-6-1
:header-rows: 1

* - Field
  - Bits
  - Description
  - Access
  - Reset
* - count
  - 31:0
  - Counter
  - R/W
  - 0
:::

### Internal Timer Bound 0/1 Register (`mitb0/1`)

The `mitb0` and `mitb1` registers hold the upper bounds of the internal timer 0 and 1, respectively.

These registers are mapped to the non-standard read/write CSR address space and hart-specific (i.e., separate registers per thread).

:::{list-table} Internal Timer Bound 0/1 Register (`mitb0/1`, at CSR 0x7D3 / 0x7D6)
:name: table-6-2
:header-rows: 1

* - Field
  - Bits
  - Description
  - Access
  - Reset
* - bound
  - 31:0
  - Bound
  - R/W
  - 0xFFFF_FFFF
:::

### Internal Timer Control 0/1 Register (`mitctl0/1`)

The `mitctl0` and `mitctl1` registers provide the control bits of the internal timer 0 and 1, respectively.

:::{note}
When in cascade mode, it is highly recommended to program the *enable*, *halt_en*, and *pause_en* control bits of the `mitctl1` register the same as the `mitctl0` register.
:::

These registers are mapped to the non-standard read/write CSR address space and hart-specific (i.e., separate registers per thread).

:::{list-table} Internal Timer Control 0/1 Register (`mitctl0/1`, at CSR 0x7D4 / 0x7D7)
:name: table-6-3
:header-rows: 1

* - Field
  - Bits
  - Description
  - Access
  - Reset
* - Reserved
  - 31:4
  - Reserved
  - R
  - 0
* - cascade (**`mitctl1` only**)
  - 3
  - Cascade mode:
    * 0: Disable cascading (i.e., both internal timers operate independently) (default)
    * 1: Enable cascading (i.e., internal timer 0 and 1 are combined into a single 64-bit timer)
  - R/W
  - 0
* - pause_en
  - 2
  - Enable/disable incrementing timer counter while executing PAUSE:
    * 0: Disable incrementing (default)
    * 1: Enable incrementing

    :::{note}
    If `1` and the thread is pausing (see Section 7.6.2), an internal timer interrupt terminates PAUSE and regular execution is resumed.
    :::
  - R/W
  - 0
* - halt_en
  - 1
  - Enable/disable incrementing timer counter while in Sleep (i.e., pmu/fw- halt) state:
    * 0: Disable incrementing (default)
    * 1: Enable incrementing

    :::{note}
    If `1` and the thread is in Sleep (i.e., pmu/fw-halt) state, an internal timer interrupt transitions the thread back to the Active (i.e., Running) state and regular execution is resumed.
    :::
  - R/W
  - 0
* - enable
  - 0
  - Enable/disable incrementing timer counter:
    * 0: Disable incrementing
    * 1: Enable incrementing (default)
  - R/W
  - 1
:::

[^35]: Since internal timer interrupts are not latched (i.e., not 'sticky') and these local interrupts are only signaled for one core clock cycle, it is unlikely that they are detected by firmware in the `mip` register.
