Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


ADEOS IPIPE Hard Real Time - Interrupt jitter latency test



Here we have an ADEOS IPIPE example that uses and modifies the 8254 timer interrupt to have a custom timer interrupt frequency in order to measure the timer interrupt jitter and also the max. latency.

We reprogram the 8254 timer to a higher (sampling) frequency. In order to maintain a 100Hz timer tick to linux, we only pass the timer interrupt to linux after PROPAGATE_INT_AFTER_COUNTS timer ticks have occured.
For further details see the adeos-ipipe 8254 timer page


Also see: Stress tests

If you need to disable SMI interrupts, check the captain.at page at High latencies with SMI not disabled

If SMI interrupts are not disbaled, you've likely experienced an interrupt behavious like this. the interrupt jitter is well between the bounds of +/-10μs, but there are the SMI interrupts of 200μs and more:
LATCH=4773
HZ=250
SAMPLE_FREQUENCY=20000
SAMPLE_FREQUENCY_NS=50000
NEWLATCH=59
PROPAGATE_INT_AFTER_COUNTS=80
CANCEL_INT_AFTER_COUNTS=89
I-pipe: Domain TestDomain registered.
Domain TestDomain started.
scaller set to 1400
period set to 50
I-pipe: Domain TestDomain unregistered.
domain wakeup jitter for timer interrupt
domain should have woken up every 50 us
   min: 40 us, max: 60 us  (21381982 samples)

   diff  : samples
    -10 us:    1
     -8 us:    3
     -7 us:   55
     -6 us:  324
     -5 us: 1780
     -4 us: 14408
     -3 us: 83385
     -2 us: 233056
     -1 us: 3731377
      0 us: 16680530
      1 us: 431904
      2 us: 157515
      3 us: 42330
      4 us: 4506
      5 us:  669
      6 us:  124
      7 us:   12
      8 us:    2
     10 us:    1
     197 us:    1
     200 us:    2
     203 us:    1

In order to eliminate those nasty SMI interrupts, there is a pretty simple linux kernel module at High latencies with SMI not disabled. With that module you'll achieve those jitter/latency values you're expecting. ADEOS-IPIPE is actually the best you can get - just depends on the hardware you're using.
LATCH=4773
HZ=250
SAMPLE_FREQUENCY=20000
SAMPLE_FREQUENCY_NS=50000
NEWLATCH=59
PROPAGATE_INT_AFTER_COUNTS=80
CANCEL_INT_AFTER_COUNTS=89
I-pipe: Domain TestDomain registered.
Domain TestDomain started.
scaller set to 1400
period set to 50
I-pipe: Domain TestDomain unregistered.
domain wakeup jitter for timer interrupt
domain should have woken up every 50 us
  min: 40 us, max: 60 us  (3470912 samples)
Feb 11 10:47:19 muon kernel:
  diff  : samples
   -10 us:    1
    -8 us:    7
    -7 us:   41
    -6 us:  126
    -5 us: 1997
    -4 us: 24572
    -3 us: 60669
    -2 us: 41353
    -1 us: 575360
     0 us: 2606700
     1 us: 48189
     2 us: 44075
     3 us: 56304
     4 us: 11012
     5 us:  363
     6 us:  105
     7 us:   29
     8 us:    7
     9 us:    1
    10 us:    1




Finally: There is a Makefile below, so compilation should be no problem if you've got a vital kernel module building system.

Insert the ipipe_irq_jitter module with
# insmod ./ipipe_irq_jitter.ko
-or-
# insmod ./ipipe_irq_jitter.o
depending on your kernel version.
After unloading the module, you'll see the histogram in the kernel log.

Developed and tested with kernel 2.4.32 (adeos-ipipe-2.4.32-i386-1.1-02.patch). Also tested on kernel 2.6.15 with the IPIPE 1.2 patch.
For further details see the comments in the source code.

NOTE: If you test on kernels 2.4 and 2.6, make sure you do a "make clean" and everything should compile well.

