CuRT OS 的原始碼

作業系統

簡介

行程管理

記憶體管理

輸出入管理

檔案管理

使用者介面

嵌入式系統

RTOS

中斷處理

案例研究

EOS0

CurtOS

F9

UNIXv6

NachOS

uCOSII

L4

RtThread

Haiku

MINIX

Linux

Windows

mykernel

訊息

相關網站

參考文獻

最新修改

簡體版

English

大綱連結

  1. 測試主程式:app/shell/main.c
  2. 執行緒:kernel/thread.c
  3. 系統核心:kernel/kernel.c
  4. 序列裝置 (輸出入):kernel/serial.c
  5. 行程同步:kernel/sync.c
  6. 鏈節串列:kernel/list.c
  7. 標準輸出入:lib/stdio.c
  8. ARM 相關的組合語言:arch/arm/mach-pxa/asm_port.S
  9. ARM 啟動程式:arch/arm/mach-pxa/start.S

目前 C 語言大約 1700 行,組合語言大約 600 行。

測試主程式:app/shell/main.c

/**
 * Sample shell for CuRT.
 *
 * Supported commands:
 *   ps, stat, help, clear, hello
 */

#include "lib/stdio.h"
#include "kernel/thread.h"
#include "kernel/kernel.h"
#include "kernel/sync.h"
#include "port.h"    /* inclusion specific to hardware */

static thread_struct shell_thread, stat_thread, info_thread;
static thread_struct hello_thread, hello2_thread;

static stk_t thread_stk[5][THREAD_STACK_SIZE - 1];

static void shell_thread_func(void *pdata);
static void print_statistics(void *pdata);
static void print_thread_info(void *pdata);
static inline void print_help_msg();
static void hello_world(void *pdata);

static tid_t shell_tid, stat_tid, info_tid;
static tid_t hello_tid, hello2_tid;

static sem_struct sem;

extern void SerialInit();

int main()
{
    SerialInit();
    init_interrupt_control();
    init_curt();

    sem_init(&sem, 1);

    shell_tid = thread_create(&shell_thread,
            &thread_stk[0][THREAD_STACK_SIZE-1],
            shell_thread_func,
            "shell_thread",
            5,
            NULL);
    info_tid = thread_create(&info_thread,
            &thread_stk[1][THREAD_STACK_SIZE-1],
            print_thread_info,
            "info_thread",
            1,
            NULL);

    stat_tid = thread_create(&stat_thread,
            &thread_stk[2][THREAD_STACK_SIZE-1],
            print_statistics,
            "statistics_thread",
            1,
            NULL);

    hello_tid = thread_create(&hello_thread,
            &thread_stk[3][THREAD_STACK_SIZE-1],
            hello_world,
            "hello_thread",
            21,
            NULL);

    hello2_tid = thread_create(&hello2_thread,
            &thread_stk[4][THREAD_STACK_SIZE-1],
            hello_world,
            "hello2_thread",
            21,
            NULL);

    printf("##################################\n"
           "#       Start CuRT....           #\n"
           "##################################\n");

    start_curt();

    /* endless loop */
    while (1)
        ;

    /* Never reach here. */
    return 0;
}

static void shell_thread_func(void *pdata)
{
    init_os_timer();

    thread_suspend(info_tid);
    thread_suspend(stat_tid);
    thread_suspend(hello_tid);

    char buf[80] = { '\0' };
    while (1) {
        printf("$ ");
        gets(buf);
        printf("\n");
        if (!strcmp(buf, "")) {
            /* Do nothing */
        }
        else if (!strcmp(buf, "stat")) {
            thread_resume(stat_tid);
            thread_delay(5);
        }
        else if (!strcmp(buf, "ps")) {
            thread_resume(info_tid);
            thread_delay(5);
        }
        else if (!strcmp(buf, "help")) {
            print_help_msg();
        }
        else if (!strcmp(buf, "clear")) {
            /* FIXME: dirty trick to clean screen */
            printf("\n\n\n\n\n\n\n\n\n\n"
                   "\n\n\n\n\n\n\n\n\n\n"
                   "\n\n\n\n\n\n\n\n\n\n"
                   "\n\n\n\n\n\n\n\n\n\n"
                   "\n\n\n\n\n\n\n\n\n\n"
                   "\n\n\n\n\n\n\n\n\n\n"
                   "\n\n\n\n\n\n\n\n\n\n");
        }
        else if (!strcmp(buf, "hello")) {
            thread_resume(hello_tid);
            thread_resume(hello2_tid);
            thread_delay(5);
        }
        else {
            printf("shell: %s: command not found\n", buf);
            printf("try help\n");
        }
    }
}

static void print_statistics(void *pdata)
{
    while (1) {
        printf("*********** CuRT statistics info ***************\n");
        printf("Total Thread Count : %x\n", total_thread_cnt);
        printf("Total Context Switch Count : %x\n", total_csw_cnt);
        printf("Current Time Tick : %x\n", os_time_tick);
        printf("**************************************************\n");
        thread_suspend(current_thread->tid);
    }
}

static char *state_to_string[7] = {
    [RUNNING]     = "Running   ",
    [READY]     = "Ready     ",
    [DELAY]     = "Delay     ",
    [BLOCK]     = "Block     ",
    [TERMINATE]     = "Terminate ",
    [EVENT_WAIT]     = "Event wait"
};

static void print_thread_info(void *pdata)
{
    int i;
    thread_struct *pthread;

    while (1) {
        printf(" %s        %s        %s\n", "ID", "State", "Name");
        for (i = 0; i < MAX_THREAD; i++) {
            if (thread_table[i] != NULL) {
                pthread = thread_table[i];
                printf("%3x        %s   %s\n",
                       pthread->tid,
                       state_to_string[pthread->state],
                       pthread->name);
            }
        }
        thread_suspend(current_thread->tid);
    }
}

static inline void print_help_msg()
{
    printf("*********** CuRT Help Info ********************\n"
           "stat - display statistics.\n"
           "ps - display thread informations.\n"
           "clear - clean screen.\n"
           "hello - sample RT task say greeting.\n"
           "*************************************************\n");
}

static void hello_world(void *pdata)
{
    /* shared local variable, one instance among all threads using
     * this function, so a semaphore is needed to enforce mutual
     * exclusion.
     */
    static int count = 0;

    /* local variable, one instance per thread using this function */
    int s = 0, t;

    while (1) {
        /* enforce mutual exclusion for access to count */
        sem_pend(&sem);
        count++;
        sem_post(&sem);

        thread_delay(1);

        sem_pend(&sem);
        count++;
        t = count;
        sem_post(&sem);
        s++;

        /* because count was copied to t, no semaphore is needed here */
        printf("Hello World: count = %d, s = %d\n", t, s);

        thread_suspend(current_thread->tid);
    }
}

執行緒:kernel/thread.c

/*
 * thread.c
 */

#include "kernel/thread.h"
#include "kernel/kernel.h"
#include "kernel/list.h"
#include "kernel/types.h"
#include "port.h"

/**
 * @brief Create thread
 *
 * @param thread - thread of the information contained threads structure
 * @param thread_stk - Created a pointer to the thread stack space
 * @param func - Generated a thread of the function
 * @param name - Thread name
 * @param prio - The priority of threads
 * @param pdata - Pass parameters to the function of a thread running
 * @retval RET_NO_ERR
 * @retval RET_ERR
 */     
tid_t thread_create(thread_struct *thread,
            stk_t *thread_stk,
            THREAD_FUNC func,
            char *name,
            u8_t prio,
            void *pdata)
{
    cpu_sr_t cpu_sr;
    stk_t *pstk;
    thread_struct *pthread;
    tid_t tid;

    if (thread == NULL || thread_stk == NULL || func == NULL ||
        name == NULL || (prio >= MAX_PRIO))
        return RET_ERR;

    pstk = thread_stk;
    pthread = thread;

    /* no failback */
    if ((tid = get_tid()) == RET_ERR) {
        return RET_ERR;
    }

    /* constrct thread_struct */
    pthread->tid = tid;
    pthread->stack_ptr = init_thread_stack(func, pdata, pstk);
    pthread->name = name;
    pthread->prio = prio;
    pthread->time_quantum = TIME_QUANTUM;
    pthread->delayed_time = 0;
    pthread->state = READY;

    cpu_sr = save_cpu_sr();
    thread_table[tid] = pthread;
    prio_exist_flag[prio] = true;
    total_thread_cnt++;
    insert_back_list(&ready_list[prio], &pthread->node);
    restore_cpu_sr(cpu_sr);

    /* if priority higher than existing thread, invoke the scheduler. */
    if (is_start_os == true && current_thread->prio > prio) {
        schedule(SCHED_THREAD_REQUEST);
    }
    return tid;
}

