오후 4:39 2006-05-30
timer 상에서 task switch하기 on smdk2440 board
조경민 bro@shinbiro.com
http://neri.cafe24.com
============================================================
IRQ 를 위한 스택을 잡아주었다.

        ; IRQ 스택 초기화
        mrs     r0, CPSR
        bic     r0, r0, #0x1f
        orr     r0, r0, #IRQMODE | NOINT        
        msr        cpsr_cxsf,r0
        mov        sp, #0x32000000

        ; SVC 스택 초기화
        mrs     r0, CPSR
        bic     r0, r0, #0x1f
        orr     r0, r0, #SVCMODE | NOINT        
        msr        cpsr_cxsf,r0
        mov        sp, #0x31000000

irq 가 오면 irq_handler c코드로 뜨게 하고, irq_handler에서 리턴이 1이면
스케줄링을 필요로 한다는 개념으로 접근했다.
irq
        ; IRQ shadow reg: r13, r14, spsr
        ; r14_svc
        ; save cpu context of old task
        stmfd        sp!, {r0-r12,lr}

        ; 수동 스케줄링 중에 갑자기 interrupt가 들어와서
        ; r14를 0으로 바꾸는 일이 없도록
        mrs     r0, CPSR
        orr         r0, r0, #0x80
        msr     CPSR_c, r0

        bl        irq_handler
        cmp        r0, #1                ; need schdule
        ldmfd        sp!, {r0-r12,lr}
        bne        irq_return
        b        need_schedule
irq_return
        subs        pc, lr, #4

타이머나 irq 처리 루틴을 위한 파일이 생겨서 memory.map도 추가되었다.

MEMORY_MAP 0x0
{
        BOOT 0x0
        {
                boot.o (boot, +First)
                * (+RO, +RW, +ZI)
        }
        KERNEL 0x30003000
        {
                main.o (+RO, +RW, +ZI)
                int.o (+RO, +RW, +ZI)
                timer.o (+RO, +RW, +ZI)
                interrupt.o (+RO, +RW, +ZI)
        }
}

소스가 좀 많이 늘어났다. 이제 ADS 1.2의 프로젝트 관리 툴을 사용해야
할 때가 온것 같다 ㅡ,.ㅡ;


;=====================================================
; boot test
; by bro, bro@shinbiro.com 오전 10:23 2006-04-26
;
; armasm main.s -o main.o
; armlink main.o --ro-base 0x0 -o main.axf
;=====================================================
        GET option.inc
        GET memcfg.inc
        GET 2440addr.inc

        ; main.c의 커널 main 함수 심볼 쓰기 위해서
        IMPORT main
        IMPORT        ||Image$$KERNEL$$ZI$$Limit||
        EXPORT        __user_initial_stackheap
        EXPORT        vector
        EXPORT        irq_return

        ; interrupt.c의 함수
        IMPORT irq_handler
        IMPORT need_schedule

        ; KERNEL region 복사를 위해 심볼 사용
        IMPORT        ||Image$$KERNEL$$Base||
        IMPORT        ||Image$$KERNEL$$Length||
        IMPORT        ||Load$$KERNEL$$Base||

;Pre-defined constants
USERMODE    EQU         0x10
FIQMODE     EQU         0x11
IRQMODE     EQU         0x12
SVCMODE     EQU         0x13
ABORTMODE   EQU         0x17
UNDEFMODE   EQU         0x1b
MODEMASK    EQU         0x1f
NOINT       EQU         0xc0

        PRESERVE8
        AREA boot, CODE, READONLY
        ENTRY
vector                                ; 인터럽트 벡터 테이블
        b        reset                 ; 주소 0번지에 reset 인터럽트가 오면 바로

reset레이블로 점프
        b        .                 ; undefined_instruction
        b        .                 ; software_interrupt
        b        .                 ; prefetch_abort
        b        .                 ; data_abort
        b        .                 ; not_used
        b        irq                 ; IRQ
        b        .                 ; FIQ

irq
        ; IRQ shadow reg: r13, r14, spsr
        ; r14_svc
        ; save cpu context of old task
        stmfd        sp!, {r0-r12,lr}

        ; 수동 스케줄링 중에 갑자기 interrupt가 들어와서
        ; r14를 0으로 바꾸는 일이 없도록
        mrs     r0, CPSR
        orr         r0, r0, #0x80
        msr     CPSR_c, r0

        bl        irq_handler
        cmp        r0, #1                ; need schdule
        ldmfd        sp!, {r0-r12,lr}
        bne        irq_return
        b        need_schedule