ipipe_irq_jitter.c
/*
   ADEOS IPIPE kernel module with 8254 timer usage
   + interrupt jitter/latency measurement
   Module (C) 2006 www.captain.at
   Interrupt jitter/latency measurement (C) 2004 Der Herr Hofrat
*/
#include <linux/version.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
// for 2.4 ipipe
#include <asm/hw_irq.h>

#define SAMPLE_FREQUENCY 20000

static struct ipipe_sysinfo sys_info;
static struct ipipe_domain this_domain;
long long parcounter = 0;
long long timercounter = 0;
unsigned int timerprop = 0;
unsigned int subtimer = 0;

#define PIT_CH0    0x40
#define PIT_MODE   0x43
#define RTHAL_8254_IRQ    0

#define NEWLATCH ((int)((LATCH * HZ) / SAMPLE_FREQUENCY))
#define PROPAGATE_INT_AFTER_COUNTS ((int)(LATCH / NEWLATCH))
#define SAMPLE_FREQUENCY_NS ((int)(1000000000/SAMPLE_FREQUENCY))
#define CANCEL_INT_AFTER_COUNTS ((int)(1.0/((1.0/HZ)/((NEWLATCH*\
         PROPAGATE_INT_AFTER_COUNTS)/1193180.0)-1.0)))

#define PERIOD (1000000/SAMPLE_FREQUENCY)

#define rtai_rdtsc() ({ unsigned long long t; \
   __asm__ __volatile__( "rdtsc" : "=A" (t)); t; })

#define NUMHIST 300

static long min,max;
static unsigned long total_samp=0;
static unsigned long period;
unsigned int graph[2][NUMHIST]; 
unsigned long scaller;
unsigned long long last,now;
long diff;
int isfirstrun = 1;

void timer_tick (unsigned irq) {
   unsigned long flags;
   flags = ipipe_critical_enter(NULL);
	last = now; 
	//hwtimer(now);
	now = rtai_rdtsc();
	if (isfirstrun) {
		last = now;
		isfirstrun = 0;
		ipipe_propagate_irq(irq);
		return;
	}
	
   timercounter++;
   timerprop++;
   
	/* allow it to be handled by 32bit instruction */
	diff = ((unsigned long)(now - last)/scaller) - period;
	/* don't let total_samp role around at 4G */
	if (total_samp != -1) {
		if (diff <= 0) {
			/* fill in the histogram */
			if (-diff < NUMHIST) {
				graph[0][-diff]++; 
			} else {
				printk("Out of bounds %ld us\n", diff);
			}
			/* record min even if out of bounds */
			if ( diff < min) { min = diff; }
		} else if (diff > 0) {
			if (diff <= NUMHIST) {
				graph[1][diff-1]++;
			} else {
				printk("Out of bounds %ld us\n", diff);
			}
			/* record max even if out of bounds */
			if ( diff > max) {
				max = diff;
			}
		}
		total_samp++;
	}
   ipipe_critical_exit (flags);
   // pass timer interrupt to linux (100Hz)
   // if we check with >= we run too fast, but we skip an interrupt later
   if ( timerprop >= PROPAGATE_INT_AFTER_COUNTS ) {
      subtimer++;
      if (subtimer <= CANCEL_INT_AFTER_COUNTS) {
         ipipe_propagate_irq(irq);
      } else {
         subtimer = 0;
      }
      timerprop = 0;
   }
}

static inline void setup_8254 (void) {
   // either use the adeos-ipipe function to programm the 8254,
   //   or do it ourselfs
   ipipe_tune_timer(SAMPLE_FREQUENCY_NS, 0);
/*
   unsigned long flags;
   flags = ipipe_critical_enter(NULL);
   period = NEWLATCH;
   if (period > LATCH) period = LATCH;
   outb(0x34,PIT_MODE);
   outb(period & 0xff,PIT_CH0);
   outb(period >> 8,PIT_CH0);
   ipipe_critical_exit (flags);
*/
}

