아이지 시스템 http://www.aijisystem.com 에서 만든 SMDK 2410 보드를 포팅한 과정을 설명하고 있다.
이 보드의 특징이라고 보자면 다음과 같다.

  1. 2 개의 FLASH(NOR, NAND) 를 가지고 있다. 점퍼 셋팅을 통해 둘 중에 하나를 BOOT FLASH 로 지정할 수 있다.
  2. 제조사에서 제공되는 부트로더, 커널이미지, 파일시스템이 없다.

보드의 스펙은 다음과 같다.

Processor S3C2410 (ARM920T)
Boot Rom NOR flash AMD 8Mbit 1EA (default)
INTEL Strata 48MB(16MBX3) (option)
NAND flash SMC 64MB 1 EA
Memory SDRAM 64MB (32MB x 2)
LCD 240 x 320 TFT/STN LCD and Touch Interface
UART 3 ch UART with IrDA
USB Host 1ch, Host/ Device 1ch
Ethernet 1 ch
PCMCIA 1 ch
SMC/SD host (MMC) Interface Yes
RTC/ IIC/ IIS/ SPI Interface Yes
ADC/EINT/Extension Interface Yes
JTAG port Yes

호스트 PC 설정하기

본격적인 타겟보드의 설정에 앞서 간단하게 나마 호스트 PC 의 설정에 대해서 알아보기로 한다.
호스트 PC 는 멀티부팅을 사용하고 있다. 각각 debian 과 windows 2003 을 사용하고 있다. windows 에서는 vmware 를 이용해서 redhat8 을 기준으로 컴파일 했다.

OS 버전 용도
DEBIAN EACH tftp, dhcp 서버
WINDOWS 2003 컴파일 및 2410 test program 을 통한 파일 업로드 & flash write

이번에는 호스트 PC 와 타겟보드의 네트워크 환경에 대해서 알아보자.

호스트 PC 166.104.144.101(eth0), 192.168.10.1(eth1)
타겟 보드 192.168.10.81(eth0)

호스트 PC 의 eth1 과 타겟보드 eth0 이 크로스 케이블로 연결되어 있다. 호스트 PC 에서의 설정은 개발환경_구축하기 을 참고하기 바란다.

툴 체인 설치하기

가장 애를 먹었던 부분이 바로 툴 체인 이었다. 아이러니하게도, smdk2410 을 판매한 아이지시스템에서는 툴체인을 제공하지 않는다. 때문에 나 나름대로 인터넷을 뒤져서 arm 계열의 툴체인들을 설치하고 지우기를 반복했다. 가장 흔했던 문제는 다음과 같다.

  1. u-boot 컴파일 도중 에러
  2. kernel 컴파일 도중 에러
  3. 커널 이미지를 타겟보드의 메모리에 올리고 실행하는 도중에 CRC 에러

처음에는 단순히 소스파일의 문제인 줄만 알고 코드를 이리저리 수정해봤다. 하지만, 마침내 제대로 컴파일되는 툴체인을 찾았기 때문에 문제는 결국 툴체인에 있었다고 결론 내릴 수 있었다. 툴체인은 일일이 소스 파일을 컴파일해서 만드는 방법이 가장 좋다고 생각되지만, 실제로 만들어봤지만, CRC 에러가 발생했다. 여기서는 ELDK 라고 하는 이미 만들어져 있는 툴체인을 사용할 것이다.
다운로드 주소는 다음과 같다. http://www.denx.de/wiki/DULG/ELDKAvailability
현재 최신버전은 4.0 이다. 하지만, 사용해본 결과 컴파일시에 에러가 발생했다. 아마도 컴파일러 버전이 너무 높아서 인 듯하다.
여기서는 2.1.0 을 사용한다. http://mirror.switch.ch/ftp/mirror/eldk/eldk/2.1.0/eldk-arm-linux-x86/ 아래의 디렉토리와 파일을 모두 다운로드받는다.
리눅스에서는 ncftp 를 사용해서 편리하게 받을 수 있다.