irq_return
        ;stmfd        sp!, {r0}
        ;; 다시 인터럽트 살려주고..
        ;mrs        r0, cpsr
        ;bic        r0, r0, #0x80
        ;msr        cpsr_c, r0        
        ;ldmfd        sp!, {r0}
        subs        pc, lr, #4

reset
        ; 현재 모드에서 IRQ|FIQ를 끈다.
        mrs     r0, CPSR
        bic     r0, r0, #0x1f
        orr     r0, r0, #(0x13 | 0xc0)        ; 0x13(0001 0011) | 0xC0 (1101 0000)

= 0xD3(1101 0011)
        msr     CPSR_cxsf, r0

        ; IRQ 스택 초기화
        mrs     r0, CPSR
        bic     r0, r0, #0x1f
        orr     r0, r0, #IRQMODE | NOINT        
        msr        cpsr_cxsf,r0
        mov        sp, #0x32000000

        ; SVC 스택 초기화
        mrs     r0, CPSR
        bic     r0, r0, #0x1f
        orr     r0, r0, #SVCMODE | NOINT        
        msr        cpsr_cxsf,r0
        mov        sp, #0x31000000

        ; 메모리 초기화
        bl        SDRAM_init

        ; 커널 복사 (memory.map에서 커널영역은 0x10000에서 하겠다고 했으니

복사해야함;)
        bl         copy_kernel

        ldr        pc, =main
        b        end

__user_initial_stackheap
        LDR        r0, =||Image$$KERNEL$$ZI$$Limit||
        MOV        pc, lr
copy_kernel
        ldr        r0, =||Load$$KERNEL$$Base||        ; source (FLASH ROM같은 영역

)
        ldr        r1, =||Image$$KERNEL$$Base||        ; destination (SDRAM 같은 영

역)
        ldr        r2, =||Image$$KERNEL$$Length||        ; kernel length
        mov        r3, #0                                ; counter
loop
        ldr        r4, [r0], #4
        str        r4, [r1], #4
        add        r3, r3, #4
        cmp        r3, r2
        bne        loop
        mov        pc, lr

Clock_Init        ; 버스 클럭 초기화
        ;To reduce PLL lock time, adjust the LOCKTIME register.
        ldr        r0,=LOCKTIME
        ldr        r1,=0xffffff
        str        r1,[r0]

    [ PLL_ON_START
        ; Added for confirm clock divide. for 2440.
        ; Setting value Fclk:Hclk:Pclk
        ldr        r0,=CLKDIVN
        ldr        r1,=CLKDIV_VAL                ; 0=1:1:1, 1=1:1:2, 2=1:2:2,

3=1:2:4, 4=1:4:4, 5=1:4:8, 6=1:3:3, 7=1:3:6.
        str        r1,[r0]
        
        ;Configure UPLL
        ldr        r0,=UPLLCON
        ldr        r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)  
        str        r1,[r0]
        nop        ; Caution: After UPLL setting, at least 7-clocks delay must

be inserted for setting hardware be completed.
        nop
        nop
        nop
        nop
        nop
        nop
        ;Configure MPLL
        ldr        r0,=MPLLCON
        ldr        r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)  ;Fin=16.9344MHz
        str        r1,[r0]
    ]

SDRAM_init        ; 0x30000000 SDRAM 메모리 초기화
        ;Set memory control registers
        ldr        r0,=SMRDATA
        ldr        r1,=BWSCON        ;BWSCON Address
        add        r2, r0, #52        ;End address of SMRDATA
0
        ldr        r3, [r0], #4
        str        r3, [r1], #4
        cmp        r2, r0
        bne        %B0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;       When EINT0 is pressed,  Clear SDRAM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;
; check if EIN0 button is pressed

       ldr        r0,=GPFCON
        ldr        r1,=0x0
        str        r1,[r0]
        ldr        r0,=GPFUP
        ldr        r1,=0xff
        str        r1,[r0]

        ldr        r1,=GPFDAT
        ldr        r0,[r1]
       bic        r0,r0,#(0x1e<<1)  ; bit clear
        tst        r0,#0x1
        bne %F1
        
; Clear SDRAM Start

        ldr        r0,=GPFCON
        ldr        r1,=0x55aa
        str        r1,[r0]
