———狗拿耗子第六篇
对gcc有过一些编程经验、且对嵌入式开发感兴趣的朋友,最适合看这篇文章。我的工作是做上层业务软件的开发,学习嵌入式开发的目的是,了解业务软件是怎么从代码一步步跑在了ppc或arm上面的。当时对gcc一无所知,不知道从哪下手学习嵌入式开发。在网上花了850块买了S3C2410的开发板,照着附送的光盘里面的简单例子,玩了一年多时间的ADS1.2,移植成功了uCos。机缘巧合,有一次下载了WinArm,从此真正走上了嵌入式开发的大路。
WinArm、Cygwin与Skyeye的共同点是,它们是gnu圈子里面的,在嵌入式开发的世界中,gnu无处不在。要编译跑在arm上的代码,必须在linux环境中,首先编译出一个交叉编译工具集,包含gcc、ld等等。这一下子吓坏了不少人,包括我,太复杂了。不过不用担心,已经有个好心的老外替我们操了心。这就是WinArm,在Windows平台下编译arm代码的开发工具集。随后有了一个新问题,编译好的程序运行在哪里呢?又有一个好心人,还是一个中国人,在gdb模拟器的基础上开发出来了SkyEye,可以模拟arm开发板,如S3C2410的开发板,包括板子上的串口、网口与Nand Flash。可是SkyEye需要运行在linux中,怎么办?最后需要提到的一个好心人,XXX,他或者他们开发出来了在Windows上模拟linux的软件Cygwin。gnu圈子里面,牛人可是刚刚的!
用ADS1.2,调通了串口、网口、Nand Flash,移植了uCos。用WCS,调通了串口、网口、Nand Flash,并移植成功了YAFFS2。下阶段的目标是用WCS,将uCos、YAFFS2以及Lwip 揉在一块跑起来。o了,让我们看正文吧。
1、WinArm的下载与安装
可从http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/下载WinArm的安装包,目前的版本还是20060606。下载完成后直接解压到某个文件夹,即可直接使用,方便啊!
2、WinArm测试用例
首先给出测试WinArm的完整用例,详细介绍参考《Initialize》与《Code Road》。共包含6个文件,其中target.ld拷贝自,在shell下执行arm-elf-ld –verbose后的输出。在shell下执行mk_cr.bar,最终生成目标文件test_winarm.elf。WinArm的路径为F:\\WinARM\\。
2.1 mk_depend
CC = arm-elf-gcc SED = sed_WinARM RM = rm %.d: %.c @echo generate depends for $<;\\ $(CC) -M $< > tmp;\\ $(SED) 's/\\($*\\)\\.o:/\\1.o $@ :/g' codes = $(wildcard *.c) depends = $(codes:.c=.d) all: $(depends) sinclude $(depends) @echo all depends have been generated! 2.2 mk_target CC = arm-elf-gcc LD = arm-elf-ld MCU = arm7tdmi WINARM_PATH = f:/winarm crt_i = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crti.o crt_begin = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crtbegin.o crt_0 = $(WINARM_PATH)/arm-elf/lib/crt0.o crt_end = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crtend.o crt_n = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crtn.o libs_to_search = --start-group -lgcc -lc --end-group lib_search_path = -L$(WINARM_PATH)/lib/gcc/arm-elf/4.1.1 -L$(WINARM_PATH)/lib/gcc -L$(WINARM_PATH)/arm-elf/lib target = test_winarm.elf codes = $(wildcard *.c) objects = $(codes:.c=.o) depends = $(codes:.c=.d) all_objects = $(crt_i) $(crt_begin) $(crt_0) $(objects) $(crt_end) $(crt_n) $(target): $(objects) sinclude $(depends) $(objects): $(CC) -c -mcpu=$(MCU) -DCONFIG_YAFFS_DIRECT -DWT133664_PORT -DCONFIG_YAFFS_YAFFS2 -gdwarf-2 -O0 $< -o $@ $(LD) -o $@ $(all_objects) $(lib_search_path) $(libs_to_search) 2.3 mk_cr.bat path = f:\\WinARM\\\bin;f:\\WinARM\\bin call make -f mk_depend call make -f mk_target 2.4 SysCall.c #include void *_sbrk_r(struct _reent *reent, ptrdiff_t incr) { } void _exit(int code) { } static char heap[0x100000]; static unsigned long level = 0; char *expected = heap + level; level += incr; return expected; 2.5 main.c int main() { } 3、试试WinArm自带的insight arm-elf-insight.exe是图形化的GDB调试工具,位于F:\\WinARM\\\insight\\bin\\。 file->open,选中test_winarm.elf; run->connect to target->target->simulator,使用GDB Simulator; run->run; 后续的使用与调试Windows软件一样,怎么样,你的测试用例跑起来了吗? 4、Cygwin的下载、安装与运行 推荐《Cygwin完全下载指南》,大家一定要认真看,过程有点复杂。文章写得很好,当初我就是靠它下完了Cygwin,可惜该文的作者已不可考。安装过程很简单,这里不再叙述。可以在Cygwin下面编译用于arm的交叉编译工具,不过我们不需要,因为已经有了WinArm。 安装完成后,打开cygwin,在shell中输入gcc –v,看看gcc版本是否高于或等于3.4.4。 5、SkyEye的下载与安装 建议下载SkyEye-1.2.6,最新的1.2.8在我的环境中运行有问题:( google一下即可下载到1.2.6,用WinRAR将它解压到Cygwin某个路径下面,我放在/home/skyeye-1.2.6_rc1。自带文件/home/skyeye-1.2.6_rc1/INSTALL叙述了详细的安装过程。 进入到/home/skyeye-1.2.6_rc1,执行./configure; 接着执行make,生成skyeye.exe; 最后执行make install。 如果在安装过程中产生了错误,只有去google上找答案了。下面列出我碰到的一些问题: 5.1 skyeye无法load elf文件 3 在最近一次的安装过程中,发现skyeye无法load elf文件,原因是skyeye使用的bfd lib无法正确分析WinArm编译出来的elf文件。解决方法如下: 在utils\\main\\skyeye.c中,有两个tea_load_exec(),前面一个不使用bfd lib来load elf,而后面用bfd lib来load elf。 通过在这两个函数中增加打印,发现make NO_BFD=1没有起作用,skyeye始终用bfd lib来load elf。 在config.h中手工将define HAVE_BFD_H 1与#define HAVE_LIBBFD 1注释掉,再编译,则问题解决。 5.2 无法打开nand.dump 在windows中,右键点击nand.dump,将“只读”属性钩掉,重新运行skyeye即可。 5.3 no bank found 在第一次运行skyeye时,出现no bank found,关闭后重新启动skyeye即可。 6、配置skyeye 在skyeye.conf放在skyeye.exe同一个目录下,其内容如下: # skyeye config file for S3C2410X cpu: arm920t mach: s3c2410x # physical memory #sram,4kB mem_bank: map=M, type=RW, addr=0x00000000, size=0x00001000 #sdram,32MB mem_bank: map=M, type=RW, addr=0x30000000, size=0x02000000 # all peripherals I/O mapping area #内部寄存器的地址范围 mem_bank: map=I, type=RW, addr=0x48000000, size=0x20000000 #外部io的地址范围 mem_bank: map=I, type=RW, addr=0x19000300, size=0x00000020 #网口芯片为cs8900a,其基地址对应于外部io地址0x19000300,使用EInt9产生IRQ net: type=cs8900a, base=0x19000300, size=0x20,int=9, mac=12:14:16:18:1a:1c, ethmod=tuntap, hostip=166.16.6.1 #用stdio来模拟串口的输入输出 uart: fd_in=/dev/stdin, fd_out=/dev/stdout #nand flash为K9F1208 nandflash: type=s3c2410x,name=K9F1208U0B,dump=./nand.dump 7、安装tap-win32 为了测试网口,需要在windows下安装一个软件tap-win32。这是一款虚拟网口软件,可google一下它的下载与安装。 4 8、skyeye测试项 串口的读写; nand flash读写; 网口的读写; 中断的产生与处理。 测试用代码包含:crti.o、crtbegin.o、crt0.o、crtend.o、crtn.o、ir.s、HandleIRQ.c、SysCall.c、main.c、Uart.c、K9F1208.c、CS8900.c。其中crt文件为WinArm自带的.o,详细情况参考《Initialize》。 9、测试用例 9.1 target.ld runtime address起始于0x30000000,IR.o放在最前面,入口为_start,栈起始于_stack。详细介绍参考《Initialize》与《CodeRoad》。 OUTPUT_FORMAT(\"elf32-littlearm\ \"elf32-littlearm\") OUTPUT_ARCH(arm) ENTRY(_start) SEARCH_DIR(\"/c/WinARM/arm-elf/lib\"); SECTIONS { /* Read-only sections, merged into text segment: */ PROVIDE (__executable_start = 0x30000000); . = 0x30000000; .init : { IR.o(.text); KEEP (*(.init)) } =0 .text : { *(.text .stub .text.* .gnu.linkonce.t.*) KEEP (*(.text.*personality*)) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) *(.glue_7t) *(.glue_7) } =0 .fini : { KEEP (*(.fini)) } =0 PROVIDE (__etext = .); PROVIDE (_etext = .); 5 PROVIDE (etext = .); .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } .rodata1 : { *(.rodata1) } .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } __exidx_start = .; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } __exidx_end = .; .eh_frame_hdr : { *(.eh_frame_hdr) } .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ . = ALIGN(256) + (. & (256 - 1)); /* Exception handling */ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } /* Thread Local Storage sections */ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } .preinit_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array)) PROVIDE_HIDDEN (__preinit_array_end = .); } .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) PROVIDE_HIDDEN (__init_array_end = .); } .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(.fini_array)) KEEP (*(SORT(.fini_array.*))) PROVIDE_HIDDEN (__fini_array_end = .); } .ctors : { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it 6 doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin*.o(.ctors)) /* We don't want to include the .ctor section from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) } .dtors : { KEEP (*crtbegin*.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) } .data : { __data_start = . ; *(.data .data.* .gnu.linkonce.d.*) KEEP (*(.gnu.linkonce.d.*personality*)) SORT(CONSTRUCTORS) } .data1 : { *(.data1) } _edata = .; PROVIDE (edata = .); __bss_start = .; __bss_start__ = .; .bss : { *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. FIXME: Why do we need it? When there is no .bss section, we don't pad the .data section. */ . = ALIGN(. != 0 ? 32 / 8 : 1); 7 } _bss_end__ = . ; __bss_end__ = . ; . = ALIGN(32 / 8); . = ALIGN(32 / 8); __end__ = . ; _end = .; PROVIDE (end = .); /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } .stack 0x30600000 : { _stack = .; __stack = .; *(.stack) 8 } .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) } /DISCARD/ : { *(.note.GNU-stack) } } 9.2 IR.s 用于处理IRQ的“胶水”代码,最终处理函数为HandleIRQ()。它位于0x30000000,main()会将irq的处理,重定向到0x30000000。 .text .code 32 stmfd sp!,{r0-r12,lr} bl HandleIRQ ldmfd sp!,{r0-r12,lr} subs pc,lr,#4 .end 9.3 HandleIRQ.c #include \"Uart.h\" #include \"cs8900.h\" #include } { } else if (0x08 == (isq & 0x0f)) { } do { int count = 0; unsigned short rx_status = 0; unsigned short rx_len = 0; unsigned short rx_ok = 0; if (!(rx_ok = (ReadCS8900Reg(0x0124) & 0x0100))) break; rx_status = (*(volatile unsigned short *)0x19000300); rx_len = (*(volatile unsigned short *)0x19000300); rx_len = (rx_len / 2) + (rx_len % 2); for (; count < rx_len; ++ count) printf(\"0x%x \printf(\"\\n\\n\"); }while (1); }while (0 != isq); *(volatile unsigned *)0x560000a8 = 0xffffffff; /*clear external int pnd*/ void HandleIRQ(void) { *(volatile unsigned *)0x4a000000 = (1 << offset); /*clear src pnd*/ switch (offset) { case 28: HandleUartInt(); break; unsigned char offset = *(volatile unsigned char *)0x4a000014; /*int Offset*/ case 14: break; case 5: HandleEInt9(); break; default: } break; 10 } *(volatile unsigned *)0x4a000010 = (1 << offset); /*clear int pnd*/ 9.4 Uart.c char ReadChar() { } void WriteChar(char content) { } while(!((*(volatile unsigned *)0x50000010) & 0x00000002)); (*(volatile unsigned char *)0x50000020) = content; while (!((*(volatile unsigned *)0x50000010) & 0x00000001)); return (*(volatile unsigned char *)0x50000024); 9.5 K9F1208.c #include \"TypeCpu.h\" #include #define NF_RDDATA() (rNFDATA) #define NF_WRDATA(data) {rNFDATA=data;} #define NF_WAITRB() {while(!(rNFSTAT&(1<<0)));} int K9F1208_Init() { INT32U i = 0; rNFCONF=(1<<15)|(1<<14)|(1<<13)|(0<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); NF_nFCE_L(); NF_CMD(0xFF); //reset command //tWB = 100ns. for (i = 0; i < 10; i++); NF_WAITRB(); //wait 200~500us; NF_nFCE_H(); return 1; } INT16U K9F1208_ReadID() { INT32U i = 0; INT16U id = 0; NF_nFCE_L(); NF_CMD(0x90); NF_ADDR(0x0); for(i=0;i<10;i++); //wait tWB id=NF_RDDATA()<<8; id|=NF_RDDATA(); NF_nFCE_H(); return id; } int K9F1208_WriteChunk(INT16U block_no, INT16U chunk_no, const INT8U *chunk_data, const INT8U *tag_data) { INT32U i = 0; INT32U blockPage = (block_no << 5) + chunk_no; NF_nFCE_L(); NF_CMD(0); NF_CMD(0x80); // Write 1st command NF_ADDR(0); NF_ADDR(blockPage & 0xff); NF_ADDR((blockPage >> 8) & 0xff); NF_ADDR((blockPage >> 16) & 0xff); for(i = 0; i < 512; i++) NF_WRDATA(chunk_data[i]); // Write one page to NFM from buffer for(i = 0; i < 16; i++) 12 NF_WRDATA(tag_data[i]); // Write spare array NF_CMD(0x10); // Write 2nd command for(i = 0; i < 10; i++); //tWB = 100ns NF_WAITRB(); //wait tPROG 200~500us; NF_CMD(0x70); // Read status command for(i=0;i<3;i++); //twhr=60ns if (NF_RDDATA()&0x1) // Page write error { NF_nFCE_H(); return 0; } else { NF_nFCE_H(); return 1; } } int K9F1208_ReadChunk(INT16U block_no, INT16U chunk_no, INT8U *chunk_data, INT8U *tag_data) { INT32U i = 0; INT32U blockPage = 0; chunk_no = chunk_no & 0x1f; blockPage = (block_no << 5) + chunk_no; NF_nFCE_L(); NF_CMD(0x00); // Read command NF_ADDR(0); NF_ADDR(blockPage & 0xff); NF_ADDR((blockPage >> 8) & 0xff); NF_ADDR((blockPage >> 16) & 0xff); for (i = 0; i < 10; i++); //wait tWB NF_WAITRB(); // Wait tR for (i = 0; i < 512; i++) chunk_data[i] = NF_RDDATA(); for (i = 0; i < 16; i++) tag_data[i] = NF_RDDATA(); // Read spare array NF_nFCE_H(); return 1; // Read one page 13 } int K9F1208_EraseBlock(INT16U block_no) { } if (NF_RDDATA()&0x1) // Erase error { } else { } NF_nFCE_H(); return 1; NF_nFCE_H(); return 0; NF_nFCE_L(); NF_CMD(0x60); // Erase one block 1st command // Page number=0 INT32U blockPage = (block_no << 5); INT32U i = 0; NF_ADDR(blockPage & 0xff); NF_ADDR((blockPage >> 8) & 0xff); NF_ADDR((blockPage >> 16) & 0xff); NF_CMD(0xd0); // Erase one blcok 2nd command for(i=0;i<10;i++); //wait tWB NF_WAITRB(); NF_CMD(0x70); // Wait tBERS max 3ms. // Read status command 9.6 CS8900.c void WriteCS8900Reg(unsigned short addr, unsigned short val) { } unsigned short ReadCS8900Reg(unsigned short addr) { } (*(volatile unsigned short *)0x1900030a) = addr; (*(volatile unsigned short *)0x1900030c) = val; (*(volatile unsigned short *)0x1900030a) = addr; return (*(volatile unsigned short *)0x1900030c); 9.7 SysCall.c #include #include _ssize_t _write_r ( struct _reent *r, int file, const void *ptr, size_t len) { } int _close_r( struct _reent *r, int file) { } _off_t _lseek_r( struct _reent *r, int file, _off_t ptr, int dir) { } int _fstat_r( struct _reent *r, int file, struct stat *st) { /* Always set as character device. st->st_mode = S_IFCHR; */ return (_off_t)0; /* Always indicate we are at file beginning. */ return 0; int i; const unsigned char *p; p = (const unsigned char*) ptr; for (i = 0; i < len; i++) { } return len; if (*p == '\\n' ) WriteChar('\\r'); WriteChar(*p++); 16 } /* assigned to strong type with implicit */ /* signed/unsigned conversion. Required by */ /* newlib. */ return 0; int isatty(int file); /* avoid warning */ int isatty(int file) { } return 1; 9.9 main.c #include } do { delay(1000); tx_ready = ReadCS8900Reg(0x0138) & 0x0100; }while (!tx_ready); for (; len > 0; len -= 2) (*(volatile unsigned short*)0x19000300) = *addr++; delay(1000); unsigned char arp_pkt[] = { }; static void WriteS3C2410Reg(unsigned long addr, unsigned long val) { } int main() { /*redirect the IRQ entry to 0x30000000*/ WriteS3C2410Reg(0x00000018, 0xe3a0f5c0); /*confgi GPH as UART*/ WriteS3C2410Reg(0x56000070, 0x000000aa); WriteS3C2410Reg(0x50000008, 0x00000000); WriteS3C2410Reg(0x5000000c, 0x00000000); WriteS3C2410Reg(0x50000000, 0x00000003); WriteS3C2410Reg(0x50000004, 0x00000045); /*config the GPG pin as EINT9*/ WriteS3C2410Reg(0x56000060, 0x00000008); /*config EINT9 through high level*/ WriteS3C2410Reg(0x5600008c, 0x00000010); /*clear interrupt*/ WriteS3C2410Reg(0x560000a8, 0xffffffff); (*(volatile unsigned long *)addr) = val; 0xff,0xff,0xff,0xff,0xff,0xff, 0x12,0x14,0x16,0x18,0x1a,0x1c, 0x08,0x06, 0x00,0x01,0x08,0x00,0x06,0x04,0x00,0x01, 0x12,0x14,0x16,0x18,0x1a,0x1c, 0xa6,0x10,0x06,0x06, 0x00,0x00,0x00,0x00,0x00,0x00, 0xa6,0x10,0x06,0x01 18 } WriteS3C2410Reg(0x4a000018, 0x000007ff); WriteS3C2410Reg(0x4a000000, 0xffffffff); WriteS3C2410Reg(0x4a000010, 0xffffffff); /*config CS8900*/ WriteCS8900Reg(0x0022, 0x0000); //use IRQ0 WriteCS8900Reg(0x0158, 0x1412); //set mac addr WriteCS8900Reg(0x015a, 0x1816); WriteCS8900Reg(0x015c, 0x1c1a); WriteCS8900Reg(0x0112, 0x00d3); //enable RX and TX WriteCS8900Reg(0x0104, 0x7d05); //recieve Individual and Broadcast Addr WriteCS8900Reg(0x0102, 0x7903); //enable RX Done Interupt WriteCS8900Reg(0x0106, 0x0107); //enable TX Done Interupt WriteCS8900Reg(0x010a, 0x010b); //enable TX Ready Interupt WriteCS8900Reg(0x0116, 0x8017); //enable IRQ /*init K9F1208*/ K9F1208_Init(); /*open mask for EINT9*/ WriteS3C2410Reg(0x560000a4, 0x00fffdf0); /*open mask for uart0 and ext int*/ WriteS3C2410Reg(0x4a000008, 0xEFFFFFCF); /*open mask for uart0 rx int*/ WriteS3C2410Reg(0x4a00001c, 0x000007fe); /*switch to the SVC mode and open IRQ*/ __asm (\"mrs r0,cpsr\"); __asm (\"bic r0,r0,#0xff\"); __asm (\"orr r0,r0,#0x53\"); __asm (\"msr cpsr_c,r0\"); printf(\"Nand Flash ID is 0x%x\\n\while (1) { } EthSend(arp_pkt, sizeof(arp_pkt)); delay(800000); 9.10 mk_depend CC = arm-elf-gcc SED = sed_WinARM RM = rm %.c_d: %.c @echo generate depends for $<;\\ 19 $(CC) -M $< > tmp;\\ $(SED) 's/\\($*\\)\\.o:/\\1.o $@ :/g' CC = arm-elf-gcc LD = arm-elf-ld MCU = arm9tdmi WINARM_PATH = f:/winarm crt_i = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crti.o crt_begin = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crtbegin.o crt_0 = $(WINARM_PATH)/arm-elf/lib/crt0.o crt_end = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crtend.o crt_n = $(WINARM_PATH)/lib/gcc/arm-elf/4.1.1/crtn.o libs_to_search = --start-group -lgcc -lc --end-group lib_search_path = -L$(WINARM_PATH)/lib/gcc/arm-elf/4.1.1 -L$(WINARM_PATH)/lib/gcc -L$(WINARM_PATH)/arm-elf/lib script_uesed = -Ttarget.ld target = code_road.elf codes = $(wildcard *.c) objects = $(codes:.c=.o) depends = $(codes:.c=.c_d) all_objects = $(crt_i) $(crt_begin) $(crt_0) $(objects) $(crt_end) $(crt_n) IR.o $(target): $(objects) IR.o IR.o: IR.S sinclude $(depends) $(CC) -c IR.S -o IR.o $(LD) -o $@ $(script_uesed) $(all_objects) $(lib_search_path) $(libs_to_search) 20 $(objects): $(CC) -c -mcpu=$(MCU) -DCONFIG_YAFFS_DIRECT -DWT133664_PORT -DCONFIG_YAFFS_YAFFS2 -gdwarf-2 -O0 $< -o $@ 9.12 mk_cr.bat path = f:\\WinARM\\\bin;f:\\WinARM\\bin call make -f mk_depend call make -f mk_target 10、测试代码说明 10.1 crt_X.o 这些.o文件完成了arm的堆栈设置,清空了.bss,初始化了库函数,并给出了程序入口_start,最后跳转到main()。详细介绍可参考《Initialize》。 10.2 IRQ main()执行WriteS3C2410Reg(0x00000018, 0xe3a0f5c0),0xe3a0f5c0是一条arm指令,等于b 0x30000000。同时通过target.ld,将IR.s生成的.o文件放置在0x30000000。从而实现IRQ处理的重定向。 10.3 栈 target.ld给出了__stack,crt0.o根据此符号来设置各种mode下的sp。 10.4 堆 SysCall.c的_sbrk_r()给出了堆,位于.bss中,大小为1MB。实际上,堆在.bss之后,紧贴着.bss。我这里为了省事,直接定义一个静态数据,用于malloc分配内存。 10.5 stdio 要使用printf()需要做一些适配,我从WinArm的示例代码中copy了适配代码,放在了SysCall.c中。想进一步了解,可参考newlib源码。 10.6 中断 开了Uart0的rx中断,与EINT9中断,即网口中断。在HandleIRQ()中完成,Uart0的读操作,与网口的读操作。 10.7 Nand Flash 通过对Nand Flash ID来验证skyeye对它的模拟。在K9F1208.c中给出了具体的读写API,但这里不再给出相应的测试代码,通过移植YAFFS2到K9F1208上,验证过了这些API的正确性。注意,移植的是YAFFS2,而不是YAFFS,如何实现的可参考《YAFFS2》。 11、WinARM的一个Bug 第一次在skyeye中运行code_road.elf,结果如下: Administrator@B16AA68D32FB4A3 ~/skyeye-1.2.6_rc1 $ ./skyeye.exe -e ./test_skyeye/code_road.elf 21 Your elf file is little endian. arch: arm cpu info: armv4, arm920t, 41009200, ff00fff0, 2 mach info: name s3c2410x, mach_init addr 0x423a50 ethmod num=1, mac addr=12:14:16:18:1a:1c, hostip=166.16.6.1 [TAP-WIN32]: Found TAP device named '本地连接 4' uart_mod:1, desc_in:/dev/stdin, desc_out:/dev/stdout, converter: nandflash: dump ./nand.dump file size:69206016 SKYEYE: use arm920t mmu ops start addr is set to 0x30000110 by exec file. SKYEYE:Error in mem_read_word, no bank found, NumInstrs 27, mem_read_word addr = 6fffc no bank SKYEYE:Error in mem_read_word, no bank found, NumInstrs 4194786, mem_read_word addr = 7afe8 no bank SKYEYE:Error in mem_read_word, no bank found, NumInstrs 4194786, mem_read_word addr = 7afec no bank SKYEYE:Error in mem_read_word, no bank found, NumInstrs 4194786, mem_read_word addr = 7aff0 no bank 重新启动skyeye,结果仍然如上,怎么办?这时前面介绍过的insight要重新出场了,现在让我们看看,到底从哪里开始出错。 11.1 找到出错的指令 在运行时带上-d,skyeye启动GDB,等待调试工具连接。运行arm-elf-insight,将target设置为GDBServer/TCP,端口为12345,然后connect to target。 Administrator@B16AA68D32FB4A3 ~/skyeye-1.2.6_rc1 $ ./skyeye.exe -e ./test_skyeye/code_road.elf -d Your elf file is little endian. arch: arm cpu info: armv4, arm920t, 41009200, ff00fff0, 2 mach info: name s3c2410x, mach_init addr 0x423a50 ethmod num=1, mac addr=12:14:16:18:1a:1c, hostip=166.16.6.1 [TAP-WIN32]: Found TAP device named '本地连接 4' uart_mod:1, desc_in:/dev/stdin, desc_out:/dev/stdout, converter: nandflash: dump ./nand.dump file size:69206016 SKYEYE: use arm920t mmu ops start addr is set to 0x30000110 by exec file. debugmode= 1, filename = skyeye.conf, server TCP port is 12345 Remote debugging using host:12345 在insight上通过step调试指令,一条条的运行。发现在第一次配置sp时,sp的值居然是0x80000,而不是在target.ld中指定的0x30600000,这就是出问题的根本所在。如下图所示: 22 11.2 __stack的定义 参考《Initialize》13.2,在crt0.o的源码crt0.s中,__stack的定义如下: ...... ldr r3, .Lstack cmp r3, #0 ldreq r3, .LC0 ...... .LC0: .word 0x80000 .Lstack: .word __stack 23 ...... .weak __stack __stack的属性为weak,该类型的符号在link时可被替换,这一点在源码的注释中也可以看到。上面蓝色标注的代码,与上图中起始于0x30000110的前三行代码完全相同。那么为什么linker未能够将crt0.o中声明为weak的符号__stack替换掉呢? 11.3 两种解决方法 方法一:重新编译newlib中的crt0.s,甚至可以修改__stack的属性后再编译。 方法二:能够在现有的crt0.o,用ultra edit来改改看? 12、“投机取巧” 下面给出的就是第二种方法,用来解决WinArm的这个Bug。不过更好的方法是,认真学习一下linker如何来处理weak属性的符号,这才是正途,不过有点漫长。 12.1 weak __stack的位置 从源代码中可以看到,在指令bl exit之后,依次存放着0x80000与__stack。通过arm-elf-objdump crt0.o –d可知bl exit位于.text section的0xe4处: F:\\WinARM\\arm-elf\\lib>arm-elf-objdump crt0.o -d crt0.o: file format elf32-littlearm Disassembly of section .text: 00000000 <_mainCRTStartup>: 0: e59f30e4 ldr r3, [pc, #228] ; ec <.text+0xec> 4: e3530000 cmp r3, #0 ; 0x0 8: 059f30d8 ldreq r3, [pc, #216] ; e8 <.text+0xe8> c: e1a0d003 mov sp, r3 10: e10f2000 mrs r2, CPSR 14: e312000f tst r2, #15 ; 0xf 18: 0a000015 beq 74 <_mainCRTStartup+0x74> 1c: e321f0d1 msr CPSR_c, #209 ; 0xd1 20: e1a0d003 mov sp, r3 24: e24daa01 sub sl, sp, #4096 ; 0x1000 28: e1a0300a mov r3, sl 2c: e321f0d7 msr CPSR_c, #215 ; 0xd7 30: e1a0d003 mov sp, r3 34: e2433a01 sub r3, r3, #4096 ; 0x1000 38: e321f0db msr CPSR_c, #219 ; 0xdb 3c: e1a0d003 mov sp, r3 24 40: e2433a01 sub r3, r3, #4096 ; 0x1000 44: e321f0d2 msr CPSR_c, #210 ; 0xd2 48: e1a0d003 mov sp, r3 4c: e2433a02 sub r3, r3, #8192 ; 0x2000 50: e321f0d3 msr CPSR_c, #211 ; 0xd3 54: e1a0d003 mov sp, r3 58: e2433902 sub r3, r3, #32768 ; 0x8000 5c: e3c330ff bic r3, r3, #255 ; 0xff 60: e3c33cff bic r3, r3, #65280 ; 0xff00 64: e5033004 str r3, [r3, #-4] 68: e9532000 ldmdb r3, {sp}^ 6c: e38220c0 orr r2, r2, #192 ; 0xc0 70: e121f002 msr CPSR_c, r2 74: e243a801 sub sl, r3, #65536 ; 0x10000 78: e3a01000 mov r1, #0 ; 0x0 7c: e1a0b001 mov fp, r1 80: e1a07001 mov r7, r1 84: e59f006c ldr r0, [pc, #108] ; f8 <.text+0xf8> 88: e59f206c ldr r2, [pc, #108] ; fc <.text+0xfc> 8c: e0422000 sub r2, r2, r0 90: ebfffffe bl 0 再通过arm-elf-objdump crt0.s –s可证实,在指令bl exit之后,依次存放着0x80000与__stack。那么weak __stack位于.text section的0xec,值为0x00000000。 F:\\WinARM\\arm-elf\\lib>arm-elf-objdump crt0.o -s crt0.o: file format elf32-littlearm Contents of section .text: 0000 e4309fe5 000053e3 d8309f05 03d0a0e1 .0....S..0...... 0010 00200fe1 0f0012e3 1500000a d1f021e3 . ............!. 0020 03d0a0e1 01aa4de2 0a30a0e1 d7f021e3 ......M..0....!. 0030 03d0a0e1 013a43e2 dbf021e3 03d0a0e1 .....:C...!..... 0040 013a43e2 d2f021e3 03d0a0e1 023a43e2 .:C...!......:C. 0050 d3f021e3 03d0a0e1 023943e2 ff30c3e3 ..!......9C..0.. 0060 ff3cc3e3 043003e5 002053e9 c02082e3 .<...0... S.. .. 0070 02f021e1 01a843e2 0010a0e3 01b0a0e1 ..!...C......... 0080 0170a0e1 6c009fe5 6c209fe5 002042e0 .p..l...l ... B. 0090 feffffeb 54309fe5 000053e3 0100000a ....T0....S..... 00a0 0fe0a0e1 03f0a0e1 44309fe5 000053e3 ........D0....S. 00b0 0100000a 0fe0a0e1 03f0a0e1 0000a0e3 ................ 00c0 0010a0e3 0040a0e1 0150a0e1 2c009fe5 .....@...P..,... 00d0 feffffeb feffffeb 0400a0e1 0510a0e1 ................ 00e0 feffffeb feffffeb 00000800 00000000 ................ 00f0 00000000 00000000 00000000 00000000 ................ 0100 00000000 .... 12.2 重定位信息 如下可见,在.text section中存在一个没有名字的条目指向地址0xec: F:\\WinARM\\arm-elf\\lib>arm-elf-objdump crt0.o -r crt0.o: file format elf32-littlearm RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 00000090 R_ARM_PC24 memset 000000d0 R_ARM_PC24 atexit 000000d4 R_ARM_PC24 __libc_init_array 000000e0 R_ARM_PC24 main 000000e4 R_ARM_PC24 exit 000000ec R_ARM_ABS32 *ABS* 000000f0 R_ARM_ABS32 *ABS* 000000f4 R_ARM_ABS32 *ABS* 000000f8 R_ARM_ABS32 __bss_start__ 26 000000fc R_ARM_ABS32 __bss_end__ 00000100 R_ARM_ABS32 __libc_fini_array 在《Code Road》中介绍过重定位的过程,我们用readelf来看看更为详细一点的信息: F:\\WinARM\\arm-elf\\lib>arm-elf-readelf crt0.o -r Relocation section '.rel.text' at offset 0x7c4 contains 11 entries: Offset Info Type Sym.Value Sym. Name 00000090 00000e01 R_ARM_PC24 00000000 memset 000000d0 00000f01 R_ARM_PC24 00000000 atexit 000000d4 00001001 R_ARM_PC24 00000000 __libc_init_array 000000e0 00001101 R_ARM_PC24 00000000 main 000000e4 00001201 R_ARM_PC24 00000000 exit 000000ec 00000002 R_ARM_ABS32 000000f0 00000002 R_ARM_ABS32 000000f4 00000002 R_ARM_ABS32 000000f8 00001602 R_ARM_ABS32 00000000 __bss_start__ 000000fc 00001702 R_ARM_ABS32 00000000 __bss_end__ 00000100 00001802 R_ARM_ABS32 00000000 __libc_fini_array 由上可知,指向.init section 0x000000ec的条目,类型为2,即数据类型重定位条目;但其对应的符号在符号表中的索引为0,即对应于未定义符号。而weak __stack应该是符号表中的第19个符号。 F:\\WinARM\\arm-elf\\lib>arm-elf-readelf crt0.o -s Symbol table '.symtab' contains 25 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS c:/winarms/newlib-1.14.0/ 2: 00000000 0 SECTION LOCAL DEFAULT 1 3: 00000000 0 SECTION LOCAL DEFAULT 3 4: 00000000 0 SECTION LOCAL DEFAULT 4 5: 00000000 0 NOTYPE LOCAL DEFAULT 1 $a 6: 000000e8 0 NOTYPE LOCAL DEFAULT 1 $d 7: 00000000 0 SECTION LOCAL DEFAULT 7 8: 00000000 0 SECTION LOCAL DEFAULT 9 9: 00000000 0 SECTION LOCAL DEFAULT 5 10: 00000000 0 SECTION LOCAL DEFAULT 10 11: 00000000 0 NOTYPE GLOBAL DEFAULT 1 _mainCRTStartup 12: 00000000 0 NOTYPE GLOBAL DEFAULT 1 _start 13: 00000000 0 NOTYPE GLOBAL DEFAULT 1 start 14: 00000000 0 NOTYPE GLOBAL DEFAULT UND memset 27 15: 00000000 0 NOTYPE GLOBAL DEFAULT UND atexit 16: 00000000 0 NOTYPE GLOBAL DEFAULT UND __libc_init_array 17: 00000000 0 NOTYPE GLOBAL DEFAULT UND main 18: 00000000 0 NOTYPE GLOBAL DEFAULT UND exit 19: 00000000 0 NOTYPE WEAK DEFAULT ABS __stack 20: 00000000 0 NOTYPE WEAK DEFAULT ABS hardware_init_hook 21: 00000000 0 NOTYPE WEAK DEFAULT ABS software_init_hook 22: 00000000 0 NOTYPE GLOBAL DEFAULT UND __bss_start__ 23: 00000000 0 NOTYPE GLOBAL DEFAULT UND __bss_end__ 24: 00000000 0 NOTYPE GLOBAL DEFAULT UND __libc_fini_array 12.3 “动手脚” 下面将上述重定位条目的符号索引改为19,即0x13。由上可知.rel.text section位于crt0.o的0x7c4,共11个重定位条目,我们需要修改的是第5个条目。每个条目大小为8字节,按照“小头”字节序,前4个字节表示重定位指向的位置,第5个字节表示类型,后三个字节表示涉及到的符号索引。那么需要修改的是0x7c4 + 8 * 5 + 5 = 0x7f1,将该位置的数据改为0x13。 我们再来看看重定位信息: 28 F:\\WinARM\\arm-elf\\lib>arm-elf-readelf crt0.o -r Relocation section '.rel.text' at offset 0x7c4 contains 11 entries: Offset Info Type Sym.Value Sym. Name 00000090 00000e01 R_ARM_PC24 00000000 memset 000000d0 00000f01 R_ARM_PC24 00000000 atexit 000000d4 00001001 R_ARM_PC24 00000000 __libc_init_array 000000e0 00001101 R_ARM_PC24 00000000 main 000000e4 00001201 R_ARM_PC24 00000000 exit 000000ec 00001302 R_ARM_ABS32 00000000 __stack 000000f0 00000002 R_ARM_ABS32 000000f4 00000002 R_ARM_ABS32 000000f8 00001602 R_ARM_ABS32 00000000 __bss_start__ 000000fc 00001702 R_ARM_ABS32 00000000 __bss_end__ 00000100 00001802 R_ARM_ABS32 00000000 __libc_fini_array 这样,我们就解决了WinArm中关于weak __stack的Bug。当然推荐的做法还是,学习linker是如何处理weak属性的符号,再重新编译crt0.o。 13、和谐 再次运行,一切正常:串口中断接收、网口中断接收、串口写、网口写、Nand Flash读ID均正常。还要说的一点是,最近在编译skyeye时,我碰到了前面提过的三个问题。不过以前的编译过程一切正常,这是为什么呢?希望大家在编译skyeye的时候顺利。 Administrator@B16AA68D32FB4A3 ~/skyeye-1.2.6_rc1 $ ./skyeye.exe -e ./test_skyeye/code_road.elf Your elf file is little endian. arch: arm cpu info: armv4, arm920t, 41009200, ff00fff0, 2 mach info: name s3c2410x, mach_init addr 0x423a50 ethmod num=1, mac addr=12:14:16:18:1a:1c, hostip=166.16.6.1 [TAP-WIN32]: Found TAP device named '本地连接 4' uart_mod:1, desc_in:/dev/stdin, desc_out:/dev/stdout, converter: nandflash: dump ./nand.dump file size:69206016 SKYEYE: use arm920t mmu ops start addr is set to 0x30000110 by exec file. Hello, I'm wt133664 Nand Flash ID is 0xec76 29 因篇幅问题不能全部显示,请点此查看更多更全内容