GNU是一个自由软件工程项目,目标在于创建一个完全兼容于UNIX的自由软件环境。GNU已经开发出了大部分UNIX系统的程序库和工具,如功能强大的文字编辑器Emacs,本章节涉及的GUN开发编译器GCC等。尤其是Linux与其它的GNU软件结合,诞生了GNU下完全自由免费的操作系统。GNU软件功能完善而强大,丝毫不输商业软件,其开源免费的特性也得到了世界各地程序员的积极响应,让GNU软件尤其是Linux得到了相当广泛的应用。
s3c2416由于性能优越,用来运行Linux等GNU软件是完全没问题的,笔者此处应读者的要求,把s3c2416的启动代码工程移植到GCC交叉编译环境上,以方便读者在Linux下作进一步的开发。

1. 启动代码工程架构

make工具是一个非常重要的编译工具,利用make工具,可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序而言,使用make工具和makefile文件就可以清晰地理顺各个源文件之间的关系。
对于一些商业的集成开发环境编译器,如MDK、IAR等,并不需要编写make脚本,因为集成开发环境通过可视化的选项或默认的参数自动帮用户生成相关的make脚本。
Linux下GCC开发是需要掌握GNU make来构建和管理自己的代码工程的,make工具最基本的功能是调用makefile文件,通过makefile文件来描述源程序之间的相互依赖关系并自动维护编译工作。当然,makefile 文件需要按照某种语法进行编写,需要说明如何编译各个源文件并连接生成可执行文件,以及定义源文件之间的依赖关系。
如果读者学习过相关的uboot源码,那么是很容易理解笔者这篇在arm交叉编译GCC下, s3c2416启动代码工程的makefile管理方式。

1.1. 顶层目录

顶层目录包括 config.mk、Makefile、rules.mk 这三个文件,start_code 启动代码源文件目录以及其它可能的源码目录。

注:象棋小子根据u-boot的Makefile制作的简易Makefile模板,简单易用,一定要掌握。

config.mk 是所有 makefile 的配置文件,如配置交叉编译工具所在的路径、代码的链接位置、库文件路径、头文件搜索路径、编译选项等。通常被所有的 makefile 包含,如设置编译选项为调试模式 -g 以及二级优化 -O2 等都在此处修改。需要注意修改任何编译选择,如果想让源码重新编译,需先在顶层目录下 make clean 后再 make,不然选项不起作用,不会重新编译。因为 makefile 一般是不常修改的,为加快编译,没有在源码的依赖关系中关联 config.mk 文件。

rules.mk 为源码依赖关系生成文件,make 工具很重要的一个功能就是根据依赖关系决定命令是否执行,如一个源码及其头文件等没有作过修改,那么下次编译是不需要再次编译的。make 工具会选择新添或修改过的源码进行编译,其它未改动的部分直接用上次编译的结果。这样的好处就是不用每次编译都全部编译,大大节省编译时间。rules.mk 通过被各个目录下的 makefile 包含,和 uboot 该部分一样,会在包含的目录下生成该目录下所有源码依赖关系信息的隐藏文件 .depend。

顶层 Makefile ,顶层 Makefile 用来管理工程各个源码的目录,并用对各个目录编译输出进行链接,最终输出可执行文件。所有的 Makefile 支持 make all 和 make clean 这两个命令,顶层的 Makefile 执行 make 后,首先会进入各个源码子目录去执行 make_depend 更新各个子目录源码的依赖关系,然后再进入各个源码子目录去执行 make 。所有子目录均执行更新完,最后调用链接命令把各个子目录生成的代码进行链接,生成最终目标文件。Make clean 删除所有 make 过后的输出文件,还原工程原来的文件关系。

1.2. start_code子目录

与启动代码相关的代码文件放在start_code目录文件夹中。目录架构如下:

s3c2416.S,启动代码文件,代码执行时的入口,用来初始化系统到一个必要的c环境中,最后进入c函数入口main执行c代码。
LowLevelInit.S,板级初始化代码,包括DDR2控制器的初始化,代码搬移到RAM的实现(sd卡启动,Nand启动),由s3c2416.S调用。
Nand.h/Nand.c,Nand flash驱动实现,包括Nand启动时,代码从Nand搬移到RAM的接口函数实现。
MMU.h/MMU.c,MMU映射,把异常向量表从0x0处映射到用户代码的首地址,其余地址空间1:1映射,内存区开启I/D-Cache。
Exception.h/Exception.c,异常处理(包括中断),各个异常的处理代码,IRQ实现统一管理各个外设中断,支持中断嵌套。
s3c2416.h,三星给出的s3c2416寄存器头文件。
s3c2416.lds,GCC链接文件,在config.mk中配置引入该链接脚本。