;        ldr        r0,=GPFUP
;        ldr        r1,=0xff
;        str        r1,[r0]
        ldr        r0,=GPFDAT
        ldr        r1,=0x0
        str        r1,[r0]        ;LED=****

        mov r1,#0
        mov r2,#0
        mov r3,#0
        mov r4,#0
        mov r5,#0
        mov r6,#0
        mov r7,#0
        mov r8,#0
        
        ldr        r9,=0x4000000   ;64MB
        ldr        r0,=0x30000000
0        
        stmia        r0!,{r1-r8}
        subs        r9,r9,#32
        bne        %B0
;Clear SDRAM End
1
        mov        pc, lr

SMRDATA DATA
; Memory configuration should be optimized for best performance
; The following parameter is not optimized.
; Memory access cycle parameter strategy
; 1) The memory settings is  safe parameters even at HCLK=75Mhz.
; 2) SDRAM refresh period is for HCLK<=75Mhz.

        DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+

(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
        DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+

(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))   ;GCS0
        DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+

(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))   ;GCS1
        DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+

(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))   ;GCS2
        DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+

(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))   ;GCS3
        DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+

(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))   ;GCS4
        DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+

(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))   ;GCS5
        DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    ;GCS6
        DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    ;GCS7
        DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+(Tchr<<16)

+REFCNT)

        DCD 0x32            ;SCLK power saving mode, BANKSIZE 128M/128M

        DCD 0x30            ;MRSR6 CL=3clk
        DCD 0x30            ;MRSR7 CL=3clk

        ALIGN

end
        b        .
        END
---------------------------------------------------------------------------

--
main.c

#define TASK_STACK_SIZE                0x200
#define TASK_NUMBER                        4

typedef struct _task
{
        char base[TASK_STACK_SIZE];
        int* stack;
        int func;
        struct _task* next;
}task;

task g_Tasks[TASK_NUMBER];
task* current_task;

void task_switch(void);                // in int.s
void task_start(int task_sp);

void memset( char* src, int c, int size )
{
        int i;
        for( i = 0; i < size; i ++)
                src[i] = c;
}

void create_task( task* pTask, int func )
{
        memset( pTask->base, 0, TASK_STACK_SIZE );
        pTask->func = func;
        pTask->stack = (int*)(pTask->base + TASK_STACK_SIZE - 4); // t0

        pTask->stack -= 1;        // pc
        *pTask->stack = func+4;        // 시작할 pc값 넣음
/*
오후 3:42 2006-05-30 현재 func+4로 초기값을 넣어야
IRQ 타이머에서 subs pc, lr, #4로 -4 한 것에 대응되어 태스크의 첫 시작이
잘된다. +4 안하면 태스크 첫 주소 -4인 test2()라면 test1()의 마지막 라인이
실행되는 불상사가 생긴다 -_-
*/

        
        pTask->stack -= 13;        // r0-r12 적음

        pTask->stack -= 1;        // CPSR flag기록
        *pTask->stack = 0xD3;        // IF_svc mode    (t1)

        // full decending (start empty)
/*
                             empty
+--------+ (0x19C)        +--------+ TASK_STACK_SIZE (0x200)
|        | <- stack (t0)  | func   |
|        |                +--------+
|        |                | r12    |
|        |                +--------+
|        |                | ...    |
|        |                +--------+
|        |                | r0     |
|        |                +--------+
|        |                | 0xD3   |
|        |                +--------+ <- stack top (t1)
|        |                |        |
+--------+ <- base (0x0)  +--------+ <- base (0x0)

*/

}

void* schedule(void* current_sp)
{
        // 만일 task_start()없이 바로 task_switch()한다면
        // if문이 필요하다. current_task없이 커널 main() 코드의 sp가 넘어오

기 때문.
        // if(current_task)
        current_task->stack = (int*)current_sp;
        
        //pTask = select_task();
        current_task = current_task->next;
        
        return current_task->stack;
}


void init_timer0(void);
void irq_enable(void);

void test1()
{
        int i = 0;


        //task_switch();
        //irq_enable();
        irq_enable();

        while(1)
        {
                //task_switch();
                i++;
        }

}

void test2()
{
        while(1)
        {
                //task_switch();
        }
        
}

void test3()
{
        int c = 0;
        while(1)
        {
                c +=2;
                //task_switch();
        }
        
}

void test4()
{
        int c = 0;
        while(1)
        {
                c +=2;
                //task_switch();
        }
        
}

