S3C2416裸机开发系列九_GCC启动代码工程应用实例
扫描二维码
随时随地手机看文章
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启动代码源文件目录以及其它可能的源码目录。
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 - __code_start即为生成的代码大小,这样启动代码根据这两个信息即可自动地把代码从存储设备拷贝到运行的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以及任何用户的失误造成编