S3C2416 bare metal development series 15_uCOS porting under GCC (1)

Publisher:DelightfulSmileLatest update time:2016-12-12 Source: eefocusKeywords:S3C2416 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

An operating system is a system software used to manage system hardware, software and data resources, control program execution, and provide support for other application software. According to different types, it can be divided into real-time operating systems, desktop operating systems, server operating systems, etc. For some small applications that have high requirements for system real-time performance and limited hardware resources, it is necessary to avoid using complex and large operating systems (such as Linux) as much as possible. Using a small real-time operating system (such as uCOS) can better meet the needs of the application. Here, the author gives a brief introduction to the transplantation of uCOS-II.

1. Code preparation

uCOS-II V2.91 source code, this version of source code is the latest version of uCOS-II, please download this version of source code from Micrium official website or other websites, of course, other versions of uCOS-II are transplanted in the same way. Micrium official website also provides some CPU transplantation examples for reference, here is to download the source code and transplant step by step.

s3c2416 startup code project, the startup code is the code that must be run before the s3c2416/50/51 series arm9 chips run the user c code main function. The startup code supports sd and Nand startup, sets the system clock for the user, initializes the memory, automatically identifies the startup device and moves the code to RAM, MMU mapping, interrupt management, etc. Users only need to focus on developing other functional functions with c. The author has a very detailed introduction to the startup code and the implementation process of the startup code in the previous chapter. Here, we will use the transplantation of uCOS under GCC as an example. Download the startup code source code in "GCC startup code project application example". If you develop under MDK, download the startup code source code in "MDK startup code project application example".

User code, all functional codes developed in C, where the entry point of user code is the main() function, where any running code of uCOS is implemented.

2. Project construction

Create a new uCOS project directory in any path under the Linux operating system, and create a new uCOS-II directory in this directory to store uCOS related parts. Download the uCOS-II V2.91 source code and decompress it, copy the entire Source directory to the uCOS-II directory, and create a new Cfg directory in the directory to store uCOS configuration files, and a new Ports directory to store uCOS transplant interface files.

Copy the startup code directory start_code to the UCGUI directory. This part of the code does not need any modification. And keep the Makefile files. The project management Makefile under the GCC startup code is extracted from uboot, which can easily add source code and code directory.

Create a new apps directory under the UCGUI directory to store application-related source code.

The final UCGUI directory contents are as follows:

uCOS/start_code, save the relevant part of s3c2416 startup code

uCOS/app, save the application part of a project

uCOS/uCOS-II/Cfg, save the configuration part of uCOS

uCOS/uCOS-II/Ports, save the uCOS porting part

uCOS/uCOS-II/Source, saves the source code of uCOS, usually directly replaces the source code of a higher version

3. uCOS transplant

When uCOS-II is used on different CPUs, it is necessary to modify and write the three files os_cpu.h, os_cpu_a.s, and os_cpu_c.c in the uCOS-II/Ports directory.

3.1. Writing os_cpu.h

3.1.1. External Declarations

uCOS-II uses OS_CPU_GLOBALS and OS_CPU_EXT to declare external variables and symbols. This part is as follows:

#ifdef OS_CPU_GLOBALS

#define OS_CPU_EXT

#else

#define OS_CPU_EXT extern

#endif

3.1.2. Data Type Definition

In order to ensure the portability of uC/OS-II, a series of type definitions are declared in os_cpu.h. These types are independent of C data types such as int, short, long, etc. The data type definitions are as follows:

typedef unsigned char BOOLEAN; /* Boolean variable */

typedef unsigned char INT8U; /* Unsigned 8-bit integer variable*/

typedef signed char INT8S; /* Signed 8-bit integer variable*/

typedef unsigned short INT16U; /* Unsigned 16-bit integer variable*/

typedef signed short INT16S; /* Signed 16-bit integer variable*/

typedef unsigned int INT32U; /* Unsigned 32-bit integer variable*/

