7.1. Counters, Clocks, Alarms, and Timers

Most processor architectures provide a clock or timer mechanism, typically a programmable register, which generates a periodic interrupt. This register is programmed with an initial value that determines how often the interrupt occurs. If the processor architecture does not support an onboard timer mechanism, the platform will have an external source for generating the periodic interrupt.

eCos uses the hardware timer mechanism to drive its timing features, which consist of:

  • Counters

  • Clocks

  • Alarms

  • Timers

The kernel uses these timing features to provide time-out, delay, and scheduling services for executing threads. Applications can use the timing features for specific timing-related needs as well.

The HAL provides macros to initialize, reset, and read the hardware device used for the kernel timing features. The implementation of the HAL macros and hardware device used is platform specific. Item List 7.1 describes the HAL clock control macros used for the kernel timing functions.

NOTE

Care must be taken if the HAL_CLOCK_XXX macros are used while using the eCos kernel. The eCos kernel uses these macro calls for its own timing-related functions.


Item list 7.1. HAL Clock Control Macros
Syntax:
									HAL_CLOCK_INITIALIZE(
 _period_
 )

Parameters:

_period_— initial value to set the timing device to achieve the desired interrupt rate.

Description:Set the timing device to interrupt at the specified period.
Syntax:
									HAL_CLOCK_RESET(
 _vector_,
 _period_
 )

Parameters:

_vector_— timing device interrupt vector. On most HAL packages, this parameter is not used.

_period_— initial value to set the timing device to achieve the desired interrupt rate.

Description:Reset the timing device with the specified period. This is only necessary for devices that require a reset after the interrupt occurs.
Syntax:
									HAL_CLOCK_READ(
 _pvalue_
 )

Parameters:

_pvalue_— pointer to counter value read from the timing device.

Description:Reads the current value of the timing device counter since the last interrupt. The hardware counter value is returned in the location pointed to by _pvalue_. This macro is hardware dependent and the definition here is the case for most hardware platforms.

The HAL architecture-specific configuration components contain a read-only configuration option describing the real-time clock constants. The HAL real-time clock configuration options can be overridden in the kernel package.

The HAL real-time clock configuration option is located under the platform-specific package and is called Real-Time Clock Constants. The read-only suboptions are Real-Time Clock Numerator (CYGNUM_HAL_RTC_NUMERATOR), Real-Time Clock Denominator (CYGNUM_HAL_RTC_ DENOMINATOR), and Real-Time Clock Period (CYGNUM_HAL_RTC_PERIOD). Dividing the Real-Time Clock Numerator by the Real-Time Clock Denominator gives the number of nanoseconds per tick. The Real-Time Clock Period is the value that is programmed into the processor's hardware timer such that the timer overflows once per kernel tick. This overflow generates a hardware interrupt. The Real-Time Clock Period is the value passed in the _period_ parameter in the macros shown in Item List 7.1.

The values for these configuration suboptions are calculated based on the clock source used on the specific target platform. When using an eCos-supported target platform, it is usually not necessary to modify these values.

It might be necessary to modify the real-time clock constants when porting to a new hardware platform. These options can be modified, as described later in this section, using the Override Default Clock Settings configuration option located under the eCos Kernel package. If this is the case, the Real-Time Clock Period is modified according to the specifications of the processor and/or hardware platform. The Real-Time Clock Numerator and Real-Time Clock Denominator are also modified to reflect the new timer resolution. For example, to increase the frequency by a factor of 10, the Real-Time Clock Period is changed in some hardware-defined way. Generally, the period is decreased by a factor of 10 and then the Real-Time Clock Denominator is increased by a factor of 10.

The kernel uses its tick to determine the timeslicing interval (CYGNUM_KERNEL_SCHED_ TIMESLICE_TICKS). The default setting uses five clock ticks per timeslice interval. The kernel real-time clock settings can be overridden by enabling the Override Default Clock Settings (CYGPKG_KERNEL_COUNTERS_CLOCK_OVERRIDE) configuration option, which is located under the Counters and Clocks component in the eCos Kernel package. Timeslicing is described further in the Multilevel Queue Scheduler section of Chapter 5, The Kernel.

Since all kernel-level clock-related operations, such as delays and time-outs, use units of ticks rather than seconds, let's look at the steps for a simple conversion.

1.
Determine the delay in nanoseconds. In our example, we want a delay of 60 milliseconds, which is the same as 60,000,000 nanoseconds.