void main()
{
        init_timer0();

        create_task( &g_Tasks[0], (int)test1 );
        create_task( &g_Tasks[1], (int)test2 );
        create_task( &g_Tasks[2], (int)test3 );
        create_task( &g_Tasks[3], (int)test4 );

        g_Tasks[0].next = &g_Tasks[1];
        g_Tasks[1].next = &g_Tasks[2];
        g_Tasks[2].next = &g_Tasks[3];
        g_Tasks[3].next = &g_Tasks[0];
        current_task = &g_Tasks[0];

        task_start((int)current_task->stack);
}
---------------------------------------------------------------------------

--
int.s


        EXPORT task_switch
        EXPORT task_start
        EXPORT need_schedule
        IMPORT schedule
        IMPORT irq_return

;Pre-defined constants
USERMODE    EQU         0x10
FIQMODE     EQU         0x11
IRQMODE     EQU         0x12
SVCMODE     EQU         0x13
ABORTMODE   EQU         0x17
UNDEFMODE   EQU         0x1b
MODEMASK    EQU         0x1f
NOINT       EQU         0xc0


;sharing stack
sp_irq                EQU        0x33000000
lr_irq                EQU        0x33000004
r0_tmp                EQU        0x33000008
r1_tmp                EQU        0x3300000C

        PRESERVE8
        AREA intr, CODE, READONLY
        ENTRY
task_switch
        ; save cpu context of old task
        stmfd        sp!, {r0-r12,lr}
        mrs     r0, CPSR
        stmfd        sp!, {r0}

        ; 수동 스케줄링 중에 갑자기 interrupt가 들어와서
        ; r14를 0으로 바꾸는 일이 없도록
        mrs     r0, CPSR
        ORR         r0, r0, #0x80
        msr     CPSR_c, r0

        ; do schdule and get next task stack top pointer
        mov        r0, sp
        bl        schedule
        mov        sp, r0

        ; 다시 인터럽트 살려주고..
        mov        r0, #1
        MRS r1,CPSR    
        ORR r1,r1,#0x80    
        BIC r0,r1,r0,LSL #7    
        MSR CPSR_c,r0

        ; restore cpu context of new task
        ldmfd        sp!, {r0}
        msr        CPSR_cxsf, r0
        ldmfd        sp!, {r0-r12,pc}


task_start
        mov        sp, r0
        ; restore cpu context of new task
        ldmfd        sp!, {r0}
        msr        CPSR_cxsf, r0
        ldmfd        sp!, {r0-r12,pc}


need_schedule
        ; IRQ 모드에서 스케줄이 필요한 상황이다.
        ; lr에는 IRQ오기 전의 SVC모드의 pc가 들어 있다.
        ; 스케줄을 한 후 새로운 리턴 주소를
        ; subs        pc, lr, #4 해서 SVC모드로 복원해야한다.
        
        ; 복원할 pc_svc를 =lr_irq메모리에 적어놓는다.
        stmfd        sp!, {r0}
        ; save lr_irq
        ldr        r0, =lr_irq
        str        lr, [r0]
        ldmfd        sp!, {r0}

        ; change to svc mode lr_irq는 저장해놨기때문에 마구쓴다.
        mrs     lr, CPSR
        bic     lr, lr, #0x1f
        orr     lr, lr, #SVCMODE
        msr        cpsr_cxsf,lr

        ; SVC모드 먼저 현재 task의 lr에 임시로 =lr_irq값을 넣는다.
        ; save cpu context of old task
        stmfd        sp!, {r0}
        ldr        r0, =lr_irq
        ldr        lr, [r0]        ; old_task.lr <= MEM_lr_irq
        ldmfd        sp!, {r0}

        ; 현재 task를 스택에 저장 이때 task.lr에 다음 시작 pc들어있음
        stmfd        sp!, {lr}        ; save old task pc
        stmfd        sp!, {r0-r12}
        mrs     r0, CPSR
        stmfd        sp!, {r0}

        ; do schdule and get next task stack top pointer
        mov        r0, sp
        bl        schedule
        mov        sp, r0

        ; restore cpu context of new task
        ldmfd        sp!, {r0}
        msr        CPSR_cxsf, r0
        ldmfd        sp!, {r0-r12,lr}

        ; 이번에 선택된 task.lr 복원 주소를 =lr_irq메모리에 씀
        ; save new task return address
        stmfd        sp!, {r0}
        ; save lr_irq
        ldr        r0, =lr_irq
        str        lr, [r0]                ; MEM_lr_irq <= new_task.lr_svc
        ldmfd        sp!, {r0}

        ; 인터럽트 복원을 위해 IRQ 모드로 돌아가자.(lr_svc 저장되었음마구씀)
        ; change to irq mode
        mrs     lr, CPSR
        bic     lr, lr, #0x1f
        orr     lr, lr, #IRQMODE
        msr        cpsr_cxsf,lr

        ; 복원 lr_irq 값을 새로운 태스크 pc인 =lr_irq값으로 바꾼다.
        ; get new task return address
        stmfd        sp!, {r0}
        ; load lr_irq
        ldr        r0, =lr_irq
        ldr        lr, [r0]                ; lr_irq <= MEM_lr_irq
        ldmfd        sp!, {r0}

        subs        pc, lr, #4
        END