#ncftp ftp://mirror.switch.ch/mirror/eldk/eldk/
ncftp /mirror/eldk/eldk > cd 2.1.0/
ncftp /mirror/eldk/eldk/2.1.0 > get -R eldk-arm-linux-x86
...
ncftp /mirror/eldk/eldk/2.1.0 > bye
#for file in tools/bin/rpm tools/usr/lib/rpm/rpmd install ELDK_MAKEDEV ELDK_FIXOWNER ; do /bin/chmod +x ../eldk-arm-linux-x86/$file; done

이렇게 하면, 설치를 위한 모든 준비가 끝난셈이다. 설치하기 위해 다음을 실행한다.

./install -d /usr/local/arm        # 설치디렉토리는 /usr/local/arm 

설치가 끝나면, PATH 를 걸어준다. 컴파일러는 'arm_920TDI-gcc' 이다.

부트로더 만들기

여기서는 u-boot 를 사용하도록 한다. http://sourceforge.net/projects/u-boot 에서 받는다. 현재 최신 버전은 1.1.4 이다. 이것을 사용할 것이다. 참고로 SMDK 2410 보드의 경우, 운 좋게도(?) u-boot 에서 기본으로 지원하고 있다. 그래서 별다른 설정파일의 수정없이 그대로 컴파일하면 된다.
압축을 풀고, u-boot-1.0.0 디렉토리에 들어가서 Makefile 을 수정해야 한다. 약 52 줄쯤에 아래와 같은 코드가 있다.

...
CROSS_COMPILE = arm_920TDI-     ## 추가
 
ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),ppc)
CROSS_COMPILE =
else
ifeq ($(ARCH),ppc)
CROSS_COMPILE = ppc_8xx-
endif
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
ifeq ($(ARCH),i386)
ifeq ($(HOSTARCH),i386)
CROSS_COMPILE =
else
CROSS_COMPILE = i386-linux-
endif
endif
ifeq ($(ARCH),mips)
CROSS_COMPILE = mips_4KC-
endif
endif
endif
 
export	CROSS_COMPILE
...

위와 같이 추가해준다. 이제 컴파일을 할 차례다.

#make clobber
#make smdk2410_config
#make

만일 컴파일 도중에

cc1 : error : invalid option 'abi=apcs-gnu'

와 같은 에러가 뜬다면, cpu/arm920t/config.mk 파일을 열어서 '-mabi=apcs-gnu' 라는 부분을 지운다.
또한 컴파일 도중에

flash.c:75: 'PHYS_FLASH_2' undeclared (first use in this function)

위와 같은 에러가 발생한다면, 해당 smdk2410/flash.c 파일의 75 번째 줄의 'case 1' 문의 break 만 남기고 모두 삭제한다.

u-boot 버전 1.1.5 이상부터 컴파일 에러가 발생한다. 에러 메세지는 다음과 같다.

arm_920TDI-gcc : bzlib.o : No such file or directory
arm_920TDI-gcc : unrecgnized option '-MQ'
...

해결방법은 높은 버전의 gcc 를 가진 툴 체인을 사용하는 것이다. 앞에서 설치한 툴 체인의 경우, gcc 의 버전이 2.95.3 이다. 하지만, gcc 3.3 대 버전의 툴 체인(aesop)을 사용해서 컴파일한 결과, 정상적으로 컴파일 되었다.

에러없이 컴파일 되었다면, u-boot.bin 이란 파일이 생성되었을 것이다. 이제 직접 FLASH 에 써줄 차례다. 제조사에서 제공하는 테스트 프로그램을 이용해서 write 할 것이다. '87. NOR flash Program → b : 28F128J3A(16MB) x 2 → y' 을 선택한다. 시리얼 포트로 앞에서 만든 u-boot.bin 파일을 전송한다.
주소 번지는 '0x0' 로 입력하면, write 가 된다. 에러없이 마무리 되었다면, 점퍼 셋팅을 하고 부팅을 해보자!!
부트로더 메세지가 뜬다면 성공이다.

