SlideShare ist ein Scribd-Unternehmen logo
1 von 23
Downloaden Sie, um offline zu lesen
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 1 -
HKUST COMP355 Embedded Systems Software
Project Report of a Small
Real Time Operating System – USTOS
Instructor: Prof. Jogesh K. Muppala
Student Name: XIAO, Jianxiong
Student Email: cs_xjx@ust.hk
Student ID: 05556262
Abstract: This report describes the details about the design and implementation of a small real
time operating system USTOS. Its major point focuses on explaining the context switching by
tricky usage of stack and synchronization mechanism.
Keywords: RTOS, Context switch, 8051, C51, Synchronization mechanism
1. Introduction
1.1 Project Introduction
It is a trend that RTOS is used more and more in the embedded systems. In today’s market of
RTOS, uC/OS, VxWork are two most popular ones among them. While at the same time, there
is large percentage of embedded system market for 8051 series microcontroller. Up to now,
8051 series microcontroller is still the most popular MCU all around the world. Because of the
important role that 8051 series MCU are playing, some researchers and engineers have been
trying to develop or transplant RTOS that can be run on it.
Keil, the manufacture of the most famous and popular C compiler for 8051, develop one RTOS
called RTX51 Real-Time Kernel. But the ROM space needed for RTX51 is more than 6K
which is pretty large comparing with the 4K ROM in 80C51 and 8K Flash ROM in 89S52 etc.
RTX51 also need the 8051 MCU to have foreign RAM to save data. Of course, Keil also offer
another solution called RTX Tiny which needs only 0.9K footprint in ROM. However, RTX
Tiny does not support priority based scheduling and interrupt manage. At the same time, both
of RTX51 and RTX51Tiny do not offer any source code which is impossible for the
application program developers to modify them according to their special requirement.
Lots of embedded engineers are trying to transplant the existing open-source RTOS into 8051
MCU. But because of the lack of RAM and low speed of 8051 serious MCU, there are many
difficulties and too high footprint to want to port most of RTOS into 8051 MCU. uC/OS, the
most popular RTOS around the world, is ported on 8051 successfully. But because of its large
footprint and its requirement that all functions should be reentrant functions, it is not suitable
enough for real application.
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 2 -
To solve all these problems, the USTOS (pronounced “use toss”), comes out. USTOS is a full
fledged real time operating system based on priority. It supports priority based task scheduling,
nesting interrupt supporting, timer functions as well as synchronizing mechanisms including
critical section, binary semaphore, mailbox and message queue. In this project, I work out the
kernel of USTOS as well as some examples of the usage of USTOS.
1.2 Usage of This Report
This report serves two purposes. Of course, the first one is to elaborate the details of my project
in order to get a good grade. Second, it is a very good tutorial for beginner of operating or
embedded system to know how the concepts are realized in practice and how a real RTOS runs.
As Prof. Jogesh always said, to practice is the only good way to learn. But because of the
complexity of the hardware of desktop machines, it is time consuming and difficult to write a
desktop OS or even to understand how a desktop OS works. (You can imagine how difficult to
understand the codes of Linux.) So, USTOS will be the savior for learners.
1.3 Prerequisite Knowledge for Beginners
In case you are not the professor or TAs, if you want to understand what this report is talking
about, you need to know the principle of programming language or compiler (COMP251),
general concept of computer organization (COMP180), operating and embedded system
(COMP252 + COMP355), 8051 hardware architecture and assembly language (ELEC254).
And C51 language experience is preferable. Of course, even if you have all these prerequisite
knowledge, you may still have to spend some effort to understanding USTOS since a real-
world OS is not a simple thing to understand.
1.4 Report Structure
In the second part of this report, I will elaborate the details of the design in USTOS. Some ugly
stuffs and details of hardware are unavoidable while implementing a real-world run-able RTOS.
In the third part, I will talk something about the architecture of 8051 MCU, and interesting
aspects of the Keil C compiler, as well as how to use USTOS for your program development.
2. Design and Implementation
USTOS contains priority based task scheduling, nesting interrupt supporting, timer functions
as well as synchronizing mechanisms including critical section, binary semaphore, mailbox and
message queue. Please notice all the designs are served for the purpose of real application.
Because of the extreme lack of hardware resource for 8051 MCU, some specific designs and
implementations are invented and may not be useful for other hardware architecture.
2.1 Task & Static Memory Allocation
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 3 -
As in other RTOS, a task in USTOS is simply a never return function which loops forever.
Besides, every task has its own stack which is defined by the application programmer. Since
the low speed of 8051 MCU and only 256 Byte RAM (for AT89S52), dynamically allocation
of memory costs too much. So, memory allocations of USTOS are done in compile time. The
task stack is defined explicitly as an array. Please pay more attention to the size of the stack.
As we know, there is only very little RAM space for data, we cannot allocation too much for
tasks stack. But there is a lower bound of the stack size because interrupt may happen during
the executing time of the task and nested interrupt may even happened. ISRs may be called
many times and they will push registers into the stack of the task that is running. For 8051,
there are at most two levels of nesting interrupts may happen. Each ISR will push 13 Bytes
registers and 2 Bytes of Return address into the stack. So in case you want to support nesting
interrupt, you should at least allocate (13+2)*2 = 30 Bytes for the stacks of tasks.
uint8 idata Stack0[OS_TASK_STACK_SIZE];
uint8 idata Stack1[OS_TASK_STACK_SIZE];
uint8 idata Stack2[OS_TASK_STACK_SIZE];
uint8 idata StackIdle[OS_TASK_STACK_SIZE];
void Task0(void);
void Task1(void);
void Task2(void);
CodePointer_t OS_TaskCode [OS_MAX_TASKS]={Task0,Task1,Task2,OS_IdleTask};
DataPointer_t OS_TaskStack[OS_MAX_TASKS]={Stack0,Stack1,Stack2,StackIdle};
In order to save memory and speed up the code, USTOS use static tasks creation mechanism.
It is the application programmers’ job to put the address of the entry code for each tasks into
the OS_TaskCode array. Also, as in many RTOS, USTOS does not support tasks killing and
assumes that each task will loop for ever.
2.2 Context Switching & Stacks
When context switching happens, it is necessary to push all register into the task stack. Also
the stack pointer will be saved in USTOS which will retrieve back to resume the right
information. The pseudo-code of context switching is shown in the following.
void OS_SaveContext(void)
{
Step 1: PUSH all registersStep 1: PUSH all registersStep 1: PUSH all registersStep 1: PUSH all registers
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
PUSH 0x00
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 4 -
PUSH 0x01
PUSH 0x02
PUSH 0x03
PUSH 0x04
PUSH 0x05
PUSH 0x06
PUSH 0x07
Step 2: Save the Stack PointerStep 2: Save the Stack PointerStep 2: Save the Stack PointerStep 2: Save the Stack Pointer
OS_TaskStack[OS_RunningTask]=SP;
}
void OS_LoadContext(void)
{
Step 1Step 1Step 1Step 1:::: RecoverRecoverRecoverRecover the Stack Pointerthe Stack Pointerthe Stack Pointerthe Stack Pointer
SP = OS_TaskStack[OS_RunningTask];
Step 2: POPStep 2: POPStep 2: POPStep 2: POP all registersall registersall registersall registers
POP 0x07
POP 0x06
POP 0x05
POP 0x04
POP 0x03
POP 0x02
POP 0x01
POP 0x00
POP PSW
POP DPL
POP DPH
POP B
POP ACC
Step 3Step 3Step 3Step 3::::
OS_EXIT_CRITICAL();
Step 4Step 4Step 4Step 4:::: Start to RunStart to RunStart to RunStart to Run
RETI or RET // Return from ISR or Functions
}
The trick technique is the fuction OS_LoadContext. This function will not return the CPU to the
callee. It is a never return function because it completely changes the stack pointer. After
popping back all registers, the CPU instruction RET (for task fuctions) or RETI (for ISRs) will
pop the data in the stack which SP stack pointer is pointing to into the PC register and change
the program running flow into the newly ready tasks or ISR (for nesting ISR). In fact, this
tricky technique is used not only in RTOS but also Desktop OS such as Linux and Minix etc. It
is one easy implementation of context switches which almost all beginners are puzzled. In fact,
context switching is not a fantastic miracle but just changing the stack pointer and RET or
RETI.
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 5 -
Another important thing is that all the above procedures must be guarded by
OS_ENTER_CRITICAL() & OS_EXIT_CRITICAL() of the callee since any interrupt interference
will cost disaster error here.
2.3 Priority Based Scheduling & Distributed TCB
In fact, the scheduler of tasks is very simple. It just need to check bit by bit whether a task is in
ready state from higher priority to lower priority ones. The real codes is shown here.
void OS_Scheduler(void)
{
uint8 i;
for(i=0;i<OS_MAX_TASKS;i++)
{
if ( OS_TaskStatus & (0x01<<i) )
{
OS_RunningTask = i;
break;
}
}
}
An interesting tricky thing in the implementation of USTOS is that there is no explicit Task
Control Block in the data structure of OS. So where are the TCBs? First, let’s recall the
functions of the TCB. TCB must contain at least three things:
1. Task PC Value
2. Task Stack Pointer Value
3. Task Registers Value
4. Task State
So if we can save all these in somewhere, there is no need for explicitly TCB storage. In
USTOS, we store them like this:
1. Task PC Value In the stack (PUSH inexplicitly by hardware when
MCU is executing CALL instruction or hardware
interrupt happens)
2. Task Stack Pointer Value In the array OS_TaskStack[]
3. Task Registers Value In the stack (PUSH explicitly by assembly language)
4. Task State In the word OS_TaskStatus
So, the so-called distributed TCB storage is implemented and save lots of RAM space and
maintaining high speeds for context switching.
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 6 -
2.4 Interrupt & Context Switching
Keil C51 compiler extends ANSI C to support a so-called interrupt function like:
void my_ISR(void) interrinterrinterrinterruptuptuptupt N
The interrupt function attribute, when included in a declaration, specifies that the associated
function is an interrupt function. The interrupt attribute takes as an argument an integer
constant in the 0 to 31 value range. Expressions with operators and the interrupt attribute are
not allowed in function prototypes. In addition, the Cx51 compiler generates the interrupt
vector automatically.
#pragma disable
void userISR(void) interrupt N
{
OS_INT_ENTER();
//user ISR code here
OS_INT_EXIT();
}
Every user ISR must be embraced by OS_INT_ENTER() and OS_INT_EXIT() between the user
ISR code. The definition is like the following.
Also, Keil C compiler will generate the PUSH registers automatically at the beginning of the
ISR. So OS_SaveContext(void) do not need to push the registers into the stacks. At the same
time, at the end of the ISR, instead of calling RET, we need to run the instruction RETI.
void OS_INT_ENTER()
{
OSIntNesting++;
OS_SaveContext_INT();
Enable_Interrupt();
}
void OS_INT_EXIT(void)
{
OS_ENTER_CRITICAL();
OSIntNesting--;
if(OSIntNesting==0)
{
OS_Scheduler();
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 7 -
OS_LoadContext_INT(); // Gone and never return
}else
{
Os_Enter_Sum--; // = OS_EXIT_CRITICAL() + Disable_Interrupt()
}
}
2.5 Nested Interrupt Service Routine
Nested interrupts is supported by recording the nesting count in OSIntNesting. Then when the
ISR call OS_INT_EXIT before return, the OS will make use of OSIntNesting to determine
whether performing scheduling algorithm, load new context and switch to the new task or just
simply come back to the low priority level ISR. So you may see that nesting interrupt support
is not so difficult to implement as you may imagine.
A tricky technique is used to reduce the Critical count Os_Enter_Sum while still keep on
disabling interrupt before POP back all registers. The Keil C51 compiler then will generate the
code to pop back all register and after that a “SETB EA” to enable the interrupt again. If the
code enable interrupt by calling OS_EXIT_CRITICAL() , the interrupt is enable when the CPU is
poping back the registers. If at that time, another interrupt occurs, unpredictable and disaster
result will come out. (This may not happen in 8051 MCU since there are at most 2 levels of
priority for interrupts and no same priority interrupt will occurred by the hardware. But it will
happen if USTOS is transplanted into other hardware.)
2.6 Critical Section
Critical Section is used for protect the shared data by disabling the interrupt.
#define OS_ENTER_CRITICAL() Disable_Interrupt() , Os_Enter_Sum++
#define OS_EXIT_CRITICAL() if (--Os_Enter_Sum==0) Enable_Interrupt()
But why do we need the variable Os_Enter_Sum to record the levels of Critical Section the
CPU enter? This is used to solve the potential problem that is mentioned at the bottom of Page
99 of the reference book [1]. A simple example to illustrate the problem by just using
en/disable interrupt mechanism for critical sections is like this:
void function_A()
{ Disable_Interrupt();
Interrupt is disabled
function_B();
Interrupt is enabled!!!! ERROR!!!!
Enable_Interrupt();
}
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 8 -
void function_B()
{
Disable_Interrupt ();
…… Interrupt is disabled
Enable_Interrupt();
Interrupt is enabled
}
2.7 Timing Service
USTOS provides the timing service by the API “void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)” which will
delay the called task but nTicks numbers of Ticks. The unit of nTicks, Tick, includes
OS_CONFIG_TICKS_CNT numbers of happening of OSTimer0_ISR(). This mechanism is to
prevent too frequent calling the OS_TimeTick() functions which may waste lots of time, i.e., to
slow down the timer in case you do not need it to run so quickly.
uint8 Current_Tick;
#pragma disable
void OSTimer0_ISR(void) interrupt 1
{
Current_Tick = (Current_Tick + 1) % OS_CONFIG_TICKS_CNT;
if (Current_Tick == 0)
{
OS_INT_ENTER();
OS_TimeTick();
OS_INT_EXIT();
}
}
void OS_TaskDelay(uint8 nTicks)
{
if(nTicks==0) // prevent dead lock happen
return;
OS_ENTER_CRITICAL();
OS_RemainTick[OS_RunningTask] = nTicks;
OS_TaskStatus = OS_TaskStatus & (~(0x01<<OS_RunningTask));
OS_SaveContext();
OS_Scheduler();
OS_LoadContext(); //Never return
}
void OS_TimeTick(void)
{
for(i=0;i<OS_MAX_TASKS;i++)
{
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 9 -
if ( OS_RemainTick[i]!=0 )
{ OS_RemainTick[i]--;
if(OS_RemainTick[i]==0)
OS_TaskStatus = OS_TaskStatus | (0x01<<i);
}
}
}
OS_TimeTick(void)OS_TimeTick(void)OS_TimeTick(void)OS_TimeTick(void) is used to awake the sleeping blocked tasks if the time for them to wake up
arrives. It reduces the count in the OS_RemainTick[] array and check whether it is equal to zero.
If yes, it will change the state of the that task into ready state.
API function “void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)” is used to set the remaining time in the
OS_RemainTick[] array and change the running task into ready state. You may see that this
function in fact is a reentrant function since nTicks is a local variable and other parts are
embraced by critical section protection mechanism. Why it is written like this way? I will
explain it in Section 2.8.
2.8 Basic Principles of Synchronization Mechanisms
All the synchronization includes three parts:
1. Create: which performs some initialization of the RAM where storages the information.
2. Receive: which may cause the task to wait for some condition and become block.
3. Send: which may cause the condition that other task is waiting for to become true and
let that task to be available to run.
The pseudo-code of the major skeleton framework is shown here.
Receive(Data_Pointer address, …)
{
OS_ENTER_CRITICAL();
if (not need to block what you need already exist)
{
//handling the return value and maintain structure
OS_EXIT_CRITICAL();
return;
}
//Let it become not blocked
OS_TaskStatus = OS_TaskStatus& (~(0x01<<OS_RunningTask));
//Decide whether or not to schedule
OS_SaveContext();
OS_Scheduler();
OS_LoadContext(); //Never return
}
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 10 -
Send(Data_Pointer address, …)
{
OS_ENTER_CRITICAL();
if (no need to block no one is waiting )
{
//maintain structure
OS_EXIT_CRITICAL();
return;
}
//Let it become not blocked
OS_TaskStatus = OS_TaskStatus | (0x01<<((uint8 *)pMailbox)[3]);
if(OSIntNesting==0)
{
OS_SaveContext();
OS_Scheduler();
OS_LoadContext(); //Never return
}else
{
OS_EXIT_CRITICAL();
}
}
You may need some time to read this pseudo-code and to figure out the mechanism of this
common framework. With this skeleton code, all codes for semaphore etc become very simple.
A special tricky part that needs more attention is the OSIntNesting problem. As we know, the
OS cannot block the ISR routine or perform context switching before return back from the ISR.
Be precisely, David E. Simon points out two rules that an ISR must obeys on Page 199 of the
reference book [2]:
Rule 1: An interrupt routine must not call any RTOS function that might block the caller.
Rule 2: An interrupt routine must not call any RTOS function that might cause the RTOS to
switch tasks unless the RTOS knows that an interrupt routine, and not a task is executing.
And since every ISR will call OS_INT_ENTER() which will let OS knows that now Send()
function is called by ISR or Tasks. The OS then can judge from this to know whether to
perform context switching or not.
Another place that needs to pay attention is the addressing of the data for synchronization, such
as the place to save the semaphore, mailbox, or message queue. I have mentioned in Section
2.1 that USTOS uses static memory allocation. So the application programmer will define a
place to put the data and pass an address pointer to the Receive() and Send() function. There is
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 11 -
not explicitly mapping between task ID and the data used for synchronization such as
semaphores. And example of this is shown here.
Mailbox_t Mailbox1;
OS_MailboxCreate(0,&Mailbox1);
OS_MailboxSend(&Mailbox1,0x1234);
OS_MailboxReceive(&Mailbox1,&rcvMsg);
As I mention is Section 2.7, the API function “void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)” is much like
the Receive() function here, which may cause the task to wait for some condition and become
block. So the major skeleton is the same useful for the function “void OS_TaskDelay(uint8void OS_TaskDelay(uint8void OS_TaskDelay(uint8void OS_TaskDelay(uint8
nTinTinTinTicks)cks)cks)cks)”.
2.9 Binary Semaphore
Binary semaphore is the most simple synchronization mechanism in USTOS. At the same time,
it is also the most efficient one in USTOS. It will cost only 1 Byte RAM.
The usage example:
BinSem_t BinSem1;
OS_BinSemCreate(&BinSem1);
OS_BinSemV(&BinSem1);
OS_BinSemP(&BinSem1);
Let me elaborate the details about the implementation of the binary semaphore. The type
definition of binary semaphore is here:
typedef uint8 BinSem_t;
One bit of the semaphore is used by indicating whether the corresponding task is waiting for
that semaphore or not. If yes, the corresponding bit will be set to 1. Since the Idle Task will not
try to get the semaphore, so the most significant bit which is corresponding to the idle task is
used to indicate the value of the semaphore. The other things are similar as the skeleton code.
void OS_BinSemCreate(BinSem_t* pBinSem) { *pBinSem = 0x80;}
void OS_BinSemP(BinSem_t* pBinSem)
{
OS_ENTER_CRITICAL();
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 12 -
if( (*pBinSem) == 0x80 )
{
(*pBinSem)=0;
OS_EXIT_CRITICAL();
return;
}
(*pBinSem) |= (0x01<<OS_RunningTask);
…… // The same as the skeleton code
}
void OS_BinSemV(BinSem_t* pBinSem)
{
OS_ENTER_CRITICAL();
if( ( (*pBinSem) & 0x7f ) == 0 )
{ *pBinSem = 0x80;
OS_EXIT_CRITICAL();
return;
}
for(i=0;i<7;i++) // Find the tasks that is waiting and has the highest priority
{
if(((*pBinSem) & (0x01<<i))!=0)
{
(*pBinSem) = (*pBinSem) & (~(0x01<<i)) & 0x7f;
OS_TaskStatus = OS_TaskStatus | (0x01<< i);
break;
}
}
…… // The same as the skeleton code
}
2.10 Mailbox
The usage example of mailbox is already shown in Section 2.8. A mailbox needs 4 Bytes to
store the information which contains 2 Bytes for message content, 1 Byte for the pointer of
return address, and 1 Byte for saving the task ID of the task that is waiting.
typedef uint32 Mailbox_t;
void OS_MailboxCreate(uint8 TaskID,Mailbox_t* pMailbox)
{ *pMailbox=0;
((uint8*)pMailbox)[3]=TaskID;
}
void OS_MailboxReceive(Mailbox_t* pMailbox,uint16* pRetV)
{ OS_ENTER_CRITICAL();
if(((uint16*)pMailbox)[0]!=0)
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 13 -
{
*pRetV=((uint16*)pMailbox)[0];
((uint16*)pMailbox)[0]=0;
((uint8 *)pMailbox)[2]=0;
OS_EXIT_CRITICAL();
return;
}
((uint8*)pMailbox)[2] = (uint8)pRetV;
…… // The same as the skeleton code
}
void OS_MailboxSend(Mailbox_t* pMailbox,uint16 Msg)
{ OS_ENTER_CRITICAL();
if(((uint8*)pMailbox)[2]==0)
{
((uint16*)pMailbox)[0]=Msg;
OS_EXIT_CRITICAL();
return;
}
*((uint16*)(((uint8*)pMailbox)[2])) = Msg;
((uint16*)pMailbox)[0]=0;
((uint8 *)pMailbox)[2]=0;
…… // The same as the skeleton code
}
2.11 Message Queue
The usage example is like here:
#define OS_MSGQ_OFFSET 5
uint8 MsgQ1[OS_MSGQ_OFFSET+4];
OS_MsgQCreate(2,MsgQ1,4);
OS_MsgQPend(MsgQ1,&Msg2);
OS_MsgQPost(MsgQ1,P1);
The message queue is kind of RAM consuming mechanism. At least 6 Bytes are needed to
implement a message queue. The message size for message queue is 1 Bytes which is a half of
the message size in mailbox.
The data structure of message queue is like this:
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 ....
[TOTAL_SIZE] [Pointer] [TaskID] [Return Address] [CurrentSize] [Data0] [Data1] [Data2] ....
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 14 -
TOTAL_SIZE: Record the length of the array Date[0] to Date[TOTAL_SIZE-1]
Pointer: Use to refer to the Data[Pointer] is the head of next out-queue message
TaskID: The task ID of the waiting task
Return Address: the pointer to RAM about where to put the message
CurrentSize: The total size of the non-empty messages slots
Date[0] to Date[TOTAL_SIZE-1]: The place to save the message.
The specific code to manipulate this structure is shown below. It looks complex. But in fact, if
you can understand the mailbox, it is easy to read through it. It is just some ugly detail stuffs to
record lots of information, so you may not even need to read this since it will not help too
much for your understanding. Why I show it here is just for completeness.
void OS_MsgQCreate(uint8 TaskID,uint8* pMsgQ,uint8 uSize)
{
pMsgQ[0]=uSize;
pMsgQ[1]=0;
pMsgQ[2]=TaskID;
pMsgQ[3]=0;
pMsgQ[4]=0;
}
void OS_MsgQPend(uint8* pMsgQ,uint8* pRetV)
{
OS_ENTER_CRITICAL();
if(pMsgQ[4]!=0)
{
*pRetV=pMsgQ[OS_MSGQ_OFFSET+pMsgQ[1]];
pMsgQ[1] = (pMsgQ[1]+1) % pMsgQ[0];
pMsgQ[4]--; pMsgQ[1]++;
OS_EXIT_CRITICAL();
return;
}
pMsgQ[3] = (uint8)pRetV;
…… // The same as the skeleton code
}
void OS_MsgQPost(uint8* pMsgQ,uint8 Msg)
{
OS_ENTER_CRITICAL();
if(pMsgQ[3]==0)
{ //No one is waiting for message
pMsgQ[4] ++; //Increase size
if(pMsgQ[4]>pMsgQ[0]) //Full now
pMsgQ[4] --; //Never block, but the last msg will be over-written
pMsgQ[ OS_MSGQ_OFFSET + ( pMsgQ[1] + pMsgQ[4] -1 ) % pMsgQ[0] ]
= Msg; //Save the message
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 15 -
OS_EXIT_CRITICAL();
return;
}
//Some one is waiting.
*((uint8*)(pMsgQ[3])) = Msg; //Return the message content
pMsgQ[3]=0; //Clear return address, Indicate that no one is waiting
…… // The same as the skeleton code
}
2.12 Idle Tasks
This is the simplest part of USTOS. The only attention needed is that since idle task is doing
nothing, some part of USTOS make use of this and do not save any values while context
switching in order to speed up the system.
void OS_IdleTask(void) { while(1){ /* Save Power and Statistic*/; } }
2.13 Event Group
I have tried for several hours and found that Event Group needs so many RAM space to store
all the information needed. So it is unpractical and unfeasible to implement it for 8051 which
has very small RAM (128Byte + 128Byte). In case USTOS is transplanted into other hardware
which has large RAM, it is feasible to implement it.
The Data Structure of EventGroup may be like this:
[EventValue] [EventMatch] [Waiting?] [Task1_Mask] [Task2_Mask] [Task2_Mask] ...
BitIndicator 0=Not Waiting Not 0 = Waiting
For AND/OR Higher Priority Lower Priority
2.14 EDF Scheduling
Earliest deadline first scheduling is optimal and efficient. If a dynamic priority schedule exists,
EDF will produce a feasible schedule. A dynamic priority schedule exists if and only if
utilization is no greater than 100%. But EDF need too many extra RAM space in order to
record the all information that EDF needed. This is the disaster reason for 8051 series MCU.
And at the same time, the application programmer must have some way to point out when is
the deadline for all tasks. And the OS have to maintain this information. Thus, EDF is
complicated enough to have unacceptable overhead. After considering the RAM limit of 8051
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 16 -
and the information needed to store for EDF, it seems impossible to implement it in 8051. So I
do not try to implement it. This is the trade off of engineering.
2.15 Keil C51 Compiler's Colorizing Memory Allocation
Because the limit size of 8051’s RAM, Keil C extent the ANSI C to generate better codes
which require less RAM and run faster. One extension is to let the compiler not to push all
local register into memory stacks when a function is calling the other functions. This means
that Keil C51 compiler allocates the local variables into fixed-addressing RAM. In another
word, Keil C51 compiler let the local variables become global variable. This is a special case
needed more attention. In the other side, it is easy to show that the RAM may have higher
probability to run out if the program keeps on running different functions. Keil C51 compiler
cures this by the way of Data Overlaying Optimizing. It will be further discussed in
Section 4.2.
2.16 Keil C51 Compiler's Unpredictable Behaviors
In Keil C51 compiler, for interrupt service routine, the contents of the R0 to R7, SFR ACC, B,
DPH, DPL, and PSW, when required, are saved on the stack at function invocation time. This
means when unnecessary, they will not be saved. So there is no way for USTOS know how
many and which registers are pushed into the stack. In order to make them all push the same
registers when interrupts happen, and deal with the over-clever Keil C51 compiler’s such
unpredictable behaviors, I have to add several lines of useless code in order to make the code
more complex and force the Keil C51 Compiler push all registers for all ISRs. Such useless
codes are put at the beginning of the OS_INT_ENTER(void).
void OS_INT_ENTER(void)
{
//The following code is used for prevent the Keil C51's stupid optimization
uint8* pointer=0x02;
*pointer = 0xff;
DPL =0x00;
DPH =0x00;
B =0x00;
PSW =0x00;
ACC =0x00;
……..
}
2.17 Other Ugly Stuffs
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 17 -
Other things that need to handle is the initial value of the system variables and hardware
registers for RTOS and the task stacks’ initialization. Such codes are put in the fuction voidvoidvoidvoid
OSInit(void)OSInit(void)OSInit(void)OSInit(void) and void OSStart(void)void OSStart(void)void OSStart(void)void OSStart(void).
Another problem is that for RTOS, there are always many source files. Some users keep on
complaining the linking errors. In order to prevent such errors and make everything become
simpler, I write all the USTOS code in one file containing the users’ source code. The
application programmer is expected to divide their source codes into two parts. One part is the
global data definition and the other part is the running code. Then they put the two parts into
the place that the source file of USTOS instructed. This will greatly enhance the probability of
successfully linking of the source code.
3. How to Use USTOS
3.1 USTOS API
There are 16 API functions for USTOS. Most of them are already mentioned in part 2.
01 uint8 OSVersion();
02 uint8 OSRunningTaskID();
03 void OS_INT_ENTER(void);
04 void OS_INT_EXIT(void);
05 void OSStart(void);
06 void OSInit(void);
07 void OS_TaskDelay(uint8 nTicks);
08 void OS_MsgQCreate(uint8 TaskID,uint8* pMsgQ,uint8 uSize);
09 void OS_MsgQPost(uint8* pMsgQ,uint8 Msg);
10 void OS_MsgQPend(uint8* pMsgQ,uint8* pRetV);
11 void OS_MailboxCreate(uint8 TaskID,Mailbox_t* pMailbox);
12 void OS_MailboxSend(Mailbox_t* pMailbox,uint16 Msg);
13 void OS_MailboxReceive(Mailbox_t* pMailbox,uint16* pRetV);
14 void OS_BinSemCreate(BinSem_t* pBinSem);
15 void OS_BinSemV(BinSem_t* pBinSem);
16 void OS_BinSemP(BinSem_t* pBinSem);
API functions of USTOS
3.2 USTOS Configuration
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 18 -
// Functional Configuration 1 = Enable 0 = Disable
#define OS_CONFIG_EN_MAILBOX 1 Support for mailbox ?
#define OS_CONFIG_EN_MSGQ 1 Support for message queue ?
#define OS_CONFIG_EN_BIN_SEMAPHORE 1 Support for binary semaphore ?
#define OS_CONFIG_EN_SYSTEM_TIMER 1 Support for system timing service?
#define OS_CONFIG_EN_INT_NESTING 1 Support for interrupt nesting?
//Timer Setup
#define OS_CONFIG_TICKS_CNT 32 How many times of interrupt convert
to one system ticks?
//Task Information Setup
#define OS_MAX_TASKS 4 Number of Tasks
#define OS_TASK_STACK_SIZE 20 Stack size of Tasks (Unit: Byte)
3.3 Examples
This is a simple example of using semaphores, mailbox, as well as messages queue and system
timing service.
//---------------------------------------------------------
// Part I: User config
//---------------------------------------------------------
uint8 idata Stack0[OS_TASK_STACK_SIZE];
uint8 idata Stack1[OS_TASK_STACK_SIZE];
uint8 idata Stack2[OS_TASK_STACK_SIZE];
uint8 idata StackIdle[OS_TASK_STACK_SIZE];
//Static Create Tasks
void Task0(void);
void Task1(void);
void Task2(void);
CodePointer_t OS_TaskCode [OS_MAX_TASKS]={Task0,Task1,Task2,OS_IdleTask};
DataPointer_t OS_TaskStack[OS_MAX_TASKS]={Stack0,Stack1,Stack2,StackIdle};
//Static Create Mailbox
Mailbox_t Mailbox1;
BinSem_t BinSem1;
uint8 MsgQ1[OS_MSGQ_OFFSET+4];
//---------------------------------------------------------
// Part II: User code
//---------------------------------------------------------
#pragma disable
void myISR1(void) interrupt 0
{ OS_INT_ENTER();
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 19 -
OS_MailboxSend(&Mailbox1,0x5678);
OS_INT_EXIT();
}
#pragma disable
void myISR2(void) interrupt 2
{ OS_INT_ENTER();
OS_MsgQPost(MsgQ1,P1);
OS_INT_EXIT();
}
void Task0(void)
{ uint16 rcvMsg;
while(1)
{ OS_MailboxReceive(&Mailbox1,&rcvMsg);
P1=rcvMsg/256;
P0=rcvMsg%256;
}
}
void Task1(void)
{ uint8 myflag=1;
while(1)
{
if(myflag)
{ myflag=0;
OS_MailboxSend(&Mailbox1,0x1234);
}else
{ OS_BinSemP(&BinSem1);
OS_BinSemP(&BinSem1);
OS_TaskDelay(1);
}
}
}
void Task2(void)
{ uint8 Msg2;
while(1)
{ OS_MsgQPend(MsgQ1,&Msg2);
OS_BinSemV(&BinSem1);
}
}
void main(void)
{ OSInit();
OS_MailboxCreate(0,&Mailbox1);
OS_BinSemCreate(&BinSem1);
OS_MsgQCreate(2,MsgQ1,4);
PX0=0; IT0=1; PX1=1; IT1=1; //Extern Interrupt
TMOD = (TMOD & 0XF0) | 0X01; //System Timer Setup
TH0 = 0x0; TL0 = 0x0; //Time interval = 0xFFFF
TR0 = 1;
ET0 = 1;
PT0 = 1;
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 20 -
OSStart();
}
4. Hardware/software Requirements
4.1 Hardware
I choose Intel 8051 microcontroller as the hardware platform in this project. This is because it
is cheap, and slow. Slow speed means that the resource is really limited and the main
characteristics of embedded system are shown most explicitly. So it is a good time to practice
the principles of embedded system. The big picture of the architecture of 8051 MCU is like the
following (It is from the reference [6]). If you need further information, you are encouraged to
read the materials in the reference [6].
4.2 Software
The tool chain that I plan to use is like following:
• Cross-compiler: Small Device C Compiler.
• Simulator: 8051 Microcontroller Series Simulator
• Cross-compiler: KeilC51 Compiler IDE
• Downloader: AT89S PC Based Programmer
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 21 -
But after trying all of them, I found that the SDCC compiler and the 8051 Microcontroller
Series Simulator are too weak in functionality and difficult and inefficient for coding and
debugging. While at the same time, Keil C51 complier together with uVision IDE, the most
popular developing environment around the world for 8051, offers an excellent programming
& debugging environment, as well as very precise simulator. Please notice that Keil C51
compiler is NOT free software. But you may get an evaluation edition at the Keil’s Website:
http://www.keil.com/demo/. Since the special characteristic of this compiler, I cannot
guarantee USTOS can be compiled successfully on all the versions of the compiler. The
version of my complier and simulator are Keil C51 V7.50 and uVision V2.40. The target MCU
is the most popular 8051 series MCU -- Atmel AT89S52.
A real world RTOS is not just to be used by one hardware platform. So I planed to divide the
USTOS into two separate parts. One is hardware platform independent part which is all written
in C language. The other is hardware dependent part written in both C and assembly language.
But when I was implementing, I realize that I almost do not need any assembly language at all
since Keil C51 compiler have all kinds of functions to help. And at the same time, the USTOS
V1.0 makes use of lots of characteristic of Keil C51 compiler and 8051 hardware architecture
in order to speed up and save RAM. So it is very difficult and inefficient in run time if I divide
USTOS into two separate parts by brute force.
And since there is no packet support package for 8051 series microcontroller, I start all my
codes from scratch under the help of some description about the hardware architecture of 8051
series microcontroller.
4. 3 Compiler Setup
Keil C51 Compiler has 10 levels of optimization. Each higher optimization level
contains all of the characteristics of the preceding lower optimization level. USTOS
supports Level 0 to Level 7 optimization. Please read the reference [7] for more details.
Level 0
Constant Folding: The compiler performs calculations that reduce expressions
to numeric constants, where possible.This includes calculations of run-time
addresses.
Simple Access Optimizing: The compiler optimizes access of internal data
and bit addresses in the 8051 system.
Jump Optimizing: The compiler always extends jumps to the final target.
Jumps to jumps are deleted.
Level 1
Dead Code Elimination: Unused code fragments and artifacts are eliminated.
Jump Negation: Conditional jumps are closely examined to see if they can be
streamlined or eliminated by the inversion of the test logic.
Level 2
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 22 -
Data Overlaying: Data and bit segments suitable for static overlay are
identified and internally marked. The BL51 Linker/Locator has the capability,
through global data flow analysis, of selecting segments which can then be
overlaid.
Level 3
Peephole Optimizing: Redundant MOV instructions are removed. This
includes unnecessary loading of objects from the memory as well as load
operations with constants. Complex operations are replaced by simple
operations when memory space or execution time can be saved.
Level 4
Register Variables: Automatic variables and function arguments are located in
registers when possible. Reservation of data memory for these variables is
omitted.
Extended Access Optimizing: Variables from the IDATA, XDATA, PDATA
and CODE areas are directly included in operations. The use of intermediate
registers is not necessary most of the time.
Local Common Sub expression Elimination: If the same calculations are
performed repetitively in an expression, the result of the first calculation is
saved and used further whenever possible. Superfluous calculations are
eliminated from the code.
Case/Switch Optimizing: Code involving switch and case statements is
optimized as jump tables or jump strings.
Level 5
Global Common Sub expression Elimination: Identical sub expressions
within a function are calculated only once when possible. The intermediate
result is stored in a register and used instead of a new calculation.
Simple Loop Optimizing: Program loops that fill a memory range with a
constant are converted and optimized.
Level 6
Loop Rotation: Program loops are rotated if the resulting program code is
faster and more efficient.
Level 7
Extended Index Access Optimizing: Uses the DPTR for register variables
where appropriate. Pointer and array access are optimized for both execution
speed and code size.
HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS
- 23 -
5. Conclusions
5.1 Summary
This project is challenging as an undergraduate course’s project within such short period of
time. But through this project, I know the principles of the embedded system software more
deeply and USTOS will be a small enough OS for other beginner to read and learn.
5.2 Future Work
Because this is just a UG course project and not a research project, no new scheduling method
is invented. And thus it emphasizes the implementation but not the creativity. If it is good to
have new scheduling algorithm, I will add it to USTOS.
Another possible improvement is to implement the Priority Inheritance Protocol to solve to
Priority Inversion Problem in USTOS.
Some other possible development may contain transplanting USTOS into other hardware.
Nevertheless, USTOS will be an open-source RTOS for anyone to use and study. Hopefully
my work can be used by industry or by education.
6. References
[1] Jogesh K. Muppala, HKUST COMP355 Embedded System Software Lecture Notes and
Course Materials, http://www.cs.ust.hk/%7Emuppala/comp355/
[2] David E. Simon, An Embedded Software Primer, Addison-Wesley, 1999.
[3] Raj Kamal, Embedded Systems: Architecture, Programming and Design, McGraw-Hill,
2003.
[4] Dreamtech Software Team, Programming for Embedded Systems: Cracking the Code,
Wiley, 2002.
[5] Atmel Corporation: Manuals of AT89S52 chip
[6] Tim K. T. WOO, HKUST ELEC 254 Course Materials, http://course.ee.ust.hk/elec254/
[7] Keil Software Corporation: User’s Guide of Cx51 Compiler, http://www.keil.com/

