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.