Article: Tick-tock Part II - Understanding the Neutrino micr

Tick-tock - Understanding the Neutrino micro kernel’s concept of time, Part
II
By Mario Charest, with Brian Stecher’s assistance.

This discussion is a follow-up to the “Tick-tock: Understanding the Neutrino
micro kernel’s concept of time” discussion. Make sure you read it first -
to keep in the right frame of mind.

The hardware timer of the PC has another side effect when it comes to
dealing with timers. Brian’s article explains the behavior of “sleep”
related functions. Timers are similarly affected by the design of the PC
hardware.

Let’s jump into the heart of the matter with some C code. The following is
a working sample for QNX Neutrino. The same principles apply to QNX4 as
well.

// — INCLUDE FILE

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/neutrino.h>
#include <sys/netmgr.h>
#include <sys/syspage.h>

// — FUNCTION (from Bottom to Top, and/or sorted)

int main( int argc, char *argv[] )
{
int pid;
int chid;
int pulse_id;
timer_t timer_id;
struct sigevent event;
struct itimerspec timer;
struct _clockperiod clkper;
struct _pulse pulse;
_uint64 last_cycles=-1;
_uint64 current_cycles;
float cpu_freq;
time_t start;

// — Get CPU frequency in order to do precise time calculation
cpu_freq = SYSPAGE_ENTRY( qtime )->cycles_per_sec;

// — Set priority to max so we don’t get disrupted but anything
// — else then interrupts
{
struct sched_param param;
int ret;
param.sched_priority = sched_get_priority_max( SCHED_RR );
ret = sched_setscheduler( 0, SCHED_RR, &param);
assert ( ret != -1 );
}

// — Create channel to receive timer event
chid = ChannelCreate( 0 );
assert ( chid != -1 );

// — setup timer and timer event
event.sigev_notify = SIGEV_PULSE;
event.sigev_coid = ConnectAttach ( ND_LOCAL_NODE, 0, chid, 0, 0 );

event.sigev_priority = getprio(0);
event.sigev_code = 1023;
event.sigev_value.sival_ptr = (void*)pulse_id;

assert ( event.sigev_coid != -1 );

if ( timer_create( CLOCK_REALTIME, &event, &timer_id ) == -1 )


perror ( “can’t create timer” );
exit( EXIT_FAILURE );
}

// — change timer request to alter behavior
#if 1
timer.it_value.tv_sec = 0;
timer.it_value.tv_nsec = 1000000;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_nsec = 1000000;
#else
timer.it_value.tv_sec = 0;
timer.it_value.tv_nsec = 999847;
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_nsec = 999847;
#endif

// — start timer
if ( timer_settime( timer_id, 0, &timer, NULL ) == -1 )


perror(“Can’t start timer\n”);
exit( EXIT_FAILURE );
}

// — set tick to 1ms otherwise if left to 10 ms default it
// — would take 65 seconds to demonstrate :wink:
clkper.nsec = 1000000;
clkper.fract = 0;
ClockPeriod ( CLOCK_REALTIME, &clkper, NULL, 0 ); // 1ms

// — keep track of time
start = time(NULL);
for( ;; )


// — wait for pulse
pid = MsgReceivePulse ( chid, &pulse, sizeof( pulse ), NULL );

// — should put pulse validation here …
current_cycles = ClockCycles();

if ( last_cycles != -1 ) // — don’t print first iteration


// — could get rid of timer by using more
// — clever timer setup
float elapse = (current_cycles - last_cycles) / cpu_freq;

// — printf if request is 50us longer then requested…
if ( elapse > .00105 )


printf(“Elapse %f at %d\n”, elapse, time(NULL)-start );
}
}

last_cycles = current_cycles;
}
}

The program checks to see if the time between two timer events is greater
then 1.05 milliseconds (ms). Most people expect that given QNX Neutrino’s
great real-time behavior, such a condition will never occur. But, surprise!
It will; not because of an ill behaving kernel, rather because of the
limitation in the PC hardware. It’s impossible for the OS to generate a
timer event at exactly 1.0 ms. It will be .99847 ms!!! This has unexpected
side effects.

Where’s the catch?

There is a 153 nanosecond (ns) discrepancy between the request and what the
hardware can do. The kernel timer manager is invoked every .999847 ms.
Every time a timer fires, the kernel checks to see if the timer is periodic
and, if so, adds the number of nanoseconds to the expected timer expiring
point, no matter what the current time is.

<Diagram included at:
http://qdn.qnx.com/support/articles/oct312000/concept_of_time.html>

