We recently featured a webinar called Learn how to run the uC/OS-III real-time kernel on an ARM Cortex M3. It was delivered by the kernel's author, Micrium's Founder, Jean Labrosse, in conjunction with one of the company's software experts, Matt Gordon. The class describes real-time kernels from generic terms to specifics involved with an ARM Cortex-M3 architecture.
At the conclusion of the webinar, many questions were asked. Some were answered live, but time did not permit all questions to be answered. So Mr. Labrosse took the time to respond to those questions that time did not allow for.
Question:How large is a typical task's stack?
Answer:That really depends on what the task needs to do. In fact, sizing the stack is not obvious and generally you need to take an educated guess and give your tasks way more stack space than they need. The good news is that uC/OS-III performs run-time stack sizing and thus, it's possible to know how much stack space your tasks actually use. You can then go back and make adjustments. You obviously don't want to set the stack size exactly as measured but instead, give yourself 25% to 100% more space.
Question:As an alternative to using mutexes, could I simply disable interrupts when accessing shared resources?
Answer:Yes and no. Technically speaking, you can obviously disable interrupts while accessing a critical section. But you want to make sure that you do so for as short as possible. C/OS-III disables interrupts to access some internal data structures but we keep that to a minimum.
Accessing a couple of variables by first surrounding them with OSMutexPend()/OSMutexPost() will most certainly create more overhead than you'd want. However, you need to be certain that the access time to those variables is indeed very short. It would be acceptable to disable/enable interrupts when you are only “copying” variables but not when you're performing complex arithmetic operations on them while interrupts are disabled.
So, if you can ensure that access is very short and won't affect interrupt latency of your system as a whole, then I'd say that disabling/enabling interrupts is acceptable.
Question:You mentioned that ISRs should be kept as short as possible. What sorts of problems can lengthy ISRs cause?
Answer:For one thing, ISRs are generally hard to debug. The simpler the ISRs, the better. If you have long ISRs and you don't allow for interrupt nesting, then you stand a chance of missing other interrupts. If you allow for nesting, you further complicate the debugging of ISRs. Also, ISRs are meant to capture asynchronous events to prevent the processor from having to poll I/O devices. The hardware typically prioritizes these I/O devices which may not correspond to the same priority as what you'd want to give them in your system. In other words, you may prefer to rearrange the priority of servicing these interrupts and that's where the kernel comes in.
Question:In the discussion of synchronization, you said that each task has a built-in semaphore. What advantages do these semaphores offer over standard semaphores?
Answer:There are two main advantages—performance and simplicity. In many cases, you know which task will be servicing a certain I/O device and thus, there's no need to create an “intermediate” object for that purpose. With the built-in semaphore, you simply signal that task. With the external semaphore, you need to first create the semaphore. When the task responsible for servicing the device depends on the semaphore, that task is placed on the wait list of that semaphore. If it's the only task pending on the semaphore, it's wasteful to go through that overhead.
Question:The message queue example involved a single task and an ISR. Can multiple tasks retrieve messages from a queue?
Answer:That depends what queue mechanism you use. Obviously, if you send a message “directly” to the task's internal queue, then only that task will get the message. However, if you prefer to implement an external message queue, then multiple tasks can receive messages. In fact, either the highest priority task waiting on the external message queue will get the message, or the ISR can post the message to all tasks waiting using a “broadcast” mechanism.Question:How do I know when I should use a RTOS instead of the classic big task loop?
Answer:Here, I assume an RTOS consists of a kernel plus other services such as TCP/IP, File System, USB, and other components. If that's what you mean, then you'd need an RTOS if you need those types of services in your product. For example, if you were to design a digital camera, you'd most likely need a kernel, a File System, a USB device stack, and a graphical user interface (GUI).
If you're only asking about the need of the kernel vs. a big loop, then that's a bit different. A kernel gives you a framework to organize your application (i.e. product). With a kernel, you can determine what the CPU will be working on based on the priority you give to the tasks that make up your product. Also, adding low priority tasks (as your system expands) doesn't affect the responsiveness of your system to high priority tasks. With a big loop, just about everything you add (because of new requirements from your sales and marketing folks) affects the behavior of the big loop.
As a general rule, you're always better off using a real-time kernel than to design a system as a big loop unless you're very limited in ROM and RAM.
Question:What OS services are available from within an ISR?
Answer:For C/OS-III, you are generally only allowed to use “post” services which are used to notify tasks that an event occurred and/or you need to send a message to a task from the ISR (such as the address of an Ethernet packet). C/OS-III allows a few other services to ISRs but it was designed to put emphasis on tasks.
You should note that you don't always need to signal or send messages to tasks. In fact, in many cases, the ISR can do all the work it needs to do right in the ISR, as long as it's kept short. For example, the ISR may be to update the value of a PWM (pulse width modulated) output. The new value might have been pre-calculated by a task and placed in a variable accessible from the ISR.
Question:Is priority inheritance automatically turned on for all mutexes or is it an option like in VxWorks?
Answer:Yes, the priority inheritance mechanism is always turned on when you use a mutex. A future version might actually offer this as an option. Currently, if you don't want to use the priority inheritance mechanism you would select to use regular semaphores.
Question:Can you briefly describe why a developer might want to use OS-III instead of OS-II, or vice-versa.
Answer:You would use C/OS-III if you needed to have multiple tasks at the same priority, round-robin scheduling, and need to create an unlimited number of tasks and other kernel objects. You would want to use C/OS-II if you needed a kernel that has been used in myriad safety-critical applications, such as avionics (DO178B-Level A), medical (510(k)), or other (IEC-61508) applications.
Question:Why would someone still use uC/OS-II when uC/OS-III is now available? Answer:C/OS-II will exist for years to come. For one thing it has been used in many safety-critical applications and is a very mature product. C/OS-II can handle up to 254 application tasks which is sufficient for many applications. In fact, we've rarely seen customers use much more than 30 or so tasks. However, as processors get more powerful and are deployed in highly complex systems with hundreds of tasks, then C/OS-III can ease the transition to those applications. A C/OS-II port can easily be ported to C/OS-III in a matter of minutes.
Question:Why don't you recommend deleting a task during run time?
Answer:Because deleting tasks (or any other kernel object) can lead to potentially unsafe operation. Just imagine a task owning a mutex and then another task deletes that task?.The mutex is never released and other tasks that are waiting on the mutex will never get the mutex. Similarly, if a mutex is deleted, then how are tasks relying on the presence of the mutex supposed to behave? So, IEC-61508 (a safety critical standard) bans the deletion (and creation) of kernel objects at run-time (at least once the system is initialized). In fact, C/OS-II and C/OS-III offer a mode to protect against creating and deleting of kernel objects at run-time.
Question:What's the smallest code/ram size needed for simple applications?
Answer:C/OS-III is scalable in that you can remove any of its services (at compile time) that you don't need in your application. This means that both code (ROM) and data (RAM) size is adjusted based on what you need. As a minimum, C/OS-III requires about 6 kbytes of code on a Cortex-M3 and about 1.5 kbytes of data. The maximum footprint for C/OS-III is about 20 kbytes of code. It's difficult to determine the amount of RAM for the maximum since it depends on the number of priority levels you need to support and other configuration values. With a typical configuration, C/OS-III would require about 4 kbytes of RAM.