Weitere ähnliche Inhalte

Was ist angesagt? (11)

Symmetrix local replication
Symmetrix local replicationSymmetrix local replication
Symmetrix local replication
 
O25074078
O25074078O25074078
O25074078
 
Components in real time systems
Components in real time systemsComponents in real time systems
Components in real time systems
 
Real time system tsp
Real time system tspReal time system tsp
Real time system tsp
 
Slides For Operating System Concepts By Silberschatz Galvin And Gagne
Slides For Operating System Concepts By Silberschatz Galvin And GagneSlides For Operating System Concepts By Silberschatz Galvin And Gagne
Slides For Operating System Concepts By Silberschatz Galvin And Gagne
 
Real-Time Scheduling
Real-Time SchedulingReal-Time Scheduling
Real-Time Scheduling
 
Run time, frequently, non-frequently reconfigurable system &
Run time, frequently, non-frequently reconfigurable system &Run time, frequently, non-frequently reconfigurable system &
Run time, frequently, non-frequently reconfigurable system &
 
Unit 5 ppt
Unit 5 pptUnit 5 ppt
Unit 5 ppt
 
Operating systems question bank
Operating systems question bankOperating systems question bank
Operating systems question bank
 
Synchronization linux
Synchronization linuxSynchronization linux
Synchronization linux
 