typedef signed int INT32S; /* Signed 32-bit integer variable*/

typedef float FP32; /* Single-precision floating point number (32-bit length) */

typedef double FP64; /* Double-precision floating point number (64-bit length) */

3.1.3. Stack Configuration

uCOS-II is suitable for 8-bit, 16-bit, and 32-bit CPUs. CPUs with different word lengths have different stack word lengths. uCOS-II uses OS_STK to represent the stack type. At the same time, the stack can grow from high address to low address, or from low address to high address. For arm architecture CPUs, the stack can grow downward or upward. However, for each compiler, it is agreed that the stack grows from high address to low address, and the stack word length is 32 bits. The stack configuration content is as follows:

typedef INT32U OS_STK; /* The stack is 32 bits wide*/

#define OS_STK_GROWTH 1 /* The stack grows from high to low*/

3.1.4. Critical Section Access

For preemptive operating systems, there is a small section of critical code that must be accessed exclusively. If a task (thread) is accessing the critical code, other tasks (threads) can no longer enter the code until the task (thread) with access rights exits the critical section. When accessing the kernel critical section, uCOS-II prohibits task preemption through the two macro switch interrupts OS_ENTER_CRITICAL()/OS_EXIT_CRITICAL() to ensure that the critical section is not destroyed. Usually, there are three ways to access the critical section. One is to directly switch the interrupt, the second is to save/restore the interrupt status from the stack and then switch the interrupt, and the third is to save/restore the interrupt status from the local variable and then switch the interrupt. uCOS-II uses the third switch interrupt method, which requires the implementation of the state save and restore switch interrupt CPU_SR_Save()/CPU_SR_Restore(). A variable of type OS_CPU_SR needs to be introduced to save the CPU interrupt status. The critical section interrupt access content is as follows:

#define OS_CRITICAL_METHOD 3 /*Local variables save/restore state and then turn on/off interrupts*/

typedef INT32U OS_CPU_SR; /*Used to save/restore interrupt status before switching interrupts*/

#define OS_ENTER_CRITICAL() {cpu_sr = CPU_SR_Save ();} /* Disable interrupt */

#define OS_EXIT_CRITICAL() {CPU_SR_Restore (cpu_sr);} /* Enable interrupt */

3.1.5. Function Declaration

uCOS-II needs to be assembled to implement switch interrupts, task switching and other architecture-related functions, which are implemented in the assembly file os_cpu_a.s. The header file declares the functions, which include the following functions:

#define OS_TASK_SW() OSCtxSw() /* Task-level task switching function*/

OS_CPU_SR CPU_SR_Save(void);

void CPU_SR_Restore(OS_CPU_SR cpu_sr);

void OSStartHighRdy(void);

void OSCtxSw(void);

void OSIntCtxSw(void);

3.2. Writing os_cpu_a.s

High-level languages ​​cannot implement save/restore registers, so uCOS-II needs to write assembly to implement six simple functions: CPU_SR_Save(), CPU_SR_Restore(), OSStartHighRdy(), OSCtxSw(), OSIntCtxSw(), and IRQ_SaveContext().

3.2.1. CPU_SR_Save() function

Since the interrupt status is saved/restored from local variables and then the interrupt is turned on and off, R0 is used to return the interrupt status and turn off the interrupt. This function is a macro implementation of OS_ENTER_CRITICAL().

    .globl CPU_SR_Save

CPU_SR_Save:

    MRS R0, CPSR

    ORR R1, R0, #0xC0 // Set IRQ and FIQ to disable interrupts

    MSR CPSR_c, R1

    BX LR // Disable interrupts and return interrupt status to R0

3.2.2. CPU_SR_Restore() function

After accessing the critical section, the interrupt status before disabling interrupts needs to be restored. This function is a macro implementation of OS_EXIT_CRITICAL().

    .globl CPU_SR_Restore

CPU_SR_Restore:    

    MSR CPSR_c, R0

    BX LR

3.2.3. OSStartHighRdy() function

