先MARK下,方便以后学习
一、目的
编写一个能够加载并启动OS内核的bootloader。
二、思路
第一阶段:
(1)arm920t的异常向量表有两种存放方式,一种是低端存放(从0x00000000处开始存放),另一种是高端存放(从0xfff000000处开始存放)。选择低端存放,建立异常向量表。
(2)s3c2440的看门狗在上电启动时默认是开启的,所有要先把看门狗关了先。免得代码运行还没完成就强制复位。
(3)屏蔽掉所有中断。
(4)初始化时钟。
(5)初始化内存。
(6)清零bss段。
(7)设置好各个模式下的栈空间。
(8)重定位代码,使得代码的运行地址与链接地址相对应,之后就可以直接使用绝对地址。
(9)使用绝对跳转指令跳转到第二阶段,用c语言来实现。
第二阶段:
(1)初始化串口0,一方面方便我们的调试,一方面也为内核启动时打印信息做好初始化。
(2)初始化nand flash,需要把nand flash里的内核镜像拷贝到内存。
(3)把内核镜像拷贝到内存指定位置。
(4)设置好传递给内核的参数,并放置在约定的位置。
(5)跳转到内核起始地址处开始启动内核。
完毕。
三、流程图设计

四、代码树结构
(1)drivers里的src目录放置与外围设备相关配置的编程文件,inc目录放置相关头文件。对应的makefile放在drivers目录里。
①drivers/Makefile
#把src目录下的所有.c文件的字符串替换成.o字符串 TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) ) TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )
all:$(TARGET) echo "$(TARGET) 编译完成"
GPATH=$(PWD) vpath %.c ./src vpath %.S ./src
%.o:%.c $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean: rm -f *.o
②drivers/inc
nand.h
#ifndef _NAND_H #define _NAND_H
/** *提取出页内列号,块内页号,块号 */ #define dnand_addr2ColAddr(addr) (addr & 0x7FF) #define dnand_addr2RowAddr(addr) (( addr & 0xFC00 ) >> 11) #define dnand_addr2BlockAddr(addr) (addr >> 17)
//初始化NAND Flash , 时钟频率改变了这个一定要记得改 #define dnand_init() \ do{ \ NFCONT = 0x73;\ NFCONF = (3<<12) | (1<<8) | (1<<4);\ }while(0) //复位 void fnand_reset(void); //等待NAND Flash就绪 #define dnand_waitReady() while(!(NFSTAT & 0x1)) //发出片选信号 #define dnand_enable() (NFCONT &= ~(1<<1)) //取消片选信号 #define dnand_disable() (NFCONT |= (1<<1)) //发出命令 #define dnand_writeCmd(cmd) (NFCMMD = cmd) //读数据 #define dnand_readData() (*(volatile unsigned char *)&NFDATA) //写数据 #define dnand_writeData(data) *(volatile unsigned char *)&NFDATA = data //写地址 #define dnand_writeAddr(addr) \ do{ NFADDR = addr & 0xff;\ NFADDR = (addr >> 8) & 0x0f; \ NFADDR = (addr >> 11) & 0xff;\ NFADDR = (addr >> 19) & 0xff;\ }while(0) //擦除块 unsigned char fnand_eraseBlocks(unsigned int startBlockNum , unsigned int blockCount); //从nand闪存里的sourAddr地址处读取size大小的数据到内存destAddr处。 void fnand_readOfSize(unsigned char *destAddr , unsigned int sourAddr , unsigned int size); //。从内存sourAddr处读取size大小的数据到nand闪存里的destAddr地址处 unsigned char fnand_writeOfSize(unsigned char *sourAddr , unsigned int destAddr , unsigned int size); #endif
uart0.h
#ifndef UART0_H #define UART0_H
#define TXD0_READY (0x01<<1) #define UBRDIV0_VAL (101250000UL/(115200*16)-1)
/**********初始化uart0中断的配置***********/ void uart0_init(void);
/*******把string消息通过uart0发送出去******/ void uart0_sent_msg(char *string); void uart0_sent_byte(char byte); /*打印hex数据*/ void uart0_sent_hex_word(unsigned int val);
#endif
③drivers/src
nand.c
#include "nand.h" #include "s3c2440.h"
/** * 复位 */ void fnand_reset(void) { dnand_enable(); dnand_writeCmd(0xff); // 复位命令 dnand_waitReady(); dnand_disable(); }
/** *块擦除函数 */ unsigned char fnand_eraseBlocks(unsigned int startBlockNum , unsigned int blockCount) { unsigned int i; dnand_enable(); for( i = 0 ; i < blockCount ; i++ ){ //发送擦除命令 dnand_writeCmd( 0x60 ); dnand_writeAddr( startBlockNum << 6 ); dnand_writeCmd(0xD0); dnand_waitReady(); //读取状态 dnand_writeCmd( 0x70 ); if ( ( dnand_readData() & 0x01 ) ){ return 1; } startBlockNum += 1; } dnand_disable(); return 0; }
void fnand_readOfSize(unsigned char *destAddr , unsigned int sourAddr , unsigned int size) { unsigned int j , col;
col = sourAddr & 0x7FF ; //该地址可能不是从页的0地址开始读 ,所以要先取出列地址
dnand_enable(); for( j = 0 ; j < size ; ){
//发出read命令 dnand_writeCmd( 0x00 ); dnand_writeAddr( sourAddr ); dnand_writeCmd( 0x30 ); dnand_waitReady( );
//开始读一页数据到destAddr里 , for( ; (col < 2048)&&(j<size) ; col++ ){ *destAddr++ = dnand_readData(); j++; sourAddr++; } col = 0; } dnand_disable(); } /** *从内存的sourAddr处写入pageCount页数据到nand flash的destAddr地址处 */ unsigned char fnand_writeOfSize(unsigned char *sourAddr , unsigned int destAddr , unsigned int size) { unsigned int col, j; unsigned int startBlockNum , blockCount ,pageCount; col = destAddr & 0x7FF ; pageCount = size/2048 + ( (col)? 1 : 0 ) ; startBlockNum = dnand_addr2BlockAddr( destAddr ) ; blockCount = ( dnand_addr2RowAddr( destAddr ) + pageCount ) >> 6 ; if ( ( dnand_addr2RowAddr( destAddr ) + pageCount ) & 0x3F ){ blockCount++; }
if ( fnand_eraseBlocks( startBlockNum , blockCount ) ){ return 1; } dnand_enable(); for( j = 0 ; j < size ; ){ //发出read命令 dnand_writeCmd( 0x80 ); dnand_writeAddr( destAddr ); //开始写一页数据到destAddr里 for( ; (col < 2048)&&(j < size) ; col++ ){ dnand_writeData( *sourAddr++ ); destAddr++; j++; } col = 0; dnand_writeCmd( 0x10 ); dnand_waitReady(); //发送读取状态命令 dnand_writeCmd( 0x70 ); if ( ( dnand_readData() & 0x01 ) ){ return 1; } } dnand_disable(); return 0; }
uart0.c
#include "Uart0.h" #include "s3c2440.h"
/* **************************************** 初始化uart0中断的配置 **************************************** */ void uart0_init(void) { GPHCON |= 0xa0; GPHUP |= 0x0f; ULCON0 = 0x03; //普通模式,禁止奇偶校验,1个结束位,8-bit字长 UBRDIV0 = UBRDIV0_VAL; //波特率选择115200,所以UBRDIV0=54 UCON0 = 0x005; //时钟源=PCLK //Rx,Tx水平触发,禁止接收超时中断,禁止接收错误状态中断, //不使用回路模式,不发送break信号 //中断方式发送接收数据到缓冲寄存器 }
/* ***************************************************** *uart0发送消息 *************************************************** */
void uart0_sent_msg(char *string) { do { while( !(UTRSTAT0&TXD0_READY) ); UTXH0 = *string++; }while(*string != '\0'); }
void uart0_sent_byte(char byte) { while(!(UTRSTAT0&TXD0_READY)); UTXH0 = byte; }
void uart0_sent_hex_word(unsigned int val) { char i ,j; uart0_sent_msg("0x");
for ( i = 0 ; i < 8 ; i++) {
j = (char)((val >> ((7-i)*4) ) & 0x0f); if( (j >= 0) && ( j <= 9)) { uart0_sent_byte('0'+j); }else{ uart0_sent_byte('A'+j-0xa); }
} uart0_sent_msg(" "); }
(2)cpu里的src目录里放置与arm920t内核相关的编程文件,inc目录放置相关头文件。
cpu/Makefile
#把src目录下的所有.c文件的字符串替换成.o字符串 TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) ) TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )
all:$(TARGET) echo "$(TARGET) 编译完成"
GPATH=$(PWD) vpath %.c ./src vpath %.S ./src
%.o:%.c $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean: rm -f *.o
cpu/src
ClkConflg.S
#把src目录下的所有.c文件的字符串替换成.o字符串 TARGET := $(patsubst %.c,%.o,$(notdir $(wildcard ./src/*.c)) ) TARGET += $(patsubst %.S,%.o,$(notdir $(wildcard ./src/*.S)) )
all:$(TARGET) echo "$(TARGET) 编译完成"
GPATH=$(PWD) vpath %.c ./src vpath %.S ./src
%.o:%.c $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean: rm -f *.o
MemCofig.S
@与内存相关 .equ BWSCON , 0x48000000 .equ BANKCON6 , 0x4800001C .equ REFRESH , 0x48000024 .equ BANKSIZE , 0x48000028 .equ MRSRB6 , 0x4800002C
.global MemConfigure
MemConfigure:
/*******内存初始化子程序*********/
@BWSCON[27:24] = 0 0 10B ldr r0 , =BWSCON ldr r1 , [r0] ldr r2 , =(0x0F<<24) bic r1 , r1 , r2 ldr r2 , =(0x02<<24) orr r1 , r1 , r2 str r1 , [r0]
@BANKCON6[16:15]=11B,BANKCON6[3:0]=00 01B ldr r0 , =BANKCON6 ldr r1 , [r0] ldr r2 , =(0x03<<15) bic r1 , r1 , r2 orr r1 , r1 , r2 ldr r2 , =0x0F bic r1 , r1 , r2 ldr r2 , = 0x01 orr r1 , r1 , r2 str r1 , [r0]
@这里的Trp要大于20ns , Trc要大于70ns,HCLK=101.25MHz @故时钟周期=1s/HCLK=9.8ns,Trp*9.8>20ns ==> Trp>=3 ==> Trp域=01b @Trp*9.8+Tsrc*9.8>70ns ==> Tsrc>=5 ==> Tsrc域=01b @REFRESH[23:18] = 1 0 01 01B,REFRESH[10:0] = 0x4E8 ldr r0 , =REFRESH ldr r1 , [r0] ldr r2 , =(0x3F<<18) bic r1 , r1 , r2 ldr r2 , =(0x25<<18) orr r1 , r1 , r2 ldr r2 , =0x7FF bic r1 , r1 , r2 ldr r2 , =0x4E9 orr r1 , r1 , r2 str r1 , [r0]
@BANKSIZE[7:0] = 1 0 1 1 0 001 B ldr r0 , =BANKSIZE ldr r1 , [r0] ldr r2 , =0xFF bic r1 , r1 , r2 ldr r2 , =0xB1 orr r1 , r1 , r2 str r1 , [r0]
@MRSRB6[11:0] = 0 00 011 0 000 B ldr r0 , =MRSRB6 ldr r1 , [r0] ldr r2 , =0x3FF bic r1 , r1 , r2 ldr r2 , =0x030 orr r1 , r1 , r2 str r1 , [r0]
bx lr @函数返回 /******END内存初始化子程序*******/
(3)根目录下的src放置其它通用源文件,inc放置通用头文件。
./inc/common.h
#ifndef _COMMON_H #define _COMMON_H
#define u32 unsigned long #define u8 unsigned char
/*****************声明外部变量********************/ extern unsigned int _wsnboot_start; extern unsigned int _bss_start; extern unsigned int _bss_end;
/*****************只使用类型***********/ #define sizeof(type) ( (unsigned long)( (type *)0 + 1 ) )
/********************粗略延时函数*******************/ inline void delay_s(volatile unsigned long second); inline void delay_ms(volatile unsigned long time_ms);
unsigned int strlen( char *string ); char *strcpy(char *destAddr ,char *srcAddr); /*************填充指定内存区域*********/ unsigned char *memclr(unsigned char *a ,unsigned int c, unsigned int len);
#endif
./inc/bootparams.h
#ifndef _BOOTPARAMS_H #define _BOOTPARAMS_H
//参考uboot的setup.h文件
#define CONFIG_NR_DRAM_BANKS 1
#define tag_next(t) ((tag *)((u32 *)(t) + (t)->hdr.size)) #define tag_size(type) ((sizeof(tag_header) + sizeof(type)) >> 2)
enum { ATAG_CORE = 0x54410001, ATAG_MEM = 0x54410002, ATAG_CMDLINE = 0x54410009, ATAG_INITRD2 = 0x54410005, ATAG_VIDEOLFB = 0x54410008, ATAG_SERIAL = 0x54410006, ATAG_REVISION = 0x54410007, ATAG_NONE = 0x00000000 };
typedef struct tag_header { u32 size; u32 tag; }tag_header; typedef struct tag_core { u32 flags; /* bit 0 = read-only */ u32 pagesize; u32 rootdev; }tag_core; typedef struct tag_mem32 { u32 size; u32 start; /* physical start address */ }tag_mem32;
typedef struct tag_cmdline { char cmdline[50]; /* this is the minimum size */ }tag_cmdline;
typedef struct tag { tag_header hdr; union { tag_core core; tag_mem32 mem; tag_cmdline cmdline; }u; }tag ;
tag *setup_start_tag (tag *params); tag *setup_memory_tags (tag *params); tag *setup_commandline_tag (tag *params, char *commandline); void setup_end_tag (tag *params); tag *setParamsAddr( unsigned int paramsAddr , char *commandline );
#endif
./src/start.S
@与看门狗相关 .equ WTCON , 0x53000000 @与中断相关 .equ INTMSK , 0x4A000008 @与LED有关的 .equ GPBCON , 0x56000010 .equ GPBDAT , 0x56000014
.global _start .global _wsnboot_start .global _bss_start .global _bss_end .text _start: /***********设置中断向量表*************/ b ResetInit @复位异常入口 HandlerUndef: ldr pc , =HandlerUndef @未定义异常入口 HandlerSWI: ldr pc , =HandlerSWI @软中断异常入口 HandlerPabort: ldr pc , =HandlerPabort @取指中止异常入口 HandlerDabort: ldr pc , =HandlerDabort @数据中止异常入口 HandlerNotUsed: ldr pc , =HandlerNotUsed @保留
ldr pc , =HandlerIRQ @中断异常入口 HandlerFIQ: ldr pc , =HandlerFIQ @快中断异常入口
_wsnboot_start: .word _start _bss_start: .word __bss_start _bss_end: .word __bss_end
ResetInit:
/**************关闭看门狗**************/ ldr r0 , =WTCON mov r1 , #0x0 str r1 , [r0]
/***************屏蔽中断***************/ ldr r0 , =INTMSK ldr r1 , =0xffffffff str r1 , [r0]
/*************初始化LED管脚************/ LedConf: bl LedConfigure
/***************初始化时钟*************/ ClkConf: bl ClkConfigure
/***************初始化内存*************/ MemConf: bl MemConfigure
/***************清零bss段**************/ Clear_Bss: bl clear_bss
/***************设置栈顶指针***********/ SetSp: ldr sp , =0x34000000
/***重定位,设置传递给第二阶段的参数***/ CopyToSdram: bl copy_bootloader_to_sdram @重定位之后,此时的运行地址跟链接地址还不一致。
/*************跳转到第二阶段***********/ jump: ldr pc , =start_armboot @重定位之后,尽快让pc跳转到链接地址去执行,之后运行地址跟链接地址一致。
/**************************************/ loop: b loop /*******************END************************/
/***********LED管脚初始化,利于调试***********/ LedConfigure: @把LED1_2_3_4管脚置为输出 ldr r0 , =GPBCON ldr r1 , [r0] ldr r2 , =(0xFF<<10) bic r1 , r1 ,r2 ldr r2 , =(0x55<<10) orr r1 , r1 , r2 str r1 , [r0] @灯全灭 ldr r0 , =GPBDAT ldr r1 , [r0] mov r2 , #(0x0F<<5) orr r1 , r1 , r2 str r1 , [r0] bx lr
/*****************清零bss段*******************/ clear_bss: ldr r0 , =__bss_start ldr r1 , =__bss_end mov r2 , #0x0 clear: str r2 , [r0] , #4 cmp r0 , r1 ble clear @这里选择小于或等于就跳转,可以处理bss没有占用内存的情况 bx lr
/***************拷贝bootloader到sdram**********/ copy_bootloader_to_sdram: mov r2 , #0x0 @r2保存了代码段的加载起始地址 ldr r0 , =_start @r0保存了代码的链接起始地址 ldr r1 , =__bss_start sub r1 , r1 , r0 @r1保存代码段的大小 cmp r1 , #4096 ble copy_all @如果小于或等于4K就跳到copy执行,一次性拷完整个代码 copy_sram: ldr r3 , [r2] , #4 str r3 , [r0] , #4 cmp r2 , #4096 bne copy_sram mov r2 , #0x00 @r2:指明是否拷贝了所有代码(’0‘表示没拷完) sub r1 , r1 , #4096 @r1:剩余要拷贝的代码大小 mov r0 , #4096 @r0:剩余代码的起始地址 bx lr copy_all: ldr r3 , [r2] , #4 str r3 , [r0] , #4 cmp r2 , r1 bne copy_all mov r2 , #0x01 @r3=1表示代码已经拷完 bx lr
/**********LED调试*******************/ @灯全亮 ldr r0 , =GPBDAT ldr r1 , [r0] mov r2 , #(0x0F<<5) bic r1 , r1 , r2 str r1 , [r0] /************************************/
./src/boot.c
#include "uart0.h" #include "nand.h" #include "s3c2440.h" #include "common.h" #include "bootparams.h"
void start_armboot(unsigned int left_code_addr , unsigned int left_code_size , unsigned char is_left_code) {
unsigned int destAddr; tag *params; volatile unsigned int *p; /***************************声明一个函数指针,该函数指针为内核的启动函数的地址***********************/ void (*JumpToKenel)(int zero, int arch ,unsigned int params );
/***********************初始化串口0,用于bootloader调试用,也用于内核启动时信息打印******************/ uart0_init(); uart0_sent_msg("bootloader first stage is finish!!!\n\r"); uart0_sent_msg(" ............\n\r"); uart0_sent_msg("bootloader second stage is begin!!!\n\r");
/******************************************nand flash 的初始化****************************************/ dnand_init(); fnand_reset();
/**********************由第一阶段传递过来的参数判段是否还剩有代码需要被拷贝***************************/ if( 1 == is_left_code ) {
uart0_sent_msg("all the code is copy completed!!!!\n\r");
}else {
uart0_sent_msg("coping the left code!!!\n\r");
destAddr = _wsnboot_start + 4096 ; /******计算拷贝期间的中断地址*********/
fnand_readOfSize( (unsigned char *)destAddr , left_code_addr , left_code_size ); uart0_sent_msg("all the code is copy completed!!!!\n\r");
}
/*****************************************设置传递给内核的参数*****************************************/ uart0_sent_msg("setting the boot params......!!!!\n\r"); params = setParamsAddr( (unsigned int )0x30000100, "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0" ) ;
if( (0x30000100 == (unsigned long)params) ) { uart0_sent_msg("boot params setting completed......!!!!\n\r"); }
/**************************************把内核从nand flash拷贝到sdram***********************************/ uart0_sent_msg("beging copying the kenel......!!!!\n\r"); fnand_readOfSize( (unsigned char *)0x30008000 , 0x60000 , 0x200000 ); p = (volatile unsigned int *)(0x30008000+80); uart0_sent_hex_word(*p); uart0_sent_msg("copying the kenel is completing....!!!!\n\r");
/**************************************跳转到内核起始地址执行内核**************************************/ uart0_sent_msg("boot the operating system , congratulations for you...!!!\n\r"); JumpToKenel = ( void (*)(int , int ,unsigned int ) )0x30008000 ; JumpToKenel( 0 , 362 , (unsigned int )params );
uart0_sent_msg("boot the operating system fail...!!!\n\r"); while(1); }
./src/common.c
#include "common.h"
inline void delay_s(volatile unsigned long second) { volatile unsigned long i; while(second--){ i=1000000; while(--i); } }
inline void delay_ms(volatile unsigned long time_ms) { volatile unsigned long i; while(time_ms--){ i=1000; while(--i); } }
/*************填充指定内存区域*********/ unsigned char *memclr(unsigned char *a ,unsigned int c, unsigned int len) { unsigned char *b = a; do{ *a++ = c; }while (--len); return b; }
unsigned int strlen( char *string ) { unsigned int i=0; while(*string != '\0'){ ++string; ++i; } return i; }
char *strcpy(char *destAddr ,char *srcAddr) {
char *dest = destAddr; while( (*destAddr++ = *srcAddr++ ) != '\0'); /*************最后加上字串结束标志符***********/ *dest = '\0'; return dest; }
./src/common.c
#include "common.h"
inline void delay_s(volatile unsigned long second) { volatile unsigned long i; while(second--){ i=1000000; while(--i); } }
inline void delay_ms(volatile unsigned long time_ms) { volatile unsigned long i; while(time_ms--){ i=1000; while(--i); } }
/*************填充指定内存区域*********/ unsigned char *memclr(unsigned char *a ,unsigned int c, unsigned int len) { unsigned char *b = a; do{ *a++ = c; }while (--len); return b; }
unsigned int strlen( char *string ) { unsigned int i=0; while(*string != '\0'){ ++string; ++i; } return i; }
char *strcpy(char *destAddr ,char *srcAddr) {
char *dest = destAddr; while( (*destAddr++ = *srcAddr++ ) != '\0'); /*************最后加上字串结束标志符***********/ *dest = '\0'; return dest; }
./src/pootparams.c
#include "common.h" #include "bootparams.h" #include "uart0.h"
tag *setup_start_tag (tag *params) { params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size(tag_core); params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = (tag *)tag_next(params); return params; }
tag *setup_memory_tags (tag *params) { int i;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = 0x30000000; params->u.mem.size = 0x4000000;
params = (tag *)tag_next (params); } return params; }
tag *setup_commandline_tag (tag *params, char *commandline) {
params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (commandline) + 1 + 3) >> 2;
strcpy (params->u.cmdline.cmdline, commandline); uart0_sent_msg("boot params = "); uart0_sent_msg(params->u.cmdline.cmdline); uart0_sent_msg("\n\r"); params = (tag *)tag_next (params); return params; } void setup_end_tag (tag *params) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; }
tag *setParamsAddr( unsigned int paramsAddr , char *commandline ) { tag *params = (tag *)memclr( (unsigned char *)paramsAddr , 0 , (unsigned int)sizeof(tag) ); params = setup_start_tag (params); params = setup_memory_tags (params); params = setup_commandline_tag(params , commandline); setup_end_tag (params); return (tag *)paramsAddr; }
(4)一级makefile跟链接脚本都放在根目录下。
./Makefile
ifndef CROSS_COMPILE CROSS_COMPILE = arm-linux- endif export CROSS_COMPILE
ifndef XECHO XECHO = echo endif export XECHO
ifndef LKSCRIPT LKSCRIPT = wsnboot.lds endif
ifndef OUTPUTNAME OUTPUTNAME = wsnboot endif
TOPTREE = $(PWD) SRCDIRS := $(TOPTREE)/src $(TOPTREE)/cpu/src $(TOPTREE)/drivers/src OBJDIRS := $(TOPTREE) $(TOPTREE)/cpu $(TOPTREE)/drivers INCDIRS := $(TOPTREE)/inc $(TOPTREE)/cpu/inc $(TOPTREE)/drivers/inc INCLUDES:= $(addprefix -I,${INCDIRS}) SUBDIRS := $(TOPTREE)/cpu $(TOPTREE)/drivers OUTPUTDIR= $(TOPTREE)/output export TOPTREE SRCDIRS OBJDIRS INCDIRS INCLUDES
#函数foreach把OBJDIRS中的单词逐个放在OBJTREE临时变量里,然后执行第三个参数(表达式), #一直到OBJDIRS中的单词被取完,把表达式多次返回的结果都返回。 OBJS := $(patsubst %.c, %.o, $(foreach OBJTREE,${SRCDIRS},$(notdir $(wildcard ${OBJTREE}/*.c)))) OBJS += $(patsubst %.S, %.o, $(foreach OBJTREE,${SRCDIRS},$(notdir $(wildcard ${OBJTREE}/*.S)))) GPATH=$(PWD) vpath %.o $(OBJDIRS) vpath %.c $(SRCDIRS) vpath %.S $(SRCDIRS)
AR = $(CROSS_COMPILE)ar AS = $(CROSS_COMPILE)as CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJDUMP = $(CROSS_COMPILE)objdump OBJCOPY = $(CROSS_COMPILE)objcopy export AR AS CC LD OBJDUMP OBJCOPY
MAKE = make CFLAGS := -Wall -O2 CPPFLAGS :=-nostdinc -nostdlib -fno-builtin export MAKE CFLAGS CPPFLAGS
subdirs: @for dir in $(SUBDIRS) ; do\ $(MAKE) -C $$dir ;\ done $(MAKE) $(OUTPUTNAME).bin
$(OUTPUTNAME).bin:$(OBJS) @$(XECHO) "链接开始" $(LD) -T$(LKSCRIPT) -o $(OUTPUTDIR)/$(OUTPUTNAME).elf $^ $(OBJCOPY) -O binary -S $(OUTPUTDIR)/$(OUTPUTNAME).elf $(OUTPUTDIR)/$@ $(OBJDUMP) -D -m arm $(OUTPUTDIR)/$(OUTPUTNAME).elf \ > $(OUTPUTDIR)/$(OUTPUTNAME).dis @$(XECHO) "链接已完成!!!!目标生成在output目录下....."
%.o:%.c $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< %.o:%.S $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean: @for dir in $(SUBDIRS) ; do\ $(MAKE) clean -C $$dir ;\ done rm -f *.o *.bin *.bak *.elf *.dis $(OUTPUTDIR)/*
./wsnboot.lds
/*指定输出机器架构为arm*/ OUTPUT_ARCH(arm)
/*设置入口点*/ ENTRY(_start)
SECTIONS { . = 0x33f80000; . = ALIGN(4); _start = . ; .text : { start.o(.text) ./cpu/*.o(.text) *(.text) } . = ALIGN(4); .rodata : {*(.rodata)}
. = ALIGN(4); .data : {*(.data)}
. = ALIGN(4); __bss_start = . ; .bss : {*(.bss) *(COMMON)} __bss_end = . ; }
(5)最终的bin输出文件被输出到output里。
转载自:http://blog.csdn.net/shengnan_wu/article/details/8579503

浙公网安备 33010602011771号