.. _ptimer: ==================== PTIMER: Timer engine ==================== .. contents:: Introduction ============ PTIMER is a small functional unit used to measure time by the card. It has a 56-bit tick counter connected to a programmable clock source. The current value of this counter is used for timestamping by many other units on the GPU. Two such timestamps can be substracted to get the wall time elapsed between their creation and measure eg. command execution time. Also, it's possible to set up an interrupt that will be triggered when the low 27 bits of the counter reach a specified value. The PTIMER's MMIO range is 0x101000:0x102000 on NV1, 0x9000:0xa000 on NV3 and later cards. It is enabled by PMC.ENABLE bit 4 [NV1 - shared with PDMA] or 16 [NV3-], and its interrupt line is connected to PMC.INTR line 20. It's available on all cards. Curiously, on NV41+ the PTIMER is also used to report MMIO faults, ie. MMIO space accesses from host that failed for some reason. .. todo:: document that some day]. MMIO register list - NV1 ======================== .. space:: 8 nv1-ptimer 0x1000 time measurement and time-based alarms 0x100 INTR ptimer-intr 0x140 INTR_ENABLE ptimer-intr-enable 0x200 CLOCK_DIV ptimer-clock-div 0x210 CLOCK_MUL ptimer-clock-mul 0x400 TIME_LOW ptimer-time-low 0x404 TIME_HIGH ptimer-time-high 0x410 ALARM ptimer-alarm MMIO register list - NV3- ========================= .. space:: 8 nv3-ptimer 0x1000 time measurement and time-based alarms 0x060 ??? ptimer-unk060 G80: 0x064 ??? ptimer-unk064 G80: 0x080 ??? ptimer-unk080-nv17 NV17:NV20,NV25:G80 0x080 ??? ptimer-unk080-gf100 GF100: 0x084 MMIO_FAULT_ADDR ptimer-mmio-fault-addr NV41: 0x088 MMIO_FAULT_DATA ptimer-mmio-fault-data NV41: 0x100 INTR ptimer-intr 0x140 INTR_ENABLE ptimer-intr-enable 0x200 CLOCK_DIV ptimer-clock-div 0x210 CLOCK_MUL ptimer-clock-mul 0x220 CLOCK_SOURCE ptimer-clock-source NV41: 0x400 TIME_LOW ptimer-time-low 0x410 TIME_HIGH ptimer-time-high 0x420 ALARM ptimer-alarm The clock source ================ The clock that PTIMER counts is generated by applying a selectable ratio to a clock source. The clock source depends on the card: - NV1:NV4: the clock source is :ref:`MCLK, the memory clock ` - NV4:NV40: the clock source is :ref:`NVCLK, the core clock ` - NV40:NV41: the clock source is :ref:`HCLK, the host clock ` - NV41:G84: the clock source can be bound to either the internal clock source or external clock source. Internal clock source is the crystal [see :ref:`pstraps`] frequency multiplied by a small ratio, while external clock source is HCLK, the host clock [:ref:`nv40 `, :ref:`g80 `] - G84 and up: like NV41, but external clock source is TCLK, the PTIMER clock [:ref:`G84 `, :ref:`GT215 `, :ref:`GF100 `] On NV41+ cards, which have both internal and external clock generators, the internal clock generator and the switch is configured by the CLOCK_SOURCE register: .. reg:: 32 ptimer-clock-source clock source selection - bits 0-7: INTERNAL_MUL - specifies the multiplier of internal clock generator minus 1 - bits 8-11: INTERNAL_DIV - specifies the divisor of internal clock generator minus 1 - bit 16: SELECT - if 0, internal clock source used, if 1 external source used The internal clock generator will generate a clock with frequency given by crystal_frequency * (MUL + 1) / (DIV + 1). However, it is not a PLL, but a simple counter - it cannot generate a clock of a higher frequency than what PTIMER logic itself is clocked at, which is equal to the external clock. The clock ratio =============== The clock source is frequency-converted by a simple counter-based converter before being used for counting. The converter multiplies the frequency by the specified ratio. The registers are: .. reg:: 32 ptimer-clock-div clock divider - bits 0-15: clock divider - should not be 0 .. reg:: 32 ptimer-clock-mul clock multiplier - bits 0-15: clock multiplier - has to be between 0 and the clock divider, 0 stops the counter entirely The clock used for the counter is clock_source * CLOCK_MUL / CLOCK_DIV. It's not possible to get a higher frequency than the clock source - the converter will misbehave. .. _ptimer-time: .. _ptimer-perf-time-b12: The time counter ================ PTIMER's clock is a 56-bit value that is spread across two 32-bit registers: .. reg:: 32 ptimer-time-low low part of the time counter - bits 5-31: low 27 bits of the counter - bits 0-4: always 0 .. reg:: 32 ptimer-time-high high part of the time counter - bits 0-28: high 29 bits of the counter - bits 29-31: always 0 The counter is thus embedded in bits 5-60 of a 64-bit number split across the two 32-bit words. Whenever the PTIMER clock is requested by other parts of the card, the returned timestamp will be this 64-bit number. Because of the 5-bit shift, the timestamps are actually counted in units of 1/32 of PTIMER tick, with resolution of 32 ticks. Also, TIME_LOW bit 17 [ie. bit 12 of the actual counter] is connected to a PCOUNTER signal on NV10:GF100, called PTIMER_TIME_B12. Reading the clock ----------------- In order to accurately read the clock, the following code should be used:: uint32 high1, high2, low; do { high1 = mmio_rd32(TIME_HIGH); low = mmio_rd32(TIME_LOW); high2 = mmio_rd32(TIME_HIGH); } while (high1 != high2); This code works around the "mutual dependency". No matter in what order the registers are read, an issue may arise and lead to an error of 2^32 as show by the following examples: - TIME_LOW is read, overflows and then TIME_HIGH is read - TIME_HIGH is read, TIME_LOW overflows, TIME_LOW is read The proposed code checks no overflow on TIME_LOW happened between the moment we read TIME_HIGH and the moment we read TIME_HIGH again. If it happened, we start again until it succeeds. .. _ptimer-intr: The alarm and interrupts ======================== PTIMER can also be used to trigger an interrupt when TIME_LOW matches a specified value. The registers dealing with interrupts are: .. reg:: 32 ptimer-intr interrupt status/acknowledge Status of interrupts generated by PTIMER. On read, returns 1 for bits corresponding to pending interrupts. On write, if 1 is written to a bit, its interrupt gets cleared, if 0 is written nothing happens. .. reg:: 32 ptimer-intr-enable interrupt enable Interrupt enable bitmask. Set to enable, clear to disable. Interrupts that are masked will still show up in INTR when they're triggered, but won't cause the PTIMER interrupt line to go active. The bitfields common to these registers are: - bit 0: ALARM - triggered whenever value of ALARM register is equal to value of TIME_LOW register The alarm time is set in: .. reg:: 32 ptimer-alarm the TIME_LOW value to interrupt on - bits 5-31: alarm time - when this equals the value of bits 5-31 of TIME_LOW, the ALARM interrupt will be triggered - bits 0-4: always 0 Unknown registers ================= .. todo:: figure these out .. reg:: 32 ptimer-unk060 ??? ??? .. reg:: 32 ptimer-unk064 ??? ??? .. reg:: 32 ptimer-unk080-nv17 ??? ??? .. reg:: 32 ptimer-unk080-gf100 ??? ??? .. reg:: 32 ptimer-mmio-fault-data ??? .. todo:: write me .. reg:: 32 ptimer-mmio-fault-addr ??? .. todo:: write me .. todo:: document MMIO_FAULT_*