static void domain_entry (void) {
   printk("Domain %s started.\n",ipipe_current_domain->name); // 
   // setup 8254 timer
   setup_8254();
   
   // grab 8254 interrupt
   ipipe_virtualize_irq(ipipe_current_domain, RTHAL_8254_IRQ,
      (ipipe_irq_handler_t)&timer_tick, NULL, NULL, IPIPE_DYNAMIC_MASK);
   // disable interrupt propagation to other domains (e.g. linux)
   ipipe_control_irq(RTHAL_8254_IRQ,0,IPIPE_HANDLE_MASK);

	ipipe_get_sysinfo(&sys_info);
    scaller = (unsigned long)sys_info.cpufreq / 1000000;
    period = PERIOD;
    printk("scaller set to %ld\n",(unsigned long) scaller);
    printk("period set to %ld\n",(long) period);

    min=100;
    max=-100;

}
        
static int __init mod_init (void) {
   struct ipipe_domain_attr attr;
   printk("LATCH=%d\n", LATCH);
   printk("HZ=%d\n", HZ);
   printk("SAMPLE_FREQUENCY=%d\n", SAMPLE_FREQUENCY);
   printk("SAMPLE_FREQUENCY_NS=%d\n", SAMPLE_FREQUENCY_NS);
   printk("NEWLATCH=%d\n", NEWLATCH);
   printk("PROPAGATE_INT_AFTER_COUNTS=%d\n", PROPAGATE_INT_AFTER_COUNTS);
   printk("CANCEL_INT_AFTER_COUNTS=%d\n", CANCEL_INT_AFTER_COUNTS);
    // start our real time domain
   ipipe_init_attr (&attr);
   attr.name      = "TestDomain";
   attr.priority   = IPIPE_ROOT_PRIO + 200;
   attr.entry      = &domain_entry;
   return ipipe_register_domain(&this_domain,&attr);
}

static void __exit mod_exit (void) {
	int i;
   // reset 8254 timer interrupt to default value
   ipipe_tune_timer(10000000,IPIPE_RESET_TIMER);
   // unregister our domain
   ipipe_unregister_domain(&this_domain);
   
	/* dump the results via printk - not very elegant....*/
	printk("domain wakeup jitter for timer interrupt\n"); 
	printk("domain should have woken up every %ld us\n",
		(unsigned long)(1000000/SAMPLE_FREQUENCY));
	printk("  min: %ld us, max: %ld us  (%ld samples)\n",
		min+period,max+period,total_samp);
	printk("\n\tdiff  : samples\n");   
	for (i = (NUMHIST - 1); i >= 0; i--){
		if (graph[0][i] > 0) {
			printk("\t%4d us: %4d\n", -i, graph[0][i]);
		}
	}
	for (i = 0; i < NUMHIST; i++){
		if (graph[1][i] > 0) {
			printk("\t%4d us: %4d\n", i+1, graph[1][i]);
		}
	}
  
}

module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");





Makefile
UNAME := $(shell uname -r)
KERNEL26 := 2.6
KERNELVERSION := $(findstring $(KERNEL26),$(UNAME))

all:: 

ifeq ($(KERNELVERSION),2.6)

obj-m	:= ipipe_irq_jitter.o

INCLUDE	:= -I/usr/include/asm/mach-default/
KDIR	:= /lib/modules/$(shell uname -r)/build
PWD		:= $(shell pwd)

all::
	$(MAKE) -C $(KDIR) $(INCLUDE) SUBDIRS=$(PWD) modules
	
else

TARGET	:= ipipe_irq_jitter
INCLUDE	:= -I/lib/modules/`uname -r`/build/include -I/usr/include/asm/mach-default/
CFLAGS	:= -O2 -Wall -DMODULE -D__KERNEL__ -DLINUX
CC	:= gcc

all:: ${TARGET}.o

${TARGET}.o: ${TARGET}.c
	$(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c
	
endif

clean::
	$(RM) .ipipe_irq_jitter* *.cmd *.o *.ko *.mod.c
	$(RM) -R .tmp*
Last-Modified: Sat, 11 Feb 2006 03:04:17 GMT

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top