---------------------------------------------------------------------------

--
MEMORY_MAP 0x0
{
        BOOT 0x0
        {
                boot.o (boot, +First)
                * (+RO, +RW, +ZI)
        }
        KERNEL 0x30003000
        {
                main.o (+RO, +RW, +ZI)
                int.o (+RO, +RW, +ZI)
                timer.o (+RO, +RW, +ZI)
                interrupt.o (+RO, +RW, +ZI)
        }
}
---------------------------------------------------------------------------

--
interrupt.c

#include "smdk2440.h"
#define IRQMODE             0x12
#define SVCMODE             0x13

unsigned int g_timeslice = 0;
int isr_timer0()
{
        if( g_timeslice > 10)
        {
                g_timeslice = 0;
                return 1;
        }
        g_timeslice++;
        return 0;
}

int irq_handler()        // return true is 'need scheduling'
{
        int need_schedule = 0;
        if( SRCPND & INT_TIMER0 )
        {
                need_schedule = isr_timer0();
        }
        SRCPND = INT_TIMER0;        //Clear pending bit
        INTPND = INT_TIMER0;
        INTPND;                        //Prevent an double interrupt pending
        return need_schedule;
}

void irq_enable()
{
        int my_cpsr;
        g_timeslice = 0;
        __asm
        {
        mrs     my_cpsr, CPSR
        ORR         my_cpsr, my_cpsr, #0x80                /* set IRQ disable bit flag

*/
        BIC        my_cpsr, my_cpsr, 1, LSL #7
        msr     CPSR_c, my_cpsr
        }
}

-------------------------------------------------------------------------
timer.c

/*
        by KyungMin Cho
        bro( bro@shinbiro.com )
        http://neri.cafe24.com

        s3c2440 PWM Timer
*/

#include "smdk2440.h"


void init_timer0()
{
    //(1/(PCLK/(Prescaler+1)/divider) * count(Max 65535) = Timer clock

(Frequency)
    //(1/(50.8MHz/1/2))  *  2000 = 0.0787 msec ( 12.7 KHz)
    //(1/(50.8MHz/1/2))  *  4000 = 0.1575 msec ( 6.35 KHz)
    //(1/(50.8MHz/1/2))  *  5000 = 0.1969 msec ( 5.080 KHz)
    //(1/(50.8MHz/1/2))  *  10000 = 0.3937 msec ( 2.54 KHz)

    //(1/(50.8MHz/8/16)) *  2000 = 5.039 msec ( 198.45 Hz)
    //(1/(50.8MHz/8/16)) *  4000 = 10.079 msec ( 99.216 KHz)
    //(1/(50.8MHz/8/16)) *  5000 = 12.598 msec ( 79.38 Hz)
    //(1/(50.8MHz/8/16)) *  10000 = 25.197 msec ( 39.687 Hz)
              
//        TCFG0 = 7;        // timer0 Prescaler 8
        TCFG0 = 100;        // timer0 Prescaler 8
        TCFG1 = 3;        // 1/16 divide

        TCNTB0 = 1000;                // down-counter init value, = 0 irq!
        TCMPB0 = 500;                // for Pulse Width Modulation(PWM)

//        TCNTB0 = 10000;                // down-counter init value, = 0 irq!
//        TCMPB0 = 5000;                // for Pulse Width Modulation(PWM)
    //(1/(50.8MHz/8/16)) *  2000 = 5.039 msec ( 198.45 Hz)

        TCON = 1<<1;   // manual update

        TCON = 0x09;   // interval, start
        INTMSK = ~INT_TIMER0;        // Timer0 interrupt bitmask clear
}
----------------------------------------------------------------------
smdk2440.h