u-boot 의 경우, 명령어 'bdinfo' 를 치면 기본적으로 가지고 있는 환경 변수들이 출력된다. 나중에 커널과 램디스크를 다운로드 하기 위해서는 ethaddr 와 ipaddr 환경 변수가 세팅되어 있어야 한다. 물론 'setenv' 명령어로 지정해 줄 수는 있지만, 매번 지정해주기는 불편하다.

그래서 미리 정의해줄 수가 있다. 바로 컴파일 하기전에 include/configs/smdk2410.h 파일을 열어서 몇 가지 변수들을 추가해주면 된다. 또한 정의된 변수들을 flash 에 write 하기 위해서는 flash 에 대한 설정도 필요하다.

#define CONFIG_BOOTDELAY	3
/*#define CONFIG_BOOTARGS    	"root=ramfs devfs=mount console=ttySA0,9600" */
#define CONFIG_ETHADDR	11:22:33:44:55:66            // MAC 주소 지정
#define CONFIG_NETMASK          255.255.255.0
#define CONFIG_IPADDR		192.168.10.81            // IP 주소 지정
#define CONFIG_SERVERIP		192.168.10.1              // 서버 IP 주소 지정
/*#define CONFIG_BOOTFILE	"elinos-lart" */
/*#define CONFIG_BOOTCOMMAND	"tftp; bootm" */
...
#define	CFG_LOAD_ADDR		0x33000000	/* default load address	*/
...
/*-----------------------------------------------------------------------
 * FLASH and environment organization
 */
 
#define PHYS_FLASH_SIZE		0x02000000 /* 32 MB */
#define PHYS_FLASH_BANK_SIZE	0x02000000 /* 32 MB Banks */
#define PHYS_FLASH_SECT_SIZE	0x00040000 /* 256 KB sectors (x2) */
 
#define CFG_FLASH_BASE		PHYS_FLASH_1
 
/*
 * FLASH and environment organization
 */
#define CFG_MAX_FLASH_BANKS	1	/* max number of memory banks		*/
#define CFG_MAX_FLASH_SECT	128  /* max number of sectors on one chip    */
 
/* timeout values are in ticks */
#define CFG_FLASH_ERASE_TOUT	(25*CFG_HZ) /* Timeout for Flash Erase */
#define CFG_FLASH_WRITE_TOUT	(25*CFG_HZ) /* Timeout for Flash Write */
 
 
#define CFG_ENV_IS_IN_FLASH	1
#define CFG_ENV_ADDR         (PHYS_FLASH_1 + 0x40000)	/* Addr of Environment Sector, 1 sector	boundary*/
#define CFG_ENV_SIZE         0x40000	/* Total Size of Environment Sector, 256k	*/
#define CFG_ENV_SECT_SIZE    0x40000 /* Size of the Environment Sector, 128k x 2 = 256k	*/

컴파일한 후에, FLASH 에 write 후에 보면, 위의 설정이 적용되었음을 알 수 있다.

커널 이미지 만들기

여기서는 2.4.18 커널을 사용할 것이다. 몇가지 패치들이 필요한 데, 다음은 커널이미지를 만드는 데 필요한 파일들이다.

  1. linux-2.4.18.tar.gz
  2. patch-2.4.18-rmk7
  3. patch-2[1].4.18-rmk7-swl8a

먼저 커널의 압축을 풀어 /usr/src 아래에 복사한다. 커널 디렉토리 아래에 두개의 패치 파일을 복사한다.

#cd /usr/src
#patch -p1 < patch-2.4.18-rmk7
#patch -p1 < patch-2.4.18-rmk7-swl8a
#make menuconfig

나오는 메뉴에서 'Load an Alternate Configuration File' 를 선택해서 'arch/arm/def-configs/s3c2410ramdisk' 을 입력한다. 그리고 빠져나온다. Makefile 을 아래와 같이 수정해야 한다.

CROSS_COMPILE   = arm_920TDI-     ## 추가
...
...
...
Version: dummy
        @rm -f include/linux/compile.h
 