err_t thread_delete(tid_t tid)
{
    cpu_sr_t cpu_sr;

    if (tid < IDLE_THREAD_TID || tid > MAX_THREAD)
        return RET_ERR;

    if (thread_table[tid] == NULL)
        return RET_ERR;

    cpu_sr = save_cpu_sr();
    if (tid == current_thread->tid) {
        current_thread->state = TERMINATE;
        insert_back_list(&termination_wait_list, &current_thread->node); 
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return RET_NO_ERR;
    }
    return RET_ERR;
}

void thread_yield()
{
    schedule(SCHED_THREAD_REQUEST);
}

err_t thread_delay(u32_t tick)
{
    cpu_sr_t cpu_sr;

    if (tick > 0) { /* Delay must be greater than 0. */
        cpu_sr = save_cpu_sr();
        current_thread->state = DELAY;
        current_thread->delayed_time = tick;
        insert_back_list(&delayed_list, &current_thread->node);
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return RET_NO_ERR;
    }
    return RET_ERR;
}

err_t thread_suspend(tid_t tid)
{
    cpu_sr_t cpu_sr;
    thread_struct *pthread;

    if (tid < IDLE_THREAD_TID || tid > MAX_THREAD)
        return RET_ERR;

    if (thread_table[tid] == NULL)
        return RET_ERR;

    cpu_sr = save_cpu_sr();
    pthread = thread_table[tid];
    if (current_thread->tid == tid) {
        pthread->state = BLOCK;
        insert_back_list(&blocked_list, &pthread->node);
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return RET_NO_ERR;
    }
    else if (pthread->prio > current_thread->prio &&
             pthread->state == READY) {
        pthread->state = BLOCK;
        delete_list(&pthread->node);
        insert_back_list(&blocked_list, &pthread->node);
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return RET_NO_ERR;
    }
    restore_cpu_sr(cpu_sr);
    return RET_ERR;
}

err_t thread_resume(tid_t tid)
{
    cpu_sr_t cpu_sr;
    thread_struct *pthread;

    if (tid < IDLE_THREAD_TID || tid > MAX_THREAD)
        return RET_ERR;

    if (thread_table[tid] == NULL)
        return RET_ERR;

    cpu_sr = save_cpu_sr();
    pthread = thread_table[tid];
    if (pthread->state == BLOCK) {
        pthread->state = READY;
        delete_list(&pthread->node);
        insert_back_list(&ready_list[pthread->prio], &pthread->node);
        prio_exist_flag[pthread->prio] = true;
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return RET_NO_ERR;
    }
    restore_cpu_sr(cpu_sr);
    return RET_ERR;
}

系統核心:kernel/kernel.c

/*
 * kernel.c
 */

#include "lib/stdio.h"
#include "port.h"
#include "kernel/kernel.h"
#include "kernel/list.h"
#include "kernel/thread.h"

/* the priority of system-wide idle thread */
#define IDLE_THREAD_PRIO 31   

/* list of priority for ready threads */
list_t ready_list[MAX_PRIO];  

/* The waiting list is being requested by the end of the executing threads.
 * It can not be completely removed by the idle thread. */
list_t termination_wait_list; 

/* list for threads requesting delay, whose status is soon to change. */
list_t delayed_list;          

/* Requesting blocked by a thread.  Once thread_resume, status returns back. */
list_t blocked_list;

/* Pointers to the currently executing thread */
thread_struct *current_thread;

/* Pointer to the executive running the context during transition. */
thread_struct *next_thread;    

/* OS wide timer incremented by tick. */
int os_time_tick;    

int current_top_prio;
bool prio_exist_flag[MAX_PRIO];
bool is_start_os;

/* Nested interrupts */
int interrupt_nesting;

/* context switching */
int total_csw_cnt;
int total_thread_cnt;

thread_struct *thread_table[MAX_THREAD];

/* idle thread */
thread_struct idle_thread;
stk_t idle_thread_stk[THREAD_STACK_SIZE];

void start_curt()
{
    cpu_sr_t cpu_sr;
    list_node_t *pnode;
    int top_prio;

    cpu_sr = save_cpu_sr();
    is_start_os = true;
    /* examine the highest priority thread executed */
    top_prio = get_top_prio();
    pnode = delete_front_list(&ready_list[top_prio]);
    if (is_empty_list(&ready_list[top_prio]))
        prio_exist_flag[top_prio] = false;
    current_thread = entry_list(pnode, thread_struct, node);
    current_thread->state = RUNNING;
    restore_cpu_sr(cpu_sr);
    restore_context();
}

tid_t get_tid()
{
    int i;
    for (i = 0; i < MAX_THREAD; i++) {
        if (thread_table[i] == NULL)
            return i;
    }
    return RET_ERR;
}

u8_t get_top_prio()
{
    int i;
    for( i = 0; i < MAX_PRIO; i++ ) {
        if( prio_exist_flag[i] == true )
            return i;
    }
    return MAX_PRIO;
}

void init_curt()
{
    int i;
    for (i = 0; i < MAX_PRIO; i++) {
        prio_exist_flag[i] = false;
        init_list(&ready_list[i]);
    }
    init_list(&delayed_list);
    init_list(&blocked_list);
    init_list(&termination_wait_list);

    for (i = 0; i < MAX_THREAD; i++)
        thread_table[i] = NULL;

    is_start_os = false;
    interrupt_nesting = 0;
    os_time_tick = 0;
    current_top_prio = MAX_PRIO;
    total_csw_cnt = 0;
    total_thread_cnt = 0;
    current_thread = NULL;
    next_thread = NULL;

    /* create idle thread internally */
    thread_create(&idle_thread,
            &idle_thread_stk[THREAD_STACK_SIZE-1],
            idle_thread_func,
            "idle-thread",
            IDLE_THREAD_PRIO,
            NULL);
}

/**
 * @brief Invoke the scheduler
 * The scheduler is called in the following two cases:
 *   - thread's time_quantum value is 0 (SCHED_TIME_EXPIRE) and then stop.
 *   - Request the execution for higher priority thread (SCHED_THREAD_REQUEST).
 */
void schedule(SCHED_TYPE sched_type)
{
    int top_prio;
    cpu_sr_t cpu_sr;
    list_node_t *pnode;

    cpu_sr = save_cpu_sr();
    top_prio = get_top_prio();
    /* If the timer expires... */
    if (sched_type == SCHED_TIME_EXPIRE) {
        /* Threads that are currently running will continue to run it
         * at the highest priority */
        if (current_thread->prio < top_prio) {
            current_thread->time_quantum = TIME_QUANTUM;
            restore_cpu_sr(cpu_sr);
            return;
        }
        /* otherwise, threads in a ready state, then run the highest
         * priority one. */
        pnode = delete_front_list(&ready_list[top_prio]);
        if (is_empty_list(&ready_list[top_prio]))
            prio_exist_flag[top_prio] = false;
        next_thread = entry_list(pnode, thread_struct, node);
        next_thread->state = RUNNING;
        next_thread->time_quantum = TIME_QUANTUM;

        /* Ready to change the status of the currently executing thread */
        current_thread->state = READY;
        insert_back_list(&ready_list[current_thread->prio],
                         &current_thread->node);
        prio_exist_flag[current_thread->prio] = true;
        total_csw_cnt++;
        /* actual context switching */
        context_switch_in_interrupt();
    }
    /* requested by a thread */
    else if (sched_type == SCHED_THREAD_REQUEST ) {
        /* Threads that are currently running will continue to run it
         * at the highest priority. */
        if (current_thread->state == RUNNING &&
            current_thread->prio < top_prio ) {
            current_thread->time_quantum = TIME_QUANTUM;
            restore_cpu_sr(cpu_sr);
            return;
        }
        /* otherwise, threads in a ready state, then run the highest
         * priority one. */
        pnode = delete_front_list(&ready_list[top_prio]);
        if (is_empty_list(&ready_list[top_prio]))
            prio_exist_flag[top_prio] = false;
        next_thread = entry_list(pnode, thread_struct, node);
        next_thread->state = RUNNING;
        next_thread->time_quantum = TIME_QUANTUM;

        /* Ready to change the status of the currently executing thread */
        if (current_thread->state == RUNNING) {
            current_thread->state = READY;
            insert_back_list(&ready_list[current_thread->prio],
                             &current_thread->node);
            prio_exist_flag[current_thread->prio] = true;
        }
        total_csw_cnt++;
        /* context switching */
        context_switch();
    }
    restore_cpu_sr(cpu_sr);
}

/**
 * @brief time tick advanced
 * Maintain time_quantum
 */