#ifndef __SMDK2440_H__
#define __SMDK2440_H__

////////////////////////////////////////////////////////////////
// interrupt
////////////////////////////////////////////////////////////////
#define INT_BASE        (unsigned long*)0x4A000000

// source pending register 1=requsted
#define SRCPND                *(INT_BASE+0)
#define INT_TIMER0        (0x1<<10)

// 0=irq 1=fiq
#define INTMOD                *(INT_BASE+0x4/4)

// 0=available 1=masked
#define INTMSK                *(INT_BASE+0x8/4)

// 0=interrupt has not been requsted 1=opposite
#define INTPND                *(INT_BASE+0x10/4)

// 어떤 INTPND의 IRQ 인터럽트가 발생되었는지 체크
#define INTOFFSET        *(INT_BASE+0x14/4)
#define INTOFFSET_TIMER0        10

///////////////////////////////////////////////////////////////
// Timer
///////////////////////////////////////////////////////////////
#define TIMER_BASE        ((unsigned long*)0x51000000)

// Prescaler0 [7:0] for Timer 0, 1
// Prescaler1 [15:8] for Timer 2,3,4
// Timer input clock Frequency = PCLK / {prescaler value+1} / {divider

value}
// {prescaler value} = 0~255
// {divider value} = 2, 4, 8, 16
#define TCFG0                *(TIMER_BASE+0)

// DMA mode [23:20] 0000=all interrupt 0001=timer0 ~ 0101=timer4
// MUX0 [3:0] 0000=1/2 0001=1/4 0010=1/8 0011=1/16 01xx=External_TCLK0 for

timer0
#define TCFG1                *(TIMER_BASE+1)


// TCON Timer Control
// Timer0 start/stop [0] 0=stop 1=start, manual update[1] 0=no 1=update,

reload[3] 0=oneshot 1=interval
// Timer1 start/stop [8]  ~ ...
#define TCON                *(TIMER_BASE+2)

// timer0 count buffer and compare [15:0]
#define TCNTB0                *(TIMER_BASE+0xC/4)
#define TCMPB0                *(TIMER_BASE+0x10/4)

// timer0 count observation register [15:0] ReadOnly
#define TCNTO0                *(TIMER_BASE+0x14/4)


#endif //__SMDK2440_H__
-------------------------------------------------------------------------
flash.cmm

// Simple & Fast Flash program macro file
// FLASH Programming Script file for Lauterbach JTAG/ICD TRACE32.

SYSTEM.RESET
SYSTEM.CPU ARM920T
SYSTEM.UP

DO C:\T32\Init.cmm

FLASH.RESET
FLASH.CREATE 1. 0x00000000--0xffff 0x2000 target WORD
FLASH.CREATE 1. 0x10000--0x3effff 0x10000 target WORD
FLASH.CREATE 1. 0x3f0000--0x3fffff 0x2000 target WORD
FLASH.TARGET 0x30000000++0xfff 0x30001000++0xfff C:\T32

\demo\arm\FLASH\WORD\AM29F100.BIN


FLASH.ERASE ALL
FLASH.PROGRAM ALL
DATA.LOAD.ELF kernel.axf /WORD
FLASH.PROGRAM OFF

ENDDO

------------------------------------------------------------------

빌드 과정

armasm boot.s -G -o boot.o
armasm int.s -G -o int.o
armcc -c main.c -debug -g -o main.o
armcc -c timer.c -debug -g -o timer.o
armcc -c interrupt.c -debug -g -o interrupt.o
armlink boot.o main.o int.o timer.o interrupt.o -map -noremove -scatter

memory.map -entry vector -o kernel.axf

pause


이번에는 타이머를 잡아서 타이머 인터럽트 발생시 스케줄링을 일으켜서
선점형 라운드 로빈 스케줄러를 만들었다.

지금 내가 짠 코드와 암 아키텍처 상의 사소한 문제가 있는지, create_task시
첫 함수 시작 주소 + 4의 코드로 처음에 태스크 스택을 세팅하게 바꾸었다.