uImage: vmlinux      ## 추가
        $(OBJCOPY) -S -O binary vmlinux vmlinux.bin
        gzip -vf9 vmlinux.bin
        mkimage -A arm -O linux -T kernel -C gzip -a 0x30008000 \
        -e 0x30008000 -n 'ARM Linux-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)' \
        -d ./vmlinux.bin.gz $@
        cp $@ /tftpboot
 
boot: vmlinux
        @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot

위에서 추가해준 것은 커널이 로딩되었을때의 메모리상의 주소를 지정해주는 것이다. Makefile 를 수정하였으면, 커널이미지를 만든다.

#make dep
#make uImage

오류없이 컴파일 되었다면, /tftpboot 아래에 uImage 파일이 생성되었을 것이다.

램 디스크 만들기

여기서는 이미 만들어져 있는 램디스크 파일(ramdisk.gz)을 사용할 것이다.

메모리상에 커널과 파일시스템 올리기

여기서는 호스트 PC 에서의 tftp 설정에 대해서는 설명하지 않겠다. 모두 정상적으로 동작한다는 가정하에서 설명할 것이다.

SMDK2410 # tftp 33000000 uImage      // 메모리의 0x33000000 번지에 커널이미지를 올린다.
SMDK2410 # tftp 30800000 ramdisk.gz   // 메모리의 0x30800000 번지에 램디스크를 올린다.
SMDK2410 # bootm 33000000            // 메모리의 0x33000000 번지로 부팅한다.

로그인 프롬프트가 보인다면, 성공이다.

커널과 램디스크 FLASH 에 저장하기

하지만 여기서 끝이 아니다. 현재는 커널과 램디스크가 램 상에 올라가 있기 때문에, 전원이 꺼질경우에는 위의 작업을 반복해주어야 한다.
이런 수고를 덜기위해, 커널과 램디스크를 flash 에 저장해야 한다.
앞에서도 설명했듯이 현재 flash(nor 기준) 는 총 128 개의 block 으로 되어 있다. 여기서 0 번은 U-BOOT(u-boot.bin) 가 저장되어 있고, 1번은 u-boot 에서 사용하는 환경변수값들이 저장되어 있다. 그러므로 0 ~ 1 번을 제외한 block 는 사용자 마음대로 쓸 수 있다.
우선, 커널 이미지(uImage) 부터 쓸 것이다. 내가 만든 uImage 의 경우, 용량이 0x987a0 이다. 약 3개의 block 를 필요로 하는 용량이다. 2 ~ 4 번의 block 에 커널을 쓸 것이다.
먼저 write 를 하기전에, protect 를 풀어주고, 해당 block 을 erase 해야 한다. 또한 write 후에는 protect 를 다시 걸어주어야 한다. 아래와 같은 명령어로 커널이미지를 flash 에 쓸 수 있다.

SMDK2410 # tftp 33000000 uImage     // 다운로드 하고 나서 파일의 용량이 표시된다. HEX 값을 잘 알아두어야 한다.
SMDK2410 # protect off 1:2-4
SMDK2410 # erase 1:2-4
...
...
...
SMDK2410 # cp.b 33000000 00080000 0x987a0     // 여기서는 33000000 번지에 업로드한 커널 이미지(uImage) 를 2 번 block(0x00080000) 에서 부터 0x987a0 만큼 write
SMDK2410 # protect on 1:2-4

타겟보드 재부팅을 한 후에,

SMDK2410 #  bootm 80000

커널이 부팅이 될 것이다. 아직 램 디스크를 올리지 않았기 때문에 커널 패닉이 발생한다. 이번에는 램디스크를 올릴 차례다. ramdisk.gz 파일을 바로 flash 에 올리는 것이 아니라, hex 파일의 형태로 변환시켜야 한다.

#mkimage -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.fat