2.
Next, we need the clock frequency. In this case, we assume a clock running at 100Hz, which corresponds to 1 tick every 10 milliseconds, or 1 tick every 10,000,000 nanoseconds. This corresponds to a numerator of 100 and a denominator of 1,000,000,000.

3.
Finally, we can calculate the tick value we need to use in the call using the equation

Therefore, in our example we plug in the values and get

4.
We then call the clock-related kernel function and pass it the parameter 6 for our 60-millisecond delay.

One more thing to remember, these conversion calculations can sometimes be computation intensive. Therefore, it is usually a good idea in an embedded system to perform these calculations whenever possible at compile time rather than at run time.

The kernel can be configured to provide a Real-Time Clock (RTC) for the system. The RTC is necessary to support clock- and alarm-related functions such as cyg_thread_delay. It is also needed for the multilevel queue scheduler when using timeslicing. Item List 7.2 details the kernel clock configuration options.

The kernel uses the HAL_CLOCK_INITIALIZE macro when it initializes the real-time clock. HAL_CLOCK_RESET is used in the ISR for the real-time clock.

Item list 7.2. Kernel Clock Configuration Options
Option NameProvide Real-Time Clock
CDL Name CYGVAR_KERNEL_COUNTERS_CLOCK
DescriptionAllows the kernel to provide the real-time clock for clock- and alarm-related functions and timeslicing (when using the multilevel queue scheduler). The default setting for this option is enabled.
Option NameOverride Default Clock Settings
CDL Name CYGPKG_KERNEL_COUNTERS_CLOCK_OVERRIDE
DescriptionAllows overriding of the default clock calculations for a particular platform. The default settings attempt to configure 100 clock interrupts per second. The default setting for this option is disabled.
Option NameMeasure Real-Time Clock Interrupt Latency
CDL Name CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY
DescriptionMeasures the latency of the real-time clock timer interrupt. This option requires the HAL macro HAL_CLOCK_LATENCY to be defined. The default setting for this option is disabled. This option is only for a performance measurement.
Option NameMeasure Real-Time Clock DSR Latency
CDL Name CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY
DescriptionMeasures the DSR latency for the real-time clock timer interrupt. This option requires the HAL macro HAL_CLOCK_LATENCY to be defined. The default setting for this option is disabled. This option is only for a performance measurement.

The kernel contains default settings for the clock interrupt frequency that are specific to each platform. The default RTC frequency is 100Hz; however, you should consult the documentation for the specific platform you are using to verify this value. The RTC settings are derived from the clock source provided on the target hardware. The configuration option Override Default Clock Settings contains three configuration suboptions:

  • Clock Hardware Initialization Value (CYGNUM_KERNEL_COUNTERS_CLOCK_ OVERRIDE_PERIOD)— initial value programmed into the programmable hardware timer register that generates the periodic interrupts for the kernel timing features.

  • Clock Resolution Numerator (CYGNUM_KERNEL_COUNTERS_CLOCK_OVERRIDE_ NUMERATOR)— numerator value for calculating the resolution of clock interrupts in nanoseconds.

  • Clock Resolution Denominator (CYGNUM_KERNEL_COUNTERS_CLOCK_OVERRIDE_ DENOMINATOR)— denominator value for calculating the resolution of clock interrupts in nanoseconds.

The resolution is represented as a numerator and denominator value to minimize the drift for frequencies that cannot be expressed as an integer. These suboptions allow you to override the default resolution of the hardware timing device. Overriding this value affects the operation of the real-time clock.

7.1.1. Counters

The first eCos timing feature is a counter. A counter is an abstraction, which maintains an increasing value that is driven by a source of ticks. The source of the tick does not have to be from a hardware device, nor does the tick need to be periodic. However, it is up to the owner of the counter to ensure that the counter object is being ticked.

eCos offers two different implementations of the counter object. The first implementation uses a single linked list for maintaining alarms attached to counters. When a tick occurs, the kernel goes through this linked list, usually at the DSR level. Therefore, if there is a sizeable number of alarms attached to a single counter object, the system dispatch latency is affected.

The second implementation uses a table of linked lists for maintaining alarms attached to counters, allowing the size of the table to be set by a configuration suboption. This can improve the responsiveness of the kernel because only one list is searched per tick; however, extra code and data is required. The counter configuration options are described in Item List 7.3.