When the user starts the uCOS kernel for management through OSStart(), OSStart() will first call OSStartHighRdy() to run the task with the highest priority among the created tasks. OSStartHighRdy() needs to complete the following tasks:

(1) Disable interrupts and switch to management mode. All tasks work in management mode.

(2) Call the task switching hook function, that is, first call the OSTaskSwHook() function

(3) Mark that the uCOS-II kernel has started running, OSRunning = 1

(4) Get the highest priority task TCB, get the task stack pointer, and switch the SP to the task stack

(5) Pop the task stack in SP, including the task status register CPSR, R0-R12, LR, and continue executing the task.

#define I_Bit 0x80 // IRQ interrupt disable bit

#define F_Bit 0x40 // FIQ interrupt disable bit

#define Mode_SVC 0x13 // Management mode

#define Mode_SYS 0x1f // System mode

    .extern OSTaskSwHook

    .extern OSRunning

   .extern OSTCBHighRdy

    .globl OSStartHighRdy

OSStartHighRdy:

    MSR CPSR_c, #(I_Bit+F_Bit+Mode_SVC) // Disable interrupts and switch to management mode

 

    LDR R0, =OSTaskSwHook // Call task switching hook function

    MOV LR, PC // Prepare function return address

    BX R0 // Support Thumb and ARM mixed coding

 

    LDR R0, =OSRunning //Set OSRunning to 1

    MOV R1, #1

    STRB R1, [R0]

 

    LDR R0, =OSTCBHighRdy // Get the highest priority task TCB

    LDR R0, [R0] // Get the task stack pointer

    LDR SP, [R0] // Switch to the new task stack

 

    LDMFD SP!, {R0} // Pop the CPSR of the new task

    MSR SPSR_cxsf, R0

    LDMFD SP!, {R0-R12, LR, PC}^ // Pop the context of the new task

3.2.4. OSCtxSw() function

uCOS-II schedules tasks through the OS_Sched() function and performs actual task switching by calling OS_TASK_SW(). OSCtxSw() is the macro implementation of OS_TASK_SW(). The task switching function OSCtxSw() needs to complete the following tasks:

(1) Save the context of the current task (R0-R12, LR, PC address of task interruption, status register CPSR) to the current task stack

(2) According to the current task TCB (task control block), obtain the current task stack pointer and save the current task SP stack into the stack pointer

(3) Call the task switching hook function, that is, first call the OSTaskSwHook() function

(4) Update the priority of the highest priority task to be run to the current priority variable

(5) Update the TCB (task control block) address of the highest priority task to be run to the current TCB (task control block) address variable

(6) Obtain the highest priority task stack pointer, SP switches to the highest priority task stack, pops the context of the new task, and executes the new task.

#define Mode_THUMB 0x20 // THUMB mode

    .extern OSTCBCur

        .extern OSTCBHighRdy

        .extern OSPrioCur

        .extern OSPrioHighRdy

 

        .globl OSCtxSw

        .globl OSIntCtxSw

OSCtxSw:

    STMFD SP!, {LR} // Push the current task PC onto the stack

    STMFD SP!, {LR} // Push the current task LR onto the stack

    STMFD SP!, {R0-R12} // Push the current task R0-R12

    MRS R0, CPSR // Get the current task CPSR

    TST LR, #1 // Test whether the task is working in Thumb mode

    ORRNE R0, R0, #Mode_THUMB // If it is Thumb, the state changes to Thumb mode

    STMFD SP!, {R0} // Push CPSR

 

    LDR R0, =OSTCBCur // Get the current task TCB

    LDR R1, [R0] // Get the current task stack pointer from TCB

    STR SP, [R1] // SP stack saves the current task stack pointer

 

