Updated: April 19, 2023

Information about the system's timebase, and other time-related information

The startup library's init_qtime() function fills in the qtime data structures, which contain the following fields:
For ClockCycles().
When added to the value in nsec, gives the number of nanoseconds from the start of the epoch (1970).
The number of nanoseconds since the system was booted.
The number of nanoseconds deemed to have elapsed each time the clock triggers an interrupt.
The time when the system was booted, in seconds since Jan 1 1970 00:00:00 GMT.
If you call ClockTime() to set the time of day, the kernel checks to see if this field is zero. If so, the kernel sets the field to the appropriate value. All startup programs support a -T option that prevents the setting of this field, so the kernel can set it the first time you call ClockTime(). This feature is useful if the RTC hardware isn't in UTC.
Set to zero at startup. Contains any current timebase adjustment runtime parameters (as specified by the kernel call ClockAdjust()).
Used with timer_scale and timer_load (see below).
Used with timer_rate and timer_load (see below).
Timer chip divisor value. The startup program leaves this as zero, and the kernel sets it based on the last ClockPeriod(), timer_rate, and timer_scale values to a certain number, which is then put into the timer chip by the timer_load and timer_reload kernel callouts. For more details, see timer_rate and timer_scale below.
Contains the interrupt vector that the clock chip uses to interrupt the processor.
Currently set to 1970, but not used.
Properties of the timer hardware (see flags below).
Used by the kernel to store the high portion of timer_load (i.e., the portion greater than 32 bits) for 64-bit systems.
The maximum value that can be programmed into the hardware timer for system ticks.
(QNX Neutrino 7.1 or later) Delay, in nanoseconds, for the initialization of high-resolution timers by the OS.
Not used.

nsec and nsec_tod_adjust

The nsec field is always increasing monotonically. It is never affected by setting the current time of day via ClockTime() or ClockAdjust().

Because both nsec and nsec_tod_adjust are modified in the kernel's timer interrupt handler and are too big to load in an atomic read operation, to inspect them you must either:
  • disable interrupts


  • get the value(s) twice and make sure that they haven't changed between the first and second read.

timer_rate and timer_scale

The values in the timer_rate and timer_scale fields relate to the external counter chip's input frequency, in Hz, as follows:

Yes, this relationship does imply that timer_scale is a negative number. The goal when expressing the relationship is to make timer_rate as large as possible in order to maximize the number of significant digits available during calculations. For example, on an x86 system the values might be 838095345UL for timer_rate and -15 for timer_scale. This indicates that the timer value is specified in femtoseconds (the -15 means “ten to the power of negative fifteen”); the actual value is 838,095,345 femtoseconds (or approximately 838 nanoseconds).

If the clock on your system drifts, you should make sure that the startup code specifies the correct clock frequency. To override the setting in the code, use the -f option in the startup command.

Image configuration best practices

You should verify time synchronization between QNX Neutrino and the external hardware source when configuring your boot image. You can do this by using sleep() and confirming that the external and internal time sources are synchronized. For example, you could pass in 60 seconds to the sleep function and verify that both the internal clock and external hardware match.
Note: If you are using ClockCycles() to synchronize with external hardware, you should use this same function to perform testing. For example, you could call this same function for the number of clock cycles in 60 seconds and verify this against the external hardware.


This bit: Means that:
QTIME_FLAG_TIMER_ON_CPU0 Timer hardware is specific to CPU0. This flag is passed by the startup code to the kernel to indicate that any interaction with the hardware timer that is used to generate the system tick (see ClockPeriod()) must be made on CPU0. For more information, see the explanation of the kernel callout timer_load() in the “Timer and clock” topic of the Building Embedded Systems guide.
QTIME_FLAG_CHECK_STABLE Set by the kernel if it is required to sample not only qtime::nsec but also qtime::nsec_stable to acquire a stable current time value. However, clock_gettime() should be called with a clock_id of CLOCK_MONOTONIC.
QTIME_FLAG_TICKLESS Tickless operation is supported. For more information, see “Clocks, timers, and power management” in the “Tick, Tock: Understanding the Microkernel's Concept of Time” chapter of the Programmer's Guide. This flag is passed by the startup code to the kernel to indicate that tickless operation is desired when appropriate. See startup-* and the -Z flag.
QTIME_FLAG_TIMECC Set by the kernel if the libmod_timecc.a module is in use. See the Tracking time page for more information on the libmod_timecc.a module.
QTIME_FLAG_GLOBAL_CLOCKCYCLES ClockCycles() is synchronized between all processors on the system. This flag is passed by the startup code to the kernel to indicate that the hardware timer used by ClockCycles() is synchronized across all CPUs. Sampling ClockCycles() twice should never result in time flowing backward, even in the presence of thread migration. As of QNX SDP 7.0, this is a requirement, but some startups have not yet been modified to reflect this.