Hello,

I've done a bit more testing.

I was mistaken about RTAI not being able to allocate large FIFOs,
it can.  The bug with RTAI (version 1.4) fifos is just that
rtf_put returns an incorrect result when only part of the requested
size was written (when fifo is almost full).  My patch fixes this.

I wrote a test application that performs some floating point
computations and sends data back to user-space via FIFOs.
I ran my test with both RTL and RTAI on a 166 MHz pentium.
With RTL and RTAI both, the task consistently took between
5500 cpu cycles (best case) and 11000 cpu cycles (worst case).
The difference is that RTL could run at a maximum frequency
of 8 KHz (20739 cpu cycles per period), but RTAI could run at
a maximum frequency of 14 KHz (11851 cpu cycles per period).

I'm using RTAI version 1.4 with Linux 2.2.16
I'm using RTL version 30pre6c with Linux 2.2.17

I am attaching my test program.  If anyone can suggest ways
to improve performance, I'd love to hear them.

Thanks,

-Truxton
#define USE_RTAI 1
/* #define USE_RTL 1 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/kernel.h>
#include <linux/timex.h>
#include <linux/sched.h>

#include <asm/io.h>

#include <math.h>
#include <time.h>

#ifdef USE_RTL
  #include <rtl.h>
  #include <rtl_fifo.h>
  #include <pthread.h>

  #define RT_WAIT_FOR_NEXT_PERIOD pthread_wait_np

  pthread_t rt_thread ;
#endif
#ifdef USE_RTAI
  #include <rtai.h>
  #include <rtai_fifos.h>
  #include <rtai_sched.h>

  #define RT_WAIT_FOR_NEXT_PERIOD rt_task_wait_period

  RT_TASK rtai_thread ;
  RTIME rtait_tick_period ;
#endif

#define INITIAL_PERIOD 10000000  /* initial period is 10 milliseconds (100 Hz) */

#define STACK_SIZE 32768
#define SMALL_RT_FIFO_SIZE 32768
#define LARGE_RT_FIFO_SIZE 16777216

#define NUMBER_LIST_LENGTH 32

#define MAX_TICK ((unsigned long) 0xFFFFFFFF)  /* (2^32)-1 = 4294967295 */

static unsigned long dummy_long ;
#define pentium_tick(tick) __asm__ ("rdtsc" : "=a" (tick), "=d" (dummy_long))

int rt_thread_exists ;

struct number_list
{
  double v ;
  struct number_list *previous ;
} ;

void trux_rt_sure_fifo_read(int rt_fifo_id, char *buf, int length)
{
  int r, size ;

  for(size=0;size<length;)
    {
      r=rtf_get(rt_fifo_id,&(buf[size]),length-size) ;
      if(r>0)
        size+=r ;
      else
        RT_WAIT_FOR_NEXT_PERIOD() ;
    }
}