OSIntCtxSw:

    LDR R0, =OSTaskSwHook // Call task switching hook function

    MOV LR, PC // Prepare function return address

    BX

 

    LDR R0, =OSPrioCur // Get the current task priority save pointer

    LDR R1, =OSPrioHighRdy // Get the highest priority task priority save pointer

    LDRB R2, [R1] // Get the highest priority task priority

    STRB R2, [R0] // Save into the current task priority pointer variable

 

    LDR R0, =OSTCBCur // Get the current task TCB save pointer

    LDR R1, =OSTCBHighRdy // Get the highest priority task TCB save pointer

    LDR R2, [R1] // The highest priority TCB address is saved into the current task TCB pointer

    STR R2, [R0]

 

    LDR SP, [R2] // SP switches to the highest priority task stack

    LDMFD SP!, {R0} // Pop the CPSR of the new task

    MSR SPSR_cxsf, R0

    LDMFD SP!, {R0-R12, LR, PC}^ // Pop the context of the new task

3.2.5. OSIntCtxSw() function

OSIntCtxSw() is used to implement interrupt-level task switching. When all interrupts (nested interrupts) are executed, the kernel needs to switch to the task to continue execution. Therefore, interrupt-level task switching is consistent with ordinary task switching. The difference is that the task context has been saved when an exception occurs, and interrupt-level task switching does not need to save the task context. It only lacks steps 1 and 2 compared to OSCtxSw(), and the rest are the same. Therefore, OSIntCtxSw() can be merged and written on OSCtxSw(). See the OSIntCtxSw() function label on OSCtxSw().

(1) Call the task switching hook function, that is, first call the OSTaskSwHook() function

(2) Update the priority of the highest priority task to be run to the current priority variable

(3) Update the TCB (task control block) address of the highest priority task to be run to the current TCB (task control block) address variable

(4) Obtain the highest priority task stack pointer, SP switches to the highest priority task stack, pops the context of the new task, and executes the new task.

3.2.6. IRQ_SaveContext() function

When any exception occurs, the task will be interrupted. When entering the exception, the context of the current task should be saved to the current task stack before executing the exception handling. IRQ exception is no exception, because uCOS-II needs a timer interrupt Tick, so IRQ handling is also part of the porting. IRQ_SaveContext() needs to complete the following work:

(1) Temporarily use some registers and push the used registers onto the IRQ stack

(2) Switch to management mode, disable interrupts, and run the task in management mode. This step will switch the SP to the task stack interrupted by the interrupt

(3) Push the context of the interrupted task into the task stack.

(4) Track the interrupt nesting count to determine whether the task is interrupted or the interrupt is nested. Interrupt nesting does not require updating the task stack.

(5) Non-interrupt nesting: obtain the stack pointer according to the current task TCB (task control block) and save the interrupted task SP stack into the stack pointer

(6) Call OSIntEnter() function to nest interrupts and count up

(7) Switch to system mode and push LR. This step is to use the system mode stack to handle interrupt functions and reduce the use of the task stack.

(8) Call the IRQ_Handler() function to actually handle the IRQ interrupt service. The IRQ interrupt can be enabled again during the interrupt service, and interrupt nesting is supported.

(9) After the interrupt service is executed, the LR is popped out of the stack and the mode is switched to management mode. Interrupts are disabled and the SP is switched to the task stack of the interrupted task.

(10) Call OSIntExit() to decrement the interrupt nesting count. If the interrupt nesting count OSIntNesting is 0, it means that all interrupts have exited. OSIntCtxSw() will be called to switch the interrupt level task and continue to execute the task.

(11) If the interrupt nesting count OSIntNesting is not 0 and all interrupts have not been exited, the context of the previous interrupt is popped out of the stack and the nested upper interrupt is executed.

        .extern OSIntEnter

        .extern OSIntExit

    .extern OSIntNesting

        .extern IRQ_Handler

   

        .globl IRQ_SaveContext

