Hardware Timers: Difference between revisions
(Add some notes on timer frequency) |
m (→Long Timers: Fix 64-bit counter reading code) |
||
Line 74: | Line 74: | ||
NOTE: on the PSVita CPU, 64-bit accesses are not guaranteed to be atomic. | NOTE: on the PSVita 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. | 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: | To ensure the readings from a timer are accurate, use code similar to the following code: | ||
<source lang="C"> | <source lang="C"> | ||
//Return value should hopefully be optimized into registers | //Return value should hopefully be optimized into registers | ||
SceKernelSysClock readCounter(volatile SceKernelSysClock* | SceKernelSysClock readCounter(volatile SceKernelSysClock* pTimer) { | ||
SceKernelSysClock ctr; | SceKernelSysClock ctr; | ||
SceUInt32 ctrHi; | SceUInt32 ctrHi; | ||
do { | do { | ||
ctr.hi = | ctr.u.hi = pTimer->u.hi; //Read high word first | ||
ctr.lo = | ctr.u.lo = pTimer->u.lo; //Read low word next | ||
ctrHi = | ctrHi = pTimer->u.hi; //Read high word again to make sure no overflow happened | ||
} while ( | } while (ctr.hi != ctrHi); //Try again if high word changed while reading low word | ||
return ctr; | return ctr; |
Revision as of 20:59, 27 February 2023
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.
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 - ?may be accessible in Secure state only? |
Word Timers
Timers with 32-bit counters, unknown base frequency (seems to be in the order of ns).
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, estimated tick period: 5.285ns±0.2ns (~189MHz±2MHz).
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 the PSVita 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; }
Configuration register
The configuration register bit mappings seems to be identical for Word and Long timers.
Timer | Value | Behaviour/notes |
---|---|---|
SceWT7 | 0xDD00000D | ~1MHz frequency, interrupt fired when counter reached threshold |
SceLT5 | 0x2F34500D | 1MHz frequency, interrupt fired when counter reaches threshold |
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? (makes timer tick slower) |