void advance_time_tick()
{
    cpu_sr_t cpu_sr;
    list_node_t *pnode;
    thread_struct *pthread;
    thread_struct *readyed_thread = NULL;

    cpu_sr = save_cpu_sr();
    os_time_tick++;
    /* If there are delays in the list of threads... */
    if (!is_empty_list(&delayed_list)) {
        for (pnode = begin_list(&delayed_list);
             pnode != end_list(&delayed_list);
             pnode = next_list(pnode) ) {
            pthread = entry_list(pnode, thread_struct, node);
            pthread->delayed_time--;
            /* ready to change the status */
            if (readyed_thread != NULL) {
                delete_list(&readyed_thread->node);
                readyed_thread->state = READY;
                readyed_thread->time_quantum = TIME_QUANTUM;
                insert_back_list(
                    &ready_list[readyed_thread->prio],
                    &readyed_thread->node);
                prio_exist_flag[readyed_thread->prio] = true;
                readyed_thread = NULL;
            }
            if (pthread->delayed_time <= 0) {
                readyed_thread = pthread;
            }
        }
        if (readyed_thread != NULL) {
            delete_list(&readyed_thread->node);
            readyed_thread->state = READY;
            readyed_thread->time_quantum = TIME_QUANTUM;
            insert_back_list(
                &ready_list[readyed_thread->prio],
                &readyed_thread->node);
            prio_exist_flag[readyed_thread->prio] = true;
        }
    }
    current_thread->time_quantum--;
    restore_cpu_sr(cpu_sr);
}

/**
 * UNIMPLEMNTED
 */
void enter_interrupt()
{
}

void exit_interrupt()
{
    if (interrupt_nesting > 0) 
        interrupt_nesting--;

    if (current_thread->time_quantum <= 0) {
        schedule(SCHED_TIME_EXPIRE);
    }
}

/**
 * @brief rountine for idle thread
 * @param data
 */
void idle_thread_func(void *data)
{
    cpu_sr_t cpu_sr;
    thread_struct *pthread;
    list_node_t *pnode;

    while (1) {
        cpu_sr = save_cpu_sr();
        if (!is_empty_list(&termination_wait_list)) {
            pnode = delete_front_list(&termination_wait_list);
            pthread = entry_list(pnode, thread_struct, node);
            thread_table[pthread->tid] = NULL;
            total_thread_cnt--;
        }
        restore_cpu_sr(cpu_sr);
        /* always does this */
        thread_yield();
    }
}

序列裝置 (輸出入):kernel/serial.c

/*
 * serial.c
 */

#include "arch/arm/mach-pxa/pxa255.h"
#include "kernel/types.h"

/* --- PXA255 specific implementation --- */

void SerialOutputByte(const char);
int SerialInputByte(char *);

void SerialInit(void)
{
    /* GP39, GP40, GP41 UART(10) */
        GAFR1_L |= 0x000A8000;
        GPDR1 |= 0x00000380;

        /* 8-bit, 1 stop, no parity */
    rFFLCR = 0x00000003;

    /* Reset tx, rx FIFO. clear. FIFO enable */
    rFFFCR = 0x00000007;

    /* UART Enable Interrupt */
    rFFIER = 0x00000040;

    /* DLAB set=latch registers, DLAB clear= . */
        rFFLCR |= 0x00000080;

    /* baud rate */
        rFFDLL = SERIAL_BAUD_115200;

    /* DLAB clear */
    rFFLCR &= 0xFFFFFF7F;

        /* Transmit Shift Register, Transmit Holding Register, FIFO
     * wait for ready */
    while ((!rFFLSR) & 0x00000040 )
        /* wait */ ;

    return;
}

void SerialOutputByte(const char c)
{
    /* FIFO
     * wait for ready */
    while ((rFFLSR & 0x00000020) == 0 )
        /* wait */ ;

    rFFTHR = ((ulong)c & 0xFF);

    /* regardless of c=='\n' or "\n\r", the same output. */
    if (c=='\n')
        SerialOutputByte('\r');
}

int SerialInputByte(char *c)
{

    /* FIFO */
    if ((rFFLSR & 0x00000001) == 0) {
        return 0;
    }
    else {
        *(volatile char *) c = (char) rFFRBR;
        return 1;
    }
}

/**
 * @brief check if serial is ready
 * @retval 1 if the data received
 */
int SerialIsReadyChar(void)
{
    /* Make sure the data is received. */
    if (rFFLSR & 0x00000001)
               return 1;
    return 0;
}

/**
 * @brief Receives a character from serial device
 * @retval
 */
char SerialIsGetChar(void)
{
    /* received data */
    return (char) rFFRBR;
}

行程通訊:kernel/ipc.c

/*
 * ipc.c
 */

#include "kernel/ipc.h"
#include "kernel/list.h"
#include "kernel/thread.h"
#include "kernel/kernel.h"
#include "port.h"

/**
 * @param mbox - message box structure
 * @param pmsg - 
 * @retval RET_NO_ERR
 * @retval RET_ERR
 */
err_t mbox_init(mbox_struct *mbox, void *pmsg)
{
    /* Validation */
    if (mbox == NULL)
        return RET_NO_ERR;

    /* initialization */
    mbox->msg = pmsg;
    init_list(&mbox->wait_list);
    return RET_NO_ERR;
}

/**
 * obtain a message if something waiting in wait_list.
 * @param mbox
 */
void *mbox_recv(mbox_struct *mbox)
{
    cpu_sr_t cpu_sr;
    list_node_t *pnode;
    thread_struct *pthread;

    cpu_sr = save_cpu_sr();
    void *pmsg = mbox->msg;
    /* incoming message in mbox */
    if (pmsg != (void *) 0) {
        mbox->msg = (void *) 0;
        restore_cpu_sr(cpu_sr);
        return pmsg;
    }
    /* if there is no message, then wait. */
    if (!is_empty_list(&mbox->wait_list)) {
        for (pnode = begin_list(&mbox->wait_list);
             pnode != end_list(&mbox->wait_list);
             pnode = next_list(pnode)) {
            pthread = entry_list(pnode, thread_struct, node);
            if (current_thread->prio < pthread->prio) {
                current_thread->state = EVENT_WAIT;
                insert_before_list(pnode, &current_thread->node);
                break;
            }
        }
    }
    if (current_thread->state != EVENT_WAIT) {
        current_thread->state = EVENT_WAIT;
        insert_back_list(&mbox->wait_list, &current_thread->node);
    }
    restore_cpu_sr(cpu_sr);
    schedule(SCHED_THREAD_REQUEST);

    cpu_sr = save_cpu_sr();
    pmsg = current_thread->msg;
    restore_cpu_sr();
    return pmsg;
}

/**
 * @brief Attempt to receive messages.
 * @retval NULL
 * @retval !NULL
 */
void *mbox_try_recv(mbox_struct *mbox)
{
    cpu_sr_t cpu_sr;
    void *pmsg;

    cpu_sr = save_cpu_sr();
    pmsg = mbox->msg;
    mbox->msg = (void *) 0;
    restore_cpu_sr(cpu_sr);
    return pmsg;
}

/**
 * @param mbox
 * @retval RET_NO_ERR
 * @retval RET_ERR
 */
err_t mbox_send(mbox_struct *mbox, void *pmsg)
{
    cpu_sr_t cpu_sr;
    thread_struct *pthread;

    cpu_sr = save_cpu_sr();
    /* If threads are waiting... */
    if (!is_empty_list(&mbox->wait_list)) {
        /* find out Threads of the highest priority */
        pthread = entry_list(delete_front_list(&mbox->wait_list),
                             thread_struct, node);
        pthread->msg = pmsg;
        pthread->state = READY;
        insert_back_list(&ready_list[pthread->prio], &pthread->node);
        prio_exist_flag[pthread->prio] = true;
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return RET_NO_ERR;
    }

    /* If there is no pending threads / there is no message to mbox */
    if (mbox->msg == (void *) 0) {
        mbox->msg = pmsg;
        restore_cpu_sr(cpu_sr);
        return RET_NO_ERR;
    }

    /* already have a message */
    restore_cpu_sr(cpu_sr);
    return RET_ERR;
}

/**
 * @brief Initialize the message queue
 * @retval RET_NO_ERR
 * @retval RET_ERR
 */
err_t msgq_init(msgq_struct *msgq)
{
    if (msgq == NULL)
        return RET_ERR;

    init_list(&msgq->msg_list);
    init_list(&msgq->wait_list);
    return RET_NO_ERR;
}

/**
 * @brief receive from message queue
 * @param msgq
 * @retval
 */
void *msgq_recv(msgq_struct *msgq)
{
    cpu_sr_t cpu_sr;
    list_node_t *pnode;
    thread_struct *pthread;
    msg_struct *spmsg;
    void *pmsg;

    cpu_sr = save_cpu_sr();
    if (!is_empty_list(&msgq->msg_list)) {
        pnode = delete_front_list(&msgq->msg_list);
        spmsg = entry_list(pnode, msg_struct, node);
        restore_cpu_sr(cpu_sr);
        return spmsg->msg;
    }
    if (!is_empty_list(&msgq->wait_list)) {
        for (pnode = begin_list(&msgq->wait_list);
             pnode != end_list(&msgq->wait_list);
             pnode = next_list(pnode) ) {
            pthread = entry_list(pnode, thread_struct, node);
            if (current_thread->prio < pthread->prio) {
                current_thread->state = EVENT_WAIT;
                insert_before_list(pnode, &current_thread->node);
                break;
            }
        }
    }
    if (current_thread->state != EVENT_WAIT) {
        current_thread->state = EVENT_WAIT;
        insert_back_list(&msgq->wait_list, &current_thread->node);
    }
    restore_cpu_sr(cpu_sr);
    schedule(SCHED_THREAD_REQUEST);
    cpu_sr = save_cpu_sr();
    pmsg = current_thread->msg;
    restore_cpu_sr();
    return pmsg;
}

