Timer Controller
17.1 Overview
The Timer Controller provides a simple yet flexible 16-bit timer clocked by the main 50Hz system clock. It can count up or down toward zero, supporting one-shot or auto-reload modes to measure time intervals or generate periodic events.
A programmable 16-bit clock divider slows down the base clock to suit slower components or desired timing intervals. The timer can be started, stopped, reset, and configured through its registers.
Like other controllers, the DMA Controller can be mapped into the device bank, starting at address 0xA0 on the I/O port. Its device index is 6.
17.2 Operation
The Timer counts toward zero, either by incrementing or decrementing a 16-bit accumulator depending on the configured direction:
- When the timer is enabled (
ENABLE bit set in CTRL register), the accumulator updates once every clock cycle defined by the clock divider (see below).
- If counting up (
DIR = 0 in CTRL register), the accumulator increases by 1 each tick until it wraps and reaches zero.
- If counting down (
DIR = 1in CTRL register), the accumulator decreases by 1 each tick until it reaches zero.
- When the accumulator hits zero:
- If
AUTO_RELOAD is enabled, the accumulator is immediately reset to the reload value and counting continues, creating a periodic timer.
- If
AUTO_RELOAD is disabled, the timer stops by clearing the ENABLE bit, effectively performing a one-shot timer.
You can reset the timer at any time by writing to the RESET bit, which immediately loads the reload value into the accumulator.
Reading and writing the current counter value is done through the two registers CNT_LO and CNT_HI, providing a consistent 16-bit snapshot by latching the low byte first.
This flexible behavior allows the Timer Controller to be used for precise timing, event generation, or periodic interrupts (when polled or extended with interrupt support later).
⚠️ All 16-bit values (clock divider, reload, and counter) must be written with the least significant byte (LSB) first, followed by the most significant byte (MSB). This is necessary because the controller latches the LSB on the first write and applies the full 16-bit value only after the MSB is written.
17.3 Clock Divider
The Timer Controller runs off the main 50 MHz system clock. However, many timing applications require slower intervals. To accommodate this, a 16-bit clock divider slows down the effective timer tick frequency. This clock divider can be set by writing registers DIV_LO and DIV_HI.
The divider works as a simple counter that generates a "tick" every $\text{clk_divider} + 1$ system clock cycles. This means the timer accumulator updates once every:
$T_{tick} = \frac{\text{clk_divider} + 1}{f_{clk}}$
where:
- $T_{tick}$ is the timer tick period (seconds)
- $\text{clk_divider}$ is the 16-bit divider value programmed by the user
- $f_{clk} = 50\text{MHz}$ is the system clock frequency
For example, if clk_divider = 4999, the timer updates every:
$T_{tick} = \frac{4999 + 1}{50,000,000} = \frac{5000}{50,000,000} = 0.0001\,\text{s} = 100\,\mu s$
This allows you to configure timer intervals ranging from 20 ns (fastest, with divider = 0) up to about 1.31 ms (max divider = 65535) per timer tick.
17.4 Registers Summary
| Name |
Description |
Base-relative address |
Access |
| CTRL |
General-purpose timer control register. Controls enable, direction, reset, and auto-reload behavior. |
0x00 |
varies |
| DIV_LO |
Lower 8 bits of the clock divider. Must be written before the HIGH register to latch full value. |
0x01 |
WO |
| DIV_HI |
Upper 8 bits of the clock divider. Writing this applies the full 16-bit value. |
0x02 |
WO |
| REL_LO |
Lower 8 bits of the reload value. Must be written before the HIGH register to latch full value. |
0x03 |
WO |
| REL_HI |
Upper 8 bits of the reload value. Writing this applies the full 16-bit value. |
0x04 |
WO |
| CNT_LO |
Lower 8 bits of the current counter value. Must be read or written before the HIGH register. |
0x05 |
RW |
| CNT_HI |
Upper 8 bits of the counter value. Read/write only after accessing CNT_LO. |
0x06 |
RW |
17.5 Registers
CTRL (0x00)
General-purpose timer control register. Controls enable, direction, reset, and auto-reload behavior.
Reset
ENABLE
Write 1 to start the timer. Cleared automatically when the counter reaches 0 if auto-reload it not enabled. (RW)
AUTO_RELOAD
Enable automatic reload of the counter when it reaches 0 (included). (RW)
DIR
Timer direction, `0` = count up, `1` = count down. (RW)
RESET
Write 1 to reset the timer counter to the current reload value. (WO)
DIV_LO (0x01)
Lower 8 bits of the clock divider. Must be written before the HIGH register to latch full value.
Reset
DIV_LSB
Lower byte of the clock divider. Latches on subsequent write to DIV_HI. (WO)
DIV_HI (0x02)
Upper 8 bits of the clock divider. Writing this applies the full 16-bit value.
Reset
DIV_MSB
Upper byte of the clock divider. Writing this applies the value latched from DIV_LO. (WO)
REL_LO (0x03)
Lower 8 bits of the reload value. Must be written before the HIGH register to latch full value.
Reset
REL_LSB
Lower byte of reload value. Latches on subsequent write to REL_HI. (WO)
REL_HI (0x04)
Upper 8 bits of the reload value. Writing this applies the full 16-bit value.
Reset
REL_MSB
Upper byte of reload value. Writing this applies the value latched from REL_LO. (WO)
CNT_LO (0x05)
Lower 8 bits of the current counter value. Must be read or written before the HIGH register.
Reset
CNT_LSB
Lower byte of counter value. Latches value for subsequent access to CNT_HI. (RW)
CNT_HI (0x06)
Upper 8 bits of the counter value. Read/write only after accessing CNT_LO.
Reset
CNT_MSB
Upper byte of counter value. Must be accessed after CNT_LO for consistent 16-bit access. (RW)