[FUN] Preliminary results

Discussions and support about ChibiOS/NIL, the almost nil RTOS.
User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: [FUN] Preliminary results

Postby Giovanni » Thu Jan 17, 2013 2:34 pm

Correct, the priorities are fixed.

The AVR and MSP430 ports are untested, I am grateful for any feedback you can provide. I will look at that issue.

Giovanni

wgreiman
Posts: 33
Joined: Sun Oct 23, 2011 3:21 pm
Been thanked: 2 times

Re: [FUN] Preliminary results

Postby wgreiman » Thu Jan 17, 2013 5:17 pm

It appears there is an inconsistency in how nilSchGoSleepTimeoutS is used. In some places the second argument is used as an interval and other places as the system time.

nilThdSleepUntilS is defined this way and assumes the second argument of nilSchGoSleepTimeoutS is the system time:

Code: Select all

#define nilThdSleepUntilS(time) nilSchGoSleepTimeoutS(nil.currp, (time))


nilSemWaitTimeoutS assumes the second argument of nilSchGoSleepTimeoutS is an interval and has this call:

Code: Select all

return nilSchGoSleepTimeoutS((void *)sp, time);


The nilSchGoSleepTimeoutS code assumes the second argument is a time but tests it as if it is an interval.

Code: Select all

  otp->timeout = (bool_t)(time != TIME_INFINITE);


I discovered this when I found that on AVR with a 16-bit systime_t there was a problem with rollover while ChibiOS/RT works fine.

The problem is that nilThdSleep can result in a call to nilSchGoSleepTimeoutS with TIME_INFINITE even though the argument to nilThdSleep is small.

I am trying to modify Nil RTOS so nilSchGoSleepTimeoutS treats its second argument as an interval.

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: [FUN] Preliminary results

Postby Giovanni » Thu Jan 17, 2013 6:23 pm

Hi,

Thanks for all the findings, I never went beyond the first tests with Nil, I really need to allocate some time to it because it is a small thing that I really like conceptually.

Giovanni

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: [FUN] Preliminary results

Postby Giovanni » Sun Jan 20, 2013 7:31 pm

wgreiman wrote:I have been playing with nil on an Arduino. It appears that nil doesn't do a reschedule in nilSysTimerHandler.

I was running two periodic threads, thread1 with a short period and thread2 with a long period. Thread1 was first in the task table but it was not rescheduled when its sleep period expired.

I added the following lines after the do-while loop in nilSysTimerHandler and it seems to work but may not be the correct solution.

Code: Select all

  nilSysLockFromIsr();
  nilSchRescheduleS(); 
  nilSysUnlockFromIsr();

I am assuming nil is intended to have a fixed priority preemptive scheduler with thread priority determined by position in the task table.


I looked into this, nilSysTimerHandler () does not reschedule because it must be called from an ISR that uses the ISR wrapper macros, the call to nilSchRescheduleS() is performed by the epilogue macro. It is just like in ChibiOS.

Giovanni

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: [FUN] Preliminary results

Postby Giovanni » Sun Jan 20, 2013 8:36 pm

I committed a fix for the problems with sleep functions, I could change it some more after deciding how to proceed with Nil.

Giovanni

wgreiman
Posts: 33
Joined: Sun Oct 23, 2011 3:21 pm
Been thanked: 2 times

Re: [FUN] Preliminary results

Postby wgreiman » Wed Jan 23, 2013 1:29 pm

Giovanni,

Thanks for the sleep fixes.

I have now a preliminary version of an Arduino library using Nil RTOS. I have written a number of demonstration and test programs.

I am very excited about Nil as a first RTOS for Arduino users. I have ported ChibiOS/RT and FreeRTOS to Arduino and a number of people are using these ports. ChibiOS/RT is great on Arduino Due with SAM3X8E and Teensy 3.0 with MK20DX128.

Nil RTOS is simple and small and I plan on writing a detailed tutorial to help new Adruino users.

Here is a demo, a tutorial blink, for AVR Arduinos.

Code: Select all

/*
 * Example to demonstrate thread definition, semaphores, and thread sleep.
 */
#include <NilRTOS.h>

// The LED is attached to pin 13 on Arduino.
const uint8_t LED_PIN = 13;

// Declare a semaphore with an inital counter value of zero.
SEMAPHORE_DECL(sem, 0);
//------------------------------------------------------------------------------
/*
 * Thread 1, turn the LED off when signalled by thread 2.
 */
// Declare a stack with 128 bytes beyond context switch and interrupt needs.
NIL_WORKING_AREA(waThread1, 128);

// Declare thread function for thread 1.
NIL_THREAD(Thread1, arg) {
  while (TRUE) {
   
    // Wait for signal from thread 2.
    nilSemWait(&sem);
   
    // Turn LED off.
    digitalWrite(LED_PIN, LOW);
  }
}
//------------------------------------------------------------------------------
/*
 * Thread 2, turn the LED on and signal thread 1 to turn the LED off.
 */