Real time-embedded-system-lec-04
Real time-embedded-system-lec-04Real time-embedded-system-lec-04
Real time-embedded-system-lec-04
 

Ähnlich wie Project report of ustos

Rtos princples adn case study
Rtos princples adn case studyRtos princples adn case study
Rtos princples adn case study
vanamali_vanu
 
Real Time OS For Embedded Systems
Real Time OS For Embedded SystemsReal Time OS For Embedded Systems
Real Time OS For Embedded Systems
Himanshu Ghetia
 
CMSIS_RTOS_Tutorial.pdf
CMSIS_RTOS_Tutorial.pdfCMSIS_RTOS_Tutorial.pdf
CMSIS_RTOS_Tutorial.pdf
chau44
 
An Overview of Intel TFLOPS Super Computer
An Overview of Intel TFLOPS Super ComputerAn Overview of Intel TFLOPS Super Computer
An Overview of Intel TFLOPS Super Computer
Serwer Alam
 

Ähnlich wie Project report of ustos (20)

RTOS implementation
RTOS implementationRTOS implementation
RTOS implementation
 
Design of Real - Time Operating System Using Keil µVision Ide
Design of Real - Time Operating System Using Keil µVision IdeDesign of Real - Time Operating System Using Keil µVision Ide
Design of Real - Time Operating System Using Keil µVision Ide
 