/**
 * @brief attempt to receive from message queue
 * @param msgq
 * @retval NULL
 * @retval !NULL
 */
void *msgq_try_recv(msgq_struct *msgq)
{
    cpu_sr_t cpu_sr;
    list_node_t *pnode;
    msg_struct *spmsg;

    cpu_sr = save_cpu_sr();
    if (!is_empty_list(&msgq->msg_list)) {
        pnode = delete_front_list(&msgq->msg_list);
        spmsg = entry_list(pnode, msg_struct, node);
        restore_cpu_sr(cpu_sr);
        return spmsg->msg;
    }
    return NULL;
}

/**
 * @brief send message to message queue
 * @param msgq
 * @param spmsg
 * @retval RET_NO_ERR
 * @retval RET_ERR
 */
err_t msgq_send(msgq_struct *msgq, msg_struct *spmsg)
{
    cpu_sr_t cpu_sr;
    thread_struct *pthread;

    cpu_sr = save_cpu_sr();
    if (!is_empty_list(&msgq->wait_list)) {
        pthread = entry_list(delete_front_list(&msgq->wait_list),
                             thread_struct, node);
        pthread->msg = spmsg->msg;
        pthread->state = READY;
        insert_back_list(&ready_list[pthread->prio], &pthread->node);
        prio_exist_flag[pthread->prio] = true;
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return 0;
    }
    insert_back_list(&msgq->msg_list, &spmsg->node);
    restore_cpu_sr(cpu_sr);
    return 0;
}

行程同步:kernel/sync.c

/*
 * sync.c
 */

#include "kernel/sync.h"
#include "kernel/kernel.h"
#include "kernel/thread.h"
#include "port.h"

err_t sem_init(sem_struct *sem, u8_t value)
{
    if (sem == NULL)
        return RET_ERR;

    sem->value = value;
    init_list(&sem->wait_list);
    return RET_NO_ERR;
}

void sem_pend(sem_struct *sem)
{
    cpu_sr_t cpu_sr;
    list_node_t *pnode;
    thread_struct *pthread;

    cpu_sr = save_cpu_sr();
    if (sem->value == 0) {
        if (!is_empty_list(&sem->wait_list)) {
            for (pnode = begin_list(&sem->wait_list);
                 pnode != end_list(&sem->wait_list); 
                 pnode = next_list(pnode)) {
                pthread = entry_list(pnode, thread_struct, node);
                if (current_thread->prio < pthread->prio) {
                    current_thread->state = EVENT_WAIT;
                    insert_before_list(
                        pnode,
                        &current_thread->node);
                    break;
                }
            }
        }
        if (current_thread->state != EVENT_WAIT) {
            current_thread->state = EVENT_WAIT;
            insert_back_list(&sem->wait_list, &current_thread->node);
        }
        schedule(SCHED_THREAD_REQUEST);
        return;
    }
    sem->value--;
    restore_cpu_sr(cpu_sr);
}

err_t sem_try_pend(sem_struct *sem)
{
    cpu_sr_t cpu_sr;

    cpu_sr = save_cpu_sr();
    if (sem->value > 0) {
        sem->value--;
        restore_cpu_sr(cpu_sr);
        return RET_NO_ERR;
    }
    else {
        restore_cpu_sr(cpu_sr);
        return RET_ERR;
    }
}

void sem_post(sem_struct *sem)
{
    thread_struct *pthread;
    cpu_sr_t cpu_sr;

    cpu_sr = save_cpu_sr();
    if (!is_empty_list(&sem->wait_list)) {
        pthread = entry_list(delete_front_list(&sem->wait_list),
                             thread_struct, node);
        pthread->state = READY;
        insert_back_list(&ready_list[pthread->prio], &pthread->node);
        prio_exist_flag[pthread->prio] = true;
        restore_cpu_sr(cpu_sr);
        schedule(SCHED_THREAD_REQUEST);
        return;
    }
    sem->value++;
    restore_cpu_sr(cpu_sr);
}

鏈節串列:kernel/list.c

#include "kernel/list.h"

bool is_head(list_node_t *pnode)
{
    return (pnode != NULL) &&
           (pnode->prev) == NULL &&
           (pnode->next != NULL);
}

bool is_interio(list_node_t *pnode)
{
    return (pnode != NULL) &&
           (pnode->prev != NULL) &&
           (pnode->next != NULL);
}

bool is_tail(list_node_t *pnode)
{
    return (pnode != NULL) &&
           (pnode->prev != NULL) &&
           (pnode->next == NULL);
}

void init_list(list_t *plist)
{
    plist->head.prev = NULL;
    plist->head.next = &plist->tail;
    plist->tail.prev = &plist->head;
    plist->tail.next = NULL;
}

list_node_t *begin_list(list_t *plist)
{
    return plist->head.next;
}

list_node_t *next_list(list_node_t *pnode)
{
    return pnode->next;
}

list_node_t *end_list(list_t *plist)
{
    return &plist->tail;
}

list_node_t *head_list(list_t *plist)
{
    return &plist->head;
}

list_node_t *tail_list(list_t *plist)
{
    return &plist->tail;
}

list_node_t *front_list(list_t *plist)
{
    return plist->head.next;
}

list_node_t *back_list(list_t *plist)
{
    return plist->tail.prev;
}

bool is_empty_list(list_t *plist)
{
    return (begin_list(plist) == end_list(plist));
}

void insert_after_list(list_node_t *before, list_node_t *pnode)
{
    pnode->prev = before->prev;
    pnode->next = before;
    before->prev->next = pnode;
    before->prev = pnode;
}

void insert_before_list(list_node_t *after, list_node_t *pnode)
{
    pnode->prev = after;
    pnode->next = after->next;
    after->next->prev = pnode;
    after->next = pnode;
}

void insert_front_list(list_t *plist, list_node_t *pnode)
{
    insert_after_list(begin_list(plist), pnode);
}

void insert_back_list(list_t *plist, list_node_t *pnode)
{
    insert_after_list(end_list(plist), pnode);
}

list_node_t *delete_list(list_node_t *pnode)
{
    pnode->prev->next = pnode->next;
    pnode->next->prev = pnode->prev;
    return pnode->next;
}

list_node_t *delete_front_list(list_t *plist)
{
    list_node_t *front = front_list(plist);
    delete_list(front);
    return front;
}

list_node_t *delete_back_list(list_t *plist)
{
    list_node_t *back = back_list(plist);
    delete_list(back);
    return back;
}

與 ARM 相關的程式:arch/arm/mach-pxa/port.c

/*
 * port.c
 */

#include "port.h"
#include "lib/stdio.h"
#include "kernel/list.h"
#include "kernel/thread.h"
#include "kernel/kernel.h"

/**
 * @brief Initialize the thread stack.
 *
 * Initialize the thread stack value with the given thread function, and
 * then save registers.
 * @param func
 * @param pdata
 * @param stk_top_ptr
 * @retval
 */
stk_t *init_thread_stack(THREAD_FUNC func, 
                         void *pdata,
                         stk_t *stk_top_ptr)
{
    stk_t *pstk;

    pstk = stk_top_ptr;
    *(pstk) = (stk_t) func;            /* r15 (pc) thread address */
    *(--pstk) = (stk_t) 0x14141414L;    /* r14 (lr) */
    *(--pstk) = (stk_t) 0x12121212L;    /* r12 */
    *(--pstk) = (stk_t) 0x11111111L;    /* r11 */
    *(--pstk) = (stk_t) 0x10101010L;    /* r10 */
    *(--pstk) = (stk_t) 0x09090909L;    /* r9 */
    *(--pstk) = (stk_t) 0x08080808L;    /* r8 */
    *(--pstk) = (stk_t) 0x07070707L;    /* r7 */
    *(--pstk) = (stk_t) 0x06060606L;    /* r6 */
    *(--pstk) = (stk_t) 0x05050505L;    /* r5 */
    *(--pstk) = (stk_t) 0x04040404L;    /* r4 */
    *(--pstk) = (stk_t) 0x03030303L;    /* r3 */
    *(--pstk) = (stk_t) 0x02020202L;    /* r2 */
    *(--pstk) = (stk_t) 0x01010101L;    /* r1 */
    *(--pstk) = (stk_t) pdata;        /* r0 */
    *(--pstk) = (stk_t) 0x13;        /* cpsr : sys_mode */
    return pstk;
}

/**
 * @brief Initialize CuRT Timer
 *
 * Once OS Timer is initialized, the timer is about to work.
 * Note: the multi-tasking environment (setup by start_curt) will be invoked
 * after this routine.
 */