// Declare a stack with 128 bytes beyond context switch and interrupt needs.
NIL_WORKING_AREA(waThread2, 128);

// Declare thread function for thread 2.
NIL_THREAD(Thread2, arg) {

  pinMode(LED_PIN, OUTPUT);
 
  while (TRUE) {
    // Turn LED on.
    digitalWrite(LED_PIN, HIGH);
   
    // Sleep for 200 milliseconds.
    nilThdSleepMilliseconds(200);
   
    // Signal thread 1 to turn LED off.
    nilSemSignal(&sem);
   
    // Sleep for 200 milliseconds.   
    nilThdSleepMilliseconds(200);
  }
}
//------------------------------------------------------------------------------
/*
 * Threads static table, one entry per thread.  A thread's priority is
 * determined by its position in the table with highest priority first.
 *
 * These threads start with a null argument.  A thread's name may also
 * be null to save RAM since the name is currently not used.
 */
NIL_THREADS_TABLE_BEGIN()
NIL_THREADS_TABLE_ENTRY("thread1", Thread1, NULL, waThread1, sizeof(waThread1))
NIL_THREADS_TABLE_ENTRY("thread2", Thread2, NULL, waThread2, sizeof(waThread2))
NIL_THREADS_TABLE_END()
//------------------------------------------------------------------------------
void setup() {
  // Start Nil RTOS.
  nilBegin();
}
//------------------------------------------------------------------------------
// Loop is the idle thread.  The idle thread must not invoke any
// kernel primitive able to change its state to not runnable.
void loop() {
  // Not used.
}

This program uses 2150 bytes of flash on an ATmega328 with the Arduino system and Nil RTOS.

The Arduino environment is not very flexible so I have modified the the thread definition macros and the NilSystem type so you don't need to define NIL_CFG_NUM_THREADS in nilconf.h. Here is the table end macro:

Code: Select all

/**
 * @brief   End of user threads table.
 */
#if WHG_MOD
#define NIL_THREADS_TABLE_END()                                             \
  {"idle", 0, NULL, NULL, 0}                                                \
};                                                                          \
static Thread nil_threads[sizeof(nil_thd_configs)/sizeof(ThreadConfig)];    \
NilSystem nil = {0, 0, 0, nil_threads,                                      \
&nil_threads[sizeof(nil_thd_configs)/sizeof(ThreadConfig) - 1]};            \
const uint8_t nil_thd_count = sizeof(nil_thd_configs)/sizeof(ThreadConfig) - 1;
#else  /* WHG_MOD */
#define NIL_THREADS_TABLE_END()                                             \
  {"idle", 0, NULL, NULL, 0}                                                \
};
#endif  /* WHG_MOD */


I have found several problems but have temporary fixes. The biggest problem is with semaphore timeout. The counter doesn't get incremented on timeout and the wrong status is returned. I will collect these and post them later.

Xamusk
Posts: 81
Joined: Sat Mar 05, 2011 10:47 pm

Re: [FUN] Preliminary results

Postby Xamusk » Wed Jan 23, 2013 1:43 pm

Looks like ChibiOS is already ported to the SAM3 and Kinetis!

User avatar
Giovanni
Site Admin
Posts: 14444
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1074 times
Been thanked: 921 times
Contact:

Re: [FUN] Preliminary results

Postby Giovanni » Wed Jan 23, 2013 10:26 pm

Hi,

I was aware of your libraries for Arduino, good job in making ChibiOS (and FreeRTOS) more accessible.

About nil, it definitely needs a test suite like the ChibiOS one, I need to decide what to do with this new toy. Thanks for all the problems you are reporting, I will try to fix everything ASAP.

Giovanni

wgreiman
Posts: 33
Joined: Sun Oct 23, 2011 3:21 pm
Been thanked: 2 times

Re: [FUN] Preliminary results

Postby wgreiman » Thu Jan 24, 2013 1:41 pm

Here are some patches I made to nil to fix what I think are bugs. These are temporary fixes, I know there are better ways to fix these problems.

The patches are between an "#if WHG_PATCH" and an "#endif". Ignore "#if WHG_MOD", these are changes to support porting to an Arduino library.

A patch in nilSysTimerHandler for nilSemWait time out:

Code: Select all

void nilSysTimerHandler(void) {
  Thread *tp;
  systime_t time;

  time = ++nil.systime;
  tp = &nil.threads[0];
  do {
    nilSysLockFromIsr();
    if (tp->timeout && (tp->wakeup.time == time)) {

#if WHG_PATCH
      nilDbgAssert(tp->waitobj.p != NULL,
                   "nilSysTimerHandler(), #1", "");
      /* Could add a field to Thread structure to indicate waitobj type. */
      if (tp->waitobj.thdp < nil.threads || tp->waitobj.thdp > nil.idlep) {
        /* Handle nilSemWaitTimeout. */
        tp->wakeup.msg = NIL_MSG_TMO;
        tp->waitobj.semp->cnt++;
      } else {
        /* Do nothing for nilThdSleep */
      }
#else /* WHG_PATCH */
      nilDbgAssert(tp->waitobj.p == NULL,
                   "nilSysTimerHandlerI(), #1", "");
#endif  /* WHG_PATCH */
      nilSchReadyI(tp);
    }
    nilSysUnlockFromIsr();
    tp++;
#if WHG_MOD
  } while (tp < nil.idlep);
#else  /* WHG_MOD */
  } while (tp < &nil.threads[NIL_CFG_NUM_THREADS]);
