|
|
View previous topic :: View next topic |
Author |
Message |
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
RTOS oddity |
Posted: Wed Aug 28, 2013 6:23 am |
|
|
Hi All,
I use the rtos because it provides a lot of control and is easy to adjust scheduling, it also makes the code easier to read and understand.
Recently it's been doing some odd things..
Here's a simplified task:-
Code: | #use rtos(timer = 0, minor_cycle=1ms)
#task (rate=34ms)
void sample_task()
{
rtos_await( AD_conversion_complete() );
x_ad_res = read_ad(ad_config_ain2);
rtos_await( AD_conversion_complete() );
y_ad_res = read_ad(ad_config_ain3);
} |
The AD_conversion takes about 16.5 ms, and I expect the task to run at the 34 ms rate, but it doesn't. It runs at 68mS. If I change the rate to 50ms, the task then runs at 100 ms.
If I change the code so it waits for the ADC ready like this, then the task runs at the 34 mS rate, but is wasting many cycles.
Code: | #task (rate=34ms)
void sample_task()
{
while( !AD_conversion_complete() );
x_ad_res = read_ad(ad_config_ain2);
while!( AD_conversion_complete() );
y_ad_res = read_ad(ad_config_ain3) - ((float)sca_temp * x_temp_coeff);
} |
I do have other very low overhead tasks, but they are not the cause, because if I disable them completely so there is only the sample_task, the results are the same.
I'm beginning to wonder if there is some odd thing with having more than one rtos_await() in a task, but I am not convinced because I have done this before and had no problems.
IDE V5.010 PCH 4.141
PIC18F25K50
Fuses..
Code: | #include <18F26K22.h>
#device CONST=ROM
#device PASS_STRINGS=IN_RAM
#FUSES NODEBUG //Disable Debug mode for use with ICD
#FUSES WDT512 //Watch Dog Timer uses 1:512 Postscale
#FUSES PUT //Power Up Timer
#FUSES BROWNOUT //No brownout reset
#FUSES NOPBADEN //PORTB pins are configured as digital I/O on RESET
#FUSES NOMCLR //Master Clear pin used for I/O
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES NOCPD //Data EEPROM not code protected
#FUSES PROTECT //Code protected from reads
#FUSES NOCPB //Boot block not code protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES HSM //External oscillator
#FUSES PLLEN //enable the x4 PLL
#use delay(clock=58982400) //xtal 14745600 * 4
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Wed Aug 28, 2013 7:41 am |
|
|
It will...
You are possibly misunderstanding how the task time, and rtos_await work.
The task is executed every 34mSec (potentially).
As soon as rtos_await returns 'false', the task exits to allow other things to be done.
Another 34mSec has to pass before the code tries again....
Best Wishes |
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Wed Aug 28, 2013 7:51 am |
|
|
Thank you Ttelmah, but the help file says differently
Quote: | This function can only be used in an RTOS task. This function waits for expre to be true before continuing execution of the rest of the code of the RTOS task. This function allows other tasks to execute while the task waits for expre to be true.
|
Although I do agree that your description would make sense. |
|
|
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Wed Aug 28, 2013 8:13 am |
|
|
Think about it. The only thing 'missing' in the help file is that it will return 34ms later to check again on await. So what Thelmah is saying and the help file actually agrees.
Regards |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Wed Aug 28, 2013 8:36 am |
|
|
Yes.
The point is it doesn't sit in the function, with 'baited breath' to go on. It has to wait for the next 'time slot' to go on. Allowing other things to execute.
Given you know that it is going to take 16.5mSec before the ADC is 'ready', you want to time slot just after this. Say at 18mSec. Then the first time the ADC will be true, so the first ADC function will execute, then it'll exit, and try again 18mSec later, by which time the second pass will be true.
It is based on the old 'co-operative' multi tasking model. So the 'await' gives an early exit if something needs to be 'waited for', with the retest, on the next time slot.
Best Wishes |
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Wed Aug 28, 2013 10:23 am |
|
|
Yes, I understand all that, but my point is that the help file says it waits.
My reading of the help file is that it means it return to the scheduler for other tasks to do their stuff, and then comes back to this task at the same point to resume.
I don't think I'm being perverse in that interpretation.? Or am I? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Wed Aug 28, 2013 10:29 am |
|
|
It'll only come back, when either:
1) The next time slot happens, or
2) Every other task also exits early.
The await, returns you to the scheduler, which will then call the other tasks.
There is no ability to jump back to the task, except via the scheduler. The 'wait' is the wait for this to call the function.
Have a look at the WikiPedia entry on 'cooperative multitasking'.
Best Wishes |
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Wed Aug 28, 2013 1:01 pm |
|
|
Ttelmah wrote: | It'll only come back, when either:
1) The next time slot happens, or
2) Every other task also exits early.
The await, returns you to the scheduler, which will then call the other tasks.
There is no ability to jump back to the task, except via the scheduler. The 'wait' is the wait for this to call the function.
Have a look at the WikiPedia entry on 'cooperative multitasking'.
Best Wishes |
So if I only have one task, then it should return to that same point before the 34ms?
I understand cooperative multitasking perfectly well. What I don't understand is exactly what to expect from the ccs implementation. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu Aug 29, 2013 3:53 am |
|
|
Oxo, we are not CCS we are users. We cannot tell you, nor anyone else, what will happen or is meant to happen. All we can do is, from our observations and previous experience, tell you what we've seen happens, and our interpretation of it.
I have not yet used CCS's RTOS. I have used others in the past. Whenever I've used such waits, i.e. wait for some event, its meant "I'm giving up the rest of this time slot, wake me in my next scheduled timeslot where the conditions of the wait are fulfilled." RTOSs, indeed any OSs main function, in no small part, to avoid "hard" waiting, ie. unproductive tight looping. In other words no OS controlled wait in task can mean "I'm sitting here using up all processor time doing nothing until something happens". Instead they imply the task is blocked and cannot proceed until something happens, and that the OS can schedule something else to run.
Cooperative OSs only schedule when task start to run, they cannot stop them. Therefore the all tasks to cooperate by doing some form of wait or yield, which includes running off the end of a task, in a timely manner. That means all waits or yields end the current timeslice for the task. Really the concept of a timeslot or timeslice is meaningless with cooperative OS. Task start at regular intervals, but they are not stopped: they have to stop themselves. OSs that can stop running tasks mid-flight are, of course, pre-emptive OSs.
Whether a cooperative OS can start a task, or continue a previously blocked task after the block has cleared, at times OTHER than at their defined start intervals is a tricky problem. Indeed OS design is a complex matter and there's all sorts of issues to consider even in the simplest of cooperative OSs. Due to the limitations of the PIC platform, CCSs RTOS is about as simple as an OS can reasonably be.
Let's look at your code:
Code: |
#use rtos(timer = 0, minor_cycle=1ms)
#task (rate=34ms)
void sample_task()
{
// First await. The chances are this DOESN'T need to wait as the ADC
// has already completed a conversion. This is because the ADC
// conversion time is less than the task scheduling interval.
rtos_await( AD_conversion_complete() );
x_ad_res = read_ad(ad_config_ain2);
// Now we will have to wait as the ADC is still converting. Therefore
// We relinquish the rest of our time slot. We can only be rescheduled
// and continue running at our NEXT time start. Therefore we take two
// timeslots, to complete this, not just one.
rtos_await( AD_conversion_complete() );
y_ad_res = read_ad(ad_config_ain3);
// By the time we get here our overall time to run will be
// a little over 34ms, repeated every 68ms.
}
|
To get this to run as a well behaved OS task you'd really need to ensure one wait, one that's unlikely to be blocked, per timeslot. I thought about this, and the trouble is that the OS is not synchronised with the presumably external ADC. That means that the samples from the ADC will not be at regular time intervals, some will be at 34ms per channel, sometimes longer as the 16.5ms conversion interval of the ADC beats with the 18ms repeat interval of the task. That may be OK depending on your application.
What you could do is run the task at smaller time intervals, maybe one or two ms and it simply checks the status of the ADC and records a result if there is one, otherwise the task yields. This will depend on how your ADC code works, in particular how the channels are selected, but something like:
Code: |
#use rtos(timer = 0, minor_cycle=1ms)
#task (rate=2ms)
boolean ADC_X = TRUE;
void sample_task()
{
// With a 16.5ms ADC conversion time and a 2ms task interval, this
// will give a 18ms task completion interval , ie. eight yields and one
// run to completion, per converted result.
// We await the ADC conversion result. If its available we use it.
// if it's not we yield and wait for our next activation.
rtos_await( AD_conversion_complete() );
if (ADC_Select)
{
x_ad_res = read_ad(ad_config_ain2);
}
else
{
y_ad_res = read_ad(ad_config_ain3);
}
ADC_X = !ADC_X;
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Thu Aug 29, 2013 7:34 am |
|
|
I think the 'key' is to understand that the RTOS does not take over the system. It allows other 'non timed' code to run in the 'main' program. It is this code that 'spare' time is allocated to. It doesn't assign all the processor time to the tasks, but tries to schedule these at the requested times.
Best Wishes |
|
|
oxo
Joined: 13 Nov 2012 Posts: 219 Location: France
|
|
Posted: Thu Aug 29, 2013 8:14 am |
|
|
Thanks for the helpful replies chaps.
I will be writing to ccs about this, because as they describe it seems to me that is the same as Code: | while( test ) rtos_yield(); |
but the code doesn't behave like that.
. and the help file/documentation is nonsense.
Whatever they give me back I will post here.
Thanks again. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu Aug 29, 2013 8:26 am |
|
|
oxo wrote: |
is the same as Code: | while( test ) rtos_yield(); |
but the code doesn't behave like that.
|
You are right, it doesn't. It (probably) behaves as:
Code: |
while( !test ) rtos_yield();
|
I assume you have to provide the code for any function used as the test. As such, have you found out how often and when its actually called? For example by pin waggling: raising and lowering an IO pin on entry and exit and observing with an oscilloscope? I assume the function must be called once every schedule, even if the task code itself is blocked. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19535
|
|
Posted: Thu Aug 29, 2013 9:07 am |
|
|
Key difference to note in what oxo types, and what RF_Developer types, is '!'.
This is what the manual says. Await, yields, _till_ test goes true.
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|