오후 1:27 2006-05-04
task switch 하기 on smdk2440 board
조경민 bro@shinbiro.com
http://neri.cafe24.com
============================================================
boot.s 코드에 버스 클럭 초기화, SDRAM 메모리 초기화 코드가 추가되고,
SMDK 2440 보드의 메모리 맵 기본 설정상 SDRAM이 0x30000000에서 시작이므로

        ; 스택 초기화        0x9000
        mov        sp, #0x31000000

스택 초기화도 좀 올려주고 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)
        }
}
커널 시작을 좀 올려주었다.

;=====================================================
; 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

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

        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
        b        .                 ; FIQ

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

        ; 메모리 초기화
        bl        SDRAM_init

        ; 스택 초기화        0x9000
        mov        sp, #0x31000000

        ; 커널 복사 (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                200
#define TASK_NUMBER                        3

typedef struct _task
{
        char base[TASK_STACK_SIZE];
        int* stack;
        int func;
}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;        // 시작할 pc값 넣음
        
        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)

*/

}

task* select_task()
{
        static int i = 0;
        if( i >= TASK_NUMBER ) i = 0;
        return &g_Tasks[i++];
}

void* schedule(void* current_sp)
{
        task* pTask;

        // 만일 task_start()없이 바로 task_switch()한다면
        // if문이 필요하다. current_task없이 커널 main() 코드의 sp가 넘어오기 때문.
        // if(current_task)
        current_task->stack = (int*)current_sp;
        
        pTask = select_task();
        
        current_task = pTask;

        return pTask->stack;
}

void test1()
{
        int i = 0;
        while(1)
        {
                task_switch();
                i++;
        }

}

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

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

void main()
{
        create_task( &g_Tasks[0], (int)test1 );
        create_task( &g_Tasks[1], (int)test2 );
        create_task( &g_Tasks[2], (int)test3 );

        current_task = &g_Tasks[0];
        task_start((int)current_task->stack);
}
-----------------------------------------------------------------------------
int.s

        EXPORT task_switch
        EXPORT task_start
        IMPORT schedule

        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}

        ; 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,pc}


task_start
        mov        sp, r0
        ; restore cpu context of new task
        ldmfd        sp!, {r0}
        msr        CPSR_cxsf, r0
        ldmfd        sp!, {r0-r12,pc}
        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)
        }
}
-----------------------------------------------------------------------------
빌드 과정

armasm boot.s -G -o boot.o
armasm int.s -G -o int.o
armcc -c main.c --arm --debug -g -o main.o
armlink boot.o main.o int.o --map --noremove --scatter memory.map --entry vector -o kernel.axf


이번에는 실제로 kernel을 board에 올려보았다.
타겟 보드로는 arm920t core를 사용하여 패키징한 삼성의 s3c2440 cpu 칩이 탑재된 메리테크 사의
smdk2440 평가 보드를 사용하고, 디버깅 및 플래시 프로그래밍을 위한 에뮬레이터 장비로
MDS사의 Power Debugger USB(Trace32 ICD USB)를 사용하고 에뮬레이터 소프트웨어는 Trace32 IDC USB를
사용하였다.

SMDK2440의 메모리 맵
------------------------------------------------------------
기본 점퍼 세팅대로는
0x0  AMD사의 Nor Flash
~ 0x3fffff 까지가 플래시 롬 메모리
0x30000000 Samsung SDRAM memory

보드에 올리기 위해서는
-------------------------------------------------------------
1. SMDK2440과 에뮬레이터를 연결
2. Trace32 프로그램 설치
3. Trace32에서 Flash프로그래밍하는 스크립트 작성
4. Trace32에서 kernel.axf를 로드후 디버깅 스탭으로 실행 확인

