Hardware Timers

From Vita Development Wiki
Jump to navigation Jump to search

The PSVita system embeds 8 Word timers (SceWT0 to SceWT7) and 6 Longrange timers (SceLT0 to SceLT5). The timers consist of a counter that gets incremented at a configurable time interval (multiple of a base frequency?), and can generate an interrupt when a certain value is reached.

Most timers are managed by SceSystimer.

Available timers

The Address column indicates the physical address at which the interface for a timer is located.

Each timer interface takes 0x1000 bytes.

List of timers
Name Address Interrupt ID Usage
SceLT0 / SceSystimerLongrangeTimer0 0xE20B1000 0x88 Available for SceSystimer
SceLT1 / SceSystimerLongrangeTimer1 0xE20B2000 0x89 Available for SceSystimer
SceLT2 / SceSystimerLongrangeTimer2 0xE20B3000 0x8A Available for SceSystimer
SceLT3 / SceSystimerLongrangeTimer3 0xE20B4000 0x8B Available for SceSystimer
SceLT4 / SceSystimerLongrangeTimer4 0xE20B5000 0x8C Available for SceSystimer
SceLT5 0xE20B6000 0x8D Used by SceKernelThreadMgr as CPU timer
SceWT0 / SceSystimerWordTimer0 0xE20B7000 0x80 Available for SceSystimer
SceWT1 / SceSystimerWordTimer1 0xE20B8000 0x81 Available for SceSystimer
SceWT2 / SceSystimerWordTimer2 0xE20B9000 0x82 Available for SceSystimer
SceWT3 / SceSystimerWordTimer3 0xE20BA000 0x83 Available for SceSystimer
SceWT4 / SceSystimerWordTimer4 0xE20BB000 0x84 Available for SceSystimer
SceWT5 / SceSystimerWordTimer5 0xE20BC000 0x85 Available for SceSystimer
SceWT6 / SceSystimerWordTimer6 0xE20BD000 0x86 Available for SceSystimer
SceWT7 / SceTimerForUsleep 0xE20BE000 0x87 Used by Tzs SceKernelIntrMgr for usleep

Misc Reg

Miscellaneous features related to all timers can be accessed from an interface at physical address 0xE20BF000.

Timer Misc Reg interface
Offset Usage
0x0 Bus Error Address
0x4 Bus Error Attributes
0x8 Secure Bus Error Address
0xC Secure Bus Error Attributes

Configuration register

SysTimer Tick can be obtained by the following formula.

1000000000 / (src_clk / (freqDiv + 1)) ns.

src_clk is from SysTimer 48Mhz or ScePower SysClock.

tick example
freq freqDiv Tick
48Mhz 0 20.833ns
48Mhz 47 1µs
48Mhz 0xFF 5.333µs
190Mhz 0 5.263ns
190Mhz 0xFF 1.347µs
222Mhz 0 4.5ns
222Mhz 0xFF 1.152µs

The configuration register bit mappings seems to be identical for Word and Long timers.

Known values
Timer Value Behaviour/notes
SceWT7 (SceTimerForUsleep) 0xDD00000D ~1MHz frequency, interrupt fired when counter reached threshold
SceLT5 0x2F34500D 1MHz frequency, interrupt fired when counter reaches threshold
Configuration register bit mappings
Mask Effect Notes
0x00000001 Counting enable 0x0 = timer stopped, 0x1 = timer counting
0x0000000E ?
0x00000070 ? ?Mode select?
0x00000080 ?
0x00000700 ?
0x00000800 ? Unused?
0x0000F000 ?
0x000F0000 ?
0x00F00000 ? Related to frequency scaling
0xFF000000 freqDiv Timer frequency is divided by this+1 (the higher the value to makes timer tick slower)

Word Timers

Timers with 32-bit counters.

typedef volatile _SceWordTimer {
    SceUInt32 unk0; // Value at which an interrupt is triggered?
    SceUInt32 current; // Current value of the timer's counter
    SceUInt32 cfg; // Configuration register
    SceUInt32 unkC; // Another counter?
    SceUInt32 unk10; // Unused?
    SceUInt32 unk14; // Interrupt status? Write 0x3 to clear pending IRQ?
} SceWordTimer;

Long Timers

Timers with 64-bit counters.

SceLT5 timer is configured to increment every microsecond (1MHz frequency).

typedef volatile _SceLongTimer {
    SceKernelSysClock current;  // Current value of the timer counter
    SceKernelSysClock unk8;  // Value at which an interrupt is triggered?
    SceKernelSysClock unk10; // Another counter?
    SceUInt32 unk18; // Interrupt status? Write 0x3 to clear pending IRQ?
    SceUInt32 cfg; // Configuration register
} SceLongTimer;

NOTE: On ARMv7 processors that do not support the Large Physical Address Extension, such as the PS Vita's CPU, 64-bit accesses are not guaranteed to be atomic.

This can lead to issues when reading the timer if the low word of a counter is about to overflow.

To ensure the readings from a timer are accurate, use code similar to the following code:

//Return value should hopefully be optimized into registers
SceKernelSysClock readCounter(volatile SceKernelSysClock* pTimer) {
   SceKernelSysClock ctr;
   SceUInt32 ctrHi;
   do {
       ctr.u.hi = pTimer->u.hi; //Read high word first
       ctr.u.lo = pTimer->u.lo; //Read low word next
       ctrHi = pTimer->u.hi;  //Read high word again to make sure no overflow happened
    } while (ctr.hi != ctrHi); //Try again if high word changed while reading low word

    return ctr;
}