void init_os_timer()
{
    INT_REG(INT_ICLR) &= ~BIT26;
    TMR_REG(TMR_OSMR0) = PXA255_TMR_CLK / OS_TICKS_PER_SEC;
    TMR_REG(TMR_OSMR1) = 0x3FFFFFFF;
    TMR_REG(TMR_OSMR2) = 0x7FFFFFFF;
    TMR_REG(TMR_OSMR3) = 0xBFFFFFFF;
    TMR_REG(TMR_OSCR) = 0x00;
    TMR_REG(TMR_OSSR) = BIT0;
    TMR_REG(TMR_OIER) = BIT0;
    INT_REG(INT_ICMR) |= BIT26;
}

/**
 * @brief Interrupt handler
 *
 * This function is invoked in the IRQ service routine.
 * source: when calling advance_time_tick, ticks advance.
 */
void interrupt_handler()
{
    if (INT_REG(INT_ICIP) & BIT26) {
        TMR_REG(TMR_OSCR) = 0x00;
        advance_time_tick();
        TMR_REG(TMR_OSSR) = BIT0;
    }
}

/**
 * @brief Initialize CPU interrupt control
 *
 * Enable the issue of interrupts.
 */
void init_interrupt_control()
{
    INT_REG(INT_ICMR) = 0;
}

標準輸出入:lib/stdio.c

/*
 * stdio.c
 */

#include "lib/stdio.h"

extern void SerialOutputByte(const char);
extern int SerialIsReadyChar( void );
extern char SerialIsGetChar( void );

/* internal helper functions */
static void _PrintChar(char *fmt, char c);
static void _PrintDec(char *fmt, int value);
static void _PrintHex(char *fmt, int value);
static void _PrintString(char *fmt, char *cptr);

void *memcpy(void *dest, const void *src, int cnt)
{
    char *s1 = dest;
    const char *s2 = src;
    char *endptr = (char *) dest + cnt;

    while (s1 < endptr)
        *s1++ = *s2++;
    return dest;
}

void *memset(void *dest, int c, int len)
{
    char *s = dest;
    char *limit = (char *) dest + len;

    while (s < limit)
        *s++ = c;
    return dest;
}

int strcmp(char *s1, char *s2)
{
    while (*s1 != '\0' && *s1 == *s2) {
        s1++;
        s2++;
    }
    return (*(unsigned char *) s1) - (*(unsigned char *) s2);
}

static bool DecToLong(char *s, long *retval)
{
    long remainder;
    if (!s || !s[0])
        return false;

    for (*retval = 0; *s; s++) {
        if (*s < '0' || *s > '9')
            return false;
        remainder = *s - '0';
        *retval = *retval * 10 + remainder;
    }

    return true;
}

/**
 * @brief printf() family produce output according to a format
 * The simple implementation in CuRT supports the following format:
 *   "%s", "%c", "%d", and "%x"
 * and the correspoding variants: "%08x", "%8x".
 */
int printf(const char *fmt, ...)
{
    int i;
    va_list args;
    const char *s = fmt;
    char format[10];

    va_start(args, fmt);
    while (*s) {
        if (*s == '%') {
            s++;
            /* s in "% 08lx" format to get the record format. */
            format[0] = '%';
            for (i = 1; i < 10; ) {
                if (*s=='c' || *s=='d' || *s=='x' ||
                    *s=='s' || *s=='%') {
                    format[i++] = *s;
                    format[i] = '\0';
                    break;
                }
                else {
                    format[i++] = *s++;
                }
            }
            /* "% s", "% c", "% d", "% x" to find the print
             * function calls */
            switch (*s++) {
                case 'c' :
                    _PrintChar(format, va_arg(args, int));
                    break;
                case 'd' :
                    _PrintDec(format, va_arg(args, int));
                    break;
                case 'x' :
                    _PrintHex(format, va_arg(args, int));
                    break;
                case 's' :
                    _PrintString(format, va_arg(args, char *));
                    break;
                case '%' :
                    _PrintChar("%c", '%');
                    break;
                default:
                    /* it shall not happend. */
                    break;
            }
        }
        else {
            _PrintChar("%c", *s);
            s++;
        }
    }
    va_end(args);

    /* FIXME: Upon successful return, printf function shall return the
     * number of characters printed.
     */
    return 1;
}

static void _PrintChar(char *fmt, char c)
{
    SerialOutputByte(c);
    return;
}

static void _PrintDec(char *fmt, int l)
{
    char tol[10];
    int remainder;
    int count = 0;
    if (l == 0) {
        SerialOutputByte('0');
        return;
    }
    if (l < 0) {
        SerialOutputByte('-');
        l = -l;
    }
    /* FIXME: we don't intend to handle the number bigger than 100000. */
    if (l > 100000)
        return;

    for ( ; count < 10; count++)
        tol[count] = '\0';
    count = 7;
    do {
        remainder = l % 10;
        tol[count--] = '0' + remainder;
        l = (l - remainder) / 10;
    } while (count > 0);
    tol[0] = ' ';

    _PrintString(fmt, tol);
}

static void _PrintHex(char *fmt, int l)
{
    int i, j;
    char c, *s = fmt, tol[10];
    bool flag0 = false, flagl = false;
    long flagcnt = 0;
    bool leading_zero = true;
    char uHex, lHex;
    int cnt;    /* note: the format like "% 5x" only prints
               the number of 5. */

    /* format like "%081" is interpreted for '0', '8', 'l'
     * individually. */
    for (i = 0; (c = s[i]) != 0; i++) {
        if (c == 'x')
            break;
        else if (c >= '1' && c <= '9') {
            for (j = 0; s[i] >= '0' && s[i] <= '9'; j++) {
                tol[j] = s[i++];
            }
            tol[j] = '\0';
            i--;
            DecToLong(tol, &flagcnt);
        }
        else if (c == '0')
            flag0 = true;
        else if (c == 'l')
            flagl = true;
        else
            continue;
    }

    s = (char *)(&l);
    l = SWAP32(l);    /* depends on the endianess. */

    /* output, based on the flag */
    if (flagcnt) {
        if (flagcnt & 0x01) {    /* upper ignored, lower the output. */
            c = s[(8 - (flagcnt + 1)) / 2];

            /* check if lower 4 bits becomes ASCII code. */
            lHex = ((c >> 0) & 0x0f);
            if (lHex != 0)
                leading_zero = false;
            if (lHex < 10)
                lHex += '0';
            else         lHex += 'A' - 10;

            /* lower 4 bits */
            if (leading_zero) {
                if (flag0)
                    SerialOutputByte('0');
                else
                    SerialOutputByte(' ');
            }
            else SerialOutputByte(lHex);

            flagcnt--;
        }

        /* byte-level data, the output Hex */
        for (cnt = 0, i = (8 - flagcnt) / 2; i < 4; i++) {
            c = s[i];

            /* get upper 4 bits and lower 4 bits. */
            uHex = ((c >> 4) & 0x0f);
            lHex = ((c >> 0) & 0x0f);

            /* upper 4 bits and lower 4 bits to '0'~'9', 'A'~'F'.
               upper 4 bits: ascii code */
            if (uHex != 0)
                leading_zero = false;
            if (uHex < 10)
                uHex += '0';
            else
                uHex += 'A' - 10;

            /* upper 4 bits */
            if (leading_zero) {
                if (flag0)
                    SerialOutputByte('0');
                else
                    SerialOutputByte(' ');
            }
            else SerialOutputByte(uHex);

            /* lower 4 bits: ascii code */
            if (lHex != 0)
                leading_zero = false;
            if (lHex < 10)
                lHex += '0';
            else
                lHex += 'A' - 10;

            /* lower 4 bits */
            if (leading_zero) {
                if (flag0)
                    SerialOutputByte('0');
                else
                    SerialOutputByte(' ');
            }
            else
                SerialOutputByte(lHex);
        }
    }
    else {
        for (i = 0; i < 4; i++){
            c = s[i];

            /* get upper 4 bits and lower 4 bits. */
            uHex = ((c >> 4) & 0x0f);
            lHex = ((c >> 0) & 0x0f);

            /* upper 4 bits and lower 4 bits to '0'~'9', 'A'~'F'. */
            if (uHex != 0)
                leading_zero = false;
            if (uHex < 10)
                uHex += '0';
            else
                uHex += 'A' - 10;
            if (!leading_zero)
                SerialOutputByte(uHex);

            if (lHex != 0 || i == 3)
                leading_zero = false;
            if (lHex < 10)
                lHex += '0';
            else
                lHex += 'A' - 10;
            if (!leading_zero)
                SerialOutputByte(lHex);
        }
    }
    return;
}

static void _PrintString(char *fmt, char *s)
{
    if (!fmt || !s)
        return;
    while (*s)
        SerialOutputByte(*s++);
    return;
}

int getc(void)
{               
    while (!SerialIsReadyChar())
        /* Keep waiting */ ;
    return SerialIsGetChar() & 0xFF;
}

