Jan, Philippe,
Attached is my test program, based on cyclictest. I removed all the
unneeded parts, and set all the options to the right values, as used for
my test. I use the PPC8540 internal highres timer, but this can be
disabled by undef-ing the define at the top of the program.
I compiled the program using the same compiler-settings as used by the
latency program.
The commands I use to run the test are:
/usr/xenomai/bin/latency -P 60
./cyclictest-Xenomai -q
The -q option gives shows the output at the end of the test run, instead
of the running data, to examine all the recorded data add the -v option.
The -h option enables the PPC8540 highres timer.
This are the results from a test run using the attached
cyclictest-Xenomai and the standard latency test (both are run in
separate terminals):
bash-3.00# ./cyclictest-Xenomai -q -h
#T: 0 P:80 I:10000 O: 0 C: 100 Min: -3.181 Avg: -0.025 Max: 2.392
#T: 1 P:79 I:10000 O: 0 C: 100 Min: -1.787 Avg: 1.377 Max: 139.257
#T: 2 P:78 I:10000 O: 0 C: 100 Min: -1.836 Avg: 5.518 Max: 553.623
#T: 3 P:77 I:10000 O: 0 C: 100 Min: -1.932 Avg: 3.153 Max: 317.275
#T: 4 P:76 I:10000 O: 0 C: 100 Min: -1.740 Avg: 0.435 Max: 45.203
#T: 5 P:75 I:10000 O: 0 C: 100 Min: -1.980 Avg: 0.047 Max: 5.443
#T: 6 P:74 I:10000 O: 0 C: 100 Min: -2.148 Avg: 0.015 Max: 2.537
#T: 7 P:73 I:10000 O: 0 C: 100 Min: -1.811 Avg: 0.020 Max: 2.440
#T: 8 P:72 I:10000 O: 0 C: 100 Min: -1.667 Avg: 0.011 Max: 2.416
#T: 9 P:71 I:10000 O: 0 C: 100 Min: -1.788 Avg: 0.051 Max: 5.347
bash-3.00# /usr/xenomai/bin/latency -P 60
== Sampling period: 100 us
== Test mode: periodic user-mode task
== All results in microseconds
warming up...
RTT| 00:00:01 (periodic user-mode task, 100 us period, priority 60)
RTH|---lat min|---lat avg|---lat max|-overrun|--lat best|--lat worst
RTD| -5.453| -4.492| -2.090| 0| -5.453| -2.090
RTD| -5.381| -4.492| 3.699| 0| -5.453| 3.699
RTD| -5.453| -4.492| -1.321| 0| -5.453| 3.699
RTD| -5.357| -4.492| 1.201| 0| -5.453| 3.699
RTD| -5.477| -4.468| 3.003| 0| -5.477| 3.699
RTD| -5.405| -4.516| 1.417| 0| -5.477| 3.699
RTD| -5.429| -4.420| 7.207| 0| -5.477| 7.207
RTD| -5.285| -4.108| 28.588| 0| -5.477| 28.588
RTD| -5.429| -4.324| 770.282| 10| -5.477| 770.282
RTD| -5.477| -4.492| 2.210| 10| -5.477| 770.282
RTD| -5.405| -4.468| -0.504| 10| -5.477| 770.282
RTD| -5.309| -4.468| -0.312| 10| -5.477| 770.282
---|----------|----------|----------|--------|----------------------
RTS| -5.477| -4.420| 770.282| 10| 00:00:13/00:00:13
Kind regards,
Johan Borkhuis
/*
* High resolution timer test software
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License Veriosn
* 2 as published by the Free Software Foundation;
*
*/
#define VERSION_STRING "V 0.11-Xenomai"
#define USE_PPC8540_TIMER
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <native/task.h>
#define USEC_PER_SEC 1000000
#define NSEC_PER_SEC 1000000000
/* Must be power of 2 ! */
#define VALBUF_SIZE 1024*1024
/* Struct to transfer parameters to the thread */
struct thread_param {
int prio;
unsigned long max_cycles;
struct thread_stat *stats;
int bufmsk;
unsigned long interval;
};
/* Struct for statistics */
struct thread_stat {
unsigned long cycles;
unsigned long cyclesread;
double min;
double max;
double act;
double avg;
double *values;
pthread_t thread;
int threadstarted;
RT_TASK task;
unsigned long overruns;
};
static int shutdown = 0;
static int hwtimer = 0;
static int priority = 80;
static int num_threads = 10;
static int max_cycles = 100;
static int verbose = 0;
static int quiet = 0;
static int interval = 10000;
/*
* High res timer for PPC 8540
* Timebase is running at CCB/8. CCB is running at 333MHz, so TB is running
* at 41.6667 MHz, which results in 24 nsec/tick
*/
#ifdef USE_PPC8540_TIMER
/* Correction needed: clock is 0.1% off */
#define TB_TICKS_NSEC ((24.0)*1.001)
__inline__ unsigned long get_tbl(void) {
unsigned long tbl;
asm volatile("mftb %0" : "=r" (tbl));
return tbl;
}
__inline__ unsigned long get_tbu(void) {
unsigned long tbl;
asm volatile("mftbu %0" : "=r" (tbl));
return tbl;
}
void timeStampGet(clockid_t which_clock, struct timespec *curtime)
{
unsigned long lo, hi, hi2;
unsigned long long tr;
long double tmpVal;
if(hwtimer == 0)
{
clock_gettime(which_clock, curtime);
return;
}
do {
hi = get_tbu();
lo = get_tbl();
hi2 = get_tbu();
} while (hi2 != hi);
tr = ((unsigned long long) hi << 32) | lo;
tmpVal = (((long double)tr)*TB_TICKS_NSEC)/1000000000.0;
curtime->tv_sec = (int)tmpVal;
curtime->tv_nsec = (int)((tmpVal - (int)tmpVal) * 1000000000);
}
#else
void timeStampGet(clockid_t which_clock, struct timespec *curtime)
{
clock_gettime(which_clock, curtime);
return;
}
#endif
static inline void tsnorm(struct timespec *ts)
{
while (ts->tv_nsec >= NSEC_PER_SEC) {
ts->tv_nsec -= NSEC_PER_SEC;
ts->tv_sec++;
}
}
static double calcdiff(struct timespec t1, struct timespec t2)
{
double diff;
diff = USEC_PER_SEC * ((int) t1.tv_sec - (int) t2.tv_sec);
diff += ((int) t1.tv_nsec - (int) t2.tv_nsec) / 1000.0;
return diff;
}
/*
* timer thread
*
*/
void timerthread(void *param)
{
struct thread_param *par = param;
struct timespec now, next, interval;
struct thread_stat *stat = par->stats;
int n = 1;
RTIME start_ticks;
RT_TIMER_INFO timer_info;
int err;
interval.tv_sec = par->interval / USEC_PER_SEC;
interval.tv_nsec = (par->interval % USEC_PER_SEC) * 1000;
err = rt_timer_inquire(&timer_info);
if (err) {
fprintf(stderr, "latency: rt_timer_inquire, code %d\n", err);
return;
}
start_ticks = timer_info.date + rt_timer_ns2ticks(1000000);
err = rt_task_set_periodic(NULL, start_ticks,
rt_timer_ns2ticks(par->interval * 1000));
if (err) {
fprintf(stderr, "latency: failed to set periodic, code %d\n", err);
return;
}
/* Get current time */
timeStampGet(CLOCK_MONOTONIC, &now);
next = now;
next.tv_sec++;
stat->threadstarted++;
while (!shutdown) {
double diff;
unsigned long ov;
if (rt_task_wait_period(&ov))
break;
stat->overruns += ov;
timeStampGet(CLOCK_MONOTONIC, &now);
if (n) { n= 0; } /* Ignore first period */
else {
diff = calcdiff(now, next);
if (diff < stat->min)
stat->min = diff;
if (diff > stat->max)
stat->max = diff;
stat->avg += diff;
stat->act = diff;
stat->cycles++;
if (par->bufmsk)
stat->values[stat->cycles & par->bufmsk] = diff;
}
next.tv_sec = now.tv_sec + interval.tv_sec;
next.tv_nsec = now.tv_nsec + interval.tv_nsec;
tsnorm(&next);
if (par->max_cycles && par->max_cycles == stat->cycles)
break;
}
stat->threadstarted = -1;
return;
}
/* Print usage information */
static void display_help(void)
{
printf("cyclictest_xenomai %s\n", VERSION_STRING);
printf("Usage:\n"
"cyclictest <options>\n\n"
"-h --hwtimer use hardware timer\n"
"-i INTV --interval=INTV base interval of thread in us default=1000\n"
"-l LOOPS --loops=LOOPS number of loops: default=100\n"
"-p PRIO --prio=PRIO priority of highest prio thread: default=80\n"
"-q --quiet print only a summary on exit\n"
"-t NUM --threads=NUM number of threads: default=10\n"
"-v --verbose output values on stdout for statistics\n"
" format: n:c:v n=tasknum c=count v=value in us\n"
);
exit(0);
}
/* Process commandline options */
static void process_options (int argc, char *argv[])
{
int error = 0;
for (;;) {
int option_index = 0;
/** Options for getopt */
static struct option long_options[] = {
{"hwtimer", no_argument, NULL, 'h'},
{"interval", required_argument, NULL, 'i'},
{"loops", required_argument, NULL, 'l'},
{"priority", required_argument, NULL, 'p'},
{"quiet", no_argument, NULL, 'q'},
{"threads", required_argument, NULL, 't'},
{"verbose", no_argument, NULL, 'v'},
{"help", no_argument, NULL, '?'},
{NULL, 0, NULL, 0}
};
int c = getopt_long (argc, argv, "hi:l:p:qt:vx",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h': hwtimer = 1; break;
case 'i': interval = atoi(optarg); break;
case 'l': max_cycles = atoi(optarg); break;
case 'p': priority = atoi(optarg); break;
case 'q': quiet = 1; break;
case 't': num_threads = atoi(optarg); break;
case 'v': verbose = 1; break;
case '?': error = 1; break;
}
}
if (priority < 0 || priority > 99 || num_threads < 1)
error = 1;
if (error)
display_help ();
}
static void sighand(int sig)
{
shutdown = 1;
}
static void print_stat(struct thread_param *par, int index, int verbose)
{
struct thread_stat *stat = par->stats;
if (!verbose) {
if (quiet != 1) {
printf("%cT:%2d P:%2d I:%ld O:%4ld C:%7lu "
"Min:%10.3lf Avg:%10.3lf Max:%10.3lf \n",
quiet == 2 ? '#' : ' ',
index, par->prio, par->interval,
stat->overruns, stat->cycles, stat->min,
stat->cycles ?
stat->avg/stat->cycles : 0, stat->max);
}
}
}
int main(int argc, char **argv)
{
struct thread_param *par;
struct thread_stat *stat;
int i, ret = -1;
char task_name[16];
if (geteuid()) {
printf("need to run as root!\n");
exit(-1);
}
process_options(argc, argv);
/* Lock all pages from being swapped out */
if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) {
perror("mlockall");
exit(1);
}
signal(SIGINT, sighand);
signal(SIGTERM, sighand);
par = calloc(num_threads, sizeof(struct thread_param));
if (!par)
goto out;
stat = calloc(num_threads, sizeof(struct thread_stat));
if (!stat)
goto outpar;
for (i = 0; i < num_threads; i++) {
if (verbose) {
stat[i].values = calloc(VALBUF_SIZE, sizeof(double));
if (!stat[i].values)
goto outall;
par[i].bufmsk = VALBUF_SIZE - 1;
}
par[i].prio = priority;
if (priority)
priority--;
par[i].interval = interval;
par[i].max_cycles = max_cycles;
par[i].stats = &stat[i];
stat[i].min = 1000000.0;
stat[i].max = -1000000.0;
stat[i].avg = 0.0;
snprintf(task_name, sizeof(task_name), "TimerTest-%d", i+1);
if((rt_task_create(&stat[i].task, task_name, 0, priority, T_FPU)) ||
(rt_task_start(&stat[i].task, &timerthread, &par[i])))
goto outall;
stat[i].threadstarted = 1;
}
while (!shutdown) {
int allstopped = 0;
for (i = 0; i < num_threads; i++) {
print_stat(&par[i], i, verbose);
if(max_cycles && stat[i].cycles >= max_cycles)
allstopped++;
}
usleep(10000);
if (shutdown || allstopped)
break;
if (!verbose && !quiet)
printf("\033[%dA", num_threads);
}
ret = 0;
outall:
shutdown = 1;
usleep(50000);
if (verbose) {
for (i = 0; i < num_threads; i++) {
unsigned int j;
if(par[i].stats->cycles > VALBUF_SIZE)
par[i].stats->cycles = VALBUF_SIZE;
j = 0;
while (par[i].stats->cycles != j) {
double diff = par[i].stats->values[j];
printf("%8d:%8u:%10.3lf\n", i, j++, diff);
}
}
}
if (quiet)
quiet = 2;
for (i = 0; i < num_threads; i++) {
if (stat[i].threadstarted > 0) {
rt_task_delete(&stat[i].task);
}
if (stat[i].threadstarted) {
if (quiet)
print_stat(&par[i], i, 0);
}
if (stat[i].values)
free(stat[i].values);
}
free(stat);
outpar:
free(par);
out:
exit(ret);
}
_______________________________________________
Xenomai-help mailing list
[email protected]
https://mail.gna.org/listinfo/xenomai-help