For anyone that knows a bit about signal frequency, this phenomenon is
called beat. When two signals of various frequencies are “added” a third
frequency is generated. This is often seen if you use your camcorder to
record a TV image. Because a TV is updated at 60 hz and camcorders usually
operate on different frequency, at playback, one can often see a white line
that scrolls in the TV image. The speed of that line is related to the
difference in frequency between the camcorder and the TV.

In this case we have two frequencies, one is 1000 Hz and the other is
1005.495 HZ. Thus, the beat frequency is 1.5 micro Hz, or one blip every
6535 milliseconds.

This behavior has the benefit of giving you the expected number of fired
timers, on average. In the example above, after 1 minute, the program would
have received 60000 fired timer event (1000 events /sec * 60 sec). If
your design requires very precise timing, you have no other choice then to
request a timer event of .999847 ms and not 1 ms. This can make the
difference between a robot moving very smoothly or scratching your car.

Now why don’t you try to figure out what would happen if the tick size is
set to 10 ms and a timer event is requested every 23 ms . and post your
answer on the qdn.publc.articles news group!

I pasted the example from “timer_settime” from the QNX RTP library
reference into a project I’m doing with PHAB. It’s in a callback so that it
will trigger when a button is activated. It’s very similar to the example
in Tick-tock Part II. I’ll include the original here. I slightly changed it
to run in the phab project. Also I set itime.it_interval.tv_nsec to 60000
and have it pulsing a stepper motor driver. There are two problems. One is
that as I move the mouse around the screen and over objects I can hear
stepper motor timing problems. The other is that I would like to be able to
change the value of itime.it_interval.tv_nsec to get acceleration. Just
modifing the value each time a pulse is received does nothing. I added a
“timer_settime(timer_id, 0, &itime, NULL)” after modifying
itime.it_interval.tv_nsec but that causes the procedure to freeze. Is there
a way to change this period on the fly?
I have read somewhere in the documentation that to get hard timing I need
to have separate processes devoted just to timing and have them at priority
above photon. This sounds pretty tough to a new user. Are there examples
on creating new threads?
Bill Knighton

/*

  • Demonstrate how to set up a timer that, on expiry,
  • sends us a pulse. This example sets the first
  • expiry to 1.5 seconds and the repetition interval
  • to 1.5 seconds.
    */

#include <stdio.h>
#include <time.h>
#include <sys/netmgr.h>
#include <sys/neutrino.h>

#define MY_PULSE_CODE _PULSE_CODE_MINAVAIL

typedef union {
struct _pulse pulse;
/* your other message structures would go
here too */
} my_message_t;

main()
{
struct sigevent event;
struct itimerspec itime;
timer_t timer_id;
int chid;
int rcvid;
my_message_t msg;

chid = ChannelCreate(0);

event.sigev_notify = SIGEV_PULSE;
event.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0,
chid,
_NTO_SIDE_CHANNEL, 0);
event.sigev_priority = getprio(0);
event.sigev_code = MY_PULSE_CODE;
timer_create(CLOCK_REALTIME, &event, &timer_id);

itime.it_value.tv_sec = 1;
/* 500 million nsecs = .5 secs /
itime.it_value.tv_nsec = 500000000;
itime.it_interval.tv_sec = 1;
/
500 million nsecs = .5 secs */
itime.it_interval.tv_nsec = 500000000;
timer_settime(timer_id, 0, &itime, NULL);

/*

  • As of the timer_settime(), we will receive our pulse
  • in 1.5 seconds (the itime.it_value) and every 1.5
  • seconds thereafter (the itime.it_interval)
    */

for (;:wink: {
rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL);
if (rcvid == 0) { /* we got a pulse /
if (msg.pulse.code == MY_PULSE_CODE) {
printf(“we got a pulse from our timer\n”);
} /
else other pulses … /
} /
else other messages … */
}
}

“Bill Knighton” <bknighton@nc.rr.com> wrote in message
news:8u1u0p$h6$1@inn.qnx.com

I pasted the example from “timer_settime” from the QNX RTP library
reference into a project I’m doing with PHAB. It’s in a callback so that
it
will trigger when a button is activated. It’s very similar to the example
in Tick-tock Part II. I’ll include the original here. I slightly changed
it
to run in the phab project. Also I set itime.it_interval.tv_nsec to 60000

60us!!! You got to be kidding right? Did you read both article? That’s
way to fast.

and have it pulsing a stepper motor driver. There are two problems. One is
that as I move the mouse around the screen and over objects I can hear
stepper motor timing problems.

Secondo the same program is handling both graphics and motor, that
means if a graphical operation last more then 60us (that will be
frequent…)
your motor will vary.

