E

Creating a VxWorks Timestamp Driver



E.1    Introduction

Detailed monitoring of real-time application performance requires timing information based on high-resolution timers. You can extend the range of information available from VxWorks kernel instrumentation by supplying a timestamp driver. For example, if a timestamp driver is available, a precise chronology can be displayed by WindView, a graphical analysis tool for real-time and embedded systems based on VxWorks.

The timer is a hardware facility; a timestamp driver is a software interface to that facility. This appendix describes the standard interfaces for a VxWorks timestamp driver, and discusses the requirements for a hardware timer for use with VxWorks kernel instrumentation. It is not a step-by-step tutorial on the process of writing a timestamp driver.

This appendix is meant for the following readers:

  • VxWorks users who need to add a timestamp driver to an existing BSP.

  • VxWorks users who wish to use an existing VxWorks timestamp driver in their own applications.

This chapter assumes that the reader has a working knowledge of the target board hardware. No knowledge of the VxWorks kernel or of WindView is assumed, although experience writing device drivers is helpful.



E.2    Hardware Overview

This section discusses typical hardware timer modes of operation and characteristics. This section also defines the VxWorks requirements for timestamp drivers.

Modes of Operation

Most target boards have multiple hardware timers available for operating system and application use. The characteristics of timers vary widely due to evolving hardware technology. Generally, timers on older target boards are not as easy to work with as timers on newer, more refined hardware. However, many different types of timers are suitable for use with VxWorks.

In its most basic form (shown in Figure E-1), a timer is simply a timing source (that is, a clock) used as input to a counter. The counter counts up or down as the associated clock transitions.     

Figure E-1:   Basic Form of Timer

There are three common modes in which timers operate: periodic, one-shot, and timestamp. Many newer timers are versatile, and can be used in any one of these modes, depending on how they are configured. The characteristics of each mode are as follows:

Periodic Interrupt Timer

The timer counts up or down to a programmed value (called the terminal count), at which point it generates a hardware interrupt. The counter is reset (either by hardware or software), and begins to count up or down again towards the terminal count. The interrupt is the sole output of a periodic interrupt timer. After acknowledging the interrupt, an interrupt service routine (ISR) usually calls an operating system facility to log the interrupt as a clock tick. In some cases, the ISR calls an application-specific routine instead.

The terminal count may be adjusted so that an interrupt is generated at a specified time interval. For instance, if the terminal count is set such that an interrupt occurs every 10 msec, 100 ticks per second are generated (100Hz). The VxWorks system and auxiliary clocks use the underlying hardware timers in periodic interrupt mode.

One-Shot Timer

The timer counts up or down to a programmed terminal count, at which point it generates a hardware interrupt. The counter is then disabled (either by hardware or software). An ISR acknowledges the interrupt, and then calls a user-specified routine.

Currently, VxWorks does not provide a one-shot timer facility, although this type of timer may be simulated by having a periodic interrupt timer disable the counter in the ISR.

Timestamp Timer

The timer counts up or down to its maximum count (0 or 0xfff) at which point it generates a hardware interrupt. The counter rolls over and begins to count again towards the maximum value. After acknowledging the interrupt, an ISR calls an operating system facility or application-specific routine to log the counter rollover. At any time, the operating system or application may read the counter value to obtain high-resolution timing information in timestamp tick units.

This mode of operation differs from a periodic interrupt timer in that the counter is usually allowed to count to its maximum value (that is, the terminal count is set to 0 or 0xfff). Additionally, the counter value is the primary output of the timestamp timer, and the interrupt is only used to announce a counter rollover. Timestamp timer components are typically similar to Figure E-2.      

Figure E-2:   Components of a Timestamp Timer

The remainder of this document deals only with timers operating in timestamp mode.

Characteristics of Hardware Timers

Several factors determine how suitable a particular hardware timer may be for a timestamp driver. This information may help to choose an appropriate timer, if several are available.

Read While Enabled

The most important characteristic of a good timestamp timer is the ability to read the counter's value without having to stop the timer from counting. If the timer must be disabled to read the timestamp value accurately, the time spent without the timer running is not recorded, although the system is actually doing work and other timers are continuing to run (the system clock, for instance). This situation is commonly called time skew. As time skew accumulates, the timestamp values become more and more removed from the absolute time of the system, as kept by the system clock. Additionally, interrupts must be locked out while the timer is stopped. Both of these effects are detrimental to real-time systems.

Prescaler Counter

The input clock is often passed through a prescaler counter to divide the input clock frequency, thereby producing a lower frequency input for the timestamp counter. Although a prescaler is not always present (and need not be used) it can be a useful way of tuning timer devices that have an unusually high input clock frequency.

Counter Width

The timer's counter should be at least 16 bits wide, although a 24- or 32-bit counter is preferable. The wider a counter, the less often it must roll over, and therefore the less system overhead its ISR incurs. The input frequency can also be higher with a wide counter, which yields more accurate timing information.

Preload After Disable

Some timers require that the counter be preloaded with a value before counting resumes. This is an issue only for timers that cannot be read while enabled. This characteristic adds to the time spent with the timer disabled, thereby increasing time-skew problems. The preload mechanism itself provides a way to correct skew, but determining the amount of the correction is difficult; see the discussion of counter preloading in Working Around Deficiencies in Hardware Timers.

Cache Coherency