Embedded os
Embedded osEmbedded os
Embedded os
 
Lab6 rtos
Lab6 rtosLab6 rtos
Lab6 rtos
 
Beyond the RTOS: A Better Way to Design Real-Time Embedded Software
Beyond the RTOS: A Better Way to Design Real-Time Embedded SoftwareBeyond the RTOS: A Better Way to Design Real-Time Embedded Software
Beyond the RTOS: A Better Way to Design Real-Time Embedded Software
 
Rtos princples adn case study
Rtos princples adn case studyRtos princples adn case study
Rtos princples adn case study
 
Real Time OS For Embedded Systems
Real Time OS For Embedded SystemsReal Time OS For Embedded Systems
Real Time OS For Embedded Systems
 
Performance analysis of real-time and general-purpose operating systems for p...
Performance analysis of real-time and general-purpose operating systems for p...Performance analysis of real-time and general-purpose operating systems for p...
Performance analysis of real-time and general-purpose operating systems for p...
 
Beyond the RTOS: A Better Way to Design Real-Time Embedded Software
Beyond the RTOS: A Better Way to Design Real-Time Embedded SoftwareBeyond the RTOS: A Better Way to Design Real-Time Embedded Software
Beyond the RTOS: A Better Way to Design Real-Time Embedded Software
 