Item list 7.3. Counter Configuration Options
Option NameImplement Counters Using a Single List
CDL Name CYGIMP_KERNEL_COUNTERS_SINGLE_LIST
DescriptionUses a single linked list for maintaining alarm objects. This is a more efficient use of memory when a small number of alarms are used in the system. The default setting for this option is enabled.
Option NameImplement Counters Using a Table of Lists
CDL Name CYGIMP_KERNEL_COUNTERS_MULTI_LIST
DescriptionUses a table of linked lists for alarm objects. This option reduces the amount of computation necessary when a timer triggers, which is useful when many alarms are used in the system. This option is disabled by default. When using a table of lists the suboption Size of Counter List Table, which has a default value of 8, can be configured. The range for the counter list table size is from 1 to 1024.
Option NameSort the Counter List
CDL Name CYGIMP_KERNEL_COUNTERS_SORT_LIST
DescriptionAllows the list of alarms that are attached to counters to be sorted so that the next alarm to trigger is at the front of the list. This reduces the amount of work that needs to be done when counter tick is processed. This option causes the operation of adding alarms to the list more expensive because the list must be sorted. The default setting for this option is disabled.

The eCos kernel API provides functions for controlling counters. The counter API functions are detailed in Item List 7.4.

Item list 7.4. Kernel Counter API Functions
Syntax:
void
cyg_counter_create(
 cyg_handle_t *counter,
 cyg_counter *the_counter
 );

Context:Init/Thread
Parameters:

counter— pointer to new counter handle.

the_counter— pointer to the new counter object.

Description:Construct a new counter.
Syntax:
void
cyg_counter_delete(
 cyg_handle_t counter
 );

Context:Init/Thread
Parameters:

counter— handle to the counter.

Description:Remove the specified counter. A counter should never be deleted if a clock or alarm object is attached.
Syntax:
void
cyg_counter_tick(
 cyg_handle_t counter
 );

Context:Init/Thread/DSR
Parameters:

counter— handle to the counter.

Description:Increment the counter value by one tick.
Syntax:
cyg_tick_count_t
cyg_counter_current_value(
 cyg_handle_t counter
 );

Context:Init/Thread/DSR
Parameters:

counter— handle to the counter.

Description:Returns the current value, in ticks, of the specified counter.
Syntax:
void
cyg_counter_set_value(
 cyg_handle_t counter,
 cyg_tick_count_t new_value
 );

Context:Init/Thread/DSR
Parameters:

counter— handle to the counter.

new_value— value, in ticks, to set counter.

Description:Sets the counter to the tick value specified by new_value.

Code Listing 7.1 shows an example of a counter that causes an alarm to trigger. We discuss alarms later in this chapter.

Code Listing 7.1. Example code using counters and alarms.
 1  #include <cyg/kernel/kapi.h>
 2
 3
 4  cyg_counter counter_obj;
 5  cyg_handle_t counter_hdl;
 6
 7  cyg_handle_t alarm_hdl;
 8  cyg_alarm alarm_obj;
 9
10  // Declare the alarm handler function so it can
11  // be passed into the alarm initialize function.
12  cyg_alarm_t alarm_handler;
13
14  unsigned long index = 0;
15
16  //
17  // Counter thread.
18  //
19  void counter_thread( cyg_addrword_t index )
20  {
21
22     // Run forever.
23     while ( 1 )
24     {
25        // Delay for 10 ticks.
26        cyg_thread_delay( 10 );
27
28        // Increment the counter.
29        cyg_counter_tick( counter_hdl );
30     }
31  }
32
33  //
34  // Main starting point for the application.
35  //
36  void cyg_user_start( void )
37  {
38     // Create the counter.
39     cyg_counter_create( &counter_hdl,
40                         &counter_obj );
41
42     // Create the alarm.
43     cyg_alarm_create( counter_hdl,
44                       alarm_handler,
45                       (cyg_addrword_t)index,
46                       &alarm_hdl,
47                       &alarm_obj );
48
49     // Initialize the alarm.
50     cyg_alarm_initialize( alarm_hdl,
51                           12,
52                           6 );
53
54     // Create and run the counter thread.
55  }
56  
57  //
58  // Alarm handler.
59  //
60  void alarm_handler(
61          cyg_handle_t alarm_handle,
62          cyg_addrword_t data )
63  {
64     (unsigned long)data++;
65  }

In Code Listing 7.1, a counter is created on line 39. Next, the alarm is created using the previously created counter handle, counter_hdl, as shown on line 43. The function that is called when the alarm triggers, alarm_handler, is passed in the parameter on line 44. The variable index, on line 45, is passed to the alarm_handler when the alarm triggers. The alarm handle, alarm_hdl on line 46, and alarm object, alarm_obj on line 47, are returned after the alarm is created successfully.

