오후 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ㅡ
'KB > embbeded sw' 카테고리의 다른 글
[idea] starcore dsp 상의 os 커널 서비스 스레드의 VLES 활용 동시 처리 (0) | 2006.05.08 |
---|---|
한국 Xlinx FPGA 판매 업소(?) (0) | 2006.05.08 |
arm-6: TRACE32 사용법 익히기 (0) | 2006.05.03 |
arm-5 : task switch 하기 (0) | 2006.05.02 |
arm-4: 커널 영역 올리기 (0) | 2006.04.27 |