#endif  /* WHG_MOD */

Patches in nilSchGoSleepTimeoutS to to return the correct wakeup.msg and prevent sleep in the idle thread:

Code: Select all

msg_t nilSchGoSleepTimeoutS(void *waitobj, bool_t timeout, systime_t time) {
  Thread *ntp, *otp = nil.currp;
 
#if WHG_PATCH
  nilDbgAssert(otp < nil.idlep, "nilSchGoSleepTimeoutS(), #1", "");
  /* Idle thread can't sleep, need better solution. (Bill Greiman)*/
  while (otp == nil.idlep) {}
#endif  /* WHG_PATCH */

  /* Timeout settings.*/
  otp->timeout = timeout;
  otp->wakeup.time = time;

  /* Storing the wait object for the current thread.*/
  otp->waitobj.p = waitobj;

  /* Scanning the whole threads array.*/
  ntp = nil.threads;
  while (TRUE) {
    /* Is this thread ready to execute?*/
    if (ntp->waitobj.p == NULL) {
      nil.currp = nil.nextp = ntp;
      port_switch(ntp, otp);
#if WHG_PATCH
      return otp->wakeup.msg;
#else  /* WHG_PATCH */
      return ntp->wakeup.msg;
#endif  /* WHG_PATCH */
    }

    /* Points to the next thread in lowering priority order.*/
    ntp++;
#if WHG_MOD
    nilDbgAssert(ntp <= nil.idlep,
                 "nilSchGoSleepTimeoutS(), #2", "");
#else  /* WHG_MOD */
    nilDbgAssert(ntp <= &nil.threads[NIL_CFG_NUM_THREADS],
                 "nilSchGoSleepTimeoutS(), #1", "");

#endif /* WHG_MOD */
  }
}

Patches in nilSemWaitTimeoutS to fix semaphore problems:

Code: Select all

msg_t nilSemWaitTimeoutS(Semaphore *sp, systime_t time) {

  /* Note, the semaphore counter is a volatile variable so accesses are
     manually optimized.*/
#if WHG_PATCH
  cnt_t cnt = sp->cnt - 1;
  if (cnt >= 0) {
    sp->cnt = cnt;
    return NIL_MSG_OK;
  }
  if (TIME_IMMEDIATE == time) {
     return NIL_MSG_TMO;
  }
  sp->cnt = cnt;
  return nilSchGoSleepTimeoutS((void *)sp,
                               time != TIME_INFINITE,
                               nilTimeNow() + time);
#else   /* WHG_PATCH */
  cnt_t cnt = sp->cnt;
  if ((cnt <= 0) && (TIME_IMMEDIATE != time)) {
    sp->cnt = cnt - 1;
    return nilSchGoSleepTimeoutS((void *)sp,
                                 time != TIME_INFINITE,
                                 nilTimeNow() - time);
  }
  return NIL_MSG_TMO;
#endif  /* WHG_PATCH */
}

A patch in nil.h for nilThdSleep:

Code: Select all

#if WHG_PATCH
#define nilThdSleepS(time)                                                  \
  nilSchGoSleepTimeoutS(nil.currp, TRUE, nilTimeNow() + time)
#else /* WHG_PATCH */
#define nilThdSleepS(time)                                                  \
  nilSchGoSleepTimeoutS(nil.currp, TRUE, nilTimeNow() - time)
#endif  /* WHG_PATCH */

wgreiman
Posts: 33
Joined: Sun Oct 23, 2011 3:21 pm
Been thanked: 2 times

Re: [FUN] Preliminary results

Postby wgreiman » Thu Jan 24, 2013 9:56 pm

I think there needs to be two macros that return nil.systime, nilTimeNow() and nilTimeNowS().

For the 8-bit AVR, fetching the 16-bit time isn't atomic when interrupts are enabled.

The following function didn't work correctly until I added nilSysLock() and nilSysUnlock().

Code: Select all

/**
 * @brief   Delay the invoking thread for the specified time.
 *
 * @param[in] time      the delay in system ticks.
 *
 * @note    This function does not sleep and will block all lower
 *          priority threads.  This function should only be used
 *          in the idle thread.
 * @api
 */
void nilThdDelay(systime_t time) {
  systime_t t;
  nilSysLock();
  systime_t t0 = nilTimeNow();
  nilSysUnlock();
  do {
    nilSysLock();
    t = nilTimeNow();
    nilSysUnlock();
  } while ((t - t0) < time);
}


Return to “ChibiOS/NIL”

Who is online

Users browsing this forum: No registered users and 4 guests