void create_task( task* pTask, int func )
{
        memset( pTask->base, 0, TASK_STACK_SIZE );
        pTask->func = func;
        pTask->stack = (int*)(pTask->base + TASK_STACK_SIZE - 4); // t0

        pTask->stack -= 1;        // pc
        *pTask->stack = func+4;        // 시작할 pc값 넣음
/*
오후 3:42 2006-05-30 현재 func+4로 초기값을 넣어야
IRQ 타이머에서 subs pc, lr, #4로 -4 한 것에 대응되어 태스크의 첫 시작이
잘된다. +4 안하면 태스크 첫 주소 -4인 test2()라면 test1()의 마지막 라인이
실행되는 불상사가 생긴다 -_-
*/

이렇게 시작 함수+4 위치를 기억하게 한 후, IRQ 타이머 스케줄링 발생시
int.s의 need_schedule가 호출되게 되는데,
이 부분에서 irq 모드에서 svc 모드 태스크로 복원해야하는데, 스케줄링을
시켜서 원래 복원시키려는 태스크가 아닌 다른 태스크로 복원하게 하는
트릭을 작성했다. 그러기 위해서 irq 모드의 lr을(복원하려는 주소값이 담김)
lr_irq을 기억하기 위한 메모리를 따로 정적으로 두었다...;

lr_irq                EQU        0x33000004

그런 후, irq모드의 lr을 이 lr_irq 메모리에 넣고, svc모드로 변환후,
현재 태스크를 저장하고 (이 때 현재 태스크의 마지막 실행 주소를 lr_irq메모리

에서
얻어와서 세팅함), 스케줄링 후 다음 태스크를 선택 후, 다음 태스크의
시작 주소를 lr_irq 메모리에 저장하고, IRQ모드로 다시 돌아와, lr_irq메모리를
읽어와서 irq 모드의 lr에 재설정하여 다음 태스크로 복원하도록 하였다.

need_schedule
        ; IRQ 모드에서 스케줄이 필요한 상황이다.
        ; lr에는 IRQ오기 전의 SVC모드의 pc가 들어 있다.
        ; 스케줄을 한 후 새로운 리턴 주소를
        ; subs        pc, lr, #4 해서 SVC모드로 복원해야한다.
        
        ; 복원할 pc_svc를 =lr_irq메모리에 적어놓는다.
        stmfd        sp!, {r0}
        ; save lr_irq
        ldr        r0, =lr_irq
        str        lr, [r0]
        ldmfd        sp!, {r0}

        ; change to svc mode lr_irq는 저장해놨기때문에 마구쓴다.
        mrs     lr, CPSR
        bic     lr, lr, #0x1f
        orr     lr, lr, #SVCMODE
        msr        cpsr_cxsf,lr

        ; SVC모드 먼저 현재 task의 lr에 임시로 =lr_irq값을 넣는다.
        ; save cpu context of old task
        stmfd        sp!, {r0}
        ldr        r0, =lr_irq
        ldr        lr, [r0]        ; old_task.lr <= MEM_lr_irq
        ldmfd        sp!, {r0}

        ; 현재 task를 스택에 저장 이때 task.lr에 다음 시작 pc들어있음
        stmfd        sp!, {lr}        ; save old task pc
        stmfd        sp!, {r0-r12}
        mrs     r0, CPSR
        stmfd        sp!, {r0}

        ; do schdule and get next task stack top pointer
        mov        r0, sp
        bl        schedule
        mov        sp, r0

        ; restore cpu context of new task
        ldmfd        sp!, {r0}
        msr        CPSR_cxsf, r0
        ldmfd        sp!, {r0-r12,lr}

        ; 이번에 선택된 task.lr 복원 주소를 =lr_irq메모리에 씀
        ; save new task return address
        stmfd        sp!, {r0}
        ; save lr_irq
        ldr        r0, =lr_irq
        str        lr, [r0]                ; MEM_lr_irq <= new_task.lr_svc
        ldmfd        sp!, {r0}

        ; 인터럽트 복원을 위해 IRQ 모드로 돌아가자.(lr_svc 저장되었음마구씀)
        ; change to irq mode
        mrs     lr, CPSR
        bic     lr, lr, #0x1f
        orr     lr, lr, #IRQMODE
        msr        cpsr_cxsf,lr

        ; 복원 lr_irq 값을 새로운 태스크 pc인 =lr_irq값으로 바꾼다.
        ; get new task return address
        stmfd        sp!, {r0}
        ; load lr_irq
        ldr        r0, =lr_irq
        ldr        lr, [r0]                ; lr_irq <= MEM_lr_irq
        ldmfd        sp!, {r0}

        subs        pc, lr, #4
        END

+ Recent posts