Hello,
I am porting the RTX Kernel to a Cortex-M3 device and ran into a difficulty.
I have set up 2 tasks to toggle 2 LEDs to see if my tasks are running as expected. As below.
/*---------------------------------------------------------------------------- * Task 4 'blink_P2': Blink LED P2 *---------------------------------------------------------------------------*/ __task void blink_P2 (void) { os_itv_set (40); for (;;) { os_itv_wait (); Toggle_P2(); } } /*---------------------------------------------------------------------------- * Task 5 'blink_P3': Blink LED P3 *---------------------------------------------------------------------------*/ __task void blink_P3 (void) { os_itv_set (40); for (;;) { os_itv_wait (); Toggle_P3(); } }
If the time delay is set the same for both tasks then there is no problem. Both tasks toggle each LED at 40mS. This works.
However if I change the time delay on one task,(for example the second task to 50mS) then both tasks now take several seconds to toggle the LEDs.
I have ported the RTX kernel previously to an ARM7 core without difficulty but cannot see the problem on the Cortex-M3 ?
Can someone advise please ?
thanks!
Are any other tasks running? What are the ask priorities? Can you show us the task create section of your code?
Note: the wait functions are in values of systick not miliseconds.
Did you properly configure your systick timer?
Note: to post code you can use < pre> and </ pre> (without the spaces).
M
Thanks for the reply Marc,
Yes only 2 tasks are setup. The init task sets up the 2 tasks as below and exits.
/*---------------------------------------------------------------------------- * Task 4 'init': Initialize *---------------------------------------------------------------------------*/ __task void init (void) { GPIO_INIT(); t_blink_P2 = os_tsk_create (blink_P2, 0); /* start task 'blink' */ t_blink_P3 = os_tsk_create (blink_P3, 1); /* start task 'blink' */ os_tsk_delete_self (); }
In the RTX_Conf_CM.c file it is as default except for :
// </h> // <h>SysTick Timer Configuration // ============================= // <o>Timer clock value [Hz] <1-1000000000> // Set the timer clock value for selected timer. // Default: 6000000 (6MHz) #ifndef OS_CLOCK #define OS_CLOCK 16000000 #endif // <o>Timer tick value [us] <1-1000000> // Set the timer tick value for selected timer. // Default: 10000 (10ms) #ifndef OS_TICK #define OS_TICK 1000 #endif
thanks Mike
So your systick is 10ms Which means an ITV wait of 40 = 40 x 10ms = 400ms.
Also not that your tasks have different priorities.
Try setting the priorities of both tasks the same and see if you get the expected behaviour.
the systick is 1000. the 10000 is just a comment placed by keil.
so 40*1000uS = 1mS. I have tried setting 0 for both priorities and no difference.
Sorry I missed that. So you mean 40 * 1000uS = 40ms.
This would mean your LEDs are toggling very fast and it may not be noticeable to the eye. Could that be the issue?
A systick of 1ms is very fast (and I think uncommon) try a systick of 10ms. Also, don't use 0 as the priority set both priorities to 1 (or greater).
bad maths on the last post , 40 * 1000uS = 40mS.
Priority 0 is reserved for the Idle task. Not sure if this is causing your problems, but you should only use priorities between 1 and 255. 255 is the highest priority a task can be. One approach would be make 100 the default priority and go higher and lower as you need to differentiate the priorities of your tasks. You will probably find most will be 100. (but there may be cases where EVERY task has a different priority - use what is needed, not what someone says is "normal")
@Robert - Who said anything was "normal" or are just speaking generally?
@Mike - that does sound weird. Is anything else going on? Interrupts, watchdog?
Just as a shot in the dark try changing your systick to 10ms and see if you get the same behaviour.
I guess I was sort of implying that 100 is a "normal" priority for a task. I didn't want anything to think that all tasks should be "normal" as they need to be what they need to be, not what someone (meaning me) says is "normal". So in general, I was speaking generally.
Also after looking, I found the the OS changes any task created at priority 0 to priority 1 for you so that is not your issue.
What version of the OS are you using. I have look through many version of code from 3.8 to the current 4.22a and have not found an issue with any of the ITV os stuff.
Note:
I use 1ms timer ticks all the time. I find it more feels more "normal" and "natural" than 10ms ticks.
Hi Robert McNamara,
I think this is for a Cortex-M3 MCU. How about an ARM7 MCU? And how fast the CCLK is? I mean, could you please teach me how to decide a proper timer tick?
I heard that, the current/recent Linux kernel is a tickless kernel, but RTX only checks the task status when a tick comes, am I right?
When Task1 executes the os_tsk_pass(), will Task1 passes control to the next task of the same priority immediately? or it needs to wait for a tick?
When Task1 executes the os_tsk_pass(), and there is no task of the same priority in the ready queue, when will Task1 passes control to the other tasks?
Sorry for asking questions in this thread, I am no longer a frequent visitor of this KEIL forum, but I remember that, it is not easy to encounter Robert McNamara here.
Note that ticks are used by operating systems - generally, and not just RTOS - to switch between tasks of same priority that are in the "runnable" state.
When an event happens, that makes a less prioritized task runnable, nothing happens. When an event happens, that makes a more prioritized task runnable, the higher prio is a reason for the OS to switch task.
When a high-priority task doesn't pause itself with a wait call, it doesn't matter what less prioritized tasks wants. The exception is if the OS has support for priority inversion - noticing that a high-prio task is locked by a lock owned by a low-prio task. Then it can give the low-prio task a high priority temporarily just to let it get CPU time to release the lock and hence allow the real high-prio task to get the resource and run.
When a task "passes", it just says: I don't have anything important to do right now. Maybe someone else wants to step in for a while. But the task can't "pass" if a higher prio task is already runnable - the lower-prio task wouldn't be running so it would not be able to call any functions.
And if the only runnable task is a lower prioritized task, then a "pass" doesn't mean much either - the low-prio task still has too little prio to do anything. "pass" doesn't mean to give up the "runnable" state. It just tries to give up the "running" state in case another task of same priority is waiting in "runnable".
When all tasks have different priority, then time slice lengths aren't really important. You don't get a time-sliced system but instead an event-driven system. Tasks are selected when they have high enough prio and wants to run. All other tasks are put to sleep. And the only reason a task runs is that it has highest prio, or all higher prioritized tasks are stuck waiting for something - i.e. not runnable.
But event-driven systems only handles tasks of different priority. When tasks have same priority, you no longer have a mathematical formula to tell which thread must run. So an OS can then support time slicing, where runnable tasks of same priority get some CPU time one by one in sequence based on the time slice settings - what granularity the OS scheduler is using.
The normal way to handle a task that passes the end of a slice to another task (of same prio) is to have a clock that runs at a higher speed than the interval between time-sliced task switches. With a clock that runs at same frequency as the time slicing, a task that gives up the slice after 50% of the time will result in the next task only receiving a half slice before the swap timer ticks. Having the swap timer running at a 10 times higher speed means that the first thread gets 10 ticks. If it gives up the CPU after 50%, the next task need not just get 5 ticks (the remaining 50%) but can get a full set of 10 ticks.
With one tick/switch, a two-task program where one task always gives up the time after 90% could result in two identically prioritized tasks receiving 90% and 10% of the CPU capacity.
What timing methods RTX uses for different processor cores is something Keil has to answer. Exactly how they handle tasks that passes up their time slice early - what amount of time the next task will get. If it is "the remaining time" (tm), then really bad things can happen.
Hi Per,
Many thanks for your help. Now I think I understand the os_tsk_pass() better.
"pass" doesn't mean to give up the "runnable" state. It just tries to give up the "running" state in case another task of same priority is waiting in "runnable".
Hi all,
The documentation about the RTX scheduler isn't bad and addresses many of these topics:
http://www.keil.com/support/man/docs/rlarm/rlarm_ar_schedopt.htm
As for the systick, I normally use 10ms because as far as I understand this is the default time for Cortex-M3 Systick interval. infocenter.arm.com/.../index.jsp
If you consider the CPU clk I'm sure you could come up with a logical sytick interval.
For example, say 50MHz CPU clock, this would mean 1ms * 50MHz = 50000 clocks per milisecond.
(I think)Most Cortex-M instructions are 1-4 clocks this makes for ~25000 instructions per tick. Depending on what your tasks do this could work fine or might not be enough.
Long story short the Systick interval is probably best determined on a case-by-case basis.
Hi Marc,
Thanks for your help.
So, what you suggest is: A. Normally 10ms or B. Cortex-M instructions averagely take 2 clocks, so use the lst file of Task_X to calculate how many instructions the Task_X contains, then get the number of clocks that Task_X may need.
Is my above understanding correct?
(I am not a native English speaker, sorry for my bad English writing.)
Hi John,
I'm not sure you can make the decision so methodical but I think you understand what I was saying.
10ms is the hardware default for the Cortex-M3 systick timer. This does not necessarily mean it is the best choice.
For example, if you have a task that executes on an event and you don't want that execution to be interrupted you better make sure the time slice of the RTOS is long enough for a complete loop execution. (Otherwise you may have to use locks... which can add more complexity)
Basically my opinion is that the OS time slice should generally be long enough for a complete cycle of a task in it's longest case. (Note the time slice is a number multiplied by the systick. eg. 5*10ms = 50ms)
Of course this is very subjective and I can imagine cases where this doesn't matter or wouldn't apply.
Understanding your system's timing is the most important thing because you will be better able to anticipate issues and debug issues when they are encountered.
It's important to separate real-time critical work from background work.
Real-time critical work should be able to finish within the slice period.
Background work can normally span any number of slices - it's even likely that background work will not even be able to use their slices because they get interrupted by "real" work.
About amount of time needed to perform a task - it's way easier to just measure the computation time needed than to try to count cycles. Especially since you may also have a background noise of interrupts running. So measuring the maximum time consumed at maximum interrupt load and then adding a bit of a safety margin would be better than just having taken the sum of all instructions.
Per is absolutely correct.
The only thing I would point out in addition is that because (at least in my experience) it can be difficult to force a tasks (a critical "real work" task) into it's "longest path" taking a look at the number of instructions can be of additional value.
Also as Per pointed out you can eliminate this issue by designing non-interruptable operations in tasks with high priorities.
Thanks for all the helps.
Sorry for that, I think I can not catch the key point.
Assuming that,
There are 4 tasks, task_A, task_B, task_C, task_D.
task_A for critical CAN receiving/processing/transmission, highest priority; needs 25ms. task_B for critical UART receiving/processing, high priority; needs 18ms. task_C for key input checking, low priority; needs 1ms. task_D for LCD displaying, lowest priority; needs 10ms.
If I set the Round-Robin time slice to 15ms. When there are no CAN and UART events, task_A and task_B are at wait_for_some_ticks() or wait_for_HW_event(). So task_A and task_B are not runnable. System is working on task_C and task_D.
When CAN event happens, soon or later, task_A wakes up from waiting_state, since task_A has the highest priority, task_A performs as long as it likes. The Round-Robin time slice does not matter.?
I know I must miss some very important pieces, but I just can not discover it.
I am no longer a KEIL/ARM user, but would like to know more about RTOS/scheduling.
I think you are correct. With 4 tasks all with different priorities the round-robin time slice doesn't really matter as you are designing a preemption based system.
task_A for critical CAN receiving/processing/transmission, high priority; needs 25ms. task_B for critical UART receiving/processing, high priority; needs 18ms. task_C for key input checking, low priority; needs 1ms. task_D for LCD displaying, low priority; needs 10ms.
If I set the Round-Robin time slice to 19ms, and the events happen in the following order, UART -1ms-> 1st-CAN -XXms-> UART -YYms-> 2nd-CAN, task_A will be blocked for (18-1)ms, then execute only 19ms, not able to finish its 1st-CAN, then be blocked for 18ms once again, when 2nd-CAN comes, there are two CAN events need to be handle.
But if I set the Round-Robin time slice to 26ms, it would be much better.
View all questions in Keil forum