IRQ_SaveContext:

    SUB LR, LR, #4 // IRQ exception return address LR-4

    STMFD SP!, {R0-R2} // Temporary working registers are pushed into the IRQ stack

    MRS R0, SPSR // Save the CPSR before the exception occurs

    MOV R1, LR // save LR

    MOV R2, SP // Save IRQ stack pointer, used to pop the working register

    ADD SP, SP, #(3 * 4) // Adjust back to the position of the IRQ stack

                                                               

    MSR CPSR_c, #(I_Bit+F_Bit+Mode_SVC) // Disable interrupts and switch to management mode

 

    STMFD SP!, {R1} // Push the PC of the interrupted task

    STMFD SP!, {LR} // Push the LR of the interrupted task

    STMFD SP!, {R3-R12} // Push the stack to interrupt the task's R12-R3

    LDMFD R2!, {R5-R7} // Restore R2-R0 from IRQ stack

    STMFD SP!, {R5-R7} // Push the stack to interrupt the task's R2-R0

    STMFD SP!, {R0} // Push the CPSR of the interrupted task

   

    LDR R0, =OSIntNesting //Get interrupt nesting count

    LDRB R1, [R0]

    CMP R1, #0 //Judge whether the task is interrupted or nested

    BNE IntteruptNesting // Interrupt nesting does not need to update the task stack pointer

   

    LDR R0, =OSTCBCur // The task is interrupted, get the interrupt task TCB

    LDR R1, [R0] // Get the interrupt task stack pointer

    STR SP, [R1] // SP stack saves the interrupt task stack pointer

   

IntteruptNesting:  

    LDR R0, =OSIntEnter //Call OSIntEnter() to count interrupt nesting

    MOV LR, PC

    BX

 

    MSR CPSR_c, #(I_Bit+F_Bit+Mode_SYS) // Switch to system mode and use the system mode stack to handle interrupts

    STMFD SP!, {LR} // Push system mode LR

   

    LDR R0, =IRQ_Handler // Call IRQ processing function

    MOV LR, PC

    BX

 

    LDMFD SP!, {LR} // Pop system mode LR 

    MSR CPSR_c, #(I_Bit+F_Bit+Mode_SVC) // Switch to management mode and use the task stack to pop

 

    LDR R0, =OSIntExit // Call OSIntExit() to decrement the interrupt, which may not return

    MOV LR, PC

    BX

 

    LDMFD SP!, {R0} // Interrupts are nested, pop the context of the previous interrupt

    MSR SPSR_cxsf, R0

    LDMFD SP!, {R0-R12, LR, PC}^

 

3.3. Writing the os_cpu_c.c file

uCOS-II needs to write ten simple hook functions, which can be left blank if there is no special requirement. OSInitHookBegin(), OSInitHookEnd(), OSTaskCreateHook(), OSTaskDelHook(), OSTaskIdleHook(), OSTaskStatHook(), OSTaskSwHook(), OSTCBInitHook(), OSTimeTickHook(), OSTaskReturnHook(). Among them, the more important one is OSTaskStkInit() function, which is used to initialize the task stack and task status, and is necessary.

3.3.1. OSTaskStkInit() function

#define Mode_SVC 0x13

#define Mode_THUMB 0x20

#define Mode_ARM 0x00

OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos,INT16U opt)

{

    OS_STK *stk;

    (void)opt; /* avoid compiler warnings */

    stk = ptos; /* Get the stack pointer */

 

    *stk = (OS_STK) task; /* pc */

    *--stk = (OS_STK) task; /* lr */

    *--stk = 0; /* r12 */

    *--stk = 0; /* r11 */

    *--stk = 0; /* r10 */

    *--stk = 0; /* r9 */

    *--stk = 0; /* r8 */

    *--stk = 0; /* r7 */

    *--stk = 0; /* r6 */

    *--stk = 0; /* r5 */

    *--stk = 0; /* r4 */

    *--stk = 0; /* r3 */

    *--stk = 0; /* r2 */

    *--stk = 0; /* r1 */

    *--stk = (unsigned int)pdata; /* Use R0 to pass parameters */

    if (((OS_STK)task & 0x01u) ==0x01u) { /* Determine whether the task is running in Thumb mode or ARM mode*/

       *--stk = (OS_STK)(Mode_SVC |Mode_THUMB); /* CPSR, task works in Thumb state management mode*/

    } else {

       *--stk = (OS_STK)(Mode_SVC |Mode_ARM); /* CPSR, task works in ARM state management mode*/

    }

    return (stk);

}