int gets(char *s)
{
    int cnt = 0;
    char  c;

    while ((c = getc()) != CR) {
        if (c != BS) {
            cnt++;
            *s++ = c;
            printf("%c",c );
        }
        else {
            if (cnt > 0) {
                cnt--;
                *s-- = ' ';
                printf("\b \b");
            }
        }
    }
    *s = 0;
    return cnt;
}

int putchar(int c)
{
    SerialOutputByte(c);
    return c;
}

int puts(const char *s)
{
    while (*s)
        SerialOutputByte(*s++);
    return 1;
}

static char * ___strtok;
char * strtok(char * s,const char * ct)
{
    char *sbegin, *send;

    sbegin  = s ? s : ___strtok;
    if (!sbegin) {
        return NULL;
    }
    sbegin += strspn(sbegin, ct);
    if (*sbegin == '\0') {
        ___strtok = NULL;
        return( NULL );
    }
    send = strpbrk(sbegin, ct);
    if (send && *send != '\0')
        *send++ = '\0';
    ___strtok = send;
    return (sbegin);
}

unsigned int strspn(const char *s, const char *accept)
{
    const char *p;
    const char *a;
    unsigned int count = 0;

    for (p = s; *p != '\0'; ++p) {
        for (a = accept; *a != '\0'; ++a) {
            if (*p == *a)
                break;
        }
        if (*a == '\0')
            return count;
        ++count;
    }
    return count;
}

char * strpbrk(const char * cs,const char * ct)
{
    const char *sc1, *sc2;

    for (sc1 = cs; *sc1 != '\0'; ++sc1) {
        for (sc2 = ct; *sc2 != '\0'; ++sc2) {
            if (*sc1 == *sc2)
                return (char *) sc1;
        }
    }
    return NULL;
}

unsigned long strtoul(const char *str, char **endptr, int requestedbase)
{
    unsigned long num = 0;
    char c;
    unsigned char digit;
    int base = 10;
    int nchars = 0;
    int leadingZero = 0;
    unsigned char strtoul_err = 0;

    while ((c = *str) != 0) {
        if (nchars == 0 && c == '0') {
            leadingZero = 1;
            goto step;
        }
        else if (leadingZero && nchars == 1) {
            if (c == 'x') {
                base = 16;
                goto step;
            }
            else if (c == 'o') {
                base = 8;
                goto step;
            }
        }
        if (c >= '0' && c <= '9') {
            digit = c - '0';
        }
        else if (c >= 'a' && c <= 'z') {
            digit = c - 'a' + 10;
        }
        else if (c >= 'A' && c <= 'Z') {
            digit = c - 'A' + 10;
        }
        else {
            strtoul_err = 3;
            return 0;
        }
        if (digit >= base) {
            strtoul_err = 4;
            return 0;
        }
        num *= base;
        num += digit;
step:
        str++;
        nchars++;
    }
    return num;
}

ARM 相關的組合語言:arch/arm/mach-pxa/asm_port.S

/*
 * asm_port.S
 */

#include "pxa255.h"

#define NO_INT 0xc0
#define NO_IRQ 0x80
#define NO_FIQ 0x40
#define SVC32_MODE 0x13
#define FIQ32_MODE 0x11
#define IRQ32_MODE 0x12

    .text
    .global restore_context
    .global context_switch
    .global context_switch_in_interrupt
    .global irq_service_routine
    .global save_cpu_sr
    .global restore_cpu_sr

/**
 * @brief Save CPU status.  Disable interrupts.
 *
 * IRQ interrupt status register value is returned to be disabled.
 * @param
 * @retval CPU status value
 * Usage: cpu_sr = save_cpu_sr(); 
 */
save_cpu_sr:
    mrs r0, CPSR
    orr r1, r0, #NO_INT
    msr CPSR_c, r1
    bx lr

/**
 * @brief The state will restore the value of registers
 *
 * Restore the given value of status register.
 * @param CPU status value
 * @retval
 * Usage: restore_cpu_sr(cpu_sr); 
 */
restore_cpu_sr:
    msr CPSR_c, r0
    bx lr

/**
 * @brief IRQ interrupt service routine
 *
 * IRQ interrupt service routine is called when raised.  Execution order:
 *   (1) IRQ registers are stored on the IRQ stack.
 *   (2) Were running to save the context of a thread.
 *   (3) About to issue the interrupt - interrupt_nesting
 *   (4) Stack pointer is stored in the structure of the current thread.
 *   (5) Call interrupt handler (tick increment).
 *   (6) Change interrupt_nesting in context - exit_interrupt,
 *       otherwise, the thread context of the existing restores.
 * @param
 * @retval
 */
irq_service_routine:
    msr CPSR_c, #(NO_INT | IRQ32_MODE)
    stmfd sp!, {r1-r3}        // push working registers onto IRQ stack
    mov r1, sp            // save IRQ stack pointer
    add sp, sp, #12            // adjust IRQ stack pointer
    sub r2, lr, #4            // adjust pc for return

    mrs r3, SPSR            // copy SPSR (interrupted thread's CPSR)
    msr CPSR_c, #(NO_INT | SVC32_MODE)    // change to SVC mode

                    // save thread's context onto thread's stack
    stmfd sp!, {r2}            // push thread's return pc
    stmfd sp!, {lr}            // push thread's LR
    stmfd sp!, {r4-r12}        // push thread's r12-r4

    ldmfd r1!, {r4-r6}        // move thread's r1-r3 from IRQ stack to
                    // SVC stack
    stmfd sp!, {r4-r6}        
    stmfd sp!, {r0}            // push thread's r0 onto thread's stack
    stmfd sp!, {r3}            // push thread's CPSR(IRQ's SPSR)

    bl enter_interrupt

    ldr r0, =interrupt_nesting
    ldrb r1, [r0]
    cmp r1, #1
    bne irq_service_routine_half

        ldr r4, =current_thread        // current_thread->stack_ptr = sp
        ldr r4, [r4]
        add r4, r4, #8
        str sp, [r4]

irq_service_routine_half:    
    msr CPSR_c, #(NO_IRQ | IRQ32_MODE) // re-enable FIQ, chagen to IRQ mode

    bl interrupt_handler

    msr CPSR_c, #(NO_INT | SVC32_MODE) // chagne to SVC mode

    bl exit_interrupt

        ldmfd sp!, {r4}
        msr SPSR_cxsf, r4
        ldmfd sp!, {r0-r12, lr, pc}^

/**
 * @brief Restore the context of the current thread.
 *
 * In multi-tasking environment, restore the context of the thread.
 * @param
 * @retval
 */
restore_context:
        ldr r4, =current_thread            // sp = current_thread->sp
        ldr r4, [r4]
        add r4, r4, #8
        ldr sp, [r4]

        ldr r4, [sp], #4
        msr SPSR_cxsf, r4
        ldmfd sp!, {r0-r12, lr, pc}^

/**
 * @brief Context switching
 *
 * During context switching, the following actions are performed:
 *   (1) Save the registers in thecurrent thread.
 *   (2) The stack pointer is stored in the current structure of the thread.
 *   (3) Run the next thread pointed by scheduler.
 *   (4) The stack pointer in the thread is read and executed.
 *   (5) Restore the registers of thread.
 * @param
 * @retval
 */
context_switch:
    stmfd sp!, {lr}            // save current thread's context
        stmfd sp!, {lr}
        stmfd sp!, {r0-r12, lr}
        mrs r4, SPSR
        stmfd sp!, {r4}

    ldr r4, =current_thread        // current_thread->stack_ptr = sp
    ldr r4, [r4]
    add r4, r4, #8
    str sp, [r4]

        ldr r4, =current_thread        // current_thread = next_thread
        ldr r6, =next_thread
        ldr r6, [r6]
        str r6, [r4]

    ldr r4, =next_thread        // sp = next_thread->sp
    ldr r4, [r4]
    add r4, r4, #8
    ldr sp, [r4]

        ldmfd sp!, {r4}        // restore next thread's context
        msr SPSR_cxsf, r4
        ldmfd sp!, {r0-r12, lr, pc}^

/**
 * @brief Context switching during interruption.
 *
 * Interrupt the context occurs later due to preemptive threads.
 * @param
 * @retval
 */
context_switch_in_interrupt:
        ldr r4, =current_thread        // current_thread = next_thread
        ldr r6, =next_thread
        ldr r6, [r6]
        str r6, [r4]

        ldr r4, =next_thread            // sp = next_thread->sp
    ldr r4, [r4]
        add r4, r4, #8
    ldr sp, [r4]
                    // restore next thread's context
        ldmfd sp!, {r4}
        msr SPSR_cxsf, r4
        ldmfd sp!, {r0-r12, lr, pc}^

ARM 板子的啟動程式:arch/arm/mach-pxa/start.S

/*
 * start.S
 */

#include "pxa255.h"

#define SDRAM_BASE_ADDRESS    0xa0000000