기존 소스에서 바뀌어야 하는 점
---------------------------------------------------------------
- 0x0번지에서 시작해야 하는데, smdk2440의 0x0번지는 Nor Flash 영역이다. 따라서 플래시에 프로그램을
기록해야 하는데 이를 플래시 프로그래밍이라고 한다. Trace32에서 플래시 프로그래밍이 가능하다.
- 0x30000000 번지 이상 부터 SDRAM 영역이므로 이 영역에 커널을 올릴 생각이다.
  그러기 위해서는 SDRAM으로 커널을 복사하기 전에 먼저 SDRAM 메모리를 초기화해야 메모리를 사용할 수
  있다 Nor 플래시 영역에서 실행되는 boot.s가 메모리를 초기화 할 것이다. 그리고 기본적으로 버스클럭
  초기화도 해야한다.
  2440 보드의 BSP (Board Support Package, 평가보드 시디 같은데서 제공하는 기본적인 보드 초기화 코드

묶음)에서 메모리 초기화와 버스 클럭 초기화 코드를 살짝 때다가 붙였다.
  공부하고자하는 것이 SDRAM은 아니기 때문에..;
  
  따라서 메모리를 초기화 한 후 스택도 초기화하고, 커널 복사를 시작하게 된다.
        ; 메모리 초기화
        bl        SDRAM_init

        ; 스택 초기화        0x9000
        mov        sp, #0x31000000

        ; 커널 복사 (memory.map에서 커널영역은 0x30003000에서 하겠다고 했으니 복사해야함;)
        bl         copy_kernel


Trace32 사용하기
---------------------------------------------------------------
Trace32는 프로그램 하단에 명령을 쓸 수 있는 명령 줄이 있다. 이 명령줄에 여러 명령을
넣을 수 있는데, 특히 cmm 확장자의 파일(매크로 스크립트 파일)을 작성하면 쉽게 긴 작업을
한번에 할 수도 있다.

먼저 MDS 사에서 무료로 제공하는 Trace32 동영상 강좌를 보도록 한다.
http://mdstec.com/ing/programs/program.html?d_id=1082
특히 8_Flash programming.avi 플래시 프로그래밍 강좌를 보아야 한다.

간략히 Trace32의 간단한 명령은 이렇다.
SYSTEM.RESET : 시스템 리셋하기
SYSTEM.CPU ARM920T : 에뮬레이터와 붙은 보드 상의 cpu는 arm920t라고 얘기해줌
SYSTEM.UP : 시스템 온
위의 세 명령은 주메뉴/CPU/System Settings 에서도 조작 가능하다.

DO C:T32Init.cmm : init.cmm이라는 매크로 스크립트를 실행한다.
DATA.LOAD.ELF D:roprojectarm[060504]boardkernel.axf /WORD
: elf 파일을 로드한다. 이 때 심볼 정보가 Trace32 프로그램에 로드된다.

주메뉴/View/List Source 를 하면 현재 어셈블리 코드 위치를 볼 수 있다.

Trace32 플래시 프로그래밍 하기
-----------------------------------------------------------------
먼저 MDS 사에 가서 고객지원/다운로드/Trace32 일반자료실에서
DialogBox for flash programming 사용법 게시물을 열어서 첨부파일을 받는다.
http://www.mdstec.com/ing/programs/program.html?

mode=view&r_id=&d_id=134&num=143&page=1&search=subject¶meter=flash

그리고 압축본 안의 Init.cmm파일과, flash_log.cmm파일을 C:T32 폴더에 복사

Trace32 프로그램 명령줄에
DO flash_log.cmm을 실행하면
스크립트에서 제공하는 플래시 프로그래밍 스크립트를 생성하는 다이얼로그창이 뜬다.

난 다행히 Trace32 동영상에서 나온 동일한 AMD Nor Flash를 사용하고 있었다 -_-;
그래서 동영상대로 설정한 후, 스크립트 저장하면
fast_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:T32Init.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:T32demoarmFLASHWORDAM29F100.BIN


FLASH.ERASE ALL
FLASH.PROGRAM ALL
DATA.LOAD.ELF D:roprojectarm[060504]boardkernel.axf /WORD
FLASH.PROGRAM OFF

ENDDO
-----------------------------------------------------------------------
이제 명령줄에서
DO fast_flash.cmm 만 하면 플래시가 자동 프로그래밍 되고 심볼도 로드되어
바로 주메뉴/View/Source List를 해서 코드를 보면서 스텝 버튼 (F2) 또는 스탭오버(F3)으로
실행 과정을 볼 수 있었다.

물론 잘돈다 ㅡvㅡ

+ Recent posts