에러없이 수행되었다면, ramdisk.fat 이라는 파일이 생겼을 것이다. 이 파일을 울트라에디터로 열어보면, hex 코드라는 것을 알 수 있다.
이제 이 파일을 flash 에 write 해보자! 앞에서 커널을 4 번째 block 까지 사용했기 때문에 여기서는 5 번째 block 부터 사용한다. 램디스크의 경우, 용량이 커널에 비해 크기 때문에 여기서는 넉넉하게 5 ~ 100 block 를 사용하기로 했다.

SMDK2410 # tftp 30800000 ramdisk.fat
SMDK2410 # protect off 1:5-100
SMDK2410 # erase 1:5-100
SMDK2410 # cp.b 30800000 00140000  0x2e817e    // 00140000 번지는 5 번째 block 이다.
SMDK2410 # protect on 1:5-100

도중에 에러없이 수행되었다면, 성공이다. 이제 보드를 재부팅하고, 부트로더 프롬프트에서

SMDK2410 # bootm 80000 140000          // 0x80000 은 커널이 저장된 주소이고, 140000 은 램디스크가 저장된 주소이다.

위와 같이 입력한다.

자동 부트 기능 추가하기

부트로더 프롬프트에다가 매번 명령어를 입력하기는 매우 번거로운 일이다. 그래서 부팅 후, 몇 초 동안 아무런 입력이 들어오지 않으면 자동으로 임의의 명령어가 실행되도록 할 수 있다.

SMDK2410 # setenv bootdelay 5     /* 5 초를 기다린다 */
SMDK2410 # setenv bootcmd 'bootm 80000 140000'  /* 5 초 후에 자동으로 실행할 명령어 */
SMDK2410 # saveenv      /* 앞의 설정을 Flash 에 저장 */

이제 보드를 재부팅 해보자!!

주의할 것들

위의 실행결과, 커널 패닉이 발생할 것이다. 이유는 커널에서 램디스크를 찾지못하기 때문이다. 이 부분을 생각해보자.
분명히 앞에서 나는 각기 다른 block 에 커널 이미지와 램디스크를 write 했다. 또한 부팅시에 각각의 시작 주소를 지정했다. 하지만, 커널에서는 램디스크를 찾지못했다.

프로그램은 flash 가 아닌 RAM 상에 로딩된 상태에서 실행된다. 커널은 0x30800000 에서 램디스크를 찾는다. 이 주소 역시 커널 컴파일시에 지정해줄 수 있다. 앞에서 커널 패치를 했다면, 커널 소스의 include/asm-arm/arch-s3c2410 아래의 S3C2410.h 에서

/* keywoard : Phy2Vir */
#define S3C2410_MEM_SIZE     (64*1024*1024) 
#define MEM_SIZE            S3C2410_MEM_SIZE
	/* Used in arm/kernel/setup.c */
 
                        /* used in asm/kernel/setup.c and asm/arch/arch.c  */
#define PA_SDRAM_BASE         0x30000000 /* used in asm/arch/arch.c     */
#define RAMDISK_DN_ADDR       0x30800000 /* used in asm/arch/arch.c     */    <<<-----  바로 여기다!!
#define ZIP_RAMDISK_SIZE      (10*1024*1024)  /* used in asm/arch/arch.c  */
 
/* if CONFIG_BLK_DEV_RAM_SIZE not defined */
#define BLK_DEV_RAM_SIZE      (8*1024*1024)  