Mit3042 embedded systems
Mit3042  embedded systemsMit3042  embedded systems
Mit3042 embedded systems
 
CMSIS_RTOS_Tutorial.pdf
CMSIS_RTOS_Tutorial.pdfCMSIS_RTOS_Tutorial.pdf
CMSIS_RTOS_Tutorial.pdf
 
Mutexes 2
Mutexes 2Mutexes 2
Mutexes 2
 
Project Report On Micro-controller Embedded System
Project Report On Micro-controller Embedded SystemProject Report On Micro-controller Embedded System
Project Report On Micro-controller Embedded System
 
Modeling and Real-Time Simulation of Induction Motor Using RT-LAB
Modeling and Real-Time Simulation of Induction Motor Using RT-LABModeling and Real-Time Simulation of Induction Motor Using RT-LAB
Modeling and Real-Time Simulation of Induction Motor Using RT-LAB
 
Question paper with solution the 8051 microcontroller based embedded systems...
Question paper with solution  the 8051 microcontroller based embedded systems...Question paper with solution  the 8051 microcontroller based embedded systems...
Question paper with solution the 8051 microcontroller based embedded systems...
 
Recipes 9 of Data Warehouse and Business Intelligence - Techniques to control...
Recipes 9 of Data Warehouse and Business Intelligence - Techniques to control...Recipes 9 of Data Warehouse and Business Intelligence - Techniques to control...
Recipes 9 of Data Warehouse and Business Intelligence - Techniques to control...
 
Embedded systems
Embedded systemsEmbedded systems
Embedded systems
 
Embedded operating system and industrial applications: a review
Embedded operating system and industrial applications: a reviewEmbedded operating system and industrial applications: a review
Embedded operating system and industrial applications: a review
 
RT-lab based real-time simulation of flywheel energy storage system associate...
RT-lab based real-time simulation of flywheel energy storage system associate...RT-lab based real-time simulation of flywheel energy storage system associate...
RT-lab based real-time simulation of flywheel energy storage system associate...
 
An Overview of Intel TFLOPS Super Computer
An Overview of Intel TFLOPS Super ComputerAn Overview of Intel TFLOPS Super Computer
An Overview of Intel TFLOPS Super Computer
 

Kürzlich hochgeladen

EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Earley Information Science
 

Kürzlich hochgeladen (20)

The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 