As with all hardware devices, the locations of timer device registers must be cache coherent. This ensures that reads and writes to timer registers are actually accessing the register locations themselves, and not CPU data cache locations. If data cache memory exists, and there is no hardware mechanism (such as an MMU) to guarantee data cache coherency for register locations, the timestamp timer driver must make explicit calls to flush and invalidate the CPU's data cache. This adds to the overhead of reading the timestamp tick value. For more information, see the VxWorks BSP Porting Kit: Cache and Virtual Memory Library Support.

VxWorks Requirements for Timestamp Timers

The VxWorks kernel instrumentation uses a timestamp timer, when available, to log timing information for selected operating system events--for example, semaphore gives and takes, task spawns and deletions, system clock ticks, and interrupts.

VxWorks requires that timestamp timers provide these features:

Rollover Interrupt

The timestamp timer must be able to generate a hardware interrupt once the maximum (or terminal) count is reached. An interrupt is needed to avoid aliasing, by announcing the rollover event. Without the interrupt, timestamps are ambiguous, since there is no way to distinguish two timestamps separated by the timer's period.

Fine Resolution

The timestamp tick resolution is calculated as follows:



 

To be effective, the resolution should be 10 usec or less (that is, a timestamp tick frequency of at least 100KHz). Although this is not a strict requirement, it is consistent with timing limitations within the VxWorks kernel. If the timestamp timer output is slower than 100KHz, some instrumented kernel events may not have distinguishable timing information.

Sizable Period

The time between timestamp rollovers is the timestamp timer's period. The period is defined as the product of the timer resolution and the timer's maximum count:



To be effective, the period should be at least 10 msec. If rollovers are more frequent, the overhead of servicing the rollover interrupt may be too intrusive. The greater the period, the better.



E.3    Timestamp Driver Interface

The timestamp timer interface is a non-standard device driver interface; it does not go through the VxWorks I/O system. Although the interface was developed for use with VxWorks kernel instrumentation, it is also useful as a general BSP facility. The timestamp driver's external interface is expected to change in the future, when a more generic, abstracted timer facility is adopted.

The following sections describe each procedure and its external interface. The descriptions are for a standard timestamp driver. Although the external functionality must remain as described here, procedure content may differ for a particular driver implementation.


*      
NOTE: Remember that each routine must return the appropriate value, as described in the following sections. For example, sysTimestampEnable( ) must return OK if successful, or ERROR if not successful. OK and ERROR are defined in the VxWorks header file installDir/target/h/vxWorks.h.

sysTimestampConnect( )

This routine specifies the timestamp callback routine, a routine to be run each time the timestamp counter rolls over. If this facility is available, sysTimeStampConnect( ) must store the function pointer in the global variable sysTimestampRoutine and return OK, to indicate success.

If the callback cannot be provided, sysTimeStampConnect( ) returns ERROR to indicate that no callback routine is connected. In this situation, the VxWorks kernel instrumentation does not use the interrupt handler sysTimestampInt( ) as part of its timestamp timer implementation, but relies instead on the system clock tick to signal a timestamp reset event (see Using the VxWorks System Clock Timer). To use the timestamp driver in other applications, you must make similar provisions for an ERROR result.


*      
NOTE: sysTimestampConnect( ) must not enable or disable the timestamp timer itself. Initialization must be done in the sysTimestampEnable( ) routine.

STATUS sysTimestampConnect 
    ( 
    FUNCPTR routine, 
    int arg 
    )

The arguments for this routine are:

routine
pointer to the function called at each timer rollover interrupt.

arg
argument for routine.

The result must be OK or ERROR.

sysTimestampEnable( )

If the timer is not already enabled, this routine performs all the necessary initialization for the timer (for example, connecting the interrupt vector, resetting registers, configuring for timestamp mode, and so on), and then enables the timestamp timer. If the timer is already enabled, this routine simply resets the timer counter value.

STATUS sysTimestampEnable (void)

This routine takes no arguments.

The result must be OK or ERROR.

sysTimestampDisable( )

This routine disables the timestamp timer. Interrupts are not disabled. However, the tick counter does not count after the timestamp timer is disabled; thus, rollover interrupts are no longer generated.

STATUS sysTimestampDisable (void)

This routine takes no arguments.

The result must be OK or ERROR.

sysTimestampPeriod( )

This routine returns the period of the timer in timestamp ticks. The period is the number of ticks the timestamp timer counts before rolling over (or resetting) and restarting the counting process.

UINT32 sysTimestampPeriod (void)

This routine takes no arguments.

The result must be the period of the timer in timestamp ticks.

sysTimestampFreq( )

This routine returns the output frequency of the timer, in timestamp ticks per second. When possible, the frequency should be derived from actual hardware register values.

If the timer input clock is programmable, do not set its clock rate in sysTimestampFreq( ). Setting the timer input clock rate should be part of the initialization performed by sysHwInit( ) in sysLib.c.

UINT32 sysTimestampFreq (void)

This routine takes no arguments.

The result must be the timestamp timer frequency, in ticks per second.

sysTimestamp( )

This routine returns the current value of the timestamp counter. To convert this tick count to seconds, divide by the result of sysTimestampFreq( ). The result must increase monotonically; that is, the timestamp values must count up. If you are working with a timer that actually counts down, see Working Around Deficiencies in Hardware Timers.

This routine should be called with interrupts locked. If interrupts are not already locked, call sysTimestampLock( ) instead.

UINT32 sysTimestamp (void)

This routine takes no arguments.

The result must be the current tick count of the timestamp timer.