#define CFG_MECR_VAL        0x00000000
#define CFG_MCMEM0_VAL        0x00004204
#define CFG_MCMEM1_VAL        0x00000000
#define CFG_MCATT0_VAL        0x00010504
#define CFG_MCATT1_VAL        0x00000000
#define CFG_MCIO0_VAL        0x00008407
#define CFG_MCIO1_VAL        0x00000000

#define FIQ_STACK_SIZE        0x10000
#define IRQ_STACK_SIZE        0x10000
#define ABT_STACK_SIZE        0x10000
#define UND_STACK_SIZE        0x10000
#define SYS_STACK_SIZE        0x10000

    /* Declare the names of stacks */
    .global _start
    .global fiq_stack
    .global irq_stack
    .global abt_stack
    .global und_stack
    .global sys_stack

    /* Stack allocation */
    .comm fiq_stack, FIQ_STACK_SIZE
    .comm irq_stack, IRQ_STACK_SIZE
    .comm abt_stack, ABT_STACK_SIZE
    .comm und_stack, UND_STACK_SIZE
    .comm sys_stack, SYS_STACK_SIZE

    .text
    /* exception handler vector table */
_start:    
    b reset_handler
    b und_handler
    b swi_handler
    b abt_pref_handler
    b abt_data_handler
    b not_used
    b irq_handler
    b fiq_handler

.align 4
und_handler:
    b loop

swi_handler:
    b loop

abt_pref_handler:
    b loop

abt_data_handler:
    b loop

not_used:
    b loop

loop:
    b loop

irq_handler:
    b irq_service_routine

fiq_handler:
    b loop

reset_handler:
    bl set_misc

    bl mask_off_int_reg

    bl init_clock_reg

    bl set_core_clock

    bl set_os_timer

    bl init_gpio

    bl init_mem_ctrl

    bl set_stack_pointer

    bl set_svc_mode

    bl relocate

    bl jump_to_main

set_misc:        
        ldr r0, =0x2001            /* Allows access to all coprocessors */
        mcr     p15, 0, r0, c15, c1, 0
        nop
        nop
        nop

        ldr     r0, =0x00000078         /* Disable MMU, caches, write buffer */
        mcr     p15, 0, r0, c1, c0, 0
        nop
        nop
        nop

        ldr     r0, =0x00000000
        mcr     p15, 0, r0, c8, c7, 0         /* Flush TLB's */
        mcr     p15, 0, r0, c7, c7, 0         /* Flush Caches */
        mcr     p15, 0, r0, c7, c10, 4         /* Flush Write Buffer */
        nop
        nop
        nop

        mvn    r0, #0                   /* grant manager access to all domains */
        mcr    p15,0,r0,c3,c0,0

        /*
         * Initializing PXA250 interrupt controller.
         */

mask_off_int_reg:    /* Mask off all IRQs and FIQs */
        ldr r1, =(INT_BASE | INT_ICMR)
        ldr r2, =0x0    /* interrupt gets mask off */
        str r2, [r1]