Makefile,start_code目录下的Makefile源码管理,指定该目录下需编译的源码,执行make后会生成该目录源码的依赖关系文件.depend,并编译指定的启动代码源码,生成一个静态库文件libstart_code.a以供顶层目录链接,子目录的Makefile功能是与uboot一致的。
笔者所用的DDR2型号为K4T51163QJ-BCE79(DDR2@400M5-5-5),64MB,行地址线13,列地址线为10,16位线宽。不同的板载DDR2,可能要修改LowLevelInit.S中DDR2的线宽时序配置,BANKCFG & BANKCON1, 2。
笔者所用的Nand flash为K9F2G08U0B,一页有(2048+64)Byte,一个block有64页,容量大小为(256M+8M)Byte,是一款8位宽的SLC flash。Nand flash不一致,需Nand启动的,则需要修改Nand.c驱动中的这些信息。其余代码文件对于s3c2416/50/51都是通用的。

2. c开发工程搭建

首先,启动代码工程是针对arm-linux-gcc的,需要在linux环境下安装交叉编译工具arm-linux-gcc。可以在windows下通过虚拟机的方式安装linux操作系统或安装Cygwin搭建一个Linux环境。
2.1. 笔者文章最后给出GCC启动代码示例工程链接,下载放在任意目录下,如果arm-linux-gcc工具路径在linux环境变量中,在代码目录下执行make即可生成相关的文件。
2.2. 工程中加入新的源码,以在apps目录中加入test.c为例,首先新建或拷贝源代码文件到目录下,修改该目录下的Makefile,在OBJS变量中加入test.c的目标文件,OBJS = main.otest.o保存Makefile,其它不用修改,再在顶层目录下make,即可看到test.c加入了编译。
2.3. 工程中加入新的目录,以在apps目录中加入games目录为例,首先新建目录或拷贝目录到apps目录下,修改顶层的Makefile,在SUBDIRS变量中加入新添的目录路径,SUBDIRS= $(TOPDIR)/start_code $(TOPDIR)/apps $(TOPDIR)/apps/games,保存顶层的Makefile即可。同时新添目录中的Makefile也应保证符合子目录的要求规则,games目录的Makefile文件名要求第一字母大写,内容可完全拷贝参考apps或start_code目录下的Makefile编写,按上一步骤添加该目录下需编译的源码,保存即可。如果源码中引用了其它路径的头文件或库,则需要在config.mk中添加头文件或库的路径。如在源码中使用了math库,则应LIBS变量中加入math库路径,LIBS = -lc -lm。在CFLAGS变量中加入games目录下头文件搜索路径,CFLAGS += -I $(TOPDIR)/start_code -I $(TOPDIR)/apps -I $(TOPDIR)/apps/games,保存即可在顶层目录中make。
2.4. 链接文件s3c2416.lds一般是无需修改的,目录层次也是与uboot一致的。在链接脚本中引出了code_start以及code_end用来定位实际生成可执行代码bin大小,在代码中代码段(.text)、只读段(.rodata)、已初始化可读写段(.data)是需要实际的存储空间保存信息的,其它的未初始化段(.bss)等都可以直接用0来填充,无需任何的固化存储空间。code_start确定了代码的运行地址(从sd、nand等存储设备加载代码到ram需确定),code_end - _codestart即为生成的代码大小,这样启动代码根据这两个信息即可自动地把代码从存储设备拷贝到运行的ram位置。同时start_code目录中Bootloader1代码应放在最前面的8k位置,顺序不能随意改变。

OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
    . = 0x00000000;
    . = ALIGN(4);
    .text   :
    {
     __code_start = .;
/*************Bootloader1********************/
     ./start_code/s3c2416.o(.text)
     ./start_code/LowLevelInit.o(.text)
     ./start_code/MMU.o(.text)
     ./start_code/NAND.o(.text)
     ./start_code/Exception.o(.text)
/*********************************************/
      *(.text)
    }
    . = ALIGN(4);
    .rodata : { *(.rodata) }   
    . = ALIGN(4);
    .data : { *(.data) }
    __code_end = .;

    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) }
    _end = .;

    . =ALIGN(1<<14);
    .mmudata : { *(.mmudata) } 
}