sysTimestampLock( )

This routine returns the current value of the timestamp counter. To convert the result to seconds, divide the tick count by the result of sysTimestampFreq( ). The result must increase monotonically; that is, the timestamp values must count up. If you are working with a timer that actually counts down, see Working Around Deficiencies in Hardware Timers.

This routine locks interrupts for cases where it is necessary to stop the tick counter to read it, or when two independent counters must be read. If interrupts are already locked, call sysTimestamp( ) instead.

UINT32 sysTimestampLock (void)

This routine takes no arguments.

The result must be the current tick count of the timestamp timer.



E.4    Writing the Driver

This section discusses three example cases of timestamp device drivers (corresponding to the sample code in E.6 Sample Drivers).

Timers That Can Be Read While Enabled

Example E-1 shows a sample device driver for hardware timers that can be read while enabled. This type of timer is the simplest to configure for timestamp mode, and the device driver code is straightforward. Note the following details:

Timer Period

The timer should be configured for the highest period possible by setting the terminal count to its maximum value (usually 0xfff when counting up, and 0 when counting down).

Interrupt Level

If programmable, a high-priority interrupt level should be used for boards with a low timer period. This ensures that frequent rollover interrupts are serviced without delay, and that the rollover event is registered in a timely manner with the timestamp callback routine (sysTimestampRoutine).

Interrupt locking

Timers that can be read while enabled do not need to lock interrupts in the sysTimestampLock( ) routine.

Working Around Deficiencies in Hardware Timers

The sample device driver in Example E-2 illustrates techniques for using a hardware timer that cannot be read while enabled, requires preloading, and counts down. This combination of timer attributes presents several problems for the device driver:

Timer Re-Synchronization

If a timestamp timer cannot be read while enabled, a second correction timer can compensate: use the correction timer to reset the timestamp timer periodically. In this scenario, the second timer runs as a periodic interrupt timer. On each interrupt it resets the first (counting) timer. The counting timer is stopped and read for timestamp values, but never generates an interrupt because it is always reset before reaching its terminal count. However, the correction timer does generate interrupts; because it is not read for timestamp values, it never has time-skew problems. The correction timer ISR resets the counting timer, and then calls the timestamp callback routine (sysTimestampRoutine).

This approach clears the time skew that accumulates in the counting timer between resets. Although a discernible time skew may be present towards the end of the timer period, it is flushed by the reset operation.

Timer Period

Because the counting timer is always reset by the correction timer, the timestamp timer period is really the correction timer period. In the Example E-2 sample code, this period is set by the TS_CORRECTION_PERIOD macro. The value must balance a short period's increased interrupt service rate with a long period's noticeable time skew accumulation.

The chosen period should be based on the amount of time skew that can accumulate, which is related to how often the timestamp facility is called and to the sensitivity of the application using the facility. Our experience is that a correction period of 100 to 150 msec sufficiently satisfies both requirements for most applications.

Down Counter

The timestamp values must increase monotonically. If the timer in use actually counts down, the tick count must be converted to an incrementing value. This is easily done by subtracting the counter value from the reset value (usually 0xfff for a down counter).

Counter Preloading

If the counter value must be preloaded before the timer can resume counting, three subroutines must perform this action: sysTimestamp( ), sysTimestampLock( ), and sysTimestampEnable( ). The preload operation adds to the time spent with the timer disabled, exacerbating time-skew problems.

After the sysTimestampEnable( ) routine enables the counting timer, it may need to delay until the preload value physically gets loaded into the counter. This is an issue for timers that synchronize the preload with a prescaler output transition. If a delay is not inserted, it may be possible for a fast target board to execute the timer preload, return from sysTimestampEnable( ), and call sysTimestamp( ), which stops the timer and specifies a different preload value. This would nullify the sysTimestampEnable( ) reset operation.

Adjustment for Time Skew

Counters that are writable or that have a preload mechanism can compensate for time skew. While the counter is stopped for a read operation, the counter value or the preload value may be adjusted by adding (for an up counter) or subtracting (for a down counter) the number of ticks spent with the timer disabled. The Example E-2 sample code subtracts the TS_SKEW macro (0, by default) from the stopped timestamp counter in an attempt to make up for "lost" time. Note that the adjustment value is not only board-dependent, it is influenced by CPU speed, cache mode, memory speed, and so on. In the default case (TS_SKEW = 0), compiler optimization eliminates the TS_SKEW adjustment.

Counter Read Optimization

Write the sysTimestamp( ) and sysTimestampLock( ) routines so that the counter and preload register locations are set before the timer is stopped, in order to reduce the time spent with the counter disabled. This minor change causes a significant reduction in time skew.

Using the VxWorks System Clock Timer

Example E-3 exhibits a sample device driver that reads the VxWorks system clock timer to obtain the timestamp tick count. This approach is useful if there are no other timers available, and if the system clock timer's counter can be read while enabled.

Timer Rollover Interrupt

When the system clock timer is used as the timestamp timer, the usual sysTimestampInt( ) routine cannot be used to service the timer interrupt. This is because the system clock timer already has an ISR. Thus, the system clock tick can be monitored to provide timestamp rollover information. The sysTimestampConnect( ) routine always returns ERROR because the sysTimestampRoutine callback routine is not used.

Timer Counter Not Reset

Because the system clock is independent of the timestamp facility, the timestamp driver must not disrupt the system clock in any way. Thus, sysTimestampEnable( ) does not reset the timer counter for the system clock. This causes inaccurate timestamp values until the first system clock tick ISR resets the timer counter. For similar reasons, sysTimestampDisable( ) does not physically disable the system clock.

