Skip to content

GPTimer — General-purpose timer

GRLIB GPTimer (GR712RC §9 / GRLIB IP §27): a prescaler driving four sub-timers with independent control registers. The fourth sub-timer defaults to watchdog mode.

Property Value
Default MMIO base 0x80000300
MMIO window size 0x100
Sub-timers 4
Default IRQ 8 (timer 1); subsequent timers chain on 9..11 if CH=1
Watchdog Timer 4, EN=RS=IE=1, counter=reload=0xFFFF after reset
Source file src/peripherals/src/gptimer.cpp

Register map

Offset Name Access Description
0x00 SCALER R/W Prescaler counter (decremented every tick)
0x04 RELOAD R/W Prescaler reload value
0x08 CONFIG R Implementation-specific config (NTIMERS, …)
0x10..0x14 TIMER 1: counter / reload / control / latch R/W First sub-timer
0x20..0x24 TIMER 2 R/W Second sub-timer
0x30..0x34 TIMER 3 R/W Third sub-timer
0x40..0x44 TIMER 4 R/W Watchdog (defaults to active)

Per-timer offsets (relative to the timer block):

Offset Name Description
+0x00 COUNTER Current value, decremented on prescaler underflow
+0x04 RELOAD Reload value, copied into COUNTER on underflow if RS=1
+0x08 CTRL Control: see bit map below
+0x0C LATCH Latch (snapshot of counter on read; not used by RTEMS BSP)

Control register bit map (Decision 15)

Bit Name Access Meaning
0 EN R/W Enable: 1 = run
1 RS R/W Restart on underflow: 1 = reload, 0 = stop
2 LD W only Load trigger: write 1 → COUNTER ← RELOAD, then bit clears
3 IE R/W Interrupt enable on underflow
4 IP W0C Interrupt pending: write 0 to clear, write 1 has no effect
5 CH R/W Chain: 1 = decrement only when previous timer underflows
6 DH R Debug halt (read-only 0 in our model)

The writable mask is therefore 0x2B (bits 0, 1, 3, 5). The other bits are either trigger-only (LD) or have specialised semantics (IP).

Tick mechanics (Decision 16)

tick(now) is called every scheduling round. The body:

prescaler -= 1
if prescaler == 0:
    prescaler = RELOAD
    for each enabled sub-timer:
        counter -= 1
        if counter underflows:
            if RS == 1:
                counter = timer.RELOAD
            else:
                EN = 0
            if IE == 1:
                IP = 1
                irqmp.external_assert(1 << irq_for(timer_index))

The tick cadence is quantum * num_cores * ns_per_insn simulated ns between calls (default 20 µs). Timers are still simulated-time accurate at the bit level — the prescaler decrements by exactly the number of nanoseconds elapsed divided by ns_per_insn.

Reset state (Decision 17)

After reset():

  • SCALER = 0, RELOAD = 0.
  • Timers 1–3: EN=0, RS=0, IE=0, COUNTER=0, RELOAD=0.
  • Timer 4: EN=1, RS=1, IE=1, COUNTER=0xFFFF, RELOAD=0xFFFF. This matches the GR712RC default where the watchdog is armed at power-on; software must either disable it or feed it.

Bootloader prescaler init (Decision 27)

During Emulator::initialize() the runtime simulates the GR712RC ROM bootloader's timer setup by writing canonical values into the prescaler counter and reload registers. Without this, the prescaler starts at 0 and takes 0xFFFF ticks (≈ 65 ms) before the first underflow, which delays the first RTEMS clock interrupt enough to break test timing.

Tests

  • tests/unit/test_gptimer.cpp — covers the writable mask, LD trigger, IP W0C, prescaler underflow, RS=0 stopping, RS=1 reloading, IE=1 IRQ raise, CH chaining between adjacent timers, and the timer-4 watchdog default state.
  • The RTEMS sptests exercise the GPTimer end-to-end as the system clock source.