init_clock_reg:        /* PXA250 Clock Register initialization */
        ldr r1, =CLK_BASE    /* base of clock registers */
        ldr r2, =0x00000241     /* memory clock: 100MHz,
                   nomal core clock: 200MHz,
                   turbo mode: 400MHz */
        str r2, [r1, #CLK_CCCR]

set_core_clock:        /* PXA250 Core Clock is set to Turbo Mode */
        ldr r0,=0
        mrc p14,0,r0,c6,c0,0
        ldr r0,=3
        mcr p14,0,r0,c6,c0,0

set_os_timer:        /* OS Timer initialization */
        ldr r1, =TMR_BASE    /* reset the OS Timer Count to zero */
        mov r2, #0
        str r2, [r1, #TMR_OSCR]
        ldr r4, =0x300    /* really 0x2E1 (0.27uS * 0x2E1 = 198uS) is about 200usec,   */
            /* so 0x300 should be plenty :    Wait OS Timer stabilize */
1:
        ldr r2, [r1, #TMR_OSCR]
        cmp r4, r2
        bgt 1b

init_gpio:
        // FFUART

        ldr r12, =FFUART_BASE

        ldr r0, =0x00000000
        str r0, [r12, #FFLCR]
        ldr r0, =0x00000000
        str r0, [r12, #FFIER]
        ldr r0, =0x00000080
        str r0, [r12, #FFLCR]
        ldr r0, =0x00000018
        str r0, [r12, #FFDLL]
        ldr r0, =0x00000000
        str r0, [r12, #FFDLH]
        ldr r0, =0x00000000
        str r0, [r12, #FFLCR]
        ldr r0, =0x00000003
        str r0, [r12, #FFLCR]
        ldr r0, =0x00000001
        str r0, [r12, #FFFCR]
        ldr r0, =0x00000007
        str r0, [r12, #FFFCR]
        ldr r0, =0x00000000
        str r0, [r12, #FFIER]
        ldr r0, =0x00000000
        str r0, [r12, #FFMCR]

        ldr r0, =FFISR_VALUE
        str r0, [r12, #FFISR]

        ldr r12, =FFUART_BASE
        ldr r0, =0x00000040
        str r0, [r12, #FFIER]

        // clear the rx fifo errors
        ldr r0, =FFFCR_VALUE
        and r0, r0, #0x00000002
        str r0, [r12, #FFFCR]

        // First set the output values to a safe/disabled state before we change
        // any GPIO's outputs
        // start by settings all of them high which is the safest for most signals

        ldr r12, =GPIO_BASE

        ldr r0, =0xffffffff
        str r0, [r12, #GPIO_GPSR0]
        str r0, [r12, #GPIO_GPSR1]
        str r0, [r12, #GPIO_GPSR2]

        // GPCR0
        // Now clear any high true outputs that need to start low
        ldr r0, =(GPIO_7 | GPIO_13 | GPIO_17 | GPIO_27)    // USB speed = high
                            // PRST = deasserted
                            // BKL_ON
                            // Passive LCD enable = off

        str r0, [r12, #GPIO_GPCR0]

        // Next, set the correct direction for out functions. A one meas output.
        ldr r0, =(GPIO_3 | GPIO_4 | GPIO_6  | GPIO_7 | GPIO_11 | GPIO_13 | GPIO_15 | GPIO_17 | GPIO_23 | GPIO_24 | GPIO_25 | GPIO_27 | GPIO_30 | GPIO_31)

        // GPDR0
        str r0, [r12, #GPIO_GPDR0]

        ldr r0, =(GPIO_32 | GPIO_33 | GPIO_39 | GPIO_40 | GPIO_41 | GPIO_43 | GPIO_45 | GPIO_47  | GPIO_48 | GPIO_49 | GPIO_50 | GPIO_51 | GPIO_52 | GPIO_53 | GPIO_54 | GPIO_55 | GPIO_58 | GPIO_59 | GPIO_60 | GPIO_61 | GPIO_62 | GPIO_63)

        //GPDR1
        str r0, [r12, #GPIO_GPDR1]

        ldr r0, =(GPIO_64 | GPIO_65 | GPIO_66 | GPIO_67 | GPIO_68 | GPIO_69 | GPIO_70 | GPIO_71 | GPIO_72 | GPIO_73 | GPIO_74 | GPIO_75 | GPIO_76 | GPIO_77 | GPIO_78 | GPIO_79 | GPIO_80 | GPIO_81 | GPIO_84)

        // GPDR2
        str r0, [r12, #GPIO_GPDR2]

        // Finally, set the alternate function registers to the correct state
        ldr r0, =GPIO_15_AF_CS1    ;

        str r0, [r12, #GPIO_GAFR0L]

        ldr r0, =(GPIO_18_AF_RDY | GPIO_23_AF_SSP_CLK | GPIO_24_AF_SSP_FRM | GPIO_25_AF_SSP_TXD | GPIO_26_AF_SSP_RXD | GPIO_28_AF_AC97_BCLK_IN | GPIO_29_AF_AC97_SDIN0 | GPIO_30_AF_AC97_SDOUT | GPIO_31_AF_AC97_SYNC)

        str r0, [r12, #GPIO_GAFR0U]

        ldr r0, =(GPIO_33_AF_CS5 | GPIO_34_AF_FF_RXD | GPIO_35_AF_FF_CTS | GPIO_36_AF_FF_DCD | GPIO_37_AF_FF_DSR | GPIO_38_AF_FF_RI | GPIO_39_AF_FF_TXD | GPIO_40_AF_FF_DTR | GPIO_41_AF_FF_RTS | GPIO_42_AF_BT_RXD | GPIO_43_AF_BT_TXD | GPIO_44_AF_BT_CTS | GPIO_45_AF_BT_RTS | GPIO_46_AF_IR_RXD | GPIO_47_AF_IR_TXD)

        str r0, [r12, #GPIO_GAFR1L]

        ldr r0, =(GPIO_48_AF_POE | GPIO_49_AF_PWE | GPIO_50_AF_PIOR | GPIO_51_AF_PIOW | GPIO_52_AF_PCE1 | GPIO_53_AF_PCE2 | GPIO_54_AF_PSKTSEL | GPIO_55_AF_PREG | GPIO_56_AF_PWAIT | GPIO_57_AF_IOIS16 | GPIO_58_AF_LDD0 | GPIO_59_AF_LDD1 | GPIO_60_AF_LDD2 | GPIO_61_AF_LDD3 | GPIO_62_AF_LDD4 | GPIO_63_AF_LDD5)

        str r0, [r12, #GPIO_GAFR1U]

        ldr r0, =(GPIO_64_AF_LDD6 | GPIO_65_AF_LDD7 | GPIO_66_AF_LDD8 | GPIO_67_AF_LDD9 | GPIO_68_AF_LDD10 | GPIO_69_AF_LDD11 | GPIO_70_AF_LDD12 | GPIO_71_AF_LDD13 | GPIO_72_AF_LDD14 | GPIO_73_AF_LDD15 | GPIO_74_AF_LCD_FCLK | GPIO_75_AF_LCD_LCLK | GPIO_76_AF_LCD_PCLK | GPIO_77_AF_LCD_BIAS | GPIO_78_AF_CS2 | GPIO_79_AF_CS3)

        str r0, [r12, #GPIO_GAFR2L]

        ldr r0, =GPIO_80_AF_CS4

        str r0, [r12, #GPIO_GAFR2U]

        // Clear the peripheral control registers bits
        ldr r12, =PSSR
        ldr r0, =(PSSR_RDH | PSSR_PH)
        str r0, [r12]

init_mem_ctrl:        /* PXA250 Memory Controller initialization */
        ldr r1, =MSC_BASE    /* base of memory controller */

        /* static memory controller initialization */

        ldr r2, =0x123412c0    /* CS0 : flash , CS1 : SMSC LAN91C1111  */
        str r2, [r1, #MSC_MSC0]
        ldr r3, [r1, #MSC_MSC0]

        ldr r2, =0x3ff923e9    /* CS2 : TD242(USB2.0),
                   CS3 : extended board (FPGA board, iom board) */
        str r2, [r1, #MSC_MSC1]
        ldr r3, [r1, #MSC_MSC1]

        ldr r2, =0x3ff97ff9    /* CS4 : SL811HS(USB1.0) , CS5 : KEY PAD  */
        str r2, [r1, #MSC_MSC2]
        ldr r3, [r1, #MSC_MSC2]

        /* MECR: Memory Expansion Card Register */
        ldr r2, =CFG_MECR_VAL
        str r2, [r1, #MSC_MECR]
        ldr r2, [r1, #MSC_MECR]

        /* MCMEM0: Card Interface slot 0 timing */
        ldr r2, =CFG_MCMEM0_VAL
        str r2, [r1, #MSC_MCMEM0]
        ldr r2, [r1, #MSC_MCMEM0]

        /* MCMEM1: Card Interface slot 1 timing */
        ldr r2, =CFG_MCMEM1_VAL
        str r2, [r1, #MSC_MCMEM1]
        ldr r2, [r1, #MSC_MCMEM1]

        /* MCATT0: Card Interface Attribute Space Timing, slot 0 */
        ldr r2, =CFG_MCATT0_VAL
        str r2, [r1, #MSC_MCATT0]
        ldr r2, [r1, #MSC_MCATT0]

        /* MCATT1: Card Interface Attribute Space Timing, slot 1 */
        ldr r2, =CFG_MCATT1_VAL
        str r2, [r1, #MSC_MCATT1]
        ldr r2, [r1, #MSC_MCATT1]

        /* MCIO0: Card Interface I/O Space Timing, slot 0 */
        ldr r2, =CFG_MCIO0_VAL
        str r2, [r1, #MSC_MCIO0]
        ldr r2, [r1, #MSC_MCIO0]

        /* MCIO1: Card Interface I/O Space Timing, slot 1 */
        ldr r2, =CFG_MCIO1_VAL
        str r2, [r1, #MSC_MCIO1]
        ldr r2, [r1, #MSC_MCIO1]

        /* SDRAM Controller initialized at 100MHz */
        ldr r2, =0x03c00fff        /* build MDREFR in a specific order */
        str r2, [r1, #MSC_MDREFR]

        ldr r2, =0x03c00018        /* REF Rate = 
                         (64MS/8192 Rows) * 100MHz /32 = 24 */
        str r2, [r1, #MSC_MDREFR]

        ldr r2, =0x03c52018        /* Set K0RUN, K1RUN and K2RUN */
        str r2, [r1, #MSC_MDREFR]

        ldr r2, =0x03852018        /* Clear Self Refresh */
        str r2, [r1, #MSC_MDREFR]

        ldr r2, =0x0385b018        /* Set E0PIN and E1PIN */
        str r2, [r1, #MSC_MDREFR]

        ldr r2, =0x0ac80ac8        /* Set CNFG, but don't enable just yet,
                       CL = 3, tRP = 3clk, tRC = 10clk,
                       tRAS = 7clk, tRCD = 3clk */
        str r2, [r1, #MSC_MDCNFG]

        /* SDRAM Controller initialized at 50MHz. */
        /* pause for ~200usec for SDRAM to init */
        ldr r1, =TMR_BASE        /* reset the OS Timer Count to zero */
        mov r2, #0
        str r2, [r1, #TMR_OSCR]
        ldr r4, =0x300            /* really 0x2E1 (0.27uS * 0x2E1 = 198uS) 
                       is about 200usec, */
                                    /* so 0x300 should be plenty */
1:
        ldr r2, [r1, #TMR_OSCR]
        cmp r4, r2
        bgt 1b

        /* force 9 cycles to SDRAM : SDRAM bank all in the CBR (refrash cycle) */
        ldr r2, =SDRAM_BASE_ADDRESS
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]
        str r2, [r2]

        ldr r1, =MSC_BASE        /* base of memory controller */

        ldr r2, =0x0acb0acb        /* now enable SDRAM */
        str r2, [r1, #MSC_MDCNFG]

        /* and do an MRS */
        ldr r2, =0x00000000
        str r2, [r1, #MSC_MDMRS]

        /* sdram initialize done */

set_stack_pointer:
        /* FIQ mode */
        mrs r0, cpsr        /* move CPSR to r0 */
        bic r0, r0, #0x1f    /* clear all mode bits */
        orr r0, r0, #0xd1    /* set FIQ mode bits */
        msr CPSR_c, r0        /* move back to CPSR */
        ldr sp, =(fiq_stack + FIQ_STACK_SIZE - 4)    /* initialize the stack ptr */
        /* IRQ mode */
        mrs r0, cpsr        /* move CPSR to r0 */
        bic r0, r0, #0x1f    /* clear all mode bits */
        orr r0, r0, #0xd2    /* set IRQ mode bits */
        msr CPSR_c, r0        /* move back to CPSR */
        ldr sp, =(irq_stack + IRQ_STACK_SIZE - 4)    /* initialize the stack ptr */
        /* Abort mode */
        mrs r0, cpsr        /* move CPSR to r0 */
        bic r0, r0, #0x1f    /* clear all mode bits */
        orr r0, r0, #0xd7    /* set Abort mode bits */
        msr CPSR_c, r0        /* move back to CPSR */
        ldr sp, =(abt_stack + ABT_STACK_SIZE - 4)    /* initialize the stack ptr */
        /* Undef mode */
        mrs r0, cpsr        /* move CPSR to r0 */
        bic r0, r0, #0x1f    /* clear all mode bits */
        orr r0, r0, #0xdb    /* set Undef mode bits */
        msr CPSR_c, r0        /* move back to CPSR */
        ldr sp, =(und_stack + UND_STACK_SIZE - 4)    /* initialize the stack ptr */
        /* System mode */
        mrs r0, cpsr        /* move CPSR to r0 */
        bic r0, r0, #0x1f    /* clear all mode bits */
        orr r0, r0, #0xdf    /* set System mode bits */
        msr CPSR_c, r0        /* move back to CPSR */
        ldr sp, =(sys_stack + SYS_STACK_SIZE - 4)    /* initialize the stack ptr */

.align 4
set_svc_mode:
    /* Change (back) to SVC mode */
    mrs r0, cpsr        /* move CPSR to r0 */
    bic r0, r0, #0x1f    /* clear all mode bits */
    orr r0, r0, #0xd3    /* set System mode bits */
    msr CPSR_c, r0        /* move back to CPSR */
    /* Reset the stack pointer for the SVC mode (our current mode) */
    ldr sp, =(0xa4000000 - 4) ;

    /*
     * trampoline - jump to C function main().
     */
.align 4
relocate:
    adr r0, _start

    // relocate the second stage loader
    add r2, r0, #(1024 * 1024)
    ldr r1, =0xa0000000

    /* r0 = source address
     * r1 = target address
     * r2 = source end address
     */
copy_loop:
    ldmia   r0!, {r3-r10}
    stmia   r1!, {r3-r10}
    cmp     r0, r2

    ble     copy_loop

jump_to_main:
    bl main

    /* the C code should never return */
    b _start
.align 4

Facebook

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License