void *rt_task(void *arg)
{
  int i, r, err, done ;
  unsigned long period, nanoseconds_per_iteration, num_iterations ;
  unsigned long old_tick, new_tick, delta, mindelta, maxdelta ;
  struct number_list *tail, *a ;
  double v ;

  printk("RT_TEST %s started...\n",VERSIONS) ;

  /* initialization section */
  {
#ifdef USE_RTL
    nanoseconds_per_iteration=INITIAL_PERIOD ;
    err=pthread_make_periodic_np(pthread_self(), gethrtime(), 
nanoseconds_per_iteration) ;
    if(err)
      printk("ERROR: pthread_make_periodic_np: %d",err) ;
#endif

    trux_rt_sure_fifo_read(0, (char *) (&period), sizeof(unsigned long)) ;
    printk("RT_TEST period == %ld ticks\n",period) ;

    trux_rt_sure_fifo_read(0, (char *) (&nanoseconds_per_iteration), sizeof(unsigned 
long)) ;
    printk("RT_TEST period == %ld nanoseconds\n",nanoseconds_per_iteration) ;

#ifdef USE_RTL
    err=pthread_make_periodic_np(pthread_self(), gethrtime(), 
nanoseconds_per_iteration) ;
#endif
#ifdef USE_RTAI
    err=rt_task_make_periodic(&rtai_thread, rt_get_time(), period) ;
#endif
    if(err)
      printk("ERROR: pthread_make_periodic_np: %d",err) ;

    
    v=0.0 ;
    tail=NULL ;
    for(i=0;i<NUMBER_LIST_LENGTH;i++)
      {
        a=kmalloc(sizeof(struct number_list),GFP_KERNEL) ;
        a->previous=tail ;
        a->v=v ;
        tail=a ;
        v+=0.1 ;
      }
  }  
  mindelta=MAX_TICK ;
  maxdelta=0 ;

  /* runtime loop */
  num_iterations=0 ;
  for(done=0;!done;)
    {
      num_iterations++ ;
      if(!(num_iterations%32768))
        {
          printk("--- timing report (%d) ---\n",num_iterations) ;
          printk("min delta = %ld\n",mindelta) ;
          printk("max delta = %ld\n",maxdelta) ;
          printk("period    = %ld\n",period) ;
          mindelta=MAX_TICK ;
          maxdelta=0 ;
        }

      pentium_tick(old_tick) ;
      {
        i=0 ;
        for(a=tail;a!=NULL;a=a->previous)
          {
            v=sin(a->v) ;
            if(i++<4)
              {
                r=rtf_put(2,&v,sizeof(double)) ;
                if(r!=sizeof(double))
                  {
                    printk("ERROR: fifo is full\n") ;
                    done=1 ;
                  }
              }
            a->v+=(1.0/(512.0+v)) ;
          }
      }
      pentium_tick(new_tick) ;

      if(new_tick>old_tick)
        delta=new_tick - old_tick ;
      else
        delta=(MAX_TICK-old_tick)+1+new_tick ;

      if(delta>maxdelta)
        {
          maxdelta=delta ;
          if(maxdelta>period)
            {
              printk("ERROR: iteration period overrun (%ld > %ld) at iteration 
%ld\n",maxdelta,period,num_iterations) ;
              done=1 ;
            }
        }
      if(delta<mindelta)
        mindelta=delta ;

      RT_WAIT_FOR_NEXT_PERIOD() ;
    }
  return(0) ;
}


int init_module(void)
{
  int err,err0,err2 ;

  printk("RT_TEST init_module...\n") ;

  err=0 ;
  rt_thread_exists=0 ;

  rtf_destroy(0) ;
  rtf_destroy(2) ;
  err0=rtf_create(0, SMALL_RT_FIFO_SIZE) ;
  err2=rtf_create(2, LARGE_RT_FIFO_SIZE) ;

  if((err0<0)||(err2<0))
    {
      printk("rtf_create returns error (%d or %d)\n",err0,err2) ;
      err=1 ;
    }

#ifdef USE_RTL
  {
    pthread_attr_t attr;
    struct sched_param sched_param ;
    hrtime_t now_nsecs ;
  
    if(!err)
      {
        err=pthread_attr_init(&attr) ;
        if(err)
          printk("ERROR: pthread_attr_init: %d",err) ;
      }
    if(!err)
      {
        err=pthread_attr_setfp_np(&attr, 1) ;
        if(err)
          printk("ERROR: pthread_attr_setfp_np: %d",err) ;
      }
    if(!err)
      {
        err=pthread_attr_setstacksize(&attr, STACK_SIZE) ;
        if(err)
          printk("ERROR: pthread_attr_setstacksize: %d",err) ;
      }
    if(!err)
      {
        sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO) ;
        err=pthread_attr_setschedparam(&attr, &sched_param);
        if(err)
          printk("ERROR: pthread_setschedparam: %d",err) ;
      }
    if(!err)
      {
        err=1 ;
        err=pthread_create(&rt_thread, &attr, rt_task, NULL) ;
        if(err)
          printk("ERROR: pthread_create: %d\n",err) ;
        else
          rt_thread_exists=1 ;
      }
  }
#endif
#ifdef USE_RTAI
  {
    err=rt_task_init(&rtai_thread, rt_task, 0, STACK_SIZE, 0, 1, 0) ;
    if(err)
      printk("rt_task_init(%d) returns error %d",STACK_SIZE,err) ;
    else
      {
        rt_thread_exists=1 ;
        rt_set_runnable_on_cpus(&rtai_thread, 1) ;
        rt_set_oneshot_mode() ;
        rt_assign_irq_to_cpu(TIMER_8254_IRQ, 3) ;
        rt_linux_use_fpu(1) ;
        rtait_tick_period = start_rt_timer(nano2count(INITIAL_PERIOD)) ;
        rt_task_make_periodic(&rtai_thread, rt_get_time(), rtait_tick_period) ;
      }
  }
#endif

  return(0) ;
}


void cleanup_module(void)
{
  printk("RT_TEST %s exiting...\n",VERSIONS) ;

  if(rt_thread_exists)
    {
#ifdef USE_RTL
      pthread_cancel(rt_thread) ;
      pthread_join(rt_thread, NULL) ;
#endif
#ifdef USE_RTAI
      rt_reset_irq_to_sym_mode(TIMER_8254_IRQ) ;
      stop_rt_timer() ;
      rt_busy_sleep(1E7) ;
      rt_task_delete(&rtai_thread) ;
#endif
    }

  rtf_destroy(0) ;
  rtf_destroy(2) ;
}
#define USE_RTAI 1
/* #define USE_RTL 1 */

/* iteration frequency of RT task in Hz */
/* #define RT_TEST_FREQUENCY 4000 */
#define RT_TEST_FREQUENCY 10000

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define RT_MODPATH "/usr/local/rtlinux/modules"
#define MY_CPU_SPEED 165915000  /* 166 Mhz */

int rt_fifo_0_fd= -1 ;  /* user -> RT */
int rt_fifo_2_fd= -1 ;  /* RT -> user (data) */

int rt_fifo_data_total=0 ;

int trux_syswrap_read(int fd, char *b, int n)
{
  int result,total_result,read_done=0 ;
  
  total_result=0 ;
  for(;!read_done;)
    {
      read_done=1 ;
      result=read(fd,b,n) ;
      if(result>=0)
        total_result+=result ;
      else
        {
          if(errno==EINTR)
            read_done=0 ;
          else
            total_result=result ;
        }
    }
  return(total_result) ;
}