Now the alarm can be initialized using the alarm handle we just created, as shown on line 50. Line 51 is the value that the counter, counter_hdl, must reach before the alarm first triggers; in this case, the value is 12. On line 52 is the interval that causes the alarm to trigger again. For this alarm initialization, the alarm triggers again when the counter reaches 18, and 24, and 30, and so on—since the interval is set at 6.

The thread creation is eliminated from this code because we covered that in previous examples. After the counter_thread is running, it delays for 10 ticks (line 26) and then increments the counter_hdl counter (line 29) using the cyg_counter_tick function call.

When the counter_hdl counter reaches 12 ticks, the alarm_handler function is called on line 60. In this case, the alarm_handler function simply increments the data parameter passed in as the second parameter, which in turn increments the variable index.

7.1.2. Clocks

A clock is a counter, with an associated resolution, which is driven by a regular source of ticks that represent time periods. The eCos kernel implements a default system clock, the RTC, which tracks real time. Item List 7.5 lists the kernel API functions for clock control. Code Listing 7.2 shows an example using the kernel clock API functions along with the kernel alarm API functions.

Item list 7.5. Kernel Clock API Functions
Syntax:
void
cyg_clock_create(
 cyg_resolution_t resolution,
 cyg_handle_t *handle,
 cyg_clock *clock
 );

Context:Init/Thread
Parameters:

resolution— numerator and denominator value in nanoseconds per tick.

handle— pointer to the new clock handle.

clock— pointer to the new clock object.

Description:Construct a new clock with the specified resolution.
Syntax:
void
cyg_clock_delete(
 cyg_handle_t clock
 );

Context:Init/Thread
Parameters:

clock— handle to the clock.

Description:Remove the specified clock.
Syntax:
void
cyg_clock_to_counter(
 cyg_handle_t clock,
 cyg_handle_t *counter
 );

Context:Init/Thread/DSR
Parameters:

clock— handle to the clock.

counter— pointer to the new counter handle.

Description:Converts a clock handle to counter handle allowing the use of kernel counter API functions. This gives access to the clock's attached counter.
Syntax:
void
cyg_clock_set_resolution(
 cyg_handle_t clock,
 cyg_resolution_t resolution
 );

Context:Init/Thread/DSR
Parameters:

clock— handle to the clock.

resolution— numerator and denominator value in nanoseconds per tick.

Description:Changes the resolution of the specified clock object. This function does not actually change the behavior of the hardware driving the clock. Instead, cyg_clock_set_resolution synchronizes the kernel clock object to match resolution of the underlying hardware clock providing the ticks.
Syntax:
cyg_resolution_t
cyg_clock_get_resolution(
 cyg_handle_t clock
 );

Context:Init/Thread/DSR
Parameters:

clock— handle to the clock.

Description:Returns the current resolution of the specified clock.
Syntax:
cyg_handle_t
cyg_real_time_clock(
 void
 );

Context:Init/Thread/DSR
Parameters:None
Description:Returns a handle to the system real-time clock.
Syntax:
cyg_tick_count_t
cyg_current_time(
 void
 );

Context:Init/Thread/DSR
Parameters:None
Description:Returns the real-time clock counter value in ticks.

7.1.3. Alarms

Another eCos timing feature is the alarm. An alarm is attached to a counter and provides a means for generating events based on the value of a counter. The event can be configured to trigger periodically or once.

When an alarm is configured, a handler function is used to perform the necessary processing for handling the event. The alarm handler must follow the same guidelines as other DSR functions, which are detailed in Chapter 3, Exceptions and Interrupts, in the section Interrupt and Scheduler Synchronization.

Item List 7.6 details the kernel alarm API functions. An example, in the file simple-alarm.c, is provided that shows an implementation of an alarm using the real-time clock. The eCos examples are provided as part of the installation process and discussed further in Chapter 12, An Example Application Using eCos.

Item list 7.6. Kernel Alarm API Functions
Syntax:
void
cyg_alarm_create(
 cyg_handle_t counter,
 cyg_alarm_t *alarm_fn,
 cyg_addrword_t data,
 cyg_handle_t *handle,
 cyg_alarm *alarm
 );

Context:Init/Thread
Parameters:

counter— handle to counter which alarm is attached.

alarm_fn— pointer to alarm handler function.

data— parameter passed into alarm handler.

handle— pointer to the new alarm handle.

alarm— pointer to the new alarm object.