'RAMDISK_DN_ADDR' 이 바로 그것이다.
아무튼 여기서는 0x30800000 에서 램디스크를 찾도록 컴파일 되어졌다.
그래서 여기서는 flash 에 저장되어 있는 램디스크를 부팅시에 커널이 찾는 0x30800000 번지로 복사해주어야 한다.
u-boot 에서 소스코드 lib_arm/armlinux.c 파일을 아래와 같이 수정한다.

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
		     ulong addr, ulong *len_ptr, int verify)
{
	DECLARE_GLOBAL_DATA_PTR;
 
	ulong len = 0, checksum;
	ulong initrd_start, initrd_end;
	ulong data;
	void (*theKernel)(int zero, int arch, uint params);
	image_header_t *hdr = &header;
	bd_t *bd = gd->bd;
 
#ifdef CONFIG_CMDLINE_TAG
	char *commandline = getenv ("bootargs");
#endif
 
	theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
 
	/*
	 * Check if there is an initrd image
	 */
	if (argc >= 3) {
		SHOW_BOOT_PROGRESS (9);
 
		addr = simple_strtoul (argv[2], NULL, 16);
 
		printf ("## Loading Ramdisk Image at %08lx ...\n", addr);  // 0x140000
 
		/* Copy header so we can blank CRC field for re-calculation */
/*
#ifdef CONFIG_HAS_DATAFLASH
		if (addr_dataflash (addr)) {
			read_dataflash (addr, sizeof (image_header_t),
					(char *) &header);
		} else
#endif
*/
			memcpy (&header, (char *) addr,
				sizeof (image_header_t));
 
		if (ntohl (hdr->ih_magic) != IH_MAGIC) {
			printf ("Bad Magic Number\n");
			SHOW_BOOT_PROGRESS (-10);
			do_reset (cmdtp, flag, argc, argv);
		}
 
		data = (ulong) & header;
		len = sizeof (image_header_t);
 
		checksum = ntohl (hdr->ih_hcrc);
		hdr->ih_hcrc = 0;
 
		if (crc32 (0, (char *) data, len) != checksum) {
			printf ("Bad Header Checksum\n");
			SHOW_BOOT_PROGRESS (-11);
			do_reset (cmdtp, flag, argc, argv);
		}
 
		SHOW_BOOT_PROGRESS (10);
 
		print_image_hdr (hdr);   /* 앞에서 ramdisk.fat 생성시에 mkimage 를 이용해서 만든 커널 헤더를 읽어서 램디스크 정보 출력  */
 
		data = addr + sizeof (image_header_t);
		len = ntohl (hdr->ih_size);
 
#ifdef CONFIG_HAS_DATAFLASH    /* 실행 안됨 */
		if (addr_dataflash (addr)) {
			read_dataflash (data, len, (char *) CFG_LOAD_ADDR);
			data = CFG_LOAD_ADDR;
		}
#endif
 
		memcpy((char *)0x30800000, (char *)data, len);        // 추가할 부분 (data : 0x140040, len : 0x34ec24)
 
		if (verify) {
			ulong csum = 0;
 
			printf ("   Verifying Checksum ... ");
			csum = crc32 (0, (char *) data, len);
			if (csum != ntohl (hdr->ih_dcrc)) {
				printf ("Bad Data CRC\n");
				SHOW_BOOT_PROGRESS (-12);
				do_reset (cmdtp, flag, argc, argv);
			}
			printf ("OK\n");
		}
 
		SHOW_BOOT_PROGRESS (11);
 
		if ((hdr->ih_os != IH_OS_LINUX) ||
		    (hdr->ih_arch != IH_CPU_ARM) ||
		    (hdr->ih_type != IH_TYPE_RAMDISK)) {
			printf ("No Linux ARM Ramdisk Image\n");
			SHOW_BOOT_PROGRESS (-13);
			do_reset (cmdtp, flag, argc, argv);
		}

위의 함수 do_bootm_linux 는 bootm 명령어를 실행할 때, 호출되는 함수이다. 만일 'bootm 80000 140000' 라고 입력했을때, 위에서 추가해준 부분에 인자 값이 들어간다. 그래서 결국 0x00140000 에 저장되어 있는 램디스크를 0x30800000 으로 복사하는 루틴이 추가되었다. 여기서 data 의 값이 0x140000 이 아닌 0x140040 이 들어간 이유는 처음에는 이미지 헤더 구조체가 차지하는 용량이 0x40 이기 때문이다.
이로써, smdk2410 보드 자유롭게 커널과 램디스크를 올려보고, 부팅할 수 있게 되었다.

  • computer/rtcclab/smdk_2410_보드_포팅하기.txt
  • Last modified: 3 years ago
  • by likewind