3.3.2. Hook Function

The hook function can extend the user's code into the kernel to implement some specific functions. If there is no special functional requirement, it can be left blank.

void OSInitHookBegin (void)

{

}

void OSInitHookEnd (void)

{

 

}

void OSTaskCreateHook (OS_TCB *ptcb)

{

    ptcb = ptcb;

}

void OSTaskDelHook (OS_TCB *ptcb)

{

    (void)ptcb;

}

void OSTaskSwHook (void)

{

 

}

void OSTaskStatHook (void)

{

 

}

void OSTCBInitHook (OS_TCB *ptcb)

{

    (void)ptcb;

}

void OSTimeTickHook (void)

{

 

}

void OSTaskIdleHook (void)

{

 

}

void OSTaskReturnHook(OS_TCB *p_tcb)

{

    (void)(p_tcb);

}


Keywords:S3C2416 Reference address:S3C2416 bare metal development series 15_uCOS porting under GCC (1)

Previous article:S3C2416 bare metal development series 15_uCOS porting under GCC (2)
Next article:S3C2416 bare metal development series 14_Transplantation of UCGUI under GCC (2)

Recommended ReadingLatest update time:2024-11-16 18:00

Arm-linux-gcc installation under ubuntu
I found the download address at random, version 4.4.3, address: http://www.cr173.com/soft/42654.html#address 1. I put it in /work/tools/ 2.sudo tar xzvf /work/tools/arm-linux-gcc-4.4.3.tar.gz 3.sudo tar xvzf arm-linux-gcc-4.4.3.tar.gz -C / 4. The command was found in /opt/FriendlyARM/toolschain/4.4.3/bin; Then sudo v
[Microcontroller]
STM32 Advanced Development - Using printf to print serial port data in GCC and GNU
When you use Keil or IAR to develop STM32 and other ARM chips, you may be familiar with using print to output some data through the serial port for debugging or other purposes. However, it should be clear that although the standard C language library used by Keil, IAR and GCC follows the same standard, their underlyin
[Microcontroller]
GCC Compiler Principles (III) ------ Compilation Principles III: Compilation Process (3) --- Compilation Assembly and Static Linking [2]
4.1.2 Symbol Resolution and Relocation (1) Relocation After completing the space and address allocation steps, the linker enters the symbol resolution and relocation steps, which is the core part of static linking. Let's take a look at the disassembly file of ao: objdump -d ao: Virtual addr
[Microcontroller]
GCC Compiler Principles (III) ------ Compilation Principles III: Compilation Process (3) --- Compilation Assembly and Static Linking [2]
AVR MCU GCC Programming: Basic Operations of Timers
The following is the query working mode of the timer, and no interrupt request will occur: #include avr/io.h int main(void) {  //8-bit timer time calculation T=(256-initial value) * pulse period    int i;  TCNT0 = 55; //Set the initial value  TCCR0 |= (1 CS01); //8-division    for (i = 0; i 10000; i++)  {   while(!
[Microcontroller]
AVR_GCC Programming Basics
1. In the project options of avr studio, in the General option, you need to pay attention to the following: edit configuration: This option means that the default output folder of the current project is default, and the name of this folder can be changed Device: is the chip we want to choose to work Frequency: is t
[Microcontroller]
AVR_GCC Programming Basics
arm-linux-gcc and simple makefile
gcc common options How to use gcc: gcc filename -v: Check the version of the gcc compiler and display the detailed process of gcc execution -o: specifies the output file name as file, which does not need to be the same as the compiled file name -E: preprocess only; do not compile, assemble or link (preprocess only, n
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号