訊息
|
大綱連結
- 測試主程式:app/shell/main.c
- 執行緒:kernel/thread.c
- 系統核心:kernel/kernel.c
- 序列裝置 (輸出入):kernel/serial.c
- 行程同步:kernel/sync.c
- 鏈節串列:kernel/list.c
- 標準輸出入:lib/stdio.c
- ARM 相關的組合語言:arch/arm/mach-pxa/asm_port.S
- 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, ¤t_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, ¤t_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],
¤t_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],
¤t_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, ¤t_thread->node);
break;
}
}
}
if (current_thread->state != EVENT_WAIT) {
current_thread->state = EVENT_WAIT;
insert_back_list(&mbox->wait_list, ¤t_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, ¤t_thread->node);
break;
}
}
}
if (current_thread->state != EVENT_WAIT) {
current_thread->state = EVENT_WAIT;
insert_back_list(&msgq->wait_list, ¤t_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,
¤t_thread->node);
break;
}
}
}
if (current_thread->state != EVENT_WAIT) {
current_thread->state = EVENT_WAIT;
insert_back_list(&sem->wait_list, ¤t_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
|
Post preview:
Close preview