.. _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 <nv1-clock-mclk>`
- NV4:NV40: the clock source is :ref:`NVCLK, the core clock <nv1-clock-nvclk>`
- NV40:NV41: the clock source is :ref:`HCLK, the host clock <nv40-clock-hclk>`
- 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 <nv40-clock-hclk>`,
  :ref:`g80 <g80-clock-hclk>`]
- G84 and up: like NV41, but external clock source is TCLK, the PTIMER clock
  [:ref:`G84 <g84-clock-tclk>`, :ref:`GT215 <gt215-clock-tclk>`,
  :ref:`GF100 <gf100-clock-tclk>`]

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_*