void rt_initialize()
{
  int r ;
  char temps[1024] ;
  unsigned long frequency, period, nanoseconds_per_iteration ;

#ifdef USE_RTL
  sprintf(temps,"insmod -s -v %s/rtl.o",RT_MODPATH) ;
  system(temps) ;
  sprintf(temps,"insmod -s -v %s/rtl_time.o",RT_MODPATH) ;
  system(temps) ;
  sprintf(temps,"insmod -s -v %s/rtl_sched.o",RT_MODPATH) ;
  system(temps) ;
  sprintf(temps,"insmod -s -v %s/rtl_posixio.o",RT_MODPATH) ;
  system(temps) ;
  sprintf(temps,"insmod -s -v %s/rtl_fifo.o",RT_MODPATH) ;
  system(temps) ;
#endif
#ifdef USE_RTAI
  system("insmod -s -v rtai") ;
  system("insmod -s -v rtai_sched") ;
  system("insmod -s -v rtai_fifos") ;
#endif

  sprintf(temps,"insmod -s -v rt_test") ;
  system(temps) ;

  frequency=RT_TEST_FREQUENCY ;
  nanoseconds_per_iteration=1000000000/frequency ;
  period=MY_CPU_SPEED/frequency ;

  fprintf(stderr,"frequency = %ld\n",frequency) ;
  fprintf(stderr,"nsecs/period = %ld\n",nanoseconds_per_iteration) ;
  fprintf(stderr,"ticks/period = %ld\n",period) ;

  if((rt_fifo_0_fd = open("/dev/rtf0", O_WRONLY)) < 0)
    perror("ERROR OPENING FIFO /dev/rtf0\n") ;

  if((rt_fifo_2_fd = open("/dev/rtf2", O_RDONLY)) < 0)
    perror("ERROR OPENING FIFO /dev/rtf2\n") ;

  r=write(rt_fifo_0_fd,&(period),sizeof(unsigned long)) ;
  r=write(rt_fifo_0_fd,&(nanoseconds_per_iteration),sizeof(unsigned long)) ;
}

void rt_unload()
{
  if(rt_fifo_0_fd>=0)
    close(rt_fifo_0_fd) ;
  if(rt_fifo_2_fd>=0)
    close(rt_fifo_2_fd) ;

  system("rmmod rt_test") ;

#ifdef USE_RTL
  system("rmmod rtl_fifo") ;
  system("rmmod rtl_posixio") ;
  system("rmmod rtl_sched") ;
  system("rmmod rtl_time") ;
  system("rmmod rtl") ;
#endif
#ifdef USE_RTAI
  system("rmmod rtai_fifos") ;
  system("rmmod rtai_sched") ;
  system("rmmod rtai") ;
#endif
}

#define RTF_BLOCK_SIZE 32768

int main(int argc, char **argv)
{
  char buf[RTF_BLOCK_SIZE] ;
  int selres, maxfd, r, done, olderrno ;
  fd_set readfds, writefds, exceptfds ;
  struct timeval timeout, t0, t1 ;
  double num_seconds, previous_num_seconds, data_rate ;

  previous_num_seconds=0.0 ;
  rt_fifo_data_total=0 ;

  rt_initialize() ;

  gettimeofday(&t0,NULL) ;
  for(done=0;!done;)
    {
      maxfd=rt_fifo_2_fd ;
      FD_ZERO(&readfds) ;
      FD_SET(rt_fifo_2_fd,&readfds) ;
      timeout.tv_sec=8 ;
      timeout.tv_usec=0 ;

      selres=select(maxfd+1,&readfds,NULL,NULL,&timeout) ;

      if(selres==0)
        {
          fprintf(stderr,"no data from RT process. (%d bytes 
total)",rt_fifo_data_total) ;
          done=1 ;
        }
      
      if((selres>0) && (FD_ISSET(rt_fifo_2_fd,&readfds)))
        {
          r=trux_syswrap_read(rt_fifo_2_fd,buf,RTF_BLOCK_SIZE) ;
          if(r<0)
            {
              olderrno=errno ;
              fprintf(stderr,"read error %d: %s\n",r,strerror(olderrno)) ;
              done=1 ;
            }
          else
            {
              rt_fifo_data_total+=r ;
              gettimeofday(&t1,NULL) ;
              num_seconds=((double) (t1.tv_sec - t0.tv_sec)) +
                0.000001*((double) (t1.tv_usec - t0.tv_usec)) ;
              if(num_seconds>(previous_num_seconds+1.0))
                {
                  previous_num_seconds=num_seconds ;
                  data_rate=((double) rt_fifo_data_total)/num_seconds ;
                  fprintf(stderr,"read %ld bytes from RT fifo in %g seconds"
                          " (%g bytes / second)\n",rt_fifo_data_total,
                          num_seconds,data_rate) ;
                }
            }
        }
    }

  rt_unload() ;

  return(0) ;
}
  

Reply via email to