VPOS gcc3 으로 포팅하기
vpos 를 gcc-3.3.4 로 포팅하는 방법과 포팅할 때 고려해야할 것들에 대해서 설명한다.
기본적으로 vpos 는 2.95.3 에서 컴파일 되었고, 그 상위 컴파일러로 컴파일 시에 어셈 파일(.S) 에서 문법 오류가 발생했다. 문법오류를 고치더라도, 쓰레드를 생성하는 도중에 시스템이 죽어버리는 문제가 있었다.
이번에 gcc-3.3.4 로 포팅하면서, 컴파일러에 대한 특성과 이에 대한 문제점들에 대해 알 수 있다.
현재 새롭게 포팅한 vpos 로 divx 까지 확인한 상태다.
먼저 포팅을 했던 환경 설정에 대해서 설명하겠다.
준비 운동 하기
컴파일러는 http://www.falinux.com FALINUX 와 http://www.aesop-embedded.org AESOP 에서 제공하는 컴파일러를 사용했다. 둘다 같은 gcc 버전을 가지고 있으면서, 둘 중에 어느 것으로 컴파일해도 문제가 없다. 각 툴체인에 대한 설치와 설정은 생략하기로 한다.
포팅할 vpos 소스코드는 나중에 테스트를 위해 최근 버전인 divx 가 포함된 것으로 한다. 이제부터는 포팅하면서 수정한 사항들을 중점으로 설명하겠다.
수정 사항
HAL_arch_startup.S 수정
vpos 의 압축을 풀고 컴파일을 한다.
#cd /opt #tar xzf vpos.tar.gz #cd vpos #make
아래와 같은 에러가 발생한다.
arm-linux-gcc -c -o HAL_arch_startup.o HAL_arch_startup.S HAL_arch_startup.S: Assembler messages: HAL_arch_startup.S:118: Error: invalid pseudo operation -- `str sp,=vk_save_swi_mode_stack_ptr' HAL_arch_startup.S:119: Warning: writeback of base register is UNPREDICTABLE HAL_arch_startup.S:125: Error: invalid pseudo operation -- `str sp,=vk_save_swi_current_tcb_bottom' HAL_arch_startup.S:132: Warning: writeback of base register is UNPREDICTABLE HAL_arch_startup.S:138: Error: invalid pseudo operation -- `str sp,=vk_save_irq_mode_stack_ptr' HAL_arch_startup.S:139: Warning: writeback of base register is UNPREDICTABLE HAL_arch_startup.S:142: Error: invalid pseudo operation -- `str sp,=vk_save_irq_current_tcb_bottom' HAL_arch_startup.S:148: Warning: writeback of base register is UNPREDICTABLE HAL_arch_startup.S:159: Warning: writeback of base register is UNPREDICTABLE make[1]: *** [HAL_arch_startup.o] Error 1 make[1]: Leaving directory `/opt/vpos/hal/cpu' make: *** [all] Error 2
HAL_arch_startup.S 파일을 아래와 같이 수정한다. 여기서 숫자는 라인넘버를 뜻한다.
vh_software_interrupt:
vh_entering_swi:
str sp, vk_save_swi_mode_stack_ptr // 수정
stmfd sp!, {r0-r14}^
mrs r0, spsr_all
stmfd sp!, {r0, lr}
mrs r0, cpsr_all
orr r0, r0, #0x80
msr cpsr_all, r0
str sp, vk_save_swi_current_tcb_bottom // 수정
ldr r0, [sp, #8]
bl vk_swi_classifier
vh_leaving_swi:
ldmfd sp!, {r0, lr}
// msr spsr_all, r0
ldmfd sp!, {r0-r14}^
movs pc, lr
vh_irq:
vh_entering_interrupt:
sub lr, lr, #4
str sp, vk_save_irq_mode_stack_ptr // 수정
stmfd sp!, {r0-r14}^
mrs r0, spsr_all
stmfd sp!, {r0, lr}
str sp, vk_save_irq_current_tcb_bottom // 수정
bl vh_hwi_classifier
vh_leaving_interrupt:
ldmfd sp!, {r0, lr}
msr spsr_all, r0
ldmfd sp!, {r0-r14}^
movs pc, lr
vh_restore_thread_ctx:
mrs r1, cpsr_all
bic r1, r1, #0xfffffff0
mov r2, #0x2
cmps r1, r2
mov sp, r0
ldmfd sp!, {r0, lr}
msr spsr_all, r0
ldmfd sp!, {r0-r14}^
ldreq sp, vk_save_irq_mode_stack_ptr // 수정
ldrne sp, vk_save_swi_mode_stack_ptr // 수정
movs pc, lr
vh_save_thread_ctx:
mrs r1, cpsr_all
bic r1, r1, #0xfffffff0
mov r2, #0x2
cmps r1, r2
ldreq r2, vk_save_irq_current_tcb_bottom // 수정
ldrne r2, vk_save_swi_current_tcb_bottom // 수정
.rept 17
ldr r1, [r2], #4
str r1, [r0], #4 .endr
mov pc, lr
vk_save_swi_mode_stack_ptr: ------ 1
.long 0
vk_save_swi_current_tcb_bottom: ------ 2
.long 0
vk_save_irq_mode_stack_ptr: ------ 3
.long 0
vk_save_irq_current_tcb_bottom: -------- 4
.long 0
위의 수정한 코드를 보면, 기본의 코드에서 모두 '=' 를 제거한 것을 알 수 있다.
수정의 이유
왜 저렇게 코드를 수정하는지 궁금할 것이다.
예전의 경우, 컴파일 에러가 발생하는 부분의 '=' 만 제거를 했기 때문에 결과적으로 컨텍스트 스위칭이 제대로 일어나지 않아서 abort 가 발생하는 문제가 있었다.
코드의 아래 부분에 보이는 1 ~ 4 까지는 컨텍스트 스위칭이 발생해서 진입한 모드에서의 가장 처음 sp 와 가장 마지막으로 저장한 tcb 의 주소를 저장하는 곳이다. 이 주소에 저장된 값들은 컨텍스트 스위칭이 끝나고 돌아오거나 다시 재진입할 때, 이 값을 읽어들어서 저장하거나 복구한다. 따라서 이 값들이 제대로 설정되어 있지 않으면 컨텍스트 스위칭에 문제가 생기게 된다. 이것은 t32 같은 툴들을 통해서 디스어셈블 해서 보는 것이 훨씬 이해하기 편하다. 나중에 수정할 때를 대비해서 아래의 표를 추가한다. 총 4 개의 주소를 사용한다.
| vh_entering_swi | 0x30000204 |
| 0x30000208 | |
| vh_entering_interrupt | 0x3000020c |
| 0x30000210 | |
| vh_restore_thread_ctx | 0x3000020c |
| 0x300000204 | |
| vh_save_thread_ctx | 0x30000210 |
| 0x300000208 |
T32 로 본 각 주소 영역에 정보이다.
| addr/line | code | label | mnemonic |
| SR:30000204 | 00000000 | vk_save_swi_mode_stack_ptr | andeq r0,r0,r0 |
| SR:30000208 | 00000000 | vk_save_swi_current_tcb_bottom | andeq r0,r0,r0 |
| SR:3000020c | 00000000 | vk_save_irq_mode_stack_ptr | andeq r0,r0,r0 |
| SR:30000210 | 00000000 | vk_save_irq_current_tcb_bottom | andeq r0,r0,r0 |
숨겨진 이야기
SCV/OS 디버깅일지 에서 언급했듯이, 나는 처음에 저런 방식으로 꽁수(?)를 사용해서 컨텍스트 스위칭을 하게끔 했다. 하지만 여러가지 문제들이 발견되었다.
- printf 문이 t32 에서 실행시에 문자가 깨진다는 점
- vpos_shell 에서 입력문자를 계속해서 출력한다는 점(디버거로 확인결과, uart 루틴이 예상과는 다르게 동작하고 있었음)
- 이외 등등(data abort 발생)
내가 생각해볼때, 미묘한 타이밍 문제인 듯 보였다. 내가 추가한 어셈코드가 기존의 코드보다 몇 라인 더 많았기도 했기 때문에 이에 걸리는 오버헤드 때문인 듯 하다. 내가 생각하는 대로 적어보면, 다음과 같다.
- 컨텍스트 스위칭에 사용하는 타이머의 값 조절
- r3 레지스터의 사용으로 인한 문제
kmalloc.c 수정
메모리를 할당하는 루틴에서 수정해야할 부분이 있다. 2.95.3 버전에서는 전역 변수가 0 으로 초기화 되지만, 3.4.4 버전에서는 0xffffffff 으로 초기화 된다.
void* vk_malloc(unsigned nbytes)
{
Header *p, *prevp;
Header *heap_memory_alloc(void);
unsigned nunits;
nunits = (nbytes+sizeof(Header)-1)/sizeof(Header) + 1;
if ((prevp = freep) == 0xffffffff) { // 수정
/* no free list yet */
base.s.ptr = freep = prevp = &base;
base.s.size = 0;
}
for (p = prevp->s.ptr; ; prevp = p, p = p->s.ptr) {
if (p->s.size >= nunits) { /* big enough */
if (p->s.size == nunits) /* exactly */
prevp->s.ptr = p->s.ptr;
else { /* allocate tail end */
p->s.size -= nunits;
p += p->s.size;
p->s.size = nunits;
}
freep = prevp;
return (void *)(p+1);
}
if (p == freep) /* wrapped around free list */
if ((p = heap_memory_alloc()) == NULL)
return NULL; /* none left */
}
}
수정의 이유
아직 이유가 밝혀지진 않았지만, 전역변수를 선언할 때 0 으로 초기화를 시켜도 0xffffffff 으로 초기화되는 현상 때문에, 코드를 수정했다.
flash_write.c 수정
flash write 에 제대로 안되는 문제의 원인은 바로 전역변수가 0xffffffff 으로 초기화되는 문제였다. 그래서 역시 flash_write.c 에 있는 모든 targetOffset 을 모두 지운다.
#include "stdio.h"
#include "stdlib.h"
#include "printf.h"
#include "pthread.h"
#include "swi_handler.h"
#include "debug.h"
#include "linux/string.h"
#include "linux/delay.h"
#include "serial.h"
#include "vh_cpu_hal.h"
#include "vh_io_hal.h"
// Flash
#define _WR(addr,data) *((volatile unsigned int *)(addr))=(unsigned int)data
#define _RD(addr) ( *((volatile unsigned int *)(addr)) )
#define _RESET() _WR(targetAddress,0x00ff00ff)
#define BIT_ALLMSK (0xffffffff)
#define rUTRSTAT0 (*(volatile unsigned *)0x50000010) //UART 0 Tx/Rx status
#define RdURXH0() (*(volatile unsigned char *)0x50000024)
////////////////////////////////////////////////////////////////////////////
static int Strata_ProgFlash(unsigned int realAddr,unsigned int data);
static void Strata_EraseSector(int targetAddr);
static int Strata_CheckID(int targetAddr);
static int Strata_CheckDevice(int targetAddr);
//static int Strata_CheckBlockLock(int targetAddr);
static int Strata_BlankCheck(int targetAddr,int targetSize);
//static int _WAIT(void);
static unsigned int srcAddress;
static unsigned int targetOffset=0; // 0 으로 초기화함에도 불구하고 0xffffffff 으로 초기화됨
static unsigned int targetAddress; ㅜ
static unsigned int targetSize;
static int error_erase=0;
static int error_program=0;
///////////////////////////////////////////////////////////////////
unsigned int downloadAddress;
unsigned int downloadProgramSize;
unsigned int downloadImageOK=0;
/////////////////////////////////////////////////////////////////////////////////
int Strata_CheckID(int targetAddr)
{
_WR(targetAddr, 0x00900090);
udelay(40);
return _RD(targetAddr);
}
int Strata_CheckDevice(int targetAddr)
{
_WR(targetAddr, 0x00900090);
udelay(40);
return _RD(targetAddr+0x4);
}
#if 0
int Strata_CheckBlockLock(int targetAddr)
{
_WR(targetAddr, 0x00900090);
return _RD(targetAddr+0x8);
}
#endif
void Strata_EraseSector(int Address)
{
unsigned long ReadStatus;
unsigned long bSR5;
unsigned long bSR5_2;
unsigned long bSR7;
unsigned long bSR7_2;
_WR(Address, 0x00200020);
udelay(100);
_WR(Address, 0x00d000d0);
udelay(100);
_WR(Address, 0x00700070);
udelay(100);
ReadStatus=_RD(Address);
bSR7=ReadStatus & (1<<7);
bSR7_2=ReadStatus & (1<<(7+16));
while(!bSR7 | !bSR7_2) {
_WR(Address, 0x00700070);
udelay(100);
ReadStatus=_RD(Address);
bSR7=ReadStatus & (1<<7);
bSR7_2=ReadStatus & (1<<(7+16));
}
_WR(Address, 0x00700070);
udelay(100);
ReadStatus=_RD(Address);
bSR5=ReadStatus & (1<<5);
bSR5_2=ReadStatus & (1<<(5+16));
if (bSR5==0 && bSR5_2==0) {
printf("Block_%x Erase O.K. \n",Address);
} else {
printf("Error in Block Erasure!! = 0x%x\n", ReadStatus);
_WR(Address, 0x00500050);
udelay(100);
error_erase=1;
}
_RESET();
}
int Strata_BlankCheck(int targetAddr,int targetSize)
{
int i,j;
for (i=0; i<targetSize; i+=4) {
j=*((volatile unsigned int *)(i+targetAddr));
if (j!=0xffffffff) {
printf("E : 0x%x = 0x%x\n", (i+targetAddr), j);
return 0;
}
}
return 1;
}
int Strata_ProgFlash(unsigned int realAddr,unsigned int data)
{
volatile unsigned int *ptargetAddr;
unsigned long ReadStatus;
unsigned long bSR4;
unsigned long bSR4_2;
unsigned long bSR7;
unsigned long bSR7_2;
ptargetAddr = (volatile unsigned int *)realAddr;
_WR(realAddr, 0x00400040);
udelay(10);
*ptargetAddr=data;
_WR(realAddr, 0x00700070);
udelay(10);
ReadStatus=_RD(realAddr);
bSR7=ReadStatus & (1<<7);
bSR7_2=ReadStatus & (1<<(7+16));
while(!bSR7 || !bSR7_2)
{
_WR(realAddr, 0x00700070);
udelay(5);
ReadStatus=_RD(realAddr);
bSR7=ReadStatus & (1<<7);
bSR7_2=ReadStatus & (1<<(7+16));
}
_WR(realAddr, 0x00700070);
udelay(10);
ReadStatus=_RD(realAddr);
bSR4=ReadStatus & (1<<4);
bSR4_2=ReadStatus & (1<<(4+16));
if (bSR4==0 && bSR4_2==0) {
// printf("Successful Program!!\n");
;
} else {
printf("Error Program!!\n");
_WR(realAddr, 0x00500050);
udelay(10);
error_program=1;
}
_RESET();
return 0;
}
void Program28F128J3A (void)
{
int i,tmp=0;
printf("\n[ 28F128J3A Flash Writing Program ]\n\n");
if (!downloadImageOK) {
printf("Error! Write image not found!\n");
return;
}
vh_rINTMSK = vh_Interrupt_All_Disable;
targetSize=downloadProgramSize;
if(targetSize==0)
{
printf("\nThe data must be downloaded using ICE or USB from 0x31000000\n");
srcAddress=downloadAddress;
} else {
srcAddress=downloadAddress+4;
}
#ifndef MYSEO
AGAIN:
#endif
printf("Source size [0x?] : 0h~%xh\n\n",downloadProgramSize);
printf("Source base address = 0x%x\n",srcAddress);
printf("Target base address = 0x%x\n",targetAddress);
printf("Target offset = 0x%x\n",targetOffset); // 굳이 안 지워도 된다
printf("Target size = 0x%x\n",targetSize);
#if 0 // MYSEO
tmp = _RD(vh_SRAM_CTRL_REG);
tmp = tmp | 0x0406;
_WR(vh_SRAM_CTRL_REG, tmp);
_RESET();
#endif
tmp = Strata_CheckID(targetAddress) & 0xffff;
printf(" - Identification check (0x0089) = 0x%04x\n", tmp);
if ( tmp != 0x0089 ) // ID number = 0x0089
{
printf("Identification check error !!\n");
return;
}
tmp = Strata_CheckDevice(targetAddress) & 0xffff;
printf(" - Device check (0x0018) = 0x%04x\n", tmp);
if ( tmp != 0x0018 ) // Device number=0x0018
{
printf("Device check error !!\n");
return;
}
printf("\nErase the sector : 0x%x.\n", targetAddress);
for(i=0;i<targetSize;i+=0x20000)
{
Strata_EraseSector(targetAddress+i); // 수정
}
if(!Strata_BlankCheck(targetAddress,targetSize)) // 수정
{
#ifdef MYSEO
printf("Blank Check Error!!!\n");
return;
#else
printf("\n\nagain flash write");
goto AGAIN;
#endif
}
printf("\nStart of the data writing...\n");
for (i=0; i<targetSize; i+=4)
{
Strata_ProgFlash(i+targetAddress, *((unsigned int *)(srcAddress+i))); // 수정
if(i%0x10000==0xfffc)
printf("[0x%x]",(i+4)/0x10000);
}
printf("\nEnd of the data writing \n");
_RESET();
udelay(10);
printf("Verifying Start...\n");
for (i=0; i<targetSize; i+=4)
{
if (*((unsigned int *)(i+targetAddress)) !=*((unsigned int *)(srcAddress+i))) // 수정
{
printf("verify error src %08x = %08x\n", srcAddress+i, *((unsigned int *)(srcAddress+i)));
printf("verify error des %08x = %08x\n", i+targetAddress, *((unsigned int *)(i+targetAddress))); // 수정
#ifdef MYSEO
{
int in;
printf("\n\nagain flash write (y/n)? ");
in = getc();
printf("\n\n");
if (in == 'y')
goto AGAIN;
}
#else
printf("\n\nagain flash write");
goto AGAIN;
#endif
return;
}
}
printf("Verifying End!!!");
}
////////////////////////////////////////////////////////////////////////////
static int DownloadData(void)
{
int i,tmp;
unsigned short checkSum=0,dnCS;
unsigned int fileSize=10;
unsigned char *downPt;
downPt=(unsigned char *)downloadAddress;
printf("\nDownload Memoey Address = 0x%x\n",downloadAddress);
printf("\nFlash write Address = 0x%x\n",targetAddress);
printf("\nSTATUS : ");
vh_rINTMSK = BIT_ALLMSK;
tmp=RdURXH0(); //To remove overrun error state.
i=0;
while(i<fileSize) {
while(!(rUTRSTAT0&0x1));
*(downPt+i)=RdURXH0();
if(i==3) {
fileSize=*((unsigned char *)(downloadAddress+0))+
(*((unsigned char *)(downloadAddress+1))<<8)+
(*((unsigned char *)(downloadAddress+2))<<16)+
(*((unsigned char *)(downloadAddress+3))<<24);
}
if((i%4000)==0)
putc('#');
i++;
}
downloadProgramSize=fileSize-6;
for(i=4;i<(fileSize-2);i++)
checkSum+=*((unsigned char *)(i+downloadAddress));
dnCS=*((unsigned char *)(downloadAddress+fileSize-2))+ (*((unsigned char *)(downloadAddress+fileSize-1) )<<8);
if(checkSum!=dnCS) {
printf("Checksum Error!!! MEM : 0x%x DN : 0x%x\n",checkSum,dnCS);
return 0;
}
printf("\nDownload O.K.\n");
return downloadProgramSize;
}
void *flash_write(void *arg)
{
int cnt=0;
char *argv[1];
char **str;
int start_addr;
str = (char **)arg;
argv[0] = str[0];
start_addr = atoi(argv[0]);
if (!start_addr || start_addr < 0x100000 || start_addr > 0x2000000)
targetAddress=vh_VPOS_FLASH_BASE;
else
targetAddress=start_addr;
downloadAddress= vh_SDRAM_BASE + 0x00f00000;
cnt = DownloadData();
if (cnt) {
downloadImageOK=1;
Program28F128J3A ();
}
return (void *)0;
}
makefile 수정
컴파일시에
GNU ld version 2.14.90.0.6 20030820 arm-linux-ld: ERROR: /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_divsi3.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_divsi3.o) arm-linux-ld: ERROR: /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_dvmd_lnx.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_dvmd_lnx.o) arm-linux-ld: ERROR: /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_modsi3.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_modsi3.o) arm-linux-ld: ERROR: /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_udivsi3.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_udivsi3.o) arm-linux-ld: ERROR: /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_umodsi3.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/local/arm/lib/gcc-lib/arm-linux/2.95.3/libgcc.a(_umodsi3.o) arm-linux-ld: ERROR: /usr/bin/../arm-linux/lib/libc.a(memmove.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/bin/../arm-linux/lib/libc.a(memmove.o) arm-linux-ld: ERROR: /usr/bin/../arm-linux/lib/libc.a(memset.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/bin/../arm-linux/lib/libc.a(memset.o) arm-linux-ld: ERROR: /usr/bin/../arm-linux/lib/libc.a(memcpy.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/bin/../arm-linux/lib/libc.a(memcpy.o) arm-linux-ld: ERROR: /usr/bin/../arm-linux/lib/libc.a(wordcopy.o) uses hardware FP, whereas vpos_kernel-elf32 uses software FP Success: failed to merge target specific data of file /usr/bin/../arm-linux/lib/libc.a(wordcopy.o) make[1]: *** [vpos_kernel-elf32] Error 1 make[1]: Leaving directory `/opt/vpos/objs' make: *** [all] Error 2
위와 같은 에러가 난다면, makefile 을 수정해야 한다.
... CFLAGS = -O0 -Wall -Wstrict-prototypes -fPIC -mshort-load-bytes -nostdinc -nostartfiles -nostdlib -march=armv4 -mtune=arm9tdmi -fomit-frame-pointer -fno-builtin -mapcs-32 $(INCLUDE) // 수정 OCFLAGS = -O binary -R .note -R .comment -R .stab -R .stabstr -S ...
'-msoft-float' 를 제거하면 된다.
컴파일 및 수행
이제 컴파일 해보자! 제대로 수행되었다면, 생성된 bin 파일을 가지고 제대로 동작하는 지 테스트해보자!