Project report of ustos

  • 1. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 1 - HKUST COMP355 Embedded Systems Software Project Report of a Small Real Time Operating System – USTOS Instructor: Prof. Jogesh K. Muppala Student Name: XIAO, Jianxiong Student Email: cs_xjx@ust.hk Student ID: 05556262 Abstract: This report describes the details about the design and implementation of a small real time operating system USTOS. Its major point focuses on explaining the context switching by tricky usage of stack and synchronization mechanism. Keywords: RTOS, Context switch, 8051, C51, Synchronization mechanism 1. Introduction 1.1 Project Introduction It is a trend that RTOS is used more and more in the embedded systems. In today’s market of RTOS, uC/OS, VxWork are two most popular ones among them. While at the same time, there is large percentage of embedded system market for 8051 series microcontroller. Up to now, 8051 series microcontroller is still the most popular MCU all around the world. Because of the important role that 8051 series MCU are playing, some researchers and engineers have been trying to develop or transplant RTOS that can be run on it. Keil, the manufacture of the most famous and popular C compiler for 8051, develop one RTOS called RTX51 Real-Time Kernel. But the ROM space needed for RTX51 is more than 6K which is pretty large comparing with the 4K ROM in 80C51 and 8K Flash ROM in 89S52 etc. RTX51 also need the 8051 MCU to have foreign RAM to save data. Of course, Keil also offer another solution called RTX Tiny which needs only 0.9K footprint in ROM. However, RTX Tiny does not support priority based scheduling and interrupt manage. At the same time, both of RTX51 and RTX51Tiny do not offer any source code which is impossible for the application program developers to modify them according to their special requirement. Lots of embedded engineers are trying to transplant the existing open-source RTOS into 8051 MCU. But because of the lack of RAM and low speed of 8051 serious MCU, there are many difficulties and too high footprint to want to port most of RTOS into 8051 MCU. uC/OS, the most popular RTOS around the world, is ported on 8051 successfully. But because of its large footprint and its requirement that all functions should be reentrant functions, it is not suitable enough for real application.
  • 2. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 2 - To solve all these problems, the USTOS (pronounced “use toss”), comes out. USTOS is a full fledged real time operating system based on priority. It supports priority based task scheduling, nesting interrupt supporting, timer functions as well as synchronizing mechanisms including critical section, binary semaphore, mailbox and message queue. In this project, I work out the kernel of USTOS as well as some examples of the usage of USTOS. 1.2 Usage of This Report This report serves two purposes. Of course, the first one is to elaborate the details of my project in order to get a good grade. Second, it is a very good tutorial for beginner of operating or embedded system to know how the concepts are realized in practice and how a real RTOS runs. As Prof. Jogesh always said, to practice is the only good way to learn. But because of the complexity of the hardware of desktop machines, it is time consuming and difficult to write a desktop OS or even to understand how a desktop OS works. (You can imagine how difficult to understand the codes of Linux.) So, USTOS will be the savior for learners. 1.3 Prerequisite Knowledge for Beginners In case you are not the professor or TAs, if you want to understand what this report is talking about, you need to know the principle of programming language or compiler (COMP251), general concept of computer organization (COMP180), operating and embedded system (COMP252 + COMP355), 8051 hardware architecture and assembly language (ELEC254). And C51 language experience is preferable. Of course, even if you have all these prerequisite knowledge, you may still have to spend some effort to understanding USTOS since a real- world OS is not a simple thing to understand. 1.4 Report Structure In the second part of this report, I will elaborate the details of the design in USTOS. Some ugly stuffs and details of hardware are unavoidable while implementing a real-world run-able RTOS. In the third part, I will talk something about the architecture of 8051 MCU, and interesting aspects of the Keil C compiler, as well as how to use USTOS for your program development. 2. Design and Implementation USTOS contains priority based task scheduling, nesting interrupt supporting, timer functions as well as synchronizing mechanisms including critical section, binary semaphore, mailbox and message queue. Please notice all the designs are served for the purpose of real application. Because of the extreme lack of hardware resource for 8051 MCU, some specific designs and implementations are invented and may not be useful for other hardware architecture. 2.1 Task & Static Memory Allocation
  • 3. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 3 - As in other RTOS, a task in USTOS is simply a never return function which loops forever. Besides, every task has its own stack which is defined by the application programmer. Since the low speed of 8051 MCU and only 256 Byte RAM (for AT89S52), dynamically allocation of memory costs too much. So, memory allocations of USTOS are done in compile time. The task stack is defined explicitly as an array. Please pay more attention to the size of the stack. As we know, there is only very little RAM space for data, we cannot allocation too much for tasks stack. But there is a lower bound of the stack size because interrupt may happen during the executing time of the task and nested interrupt may even happened. ISRs may be called many times and they will push registers into the stack of the task that is running. For 8051, there are at most two levels of nesting interrupts may happen. Each ISR will push 13 Bytes registers and 2 Bytes of Return address into the stack. So in case you want to support nesting interrupt, you should at least allocate (13+2)*2 = 30 Bytes for the stacks of tasks. uint8 idata Stack0[OS_TASK_STACK_SIZE]; uint8 idata Stack1[OS_TASK_STACK_SIZE]; uint8 idata Stack2[OS_TASK_STACK_SIZE]; uint8 idata StackIdle[OS_TASK_STACK_SIZE]; void Task0(void); void Task1(void); void Task2(void); CodePointer_t OS_TaskCode [OS_MAX_TASKS]={Task0,Task1,Task2,OS_IdleTask}; DataPointer_t OS_TaskStack[OS_MAX_TASKS]={Stack0,Stack1,Stack2,StackIdle}; In order to save memory and speed up the code, USTOS use static tasks creation mechanism. It is the application programmers’ job to put the address of the entry code for each tasks into the OS_TaskCode array. Also, as in many RTOS, USTOS does not support tasks killing and assumes that each task will loop for ever. 2.2 Context Switching & Stacks When context switching happens, it is necessary to push all register into the task stack. Also the stack pointer will be saved in USTOS which will retrieve back to resume the right information. The pseudo-code of context switching is shown in the following. void OS_SaveContext(void) { Step 1: PUSH all registersStep 1: PUSH all registersStep 1: PUSH all registersStep 1: PUSH all registers PUSH ACC PUSH B PUSH DPH PUSH DPL PUSH PSW PUSH 0x00
  • 4. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 4 - PUSH 0x01 PUSH 0x02 PUSH 0x03 PUSH 0x04 PUSH 0x05 PUSH 0x06 PUSH 0x07 Step 2: Save the Stack PointerStep 2: Save the Stack PointerStep 2: Save the Stack PointerStep 2: Save the Stack Pointer OS_TaskStack[OS_RunningTask]=SP; } void OS_LoadContext(void) { Step 1Step 1Step 1Step 1:::: RecoverRecoverRecoverRecover the Stack Pointerthe Stack Pointerthe Stack Pointerthe Stack Pointer SP = OS_TaskStack[OS_RunningTask]; Step 2: POPStep 2: POPStep 2: POPStep 2: POP all registersall registersall registersall registers POP 0x07 POP 0x06 POP 0x05 POP 0x04 POP 0x03 POP 0x02 POP 0x01 POP 0x00 POP PSW POP DPL POP DPH POP B POP ACC Step 3Step 3Step 3Step 3:::: OS_EXIT_CRITICAL(); Step 4Step 4Step 4Step 4:::: Start to RunStart to RunStart to RunStart to Run RETI or RET // Return from ISR or Functions } The trick technique is the fuction OS_LoadContext. This function will not return the CPU to the callee. It is a never return function because it completely changes the stack pointer. After popping back all registers, the CPU instruction RET (for task fuctions) or RETI (for ISRs) will pop the data in the stack which SP stack pointer is pointing to into the PC register and change the program running flow into the newly ready tasks or ISR (for nesting ISR). In fact, this tricky technique is used not only in RTOS but also Desktop OS such as Linux and Minix etc. It is one easy implementation of context switches which almost all beginners are puzzled. In fact, context switching is not a fantastic miracle but just changing the stack pointer and RET or RETI.
  • 5. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 5 - Another important thing is that all the above procedures must be guarded by OS_ENTER_CRITICAL() & OS_EXIT_CRITICAL() of the callee since any interrupt interference will cost disaster error here. 2.3 Priority Based Scheduling & Distributed TCB In fact, the scheduler of tasks is very simple. It just need to check bit by bit whether a task is in ready state from higher priority to lower priority ones. The real codes is shown here. void OS_Scheduler(void) { uint8 i; for(i=0;i<OS_MAX_TASKS;i++) { if ( OS_TaskStatus & (0x01<<i) ) { OS_RunningTask = i; break; } } } An interesting tricky thing in the implementation of USTOS is that there is no explicit Task Control Block in the data structure of OS. So where are the TCBs? First, let’s recall the functions of the TCB. TCB must contain at least three things: 1. Task PC Value 2. Task Stack Pointer Value 3. Task Registers Value 4. Task State So if we can save all these in somewhere, there is no need for explicitly TCB storage. In USTOS, we store them like this: 1. Task PC Value In the stack (PUSH inexplicitly by hardware when MCU is executing CALL instruction or hardware interrupt happens) 2. Task Stack Pointer Value In the array OS_TaskStack[] 3. Task Registers Value In the stack (PUSH explicitly by assembly language) 4. Task State In the word OS_TaskStatus So, the so-called distributed TCB storage is implemented and save lots of RAM space and maintaining high speeds for context switching.
  • 6. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 6 - 2.4 Interrupt & Context Switching Keil C51 compiler extends ANSI C to support a so-called interrupt function like: void my_ISR(void) interrinterrinterrinterruptuptuptupt N The interrupt function attribute, when included in a declaration, specifies that the associated function is an interrupt function. The interrupt attribute takes as an argument an integer constant in the 0 to 31 value range. Expressions with operators and the interrupt attribute are not allowed in function prototypes. In addition, the Cx51 compiler generates the interrupt vector automatically. #pragma disable void userISR(void) interrupt N { OS_INT_ENTER(); //user ISR code here OS_INT_EXIT(); } Every user ISR must be embraced by OS_INT_ENTER() and OS_INT_EXIT() between the user ISR code. The definition is like the following. Also, Keil C compiler will generate the PUSH registers automatically at the beginning of the ISR. So OS_SaveContext(void) do not need to push the registers into the stacks. At the same time, at the end of the ISR, instead of calling RET, we need to run the instruction RETI. void OS_INT_ENTER() { OSIntNesting++; OS_SaveContext_INT(); Enable_Interrupt(); } void OS_INT_EXIT(void) { OS_ENTER_CRITICAL(); OSIntNesting--; if(OSIntNesting==0) { OS_Scheduler();
  • 7. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 7 - OS_LoadContext_INT(); // Gone and never return }else { Os_Enter_Sum--; // = OS_EXIT_CRITICAL() + Disable_Interrupt() } } 2.5 Nested Interrupt Service Routine Nested interrupts is supported by recording the nesting count in OSIntNesting. Then when the ISR call OS_INT_EXIT before return, the OS will make use of OSIntNesting to determine whether performing scheduling algorithm, load new context and switch to the new task or just simply come back to the low priority level ISR. So you may see that nesting interrupt support is not so difficult to implement as you may imagine. A tricky technique is used to reduce the Critical count Os_Enter_Sum while still keep on disabling interrupt before POP back all registers. The Keil C51 compiler then will generate the code to pop back all register and after that a “SETB EA” to enable the interrupt again. If the code enable interrupt by calling OS_EXIT_CRITICAL() , the interrupt is enable when the CPU is poping back the registers. If at that time, another interrupt occurs, unpredictable and disaster result will come out. (This may not happen in 8051 MCU since there are at most 2 levels of priority for interrupts and no same priority interrupt will occurred by the hardware. But it will happen if USTOS is transplanted into other hardware.) 2.6 Critical Section Critical Section is used for protect the shared data by disabling the interrupt. #define OS_ENTER_CRITICAL() Disable_Interrupt() , Os_Enter_Sum++ #define OS_EXIT_CRITICAL() if (--Os_Enter_Sum==0) Enable_Interrupt() But why do we need the variable Os_Enter_Sum to record the levels of Critical Section the CPU enter? This is used to solve the potential problem that is mentioned at the bottom of Page 99 of the reference book [1]. A simple example to illustrate the problem by just using en/disable interrupt mechanism for critical sections is like this: void function_A() { Disable_Interrupt(); Interrupt is disabled function_B(); Interrupt is enabled!!!! ERROR!!!! Enable_Interrupt(); }
  • 8. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 8 - void function_B() { Disable_Interrupt (); …… Interrupt is disabled Enable_Interrupt(); Interrupt is enabled } 2.7 Timing Service USTOS provides the timing service by the API “void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)” which will delay the called task but nTicks numbers of Ticks. The unit of nTicks, Tick, includes OS_CONFIG_TICKS_CNT numbers of happening of OSTimer0_ISR(). This mechanism is to prevent too frequent calling the OS_TimeTick() functions which may waste lots of time, i.e., to slow down the timer in case you do not need it to run so quickly. uint8 Current_Tick; #pragma disable void OSTimer0_ISR(void) interrupt 1 { Current_Tick = (Current_Tick + 1) % OS_CONFIG_TICKS_CNT; if (Current_Tick == 0) { OS_INT_ENTER(); OS_TimeTick(); OS_INT_EXIT(); } } void OS_TaskDelay(uint8 nTicks) { if(nTicks==0) // prevent dead lock happen return; OS_ENTER_CRITICAL(); OS_RemainTick[OS_RunningTask] = nTicks; OS_TaskStatus = OS_TaskStatus & (~(0x01<<OS_RunningTask)); OS_SaveContext(); OS_Scheduler(); OS_LoadContext(); //Never return } void OS_TimeTick(void) { for(i=0;i<OS_MAX_TASKS;i++) {
  • 9. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 9 - if ( OS_RemainTick[i]!=0 ) { OS_RemainTick[i]--; if(OS_RemainTick[i]==0) OS_TaskStatus = OS_TaskStatus | (0x01<<i); } } } OS_TimeTick(void)OS_TimeTick(void)OS_TimeTick(void)OS_TimeTick(void) is used to awake the sleeping blocked tasks if the time for them to wake up arrives. It reduces the count in the OS_RemainTick[] array and check whether it is equal to zero. If yes, it will change the state of the that task into ready state. API function “void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)” is used to set the remaining time in the OS_RemainTick[] array and change the running task into ready state. You may see that this function in fact is a reentrant function since nTicks is a local variable and other parts are embraced by critical section protection mechanism. Why it is written like this way? I will explain it in Section 2.8. 2.8 Basic Principles of Synchronization Mechanisms All the synchronization includes three parts: 1. Create: which performs some initialization of the RAM where storages the information. 2. Receive: which may cause the task to wait for some condition and become block. 3. Send: which may cause the condition that other task is waiting for to become true and let that task to be available to run. The pseudo-code of the major skeleton framework is shown here. Receive(Data_Pointer address, …) { OS_ENTER_CRITICAL(); if (not need to block what you need already exist) { //handling the return value and maintain structure OS_EXIT_CRITICAL(); return; } //Let it become not blocked OS_TaskStatus = OS_TaskStatus& (~(0x01<<OS_RunningTask)); //Decide whether or not to schedule OS_SaveContext(); OS_Scheduler(); OS_LoadContext(); //Never return }
  • 10. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 10 - Send(Data_Pointer address, …) { OS_ENTER_CRITICAL(); if (no need to block no one is waiting ) { //maintain structure OS_EXIT_CRITICAL(); return; } //Let it become not blocked OS_TaskStatus = OS_TaskStatus | (0x01<<((uint8 *)pMailbox)[3]); if(OSIntNesting==0) { OS_SaveContext(); OS_Scheduler(); OS_LoadContext(); //Never return }else { OS_EXIT_CRITICAL(); } } You may need some time to read this pseudo-code and to figure out the mechanism of this common framework. With this skeleton code, all codes for semaphore etc become very simple. A special tricky part that needs more attention is the OSIntNesting problem. As we know, the OS cannot block the ISR routine or perform context switching before return back from the ISR. Be precisely, David E. Simon points out two rules that an ISR must obeys on Page 199 of the reference book [2]: Rule 1: An interrupt routine must not call any RTOS function that might block the caller. Rule 2: An interrupt routine must not call any RTOS function that might cause the RTOS to switch tasks unless the RTOS knows that an interrupt routine, and not a task is executing. And since every ISR will call OS_INT_ENTER() which will let OS knows that now Send() function is called by ISR or Tasks. The OS then can judge from this to know whether to perform context switching or not. Another place that needs to pay attention is the addressing of the data for synchronization, such as the place to save the semaphore, mailbox, or message queue. I have mentioned in Section 2.1 that USTOS uses static memory allocation. So the application programmer will define a place to put the data and pass an address pointer to the Receive() and Send() function. There is
  • 11. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 11 - not explicitly mapping between task ID and the data used for synchronization such as semaphores. And example of this is shown here. Mailbox_t Mailbox1; OS_MailboxCreate(0,&Mailbox1); OS_MailboxSend(&Mailbox1,0x1234); OS_MailboxReceive(&Mailbox1,&rcvMsg); As I mention is Section 2.7, the API function “void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)void OS_TaskDelay(uint8 nTicks)” is much like the Receive() function here, which may cause the task to wait for some condition and become block. So the major skeleton is the same useful for the function “void OS_TaskDelay(uint8void OS_TaskDelay(uint8void OS_TaskDelay(uint8void OS_TaskDelay(uint8 nTinTinTinTicks)cks)cks)cks)”. 2.9 Binary Semaphore Binary semaphore is the most simple synchronization mechanism in USTOS. At the same time, it is also the most efficient one in USTOS. It will cost only 1 Byte RAM. The usage example: BinSem_t BinSem1; OS_BinSemCreate(&BinSem1); OS_BinSemV(&BinSem1); OS_BinSemP(&BinSem1); Let me elaborate the details about the implementation of the binary semaphore. The type definition of binary semaphore is here: typedef uint8 BinSem_t; One bit of the semaphore is used by indicating whether the corresponding task is waiting for that semaphore or not. If yes, the corresponding bit will be set to 1. Since the Idle Task will not try to get the semaphore, so the most significant bit which is corresponding to the idle task is used to indicate the value of the semaphore. The other things are similar as the skeleton code. void OS_BinSemCreate(BinSem_t* pBinSem) { *pBinSem = 0x80;} void OS_BinSemP(BinSem_t* pBinSem) { OS_ENTER_CRITICAL();
  • 12. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 12 - if( (*pBinSem) == 0x80 ) { (*pBinSem)=0; OS_EXIT_CRITICAL(); return; } (*pBinSem) |= (0x01<<OS_RunningTask); …… // The same as the skeleton code } void OS_BinSemV(BinSem_t* pBinSem) { OS_ENTER_CRITICAL(); if( ( (*pBinSem) & 0x7f ) == 0 ) { *pBinSem = 0x80; OS_EXIT_CRITICAL(); return; } for(i=0;i<7;i++) // Find the tasks that is waiting and has the highest priority { if(((*pBinSem) & (0x01<<i))!=0) { (*pBinSem) = (*pBinSem) & (~(0x01<<i)) & 0x7f; OS_TaskStatus = OS_TaskStatus | (0x01<< i); break; } } …… // The same as the skeleton code } 2.10 Mailbox The usage example of mailbox is already shown in Section 2.8. A mailbox needs 4 Bytes to store the information which contains 2 Bytes for message content, 1 Byte for the pointer of return address, and 1 Byte for saving the task ID of the task that is waiting. typedef uint32 Mailbox_t; void OS_MailboxCreate(uint8 TaskID,Mailbox_t* pMailbox) { *pMailbox=0; ((uint8*)pMailbox)[3]=TaskID; } void OS_MailboxReceive(Mailbox_t* pMailbox,uint16* pRetV) { OS_ENTER_CRITICAL(); if(((uint16*)pMailbox)[0]!=0)
  • 13. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 13 - { *pRetV=((uint16*)pMailbox)[0]; ((uint16*)pMailbox)[0]=0; ((uint8 *)pMailbox)[2]=0; OS_EXIT_CRITICAL(); return; } ((uint8*)pMailbox)[2] = (uint8)pRetV; …… // The same as the skeleton code } void OS_MailboxSend(Mailbox_t* pMailbox,uint16 Msg) { OS_ENTER_CRITICAL(); if(((uint8*)pMailbox)[2]==0) { ((uint16*)pMailbox)[0]=Msg; OS_EXIT_CRITICAL(); return; } *((uint16*)(((uint8*)pMailbox)[2])) = Msg; ((uint16*)pMailbox)[0]=0; ((uint8 *)pMailbox)[2]=0; …… // The same as the skeleton code } 2.11 Message Queue The usage example is like here: #define OS_MSGQ_OFFSET 5 uint8 MsgQ1[OS_MSGQ_OFFSET+4]; OS_MsgQCreate(2,MsgQ1,4); OS_MsgQPend(MsgQ1,&Msg2); OS_MsgQPost(MsgQ1,P1); The message queue is kind of RAM consuming mechanism. At least 6 Bytes are needed to implement a message queue. The message size for message queue is 1 Bytes which is a half of the message size in mailbox. The data structure of message queue is like this: Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8 .... [TOTAL_SIZE] [Pointer] [TaskID] [Return Address] [CurrentSize] [Data0] [Data1] [Data2] ....
  • 14. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 14 - TOTAL_SIZE: Record the length of the array Date[0] to Date[TOTAL_SIZE-1] Pointer: Use to refer to the Data[Pointer] is the head of next out-queue message TaskID: The task ID of the waiting task Return Address: the pointer to RAM about where to put the message CurrentSize: The total size of the non-empty messages slots Date[0] to Date[TOTAL_SIZE-1]: The place to save the message. The specific code to manipulate this structure is shown below. It looks complex. But in fact, if you can understand the mailbox, it is easy to read through it. It is just some ugly detail stuffs to record lots of information, so you may not even need to read this since it will not help too much for your understanding. Why I show it here is just for completeness. void OS_MsgQCreate(uint8 TaskID,uint8* pMsgQ,uint8 uSize) { pMsgQ[0]=uSize; pMsgQ[1]=0; pMsgQ[2]=TaskID; pMsgQ[3]=0; pMsgQ[4]=0; } void OS_MsgQPend(uint8* pMsgQ,uint8* pRetV) { OS_ENTER_CRITICAL(); if(pMsgQ[4]!=0) { *pRetV=pMsgQ[OS_MSGQ_OFFSET+pMsgQ[1]]; pMsgQ[1] = (pMsgQ[1]+1) % pMsgQ[0]; pMsgQ[4]--; pMsgQ[1]++; OS_EXIT_CRITICAL(); return; } pMsgQ[3] = (uint8)pRetV; …… // The same as the skeleton code } void OS_MsgQPost(uint8* pMsgQ,uint8 Msg) { OS_ENTER_CRITICAL(); if(pMsgQ[3]==0) { //No one is waiting for message pMsgQ[4] ++; //Increase size if(pMsgQ[4]>pMsgQ[0]) //Full now pMsgQ[4] --; //Never block, but the last msg will be over-written pMsgQ[ OS_MSGQ_OFFSET + ( pMsgQ[1] + pMsgQ[4] -1 ) % pMsgQ[0] ] = Msg; //Save the message
  • 15. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 15 - OS_EXIT_CRITICAL(); return; } //Some one is waiting. *((uint8*)(pMsgQ[3])) = Msg; //Return the message content pMsgQ[3]=0; //Clear return address, Indicate that no one is waiting …… // The same as the skeleton code } 2.12 Idle Tasks This is the simplest part of USTOS. The only attention needed is that since idle task is doing nothing, some part of USTOS make use of this and do not save any values while context switching in order to speed up the system. void OS_IdleTask(void) { while(1){ /* Save Power and Statistic*/; } } 2.13 Event Group I have tried for several hours and found that Event Group needs so many RAM space to store all the information needed. So it is unpractical and unfeasible to implement it for 8051 which has very small RAM (128Byte + 128Byte). In case USTOS is transplanted into other hardware which has large RAM, it is feasible to implement it. The Data Structure of EventGroup may be like this: [EventValue] [EventMatch] [Waiting?] [Task1_Mask] [Task2_Mask] [Task2_Mask] ... BitIndicator 0=Not Waiting Not 0 = Waiting For AND/OR Higher Priority Lower Priority 2.14 EDF Scheduling Earliest deadline first scheduling is optimal and efficient. If a dynamic priority schedule exists, EDF will produce a feasible schedule. A dynamic priority schedule exists if and only if utilization is no greater than 100%. But EDF need too many extra RAM space in order to record the all information that EDF needed. This is the disaster reason for 8051 series MCU. And at the same time, the application programmer must have some way to point out when is the deadline for all tasks. And the OS have to maintain this information. Thus, EDF is complicated enough to have unacceptable overhead. After considering the RAM limit of 8051
  • 16. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 16 - and the information needed to store for EDF, it seems impossible to implement it in 8051. So I do not try to implement it. This is the trade off of engineering. 2.15 Keil C51 Compiler's Colorizing Memory Allocation Because the limit size of 8051’s RAM, Keil C extent the ANSI C to generate better codes which require less RAM and run faster. One extension is to let the compiler not to push all local register into memory stacks when a function is calling the other functions. This means that Keil C51 compiler allocates the local variables into fixed-addressing RAM. In another word, Keil C51 compiler let the local variables become global variable. This is a special case needed more attention. In the other side, it is easy to show that the RAM may have higher probability to run out if the program keeps on running different functions. Keil C51 compiler cures this by the way of Data Overlaying Optimizing. It will be further discussed in Section 4.2. 2.16 Keil C51 Compiler's Unpredictable Behaviors In Keil C51 compiler, for interrupt service routine, the contents of the R0 to R7, SFR ACC, B, DPH, DPL, and PSW, when required, are saved on the stack at function invocation time. This means when unnecessary, they will not be saved. So there is no way for USTOS know how many and which registers are pushed into the stack. In order to make them all push the same registers when interrupts happen, and deal with the over-clever Keil C51 compiler’s such unpredictable behaviors, I have to add several lines of useless code in order to make the code more complex and force the Keil C51 Compiler push all registers for all ISRs. Such useless codes are put at the beginning of the OS_INT_ENTER(void). void OS_INT_ENTER(void) { //The following code is used for prevent the Keil C51's stupid optimization uint8* pointer=0x02; *pointer = 0xff; DPL =0x00; DPH =0x00; B =0x00; PSW =0x00; ACC =0x00; …….. } 2.17 Other Ugly Stuffs
  • 17. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 17 - Other things that need to handle is the initial value of the system variables and hardware registers for RTOS and the task stacks’ initialization. Such codes are put in the fuction voidvoidvoidvoid OSInit(void)OSInit(void)OSInit(void)OSInit(void) and void OSStart(void)void OSStart(void)void OSStart(void)void OSStart(void). Another problem is that for RTOS, there are always many source files. Some users keep on complaining the linking errors. In order to prevent such errors and make everything become simpler, I write all the USTOS code in one file containing the users’ source code. The application programmer is expected to divide their source codes into two parts. One part is the global data definition and the other part is the running code. Then they put the two parts into the place that the source file of USTOS instructed. This will greatly enhance the probability of successfully linking of the source code. 3. How to Use USTOS 3.1 USTOS API There are 16 API functions for USTOS. Most of them are already mentioned in part 2. 01 uint8 OSVersion(); 02 uint8 OSRunningTaskID(); 03 void OS_INT_ENTER(void); 04 void OS_INT_EXIT(void); 05 void OSStart(void); 06 void OSInit(void); 07 void OS_TaskDelay(uint8 nTicks); 08 void OS_MsgQCreate(uint8 TaskID,uint8* pMsgQ,uint8 uSize); 09 void OS_MsgQPost(uint8* pMsgQ,uint8 Msg); 10 void OS_MsgQPend(uint8* pMsgQ,uint8* pRetV); 11 void OS_MailboxCreate(uint8 TaskID,Mailbox_t* pMailbox); 12 void OS_MailboxSend(Mailbox_t* pMailbox,uint16 Msg); 13 void OS_MailboxReceive(Mailbox_t* pMailbox,uint16* pRetV); 14 void OS_BinSemCreate(BinSem_t* pBinSem); 15 void OS_BinSemV(BinSem_t* pBinSem); 16 void OS_BinSemP(BinSem_t* pBinSem); API functions of USTOS 3.2 USTOS Configuration
  • 18. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 18 - // Functional Configuration 1 = Enable 0 = Disable #define OS_CONFIG_EN_MAILBOX 1 Support for mailbox ? #define OS_CONFIG_EN_MSGQ 1 Support for message queue ? #define OS_CONFIG_EN_BIN_SEMAPHORE 1 Support for binary semaphore ? #define OS_CONFIG_EN_SYSTEM_TIMER 1 Support for system timing service? #define OS_CONFIG_EN_INT_NESTING 1 Support for interrupt nesting? //Timer Setup #define OS_CONFIG_TICKS_CNT 32 How many times of interrupt convert to one system ticks? //Task Information Setup #define OS_MAX_TASKS 4 Number of Tasks #define OS_TASK_STACK_SIZE 20 Stack size of Tasks (Unit: Byte) 3.3 Examples This is a simple example of using semaphores, mailbox, as well as messages queue and system timing service. //--------------------------------------------------------- // Part I: User config //--------------------------------------------------------- uint8 idata Stack0[OS_TASK_STACK_SIZE]; uint8 idata Stack1[OS_TASK_STACK_SIZE]; uint8 idata Stack2[OS_TASK_STACK_SIZE]; uint8 idata StackIdle[OS_TASK_STACK_SIZE]; //Static Create Tasks void Task0(void); void Task1(void); void Task2(void); CodePointer_t OS_TaskCode [OS_MAX_TASKS]={Task0,Task1,Task2,OS_IdleTask}; DataPointer_t OS_TaskStack[OS_MAX_TASKS]={Stack0,Stack1,Stack2,StackIdle}; //Static Create Mailbox Mailbox_t Mailbox1; BinSem_t BinSem1; uint8 MsgQ1[OS_MSGQ_OFFSET+4]; //--------------------------------------------------------- // Part II: User code //--------------------------------------------------------- #pragma disable void myISR1(void) interrupt 0 { OS_INT_ENTER();
  • 19. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 19 - OS_MailboxSend(&Mailbox1,0x5678); OS_INT_EXIT(); } #pragma disable void myISR2(void) interrupt 2 { OS_INT_ENTER(); OS_MsgQPost(MsgQ1,P1); OS_INT_EXIT(); } void Task0(void) { uint16 rcvMsg; while(1) { OS_MailboxReceive(&Mailbox1,&rcvMsg); P1=rcvMsg/256; P0=rcvMsg%256; } } void Task1(void) { uint8 myflag=1; while(1) { if(myflag) { myflag=0; OS_MailboxSend(&Mailbox1,0x1234); }else { OS_BinSemP(&BinSem1); OS_BinSemP(&BinSem1); OS_TaskDelay(1); } } } void Task2(void) { uint8 Msg2; while(1) { OS_MsgQPend(MsgQ1,&Msg2); OS_BinSemV(&BinSem1); } } void main(void) { OSInit(); OS_MailboxCreate(0,&Mailbox1); OS_BinSemCreate(&BinSem1); OS_MsgQCreate(2,MsgQ1,4); PX0=0; IT0=1; PX1=1; IT1=1; //Extern Interrupt TMOD = (TMOD & 0XF0) | 0X01; //System Timer Setup TH0 = 0x0; TL0 = 0x0; //Time interval = 0xFFFF TR0 = 1; ET0 = 1; PT0 = 1;
  • 20. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 20 - OSStart(); } 4. Hardware/software Requirements 4.1 Hardware I choose Intel 8051 microcontroller as the hardware platform in this project. This is because it is cheap, and slow. Slow speed means that the resource is really limited and the main characteristics of embedded system are shown most explicitly. So it is a good time to practice the principles of embedded system. The big picture of the architecture of 8051 MCU is like the following (It is from the reference [6]). If you need further information, you are encouraged to read the materials in the reference [6]. 4.2 Software The tool chain that I plan to use is like following: • Cross-compiler: Small Device C Compiler. • Simulator: 8051 Microcontroller Series Simulator • Cross-compiler: KeilC51 Compiler IDE • Downloader: AT89S PC Based Programmer
  • 21. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 21 - But after trying all of them, I found that the SDCC compiler and the 8051 Microcontroller Series Simulator are too weak in functionality and difficult and inefficient for coding and debugging. While at the same time, Keil C51 complier together with uVision IDE, the most popular developing environment around the world for 8051, offers an excellent programming & debugging environment, as well as very precise simulator. Please notice that Keil C51 compiler is NOT free software. But you may get an evaluation edition at the Keil’s Website: http://www.keil.com/demo/. Since the special characteristic of this compiler, I cannot guarantee USTOS can be compiled successfully on all the versions of the compiler. The version of my complier and simulator are Keil C51 V7.50 and uVision V2.40. The target MCU is the most popular 8051 series MCU -- Atmel AT89S52. A real world RTOS is not just to be used by one hardware platform. So I planed to divide the USTOS into two separate parts. One is hardware platform independent part which is all written in C language. The other is hardware dependent part written in both C and assembly language. But when I was implementing, I realize that I almost do not need any assembly language at all since Keil C51 compiler have all kinds of functions to help. And at the same time, the USTOS V1.0 makes use of lots of characteristic of Keil C51 compiler and 8051 hardware architecture in order to speed up and save RAM. So it is very difficult and inefficient in run time if I divide USTOS into two separate parts by brute force. And since there is no packet support package for 8051 series microcontroller, I start all my codes from scratch under the help of some description about the hardware architecture of 8051 series microcontroller. 4. 3 Compiler Setup Keil C51 Compiler has 10 levels of optimization. Each higher optimization level contains all of the characteristics of the preceding lower optimization level. USTOS supports Level 0 to Level 7 optimization. Please read the reference [7] for more details. Level 0 Constant Folding: The compiler performs calculations that reduce expressions to numeric constants, where possible.This includes calculations of run-time addresses. Simple Access Optimizing: The compiler optimizes access of internal data and bit addresses in the 8051 system. Jump Optimizing: The compiler always extends jumps to the final target. Jumps to jumps are deleted. Level 1 Dead Code Elimination: Unused code fragments and artifacts are eliminated. Jump Negation: Conditional jumps are closely examined to see if they can be streamlined or eliminated by the inversion of the test logic. Level 2
  • 22. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 22 - Data Overlaying: Data and bit segments suitable for static overlay are identified and internally marked. The BL51 Linker/Locator has the capability, through global data flow analysis, of selecting segments which can then be overlaid. Level 3 Peephole Optimizing: Redundant MOV instructions are removed. This includes unnecessary loading of objects from the memory as well as load operations with constants. Complex operations are replaced by simple operations when memory space or execution time can be saved. Level 4 Register Variables: Automatic variables and function arguments are located in registers when possible. Reservation of data memory for these variables is omitted. Extended Access Optimizing: Variables from the IDATA, XDATA, PDATA and CODE areas are directly included in operations. The use of intermediate registers is not necessary most of the time. Local Common Sub expression Elimination: If the same calculations are performed repetitively in an expression, the result of the first calculation is saved and used further whenever possible. Superfluous calculations are eliminated from the code. Case/Switch Optimizing: Code involving switch and case statements is optimized as jump tables or jump strings. Level 5 Global Common Sub expression Elimination: Identical sub expressions within a function are calculated only once when possible. The intermediate result is stored in a register and used instead of a new calculation. Simple Loop Optimizing: Program loops that fill a memory range with a constant are converted and optimized. Level 6 Loop Rotation: Program loops are rotated if the resulting program code is faster and more efficient. Level 7 Extended Index Access Optimizing: Uses the DPTR for register variables where appropriate. Pointer and array access are optimized for both execution speed and code size.
  • 23. HKUST COMP355 Project Report of a Small Real Time Operating System – USTOS - 23 - 5. Conclusions 5.1 Summary This project is challenging as an undergraduate course’s project within such short period of time. But through this project, I know the principles of the embedded system software more deeply and USTOS will be a small enough OS for other beginner to read and learn. 5.2 Future Work Because this is just a UG course project and not a research project, no new scheduling method is invented. And thus it emphasizes the implementation but not the creativity. If it is good to have new scheduling algorithm, I will add it to USTOS. Another possible improvement is to implement the Priority Inheritance Protocol to solve to Priority Inversion Problem in USTOS. Some other possible development may contain transplanting USTOS into other hardware. Nevertheless, USTOS will be an open-source RTOS for anyone to use and study. Hopefully my work can be used by industry or by education. 6. References [1] Jogesh K. Muppala, HKUST COMP355 Embedded System Software Lecture Notes and Course Materials, http://www.cs.ust.hk/%7Emuppala/comp355/ [2] David E. Simon, An Embedded Software Primer, Addison-Wesley, 1999. [3] Raj Kamal, Embedded Systems: Architecture, Programming and Design, McGraw-Hill, 2003. [4] Dreamtech Software Team, Programming for Embedded Systems: Cracking the Code, Wiley, 2002. [5] Atmel Corporation: Manuals of AT89S52 chip [6] Tim K. T. WOO, HKUST ELEC 254 Course Materials, http://course.ee.ust.hk/elec254/ [7] Keil Software Corporation: User’s Guide of Cx51 Compiler, http://www.keil.com/