The other is that I would like to be able to
change the value of itime.it_interval.tv_nsec to get acceleration. Just
modifing the value each time a pulse is received does nothing. I added a
“timer_settime(timer_id, 0, &itime, NULL)” after modifying
itime.it_interval.tv_nsec but that causes the procedure to freeze. Is
there
a way to change this period on the fly?

I have read somewhere in the documentation that to get hard timing I need
to have separate processes devoted just to timing and have them at
priority
above photon. This sounds pretty tough to a new user.

These are the type of thing you will have to learn.

Are there examples on creating new threads?

If 60us is what you need forget about threads or timer, you will
need an interrupt to deal with that.

Bill Knighton

/*

  • Demonstrate how to set up a timer that, on expiry,
  • sends us a pulse. This example sets the first
  • expiry to 1.5 seconds and the repetition interval
  • to 1.5 seconds.
    */

#include <stdio.h
#include <time.h
#include <sys/netmgr.h
#include <sys/neutrino.h

#define MY_PULSE_CODE _PULSE_CODE_MINAVAIL

typedef union {
struct _pulse pulse;
/* your other message structures would go
here too */
} my_message_t;

main()
{
struct sigevent event;
struct itimerspec itime;
timer_t timer_id;
int chid;
int rcvid;
my_message_t msg;

chid = ChannelCreate(0);

event.sigev_notify = SIGEV_PULSE;
event.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0,
chid,
_NTO_SIDE_CHANNEL, 0);
event.sigev_priority = getprio(0);
event.sigev_code = MY_PULSE_CODE;
timer_create(CLOCK_REALTIME, &event, &timer_id);

itime.it_value.tv_sec = 1;
/* 500 million nsecs = .5 secs /
itime.it_value.tv_nsec = 500000000;
itime.it_interval.tv_sec = 1;
/
500 million nsecs = .5 secs */
itime.it_interval.tv_nsec = 500000000;
timer_settime(timer_id, 0, &itime, NULL);

/*

  • As of the timer_settime(), we will receive our pulse
  • in 1.5 seconds (the itime.it_value) and every 1.5
  • seconds thereafter (the itime.it_interval)
    */

for (;:wink: {
rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL);
if (rcvid == 0) { /* we got a pulse /
if (msg.pulse.code == MY_PULSE_CODE) {
printf(“we got a pulse from our timer\n”);
} /
else other pulses … /
} /
else other messages … */
}
}

60us!!! You got to be kidding right? Did you read both article? That’s
way to fast.

I did read the articles and I’m not kidding. I had the speed cranked up

quite a bit faster than that(I thought). I really need about 120,000
steps/sec(this is a micro stepping drive), but I need to accelerate to that
speed. It turns out that while I was assigning that value I was not looking
too closely at the mill’s table. It was moving far more slowly than what I
thought. I started assigning larger values until I noticed a change in pitch
and it was somewhere around 1,000,000 that it started to slow down. I guess
there’s an error value
What I’ve got working now is a separate thread that pulses the motor
driver and then repeatedly checks ClockCycles() until the correct elapsed
time has gone by. This seems kind of crude and I had to do setprio(12) or
13 to keep mouse movement from causing problems(this ceases mouse movement
until the thread is done).
Is there a way to use QNX with native PC resources to trigger events at
these frquencies and allow a few things like mouse movment?

“Bill Knighton” <bknighton@nc.rr.com> wrote in message
news:8u4ihc$ijc$1@inn.qnx.com

60us!!! You got to be kidding right? Did you read both article?
That’s
way to fast.

I did read the articles and I’m not kidding. I had the speed cranked up
quite a bit faster than that(I thought). I really need about 120,000
steps/sec(this is a micro stepping drive), but I need to accelerate to
that
speed. It turns out that while I was assigning that value I was not
looking
too closely at the mill’s table. It was moving far more slowly than what I
thought. I started assigning larger values until I noticed a change in
pitch
and it was somewhere around 1,000,000 that it started to slow down. I
guess
there’s an error value

What I’ve got working now is a separate thread that pulses the motor
driver and then repeatedly checks ClockCycles() until the correct elapsed
time has gone by. This seems kind of crude and I had to do setprio(12) or
13 to keep mouse movement from causing problems(this ceases mouse movement
until the thread is done).
Is there a way to use QNX with native PC resources to trigger events at
these frquencies and allow a few things like mouse movment?

You would need to add a timer board that generates 120000 interrupt
a seconds, but depending on you CPU this could be asking for trouble.
Or you could use a dual cpu machine :wink:


\

Bill Knighton wrote:

60us!!! You got to be kidding right? Did you read both article? That’s
way to fast.

I did read the articles and I’m not kidding. I had the speed cranked up
quite a bit faster than that(I thought). I really need about 120,000
steps/sec(this is a micro stepping drive), but I need to accelerate to that
speed. It turns out that while I was assigning that value I was not looking
too closely at the mill’s table. It was moving far more slowly than what I
thought. I started assigning larger values until I noticed a change in pitch
and it was somewhere around 1,000,000 that it started to slow down. I guess
there’s an error value
What I’ve got working now is a separate thread that pulses the motor
driver and then repeatedly checks ClockCycles() until the correct elapsed
time has gone by. This seems kind of crude and I had to do setprio(12) or
13 to keep mouse movement from causing problems(this ceases mouse movement
until the thread is done).
Is there a way to use QNX with native PC resources to trigger events at
these frquencies and allow a few things like mouse movment?

No … there isn’t such a PC resource. The
smallest ticksize of the RTC
is ~122us. You have also to check the requested
processing of instructions/s against the CPU power
… just to make sure that ‘steps*instruction/s’
is smaller than the MIPS of your CPU and there is
still left power for other tasks.

However … in most cases you have to use a second
CPU, regardless if it is located on an intelligent
controller board or on a second SMP CPU.

Armin

Can somebody tell me how the clock_gettime() and clock_settime() work?Does
clock_gettime() simply get the time from the BIOS and clock_settime() simply
set the value in time_spce to the BIOS?Do they take the timezone setting as
consider?
thanks.
ChaoLi.

Can somebody tell me how the clock_gettime() and clock_settime() work?Does
clock_gettime() simply get the time from the BIOS and clock_settime() simply
set the value in time_spce to the BIOS?Do they take the timezone setting as
consider?
thanks.
ChaoLi.

ChaoLi <ChaoLi@sohu.com> wrote:

Can somebody tell me how the clock_gettime() and clock_settime() work?Does
clock_gettime() simply get the time from the BIOS and clock_settime() simply
set the value in time_spce to the BIOS?Do they take the timezone setting as
consider?

They don’t talk to the BIOS at all.

At startup, the OS is giving a current “real” time from somewhere. This
is stored as an offset from Jan 1, 1970 UTC. (Internally as a 64-bit
nanosecond value, but often seen/used as a 32 bit signed seconds and
32 bit nanoseconds since last second.) clock_gettime() asks how many
seconds & nanoseconds since the epoch, clock_settime() changes the
number of seconds & nanoseconds since the epoch. Neither talk to the
BIOS. The BIOS may be the “somewhere” used as the initial source of
the offset from epoch.

The timezone (TZ) may have an effect on the translation from BIOS time
to offset time, and it may have an effect on the translation from
the seconds & nanoseconds since epoch to human-readable form
(hh:mm:ss dd mm yy) or from human-intelligible form to seconds (and
nanoseconds).

-David

QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.

“ChaoLi” <ChaoLi@sohu.com> wrote in message news:aar5dd$nch$1@inn.qnx.com

Can somebody tell me how the clock_gettime() and clock_settime() work?Does
clock_gettime() simply get the time from the BIOS and clock_settime()
simply
set the value in time_spce to the BIOS?Do they take the timezone setting
as
consider?

None of the QNX function get the time from the hardware clock/BIOS. It
always
gets it from internal OS clock.

thanks.
ChaoLi.
\

Thank you,Mario Charest, then how can I set and get the Real Time Clock?

Best Regards.
ChaoLi
“Mario Charest” <goto@nothingness.com> дÈëÏûÏ¢ÐÂÎÅ
:aarl3d$5pr$1@inn.qnx.com

“ChaoLi” <> ChaoLi@sohu.com> > wrote in message
news:aar5dd$nch$> 1@inn.qnx.com> …
Can somebody tell me how the clock_gettime() and clock_settime()
work?Does
clock_gettime() simply get the time from the BIOS and clock_settime()
simply
set the value in time_spce to the BIOS?Do they take the timezone setting
as
consider?

None of the QNX function get the time from the hardware clock/BIOS. It
always
gets it from internal OS clock.

thanks.
ChaoLi.


\

ChaoLi <ChaoLi@sohu.com> wrote:

Thank you,Mario Charest, then how can I set and get the Real Time Clock?

The rtc utility will get/set some flavours of hardware clock. You can
spawn it.

Otherwise, you would need to do the hardware work – map in the area of
memory it resides in, and update that or equivalent work.

-David

QNX Training Services
http://www.qnx.com/support/training/
Please followup in this newsgroup if you have further questions.