Description:Construct an alarm object that is attached to the specified counter. The alarm handler is called when the alarm triggers and executes in the context of the function that incremented the counter. The alarm does not run until after the call to cyg_alarm_initialize.
Syntax:
void
cyg_alarm_delete(
 cyg_handle_t alarm
 ); 

Context:Init/Thread
Parameters:

alarm— handle to the alarm.

Description:Disables the specified alarm and detaches it from the counter.
Syntax:
void
cyg_alarm_initialize(
 cyg_handle_t alarm,
 cyg_tick_count_t trigger,
 cyg_tick_count_t interval
 );

Context:Init/Thread/DSR
Parameters:

alarm— handle to the alarm.

trigger— tick value that causes alarm to occur.

interval— tick value that causes alarm to reoccur. Setting this parameter to zero disables the alarm after it occurs once.

Description:Initializes the specified alarm to trigger when the tick value is equal to the trigger parameter. If the interval parameter is set to zero, the alarm is disabled after it occurs once. Otherwise, the alarm reoccurs according to the interval parameter setting.
Syntax:
void
cyg_alarm_enable(
 cyg_handle_t alarm
 );

Context:Init/Thread/DSR
Parameters:

alarm— handle to the alarm.

Description:Enables the specified alarm, allowing it to occur in phase with the original settings from the cyg_alarm_initialize function.
Syntax:
void
cyg_alarm_disable(
 cyg_handle_t alarm
 );

Context:Init/Thread/DSR
Parameters:

alarm— handle to the alarm.

Description:Disables the specified alarm preventing it from occurring. The alarm can be re-enabled using the cyg_alarm_initialize or cyg_alarm_enable functions.

In Code Listing 7.2, we see an example using the kernel clock API along with the kernel alarm API.

Code Listing 7.2. Example code using clocks and alarms.
 1  #include <cyg/kernel/kapi.h>
 2
 3  cyg_handle_t counter_hdl;
 4  cyg_handle_t sys_clk;
 5  cyg_handle_t alarm_hdl;
 6  cyg_tick_count_t ticks;
 7  cyg_alarm_t alarm_handler;
 8  cyg_alarm alarm_obj;
 9
10  unsigned long index;
11
12  //
13  // Main starting point for the application.
14  //
15  void cyg_user_start( void )
16  {
17     sys_clk = cyg_real_time_clock();
18
19     cyg_clock_to_counter( sys_clk,
20                           &counter_hdl );
21
22     cyg_alarm_create( counter_hdl,
23                       alarm_handler,
24                       (cyg_addrword_t)&index,
25                       &alarm_hdl,
26                       &alarm_obj );
27
28     cyg_alarm_initialize( alarm_hdl,
29                           cyg_current_time() + 100,
30                           100 );
31  }
32
33  //
34  // Alarm handler.
35  //
36  void alarm_handler(
37          cyg_handle_t alarm_handle,
38          cyg_addrword_t data )
39  {
40     (unsigned long)data++;
41  }

Code Listing 7.2 is an example of how to use an alarm with the system real-time clock. In the cyg_user_start function, shown on line 15, a handle to the real-time clock is stored in sys_clk using the function cyg_real_time_clock, as shown on line 17.

Next, we get access to the real-time clock's attached counter, on line 19. The handle is stored in the variable counter_hdl, as we see on line 20. On line 22, we use the handle to the system real-time clock to create an alarm. When the alarm triggers, the function passed in on line 23, alarm_handler, is called and the function is passed the variable index, shown on line 24. The alarm handle is returned in the parameter passed in on line 25, alarm_hdl. The alarm object is stored in the parameter passed on line 26, alarm_obj.

Finally, we initialize the alarm on line 28 using the alarm handle we just created. In the cyg_alarm_initialize function call, we set the initial trigger of the alarm on line 29. In this case, we use the cyg_current_time function call, which returns the current real-time clock counter value, and add 100 to the tick value. This causes the alarm to trigger in 100 ticks from the current time. The parameter on line 30 determines the interval to trigger the alarm after the initial trigger. In this case, the alarm_handler function is called every 100 real-time clock ticks.

The alarm_handler function is shown on lines 36 through 41, which simply increments the data parameter passed into the function and in turn increments the index variable setup when the alarm was created on line 24.

7.1.4. Timers

A timer is an alarm that is attached to a clock. There is a timer object defined by the kernel. However, eCos does not provide a formal implementation, or kernel API functions, for the timer object.

Timers in the eCos system are used within the μITRON compatibility layer package. The μITRON package uses the timer object attached to the real-time clock for performing its needed timing related functions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.42.196