2.5. 所有代码加入到工程后,即可在顶层目录中make,编译完成后即可在顶层目录生成s3c2416.elf、s3c2416.map、s3c2416.srec、s3c2416.bin、s3c2416.dis这五个文件。其中,s3c2416.map为链接Mapping文件,这里可以看到各个全局符号、各个段内存链接位置、大小等信息。s3c2416.dis为工程的汇编生成文件,这是编译器经过编译所有的源码并进行链接最终给出的汇编文件,这是最权威的查错文件,编译器的bug以及任何用户的失误造成编译器未能很好地按照用户的本意编译等出现的问题,在这里都可以查找出。s3c2416.bin即为我们用来烧录进SD卡或nand等存储器的二进制代码文件。
2.6. 在MDK与arm-linux-gcc都设置二级优化条件下,同一个启动代码测试工程编译生成的二进制代码大小MDK为4868 byte,GCC为4916 byte。可见,GCC与其它专业商业软件具有同样强悍的性能。GNU软件完善而强大,但相对不易用,由于其开源免费的特性,让越来越多的公司在项目中选择GNU软件来作开发。

3. 代码烧写

代码可以存储在Nand或sd/mmc中,不借助昂贵的烧写工具,代码烧写进Nand,只能先通过sd卡启动,sd卡代码中包含Nand写的函数实现,即可把代码从sd卡读出再烧写进Nand。Sd卡启动需要二个工具,SdBoot.exe和IROM_Fusing_Tool.exe。SdBoot工具是笔者专为Samsung arm9 S3C2450/S3C2451/S3C2416和arm11 S3C6410从sd卡启动开发的。编译器编译的二进制代码格式是不满足sd卡启动要求的,SdBoot能够转换编译器生成的bin文件,使之转换成符合sd卡启动的二进制代码格式,转换后会在原bin目录生成一个文件名多1的bin文件。IROMFusing_Tool.exe为三星开发的工具,用来把二进制文件烧写进sd卡特定位置,以支持sd卡启动。通过SdBoot工具转换后的二进制代码,再通过ROM_Fusing_Tool.exe烧写进sd卡,即可从sd卡启动,运行代码。
3.1. 从linux操作系统中把GCC编译生成的s3c2416.bin文件拷贝到windows下,打开SdBoot.exe,点击浏览加入s3c2416.bin

09_GCC启动代码工程应用实例 - 图1


3.2. 确保选中“制作SD卡启动文件”,点击“生成”,如果成功,将在原bin目录下生成一个文件名比原bin多_1的文件。这个转换后的bin文件即能符合sd卡启动的格式要求。

09_GCC启动代码工程应用实例 - 图2

3.3. 如果没有点选“制作SD卡启动文件”,可以生成一个任意大小的文件(对文件扩充),该功能只为有需要的人使用(可生成特定大小的文件进行测试等)。
3.4. 通过IROM_Fusing_Tool.exe把转换后的代码烧写进sd卡,设置板从sd卡启动。请匆对生成的sd卡启动bin再重复转换,因为工具无法识别二制进代码是否已转换,重复转换将破坏原有的格式。
3.5. 代码烧写进Nand,C工程代码中调用WriteCodeToNand()函数即可把代码从内存中烧写进Nand,以后设置从Nand启动即可。本章测试c代码main.c中实现在开机时按住下载键,即可调用WriteCodeToNand()函数把代码固化进Nand。

4. 附录

至此,s3c2416/50/51的GCC启动代码功能己完整,相比专业的bootloader如uboot而说,笔者的启动代码具有单一针对性,不作修改只适用于s3c2416/50/51这系列arm9芯片,因此也具有相当的简洁、明确性。整体的启动代码只需4KB左右即可,支持sd、Nand启动,为用户设置系统时钟,初始化内存,自动识别启动设备并搬移代码到RAM,MMU映射,中断管理等,用户只需专注于用c开发其它功能函数即可。对于s3c2416/50/51裸机开发、嵌入式操作系统开发(ucos、embOS等),用笔者的启动代码来作进一步开发能达到较低的资源消耗、更快的启动速度等。
StartCode_GCC.rar,启动代码GCC测试工程,Makefile采用了uboot的Makefile代码管理方式,添加源码以及添加目录到工程都是极其简单的,在start_code目录下包含启动代码的所有源码,apps为c功能实现目录,main.c有代码固化进Nand的实例。
源码下载: http://pan.baidu.com/s/1hqmQNKK