Timer Period

The period of the system clock timer is under the control of the system clock facility, not under the control of the timestamp driver. Thus, the system and the application should not call sysClkRateSet( ) to change the system clock rate once sysTimestampPeriod( ) has been called to determine the timestamp timer period.



E.5    Using the Timestamp Driver with WindView

Although the timestamp timer is meant to be a general facility, some specific information is needed to use it with the kernel instrumentation support for WindView. This section describes the configuration and attachment of the timestamp driver to the VxWorks kernel instrumentation.

Attaching the Timestamp Driver to VxWorks

For information on configuring timestamp drivers into VxWorks, see the Tornado User's Guide: Projects. The configuration macro for this facility is INCLUDE_TIMESTAMP; selecting it makes the timestamp timer routines available to instrumentation logging routines with wvTmrRegister( ).1 This enables the code in usrRoot( ) (in installDir/target/config/all/usrConfig.c) that connects the timestamp driver to the VxWorks kernel instrumentation package.

If you use the standard routine names (described in E.3 Timestamp Driver Interface), no other changes are necessary. However, you can also create routines with custom names. This is necessary if a VxWorks timestamp driver is already available for a particular target board, and an alternate driver is to be connected. If this is the case, define INCLUDE_USER_TIMESTAMP as well as INCLUDE_TIMESTAMP, to connect the routines named by the USER_TIMExxx macros instead of the default timestamp routines. This does not change in any way the functionality required for any of the routines. It merely provides the ability to connect routines with different names. The connected routines must still adhere to the requirements and functionality specified in E.3 Timestamp Driver Interface.

Using the System Clock

The kernel instrumentation expects each rollover event to trigger a call to the timestamp callback routine (saved in the variable sysTimestampRoutine). As described in section Using the VxWorks System Clock Timer, the timestamp driver may use the VxWorks system clock facility. If sysTimestampConnect( ) returns ERROR, the VxWorks kernel instrumentation assumes the system clock is used, and relies on the system clock tick to signal a timestamp timer rollover event.



E.6    Sample Drivers

The following sections contain skeleton code for three different types of timestamp driver:

  • for a hardware timer that can be read while enabled

  • for a hardware timer that cannot be read while enabled

  • for systems that have no suitable spare timers, thus requiring that timestamps be derived from the VxWorks system clock timer

For an online template that you can use as the basis of your own timestamp driver, see target/src/drv/templateTimer.c.

Example E-1:   Timestamp Drivers That Can Be Read While Enabled

This example shows a skeleton timestamp device driver for a hardware timer that can be read while enabled. This type of timer is the simplest to configure for timestamp mode. See Timers That Can Be Read While Enabled, for a discussion of the most important details involved in writing this kind of driver.

/* sampleATimer.c - sample A timer library */ 
 
/* Copyright 1994 Wind River Systems, Inc. */ 
#include "copyright_wrs.h" 
 
/* 
modification history 
-------------------- 
01a,23mar94,dzb  written. 
*/ 
 
/* 
DESCRIPTION 
This library contains sample routines to manipulate the timer functions on 
the sample A chip with a board-independent interface.  This library handles 
the timestamp timer facility. 
 
To include the timestamp timer facility, the macro INCLUDE_TIMESTAMP must be 
defined. 
 
NOTE:  This module provides an example of a VxWorks timestamp timer driver 
for a timer that can be read while enabled.  It illustrates the structures 
and routines discussed in the Appendix, "Creating a VxWorks Timestamp 
Driver."  This module is only a template.  In its current form, it will not 
compile. 
*/ 
 
#ifdef  INCLUDE_TIMESTAMP 
 
#include "drv/timer/timestampDev.h" 
#include "drv/timer/sampleATimer.h" 
 
/* Locals */  
 
LOCAL BOOL    sysTimestampRunning = FALSE;      /* running flag */ 
LOCAL FUNCPTR sysTimestampRoutine = NULL;       /* user rollover routine */ 
LOCAL int     sysTimestampArg     = NULL;       /* arg to user routine */ 
 
/*************************************************************************** 
* 
* sysTimestampInt - timestamp timer interrupt handler 
* 
* This routine handles the timestamp timer interrupt.  A user routine is 
* called, if one was connected by sysTimestampConnect(). 
* 
* RETURNS: N/A    
* 
* SEE ALSO: sysTimestampConnect() 
*/ 
 
LOCAL void sysTimestampInt (void) 
    { 
    /* acknowledge the timer rollover interrupt here */ 
 
    if (sysTimestampRoutine != NULL)    /* call user-connected routine */ 
        (*sysTimestampRoutine) (sysTimestampArg); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampConnect - connect a user routine to the timestamp timer 
*                         interrupt 
* 
* This routine specifies the user interrupt routine to be called at each 
* timestamp timer interrupt.  It does not enable the timestamp timer itself. 
* 
* RETURNS: OK, or ERROR if sysTimestampInt() interrupt handler is not used. 
*/ 
  
STATUS sysTimestampConnect 
    ( 
    FUNCPTR routine,    /* called at each timestamp timer interrupt */ 
    int arg             /* argument with which to call routine */ 
    ) 
    { 
    sysTimestampRoutine = routine; 
    sysTimestampArg = arg; 
    return (OK); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampEnable - initialize and enable the timestamp timer 
* 
* This routine connects the timestamp timer interrupt and initializes the 
* counter registers.  If the timestamp timer is already running, this routine 
* merely resets the timer counter. 
* 
* Set the rate of the timestamp timer input clock explicitly within the 
* BSP, in the sysHwInit() routine.  This routine does not initialize 
* the timer clock rate. 
* 
* RETURNS: OK, or ERROR if the timestamp timer cannot be enabled. 
*/ 
  
STATUS sysTimestampEnable (void) 
    { 
    if (sysTimestampRunning) 
        { 
        /* clear the timer counter here */ 
 
        return (OK); 
        } 
     
    /* connect interrupt handler for the timestamp timer */ 
 
    (void) intConnect (INUM_TO_IVEC (XXX), sysTimestampInt, NULL); 
 
    sysTimestampRunning = TRUE; 
 
    /* set the timestamp timer's interrupt vector to XXX (if necessary) */ 
    /* reset & enable the timestamp timer interrupt */ 
 
    /* set the period of timestamp timer (see sysTimestampPeriod()) */ 
  
    /* clear the timer counter here */ 
    /* enable the timestamp timer here */ 
  
    return (OK); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampDisable - disable the timestamp timer 
* 
* This routine disables the timestamp timer.  Interrupts are not disabled. 
* However, the tick counter will not increment after the timestamp timer 
* is disabled, ensuring that interrupts are no longer generated. 
* 
* RETURNS: OK, or ERROR if the timestamp timer cannot be disabled. 
*/ 
  
STATUS sysTimestampDisable (void) 
    { 
    if (sysTimestampRunning) 
        { 
        /* disable the timestamp timer here */ 
 
        sysTimestampRunning = FALSE; 
        } 
 
    return (OK); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampPeriod - get the timestamp timer period 
* 
* This routine returns the period of the timer in timestamp ticks. 
* The period, or terminal count, is the number of ticks to which the 
* timestamp timer counts before rolling over and restarting the counting 
* process. 
* 
* RETURNS: The period of the timer in timestamp ticks. 
*/ 
  
UINT32 sysTimestampPeriod (void) 
    { 
    /* 
     * Return the timestamp timer period here. 
     * The highest period (maximum terminal count) should be used so 
     * that rollover interrupts are kept to a minimum. 
     */ 
    } 
 
/*************************************************************************** 
* 
* sysTimestampFreq - get the timestamp timer clock frequency 
* 
* This routine returns the frequency of the timer clock, in ticks per second. 
* The rate of the timestamp timer should be set explicitly in the BSP, 
* in the sysHwInit() routine. 
* 
* RETURNS: The timestamp timer clock frequency, in ticks per second. 
*/ 
  
UINT32 sysTimestampFreq (void) 
    { 
    UINT32 timerFreq; 
 
    /* 
     * Return the timestamp tick output frequency here. 
     * This value can be determined from the following equation: 
     *     timerFreq = clock input frequency / prescaler 
     *  
     * When possible, read the clock input frequency and prescaler values 
     * directly from chip registers. 
     */ 
  
    return (timerFreq); 
    } 
 
/*************************************************************************** 
* 
* sysTimestamp - get the timestamp timer tick count 
* 
* This routine returns the current value of the timestamp timer tick counter. 
* The tick count can be converted to seconds by dividing by the return of 
* sysTimestampFreq(). 
* 
* Call this routine with interrupts locked.  If interrupts are 
* not already locked, use sysTimestampLock() instead. 
* 
* RETURNS: The current timestamp timer tick count. 
* SEE ALSO: sysTimestampLock() 
*/ 
  
UINT32 sysTimestamp (void) 
    { 
    /* return the timestamp timer tick count here */ 
    } 
 
/*************************************************************************** 
* 
* sysTimestampLock - get the timestamp timer tick count 
* 
* This routine returns the current value of the timestamp timer tick counter. 
* The tick count can be converted to seconds by dividing by the return of 
* sysTimestampFreq(). 
* 
* This routine locks interrupts for cases where it is necessary to stop the 
* tick counter before reading it, or when two independent counters must 
* be read.  If interrupts are already locked, use sysTimestamp() instead. 
* 
* RETURNS: The current timestamp timer tick count. 
* 
* SEE ALSO: sysTimestamp() 
*/ 
  
UINT32 sysTimestampLock (void) 
    { 
    /* 
     * Return the timestamp timer tick count here. 
     * Interrupts do *not* need to be locked in this routine if 
     * the counter need not be stopped before reading. 
     */ 
    } 
 
#endif  /* INCLUDE_TIMESTAMP */

Example E-2:   Timestamp Drivers for Deficient Timers

This example shows a skeleton timestamp device driver for a hardware timer that cannot be read while enabled, requires preloading, and counts down. See Working Around Deficiencies in Hardware Timers, for a discussion of the most important details involved in writing this kind of driver.

/* sampleBTimer.c - sample B timer library */ 
 
/* Copyright 1984-1994 Wind River Systems, Inc. */ 
#include "copyright_wrs.h" 
 
/*
modification history 
-------------------- 
01a,23mar94,dzb  written. 
*/ 
 
/* 
DESCRIPTION 
This library contains sample routines to manipulate the timer functions on 
the sample B chip with a board-independent interface.  This library handles 
the timestamp timer facility.   
 
To include the timestamp timer facility, the macro INCLUDE_TIMESTAMP must be 
defined. 
 
To support the timestamp timer facility, two timers are used:  a counting timer, and a correction timer.  The counting timer is used as the timestamp counter, but must be stopped to be read, thereby introducing time skew.  The correction timer periodically resets the counting timer in an effort to alleviate cumulative time skew.  In addition, the correction timer interrupt is used for one other purpose: to alert the user to a counting timer reset (analogous to a timestamp rollover event). 
 
The TS_CORRECTION_PERIOD macro defines the period of the correction 
timer, which translates to the period of the counting timer reset 
(analogous to a timestamp rollover event).  The TS_SKEW macro 
can be used to compensate for time skew incurred when the counting timer 
is stopped in sysTimestamp() and sysTimestampLock().  The value 
of TS_SKEW is subtracted from the stopped timestamp counter in an attempt to  
make up for "lost" time.  The correct value to adjust the timestamp counter 
is not only board-dependent, it is influenced by CPU speed, cache mode, 
memory speed, and so on. 
 
NOTE:  This module provides an example of a VxWorks timestamp timer driver 
for a timer that cannot be read while enabled, requires preloading, and 
counts down.  It illustrates the structures and routines discussed in the 
Appendix, "Creating a VxWorks Timestamp Driver."  This module is only a 
template.  In its current form, it will not compile. 
*/ 
 
/* includes */ 
 
#include "drv/timer/timestampDev.h" 
#include "drv/timer/sampleBTimer.h" 
 
#ifdef  INCLUDE_TIMESTAMP 
 
/* defines */ 
 
#ifndef TS_CORRECTION_PERIOD 
#define TS_CORRECTION_PERIOD 0xXXX...   /* timestamp skew correction per. */ 
#endif  /* TS_CORRECTION_PERIOD */      /* see sysTimestampPeriod() */ 
 
#ifndef TS_SKEW 
#define TS_SKEW 0                       /* timestamp skew correction time */ 
#endif  /* TS_SKEW */ 
 
/* locals */ 
 
LOCAL BOOL    sysTimestampRunning = FALSE;      /* running flag */ 
LOCAL FUNCPTR sysTimestampRoutine = NULL;       /* user rollover routine */ 
LOCAL int     sysTimestampArg     = NULL;       /* arg to user routine */ 
 
/*************************************************************************** 
* 
* sysTimestampInt - correction timer interrupt handler 
* 
* This routine handles the correction timer interrupt.  A user routine is 
* called, if one was connected by sysTimestampConnect(). 
* 
* RETURNS: N/A 
* 
* SEE ALSO: sysTimestampConnect() 
*/ 
 
LOCAL void sysTimestampInt (void) 
    { 
    /* acknowledge the correction timer interrupt here */ 
 
    sysTimestampEnable (); 
 
    if (sysTimestampRoutine != NULL)    /* call user-connected routine */ 
        (*sysTimestampRoutine) (sysTimestampArg); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampConnect - connect a user routine to the timestamp timer 
*                         interrupt 
* 
* This routine specifies the user interrupt routine to be called at each 
* timestamp timer interrupt.  It does not enable the timestamp timer itself. 
* 
* RETURNS: OK, or ERROR if sysTimestampInt() interrupt handler is not used. 
*/ 
  
STATUS sysTimestampConnect 
    ( 
    FUNCPTR routine,    /* routine called at each timestamp timer interrupt */ 
    int arg             /* argument with which to call routine */ 
    ) 
    { 
    sysTimestampRoutine = routine; 
    sysTimestampArg = arg; 
 
    return (OK); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampEnable - initialize and enable the timestamp timer 
* 
* This routine connects the timestamp timer interrupt and initializes the 
* counter registers.  If the timestamp timer is already running, this routine 
* merely resets the timer counter. 
* 
* Set the rate of the timestamp timer input clock explicitly within the 
* BSP, in the sysHwInit() routine.  This routine does not initialize 
* the timer clock rate. 
* 
* RETURNS: OK, or ERROR if the timestamp timer cannot be enabled. 
*/ 
 
STATUS sysTimestampEnable (void) 
    { 
    int lockKey; 
 
    if (sysTimestampRunning) 
        { 
        lockKey = intLock ();                   /* LOCK INTERRUPTS */
        /* disable the counting timer here */ 
 
        /* preload the reset count here */ 
 
        /* enable the counting timer here */ 
 
        /* wait for preload to take effect here */ 
 
        intUnlock (lockKey);                    /* UNLOCK INTERRUPTS */ 
 
        return (OK); 
        } 
 
    /* connect interrupt handler for the correction timer */ 
 
    (void) intConnect (INUM_TO_IVEC (XXX), sysTimestampInt, NULL); 
 
    /* set the correction timer's interrupt vector to XXX (if necessary) */ 
 
    sysTimestampRunning = TRUE; 
 
    /* set the period of the correction timer (see sysTimestampPeriod()) */ 
    /* set the period of the  counting timer = reset count */ 
 
    /* enable the counting timer here */ 
    /* enable the correction timer here */ 
 
    /* wait for preload to take effect on both timers here */ 
 
    return (OK); 
    }
/*************************************************************************** 
* 
* sysTimestampDisable - disable the timestamp timer 
* 
* This routine disables the timestamp timer.  Interrupts are not disabled. 
* However, the tick counter will not decrement after the timestamp timer 
* is disabled, ensuring that interrupts are no longer generated. 
* 
* RETURNS: OK, or ERROR if the timestamp timer cannot be disabled. 
*/ 
  
STATUS sysTimestampDisable (void) 
    { 
    if (sysTimestampRunning) 
        { 
        sysTimestampRunning = FALSE; 
 
        /* disable the correction timer here */ 
        /* disable the counting timer here */ 
        } 
  
    return (OK); 
    } 
 
/*************************************************************************** 
* sysTimestampPeriod - get the timestamp timer period 
* 
* This routine returns the period of the timer in timestamp ticks. 
* The period, or terminal count, is the number of ticks to which the 
* timestamp timer counts before rolling over and restarting the counting 
* process. 
* 
* RETURNS: The period of the timer in timestamp ticks. 
*/ 
  
UINT32 sysTimestampPeriod (void) 
    { 
    /* 
     * Return the correction timer period here. 
     * A reasonable correction period should be chosen.  A short period 
     * causes increased CPU overhead due to correction timer interrupts. 
     * A long period allows for a large accumulation of time skew 
     * due to sysTimestamp() calls stopping the counting timer. 
     */ 
 
    return (TS_CORRECTION_PERIOD); 
    } 
 
 
/*************************************************************************** 
* sysTimestampFreq - get the timestamp timer clock frequency 
* 
* This routine returns the frequency of the timer clock, in ticks per second. 
* The rate of the timestamp timer should be set explicitly in the BSP, 
* in the sysHwInit() routine. 
* 
* RETURNS: The timestamp timer clock frequency, in ticks per second. 
*/ 
  
UINT32 sysTimestampFreq (void) 
    { 
    UINT32 timerFreq; 
 
    /* 
     * Return the timestamp tick output frequency here. 
     * This value can be determined from the following equation: 
     *     timerFreq = clock input frequency / prescaler  
     *  
     * When possible, read the clock input frequency and prescaler values  
     * directly from chip registers.  
     */   
 
    return (timerFreq); 
    } 
 
/*************************************************************************** 
* 
* sysTimestamp - get the timestamp timer tick count 
* 
* This routine returns the current value of the timestamp timer tick counter. 
* The tick count can be converted to seconds by dividing by the return of 
* sysTimestampFreq(). 
* 
* Call this routine with interrupts locked.  If interrupts are 
* not already locked, use sysTimestampLock() instead. 
* 
* RETURNS: The current timestamp timer tick count. 
* 
* SEE ALSO: sysTimestampLock() 
*/ 
  
UINT32 sysTimestamp (void) 
    { 
    UINT32 tick = 0; 
    register UINT32 * pTick; 
    register UINT32 * pPreload; 
 
    if (sysTimestampRunning) 
        { 
        /* pTick = counter read register location */ 
        /* pPreload = counter preload register location */ 
 
        /* disable counting timer here */ 
 
        tick = *pTick;                  /* read counter value */ 
        *pPreload = tick - TS_SKEW;     /* set preload value  
                                           (with time-skew adjustment) */ 
 
        /* enable counting timer here */ 
 
        tick -= (0xfff...);             /* adjust to incrementing value */ 
        } 
 
    return (tick); 
    } 
 
/*************************************************************************** 
* sysTimestampLock - get the timestamp timer tick count 
* 
* This routine returns the current value of the timestamp timer tick counter. 
* The tick count can be converted to seconds by dividing by the return of 
* sysTimestampFreq(). 
* 
* This routine locks interrupts for cases where it is necessary to stop the 
* tick counter before reading it, or when two independent counters must 
* be read.  If interrupts are already locked, use sysTimestamp() instead. 
* 
* RETURNS: The current timestamp timer tick count. 
* SEE ALSO: sysTimestamp() 
*/ 
  
UINT32 sysTimestampLock (void) 
    { 
    UINT32 tick = 0; 
    register UINT32 * pTick; 
    register UINT32 * pPreload; 
    int lockKey; 
 
    if (sysTimestampRunning) 
        { 
        lockKey = intLock ();           /* LOCK INTERRUPTS */ 
 
        /* pTick = counter read register location */ 
        /* pPreload = counter preload register location */ 
 
        /* disable counting timer here */ 
 
        tick = *pTick;                  /* read counter value */ 
        *pPreload = tick - TS_SKEW;     /* set preload value */ 
                                          /* (with time-skew adjustment) */ 
 
        /* enable counting timer here */ 
 
        intUnlock (lockKey);            /* UNLOCK INTERRUPTS */ 
 
        tick -= (0xfff...);             /* adjust to incrementing value */ 
        } 
 
    return (tick); 
    } 
 
#endif  /* INCLUDE_TIMESTAMP */

Example E-3:   Timestamp Drivers for the VxWorks System Clock Timer

This example shows a skeleton timestamp driver for systems that have no suitable spare timers, so that timestamps must be derived from the VxWorks system clock timer. See Using the VxWorks System Clock Timer, for a discussion of the most important details involved in writing this kind of driver.

/* sampleCTimer.c - sample C timer library */ 
 
/* Copyright 1994 Wind River Systems, Inc. */ 
#include "copyright_wrs.h" 
 
/* 
modification history 
-------------------- 
01a,23mar94,dzb  written. 
*/ 
 
/* 
DESCRIPTION 
This library contains sample routines to manipulate the timer functions on 
the sample C chip with a board-independent interface.  This library handles 
the timestamp timer facility. 
 
To include the timestamp timer facility, the macro INCLUDE_TIMESTAMP must be 
defined.
NOTE:  This module provides an example of a VxWorks timestamp timer driver 
implemented by reading the system clock timer counter.  It illustrates the 
structures and routines discussed in the Appendix, "Creating a VxWorks 
Timestamp Driver."  This module is only a template. In its current form,  
it will not compile. 
*/ 
 
#ifdef  INCLUDE_TIMESTAMP 
 
#include "drv/timer/timestampDev.h" 
#include "drv/timer/sampleCTimer.h" 
 
/* Locals */  
 
LOCAL BOOL    sysTimestampRunning = FALSE;      /* running flag */ 
 
/*************************************************************************** 
* 
* sysTimestampConnect - connect a user routine to the timestamp timer 
*                         interrupt 
* 
* This routine specifies the user interrupt routine to be called at each 
* timestamp timer interrupt.  It does not enable the timestamp timer itself. 
* 
* RETURNS: OK, or ERROR if sysTimestampInt() interrupt handler is not used. 
*/ 
  
STATUS sysTimestampConnect 
    ( 
    FUNCPTR routine,    /* routine called at each timestamp timer interrupt */ 
    int arg             /* argument with which to call routine */ 
    ) 
    { 
    /* ERROR indicates that the system clock tick specifies a rollover event */ 
 
    return (ERROR); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampEnable - initialize and enable the timestamp timer 
* 
* This routine connects the timestamp timer interrupt and initializes the 
* counter registers.  If the timestamp timer is already running, this routine 
* merely resets the timer counter. 
* 
* Set the rate of the timestamp timer input clock explicitly within the 
* BSP, in the sysHwInit() routine.  This routine does not initialize 
* the timer clock rate. 
* 
* RETURNS: OK, or ERROR if the timestamp timer cannot be enabled. 
*/
STATUS sysTimestampEnable (void) 
    { 
    if (sysTimestampRunning) 
        return (OK); 
     
    sysTimestampRunning = TRUE; 
 
    sysClkEnable ();            /* ensure the system clock is running */ 
  
    return (OK); 
    } 
 
/*************************************************************************** 
* sysTimestampDisable - disable the timestamp timer 
* 
* This routine disables the timestamp timer.  Interrupts are not disabled. 
* However, the tick counter does not increment after the timestamp timer 
* is disabled, ensuring that interrupts are no longer generated. 
* 
* RETURNS: OK, or ERROR if the timestamp timer cannot be disabled. 
*/ 
  
STATUS sysTimestampDisable (void) 
    { 
    sysTimestampRunning = FALSE; 
 
    return (ERROR); 
    } 
 
/*************************************************************************** 
* 
* sysTimestampPeriod - get the timestamp timer period 
* 
* This routine returns the period of the timer in timestamp ticks. 
* The period, or terminal count, is the number of ticks to which the 
* timestamp timer counts before rolling over and restarting the counting 
* process. 
* 
* RETURNS: The period of the timer in timestamp ticks. 
*/ 
  
UINT32 sysTimestampPeriod (void) 
    { 
    /* return the system clock period in timestamp ticks */ 
 
    return (sysTimestampFreq ()/sysClkRateGet ())        
    } 
 
/*************************************************************************** 
* 
* sysTimestampFreq - get the timestamp timer clock frequency 
* 
* This routine returns the frequency of the timer clock, in ticks per second. 
* The rate of the timestamp timer should be set explicitly in the BSP, 
* in the sysHwInit() routine. 
* 
* RETURNS: The timestamp timer clock frequency, in ticks per second. 
*/ 
  
UINT32 sysTimestampFreq (void) 
    { 
    UINT32 timerFreq; 
 
    /* 
     * Return the timestamp tick output frequency here. 
     * This value can be determined from the following equation: 
     *     timerFreq = clock input frequency / prescaler 
     *  
     * When possible, read the clock input frequency and prescaler values 
     * directly from chip registers. 
     */ 
  
    return (timerFreq); 
    } 
 
/*************************************************************************** 
* 
* sysTimestamp - get the timestamp timer tick count 
* 
* This routine returns the current value of the timestamp timer tick counter. 
* The tick count can be converted to seconds by dividing by the return of 
* sysTimestampFreq(). 
* 
* Call this routine with interrupts locked.  If interrupts are 
* not already locked, use sysTimestampLock() instead. 
* 
* RETURNS: The current timestamp timer tick count. 
* SEE ALSO: sysTimestampLock() 
*/ 
  
UINT32 sysTimestamp (void) 
    { 
    /* return the system clock timer tick count here */ 
    } 
 
/*************************************************************************** 
* 
* sysTimestampLock - get the timestamp timer tick count 
* 
* This routine returns the current value of the timestamp timer tick counter. 
* The tick count can be converted to seconds by dividing by the return of 
* sysTimestampFreq(). 
* 
* This routine locks interrupts for cases where it is necessary to stop the 
* tick counter before reading it, or when two independent counters must 
* be read.  If interrupts are already locked, use sysTimestamp() instead. 
* 
* RETURNS: The current timestamp timer tick count. 
* 
* SEE ALSO: sysTimestamp() 
*/ 
  
UINT32 sysTimestampLock (void) 
    { 
    /* 
     * Return the system clock timer tick count here. 
     * Interrupts do *not* need to be locked in this routine if 
     * the counter does not need to be stopped to be read. 
     */ 
    } 
 
#endif  /* INCLUDE_TIMESTAMP */


1:  See the VxWorks API Reference or the reference entry online for information about wvTmrRegister( ).