原创 【连载】Sunplus DVD软件Makefile分析(五)

2009-12-8 13:57 3357 6 6 分类: MCU/ 嵌入式

Sunplus DVD软件Makefile详解(五)


         xluoly@hotmail.com<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


链接脚本文件


为了理解romL.binrom.bin的生成原理,我们还需要理解链接脚本文件dvdL.ld。链接脚本是协助链接工具完成任务的,它主要描述的内容包括:输入段与输出段之间的关系、各个输出段在系统存储器中的布局等信息。最终生成可固化到ROM(flash)的可执行程序。一般地,可ROM化的程序具有下结构。


<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />bffa9303-2533-4a82-afa7-9094a2a1cf94.JPG


下面我们来看看SPHE8104L的可执行程序是怎样存放的。前面说过,dvdL.lddvdL.ldpcpp处理后生成的文件,因此我们直接看dvdL.ld文件会更清楚些。下面是dvdL.ld的内容:


dvdL.ld


1   INPUT(-lsys)


2   INPUT(-latapi)


3   INPUT(-lsys)


4   INPUT(-lir)


5   INPUT(-lpe)


6   INPUT(-ltask)


7   INPUT(-lauddrv)


8   INPUT(-ldsp3)


9   INPUT(-lauth)


10  INPUT(-lparser)


11  INPUT(-lfs_krnl)


12  INPUT(srv-elf\libsrv_sa7km0_slt_8104T_.a)


13  INPUT(-lsrvdp_8104T_)


14  GROUP( -lkern_8202L)


15  INPUT(-lstd)


16  INPUT(-lm)


17  INPUT(-lgcc)


18  INPUT(-lsys)


19  INPUT(-latapi)


20  INPUT(-lsys)


21  INPUT(-lir)


22  INPUT(-lpe)


23  INPUT(-ltask)


24  INPUT(-lauddrv)


25  INPUT(-ldsp3)


26  INPUT(-lauth)


27  INPUT(-lparser)


28  INPUT(-lfs_krnl)


29  INPUT(srv-elf\libsrv_sa7km0_slt_8104T_.a)


30  INPUT(-lsrvdp_8104T_)


31  GROUP( -lkern_8202L)


32  INPUT(-lstd)


33  INPUT(-lm)


34  INPUT(-lgcc)


35 


36  SECTIONS


37  {


38      .drv_iop :


39      {


40          _text_drv_iop = .;


41          . = ALIGN(16);


42          KEEP(*iop.o(.rodata*))


43      } > sdram_drv_iop


44 


45      .drv_iop_rst :


46      {


47          . = ALIGN(16);


48          KEEP(*ioprom.o(.rodata*))


49      } > sdram_iop_rst


50  }


51 


52  SECTIONS


53  {


54      .rom1 :


55      {


56          MIPS/init0.o (.text*)


57          MIPS/rominit.o (.text*)


58          MIPS/crt0.o (.text*)


59          *reset.o (.text*)


60          *sysmain.o (.text*)


61          *lexra.o (.text*)


62          *bitop.o (.text*)


63          *intdrv.o (.text*)


64          *other.o (.text*)


65          *hwcpu0.o (.text*)


66          *hwcpu1.o (.text*)


67          *crt0.o (.rodata*)


68          *init0.o (.rodata*)


69          *watchdog.o* (.text*)


70          . = ALIGN(16);


71      } > rom


72 


73      .ram1 :


74      {


75          _data = .;


76          * (.data*)


77          _gp = .;


78          * (.sdata*)


79          * (.lit8*)


80          * (.lit4*)


81          * (.lita*)


82          _edata = .;


83          . = ALIGN(16);


84      } > sdram_cs


85 


86      .rom1_2 :


87      {


88          _data_ps = .;


89          . += SIZEOF(.ram1);


90          _data_pe = .;


91          . = ALIGN(16);


92      } > rom


93 


94      .rom2 :


95      {


96          MIPS/rominit.o (.rodata*)


97          MIPS/crt0.o (.rodata*)


98          *reset.o (.rodata*)


99          *sysmain.o (.rodata*)


100         *lexra.o (.rodata*)


101         *bitop.o (.rodata*)


102         . = ALIGN(16);


103     } > rom


104


105     .drv_ap1 :


106     {


107        _text_drv_ap = .;


108         * (.rdata*)


109         * (.rodata*)


110         * (.text*)


111        . = ALIGN(16);


112     } > sdram_ap1


113


114     .ramF :


115     {


116         MIPS/databuf.o (.bss*)


117         MIPS/databuf.o (COMMON*)


118         . = ALIGN(16);


119     } > sdram_cs


120


121     .ram2 :


122     {


123         _bstart = .;


124         * (.sbss*)


125         * (.scommon*)


126         * (.bss*)


127         * (COMMON*)


128         . = ALIGN(16);


129         _bend = .;


130     } > sdram_cs


131


132     .rel :


133     {


134         * (.rel.dyn)


135     } > garbage


136


137     _etext = ADDR(.rom1) + SIZEOF(.rom1) + SIZEOF(.rom2);


138     _stextf = _etext + SIZEOF(.ram1);


139     _tt_gb_size = SIZEOF(.ram1) + SIZEOF(.ram2) + SIZEOF(.ramF);


140


141     .udf_buf :


142     {


143         _udf_work_buf = .;


144         . += (1024*((40)));


145         _udf_work_buf_end = .;


146     } > sdram_udfbuf


147


148     wb_buf = (0x080000000+1024*(((((((((0) + 0) + (2))+(3))) + (4)) + (18)))));


149     ADPCM_ESP_var = (0x0bfff0000+1024*(0)+0);


150     _stkptr = (0x080000000+1024*(((((0) + 0) + (2))+(3))));


151     _stkptr_dbg = (0x080000000+1024*((((2048) - (300+50)))));


152     _stkbtm = (0x080000000+1024*(((((0) + 0) + (2))+(3))-((3)+1)));


153     _stkbtm_dbg = (0x080000000+1024*((((2048) - (300+50)))-(2)));


154 }


155


156 NOCROSSREFS( .ramdrv1 .ramdrv2 )


157 NOCROSSREFS( .drv_ap2 .drv_ap3 )


158


159 MEMORY


160 {


161     garbage : org = 0x00000000, l = 1m


162     bootrom : org = 0xbfc00000, l = 512k


163     rom : org = 0x88000000, l = 1024k


164     sdram_cs : org = (0x080000000+1024*(((((((0+0)+(2))+(3))) + (4)))), \


165                     l = (1024*((18)))


166     sdram_drv_iop : org = 0x080000000, l = 2k


167     sdram_iop_rst : org = 0x180000000, l = 2k


168     sdram_ap1 : org = (0x080000000+1024*(((2048) - (300+50)))), \


169                      l = (1024*((300+50)))


170     sdram_udfbuf : org = (0x080000000+1024*((((((((((0) + 0) \


171                              + (2))+(3))) + (4)) + (18)))+(32)))), \


172                         l = (1024*((40)))


173 }


174


175 ENTRY(__romstart)


178


134)设置链接输入文件,这里列出的都是静态链接库文件。前面已经介绍过,每个子模块的编译结果都是生成一个库文件,因此这里把它们当成输入文件链接进来。“INPUT(FILE1 FILE2 ...)”:输入命令促使链接器在链接时包含列出的文件,就像在命令行指定文件名一样。“GROUP(FILE FILE ...)”:这个命令类似“INPUT”,但是所有命名文件完全是归档文件,它们被重复搜索直到不再有未定义的参考(references)


175)定义程序入口为“__romstart”。这个symbol位于Source Code文件init0_8202.S中。


159173)定义各个程序段(program segment)的起始地址和长度。注意一下sdram_iop_rst,它的起始地址是0x180000000,实际上已经超出了MIPS内核的寻址空间,最终会被强制变为0x80000000。不知道这个模块有没有真正起作用,我没有测试过。起始地址为0x80000000的空间是SDRAM。起始地址为0x88000000的空间是ROM(系统中用的是flash)。


3650)定义iop相关的两个section .drv_iop.drv_iop_rst。分别存放到segment sdram_drv_iopsdram_iop_rst中。


5471)定义section .rom1。其中存放的内容是系统启动阶段的程序代码(.text)和只读数据(.rodata),存放到segment rom中。它是放入rom的第一段内容,因此它的其起始地址就是rom的起始地址0x88000000


7384)定义section .ram1。其中存放所有输入文件中的某些数据段(.data.sdata.lit8.lit4.lita),这些段存放可写的、为初始化的变量。它们被定位到segment sdram_cs中,起始地址等于sdram_cs的起始地址。同时用_data_edata两个symbol分别记录.ram1的起始和结束地址,后面会介绍这两个symbol的作用。_gp的值用于初始化MIPS内核全局变量指针(寄存器$28),相应的程序在源代码文件crt0_8202.S


8692)定义section .rom1_2。内容为空,预留一个大小等于.ram1长度的空间,定位在程序段rom内,起始地址紧接在.rom1之后。用_data_ps_data_pe记录.rom1_2起始和结束的地址。从前面介绍过的romL.dump生成romL.bin的过程可以看出,实际上存放在.rom1_2中的是.ram1的映像。因为.ram1中存放有全局变量的初始化值,所以必须把它们固化到rom空间,在系统启动阶段将它们copySDRAM_data值对应的地址。这个过程就是文件rominit.c中函数rominit()的工作,crt0_8202.S中被调用。


94103)定义section .rom2。存放的内容是启动程序的部分只读数据(其实应该可以并入.rom1),定位在rom中,起始地址紧接在.rom1_2之后。


105112)定义section .drv_ap1。存放的内容是未指明文件的只读数据段(.rdata.rodata)和程序段(.text),定位在sdram_cs中,起始地址紧接在.ram1之后,用_text_drv_ap保存起始地址,在程序中会用到。从前面介绍的Makefile中可以看到,这个section被压缩程序处理后,保存在romL.bin文件中,也就是保存在flash中。程序运行时,从flash中对应地址读出并解压缩后存入SDRAM中起始地址等于_text_drv_ap值的位置,然后程序指针跳转到这部分code的入口地址运行,这个过程见源程序函数LoadModual()。这是为了优化程序的运行速度而设计的一种运行模式,因为内核对SDRAM的读取速度远远快于对flash


114130)定义section .ramF.ram2。存放的内容是输入文件中的未初始化变量,定位在sdram_cs中,起始地址紧接在.drv_ap1之后。


132135)定义section .rel,定位在起始地址为0x000000001MB空间,实际上这是一段无效的空间,并不用于生成最后的可执行程序,只是用于收集链接过程中的垃圾信息。


141146)定义section .udf_buf。定位在sdram_udfbuf,只是预留一段SDRAM的空间在程序中用到。


148153)计算这几个symbol的值,供程序中使用。其中包括主任务(main task)和调试任务(debug task)的堆栈位置(用于实际生产的程序是只运行主任务的,开发中用于调试的程序会增加一个调试任务,两个任务采用时间片轮转调度方法)。


156157)检查列出的section,如果发现它们之间有相互引用就会报错。这里列出的四个section实际上不存在,不起作用(应该是DVD软件中遗留下来的,多余)。


162)这里定义了一个名为bootromsegment,起始地址为0xbfc00000,长度为512KB,这里没有用到。MIPS内核的上电复位向量地址是0xbfc00000,据说IC内部已经固化有一段上电启动程序。应用程序存放在外部flash中,接口是SPI,与NOR flash不同,它是不能直接被CPU内核访问到的。因此,可以猜测IC内部有一部分启动代码,在CPU执行到外部SPI flash中的程序之前,由它完成CPU的线性寻址方式到外部SPI通信的转换。然后才将程序指针(PC)跳转到0x88000000,这就是我们看到的程序入口地址(__romstart)。


下面是用readelf –S看到的输出段情况,有些段是ld默认生成的。


点击看大图


下面是最终生成的rom.bin文件,也是flash中的数据组成。


0be41d98-b00d-4b6a-bc2d-6ac7b67e17dc.JPG


以上介绍的SPHE8104LSunplus的一款CD/USB/SD解决方案SOC。从DVD IC衍生而来,硬件上比DVD IC少了视频等模块,基本软件也是由DVD软件裁剪而成,只是软件架构是重新设计了,比DVD软件简洁多了,看着都不像Sunplus糟糕的代码风格了。但是,里面还是保留了不少DVD软件的痕迹,因此,如果不清楚这点历史的话,可能看着会有点疑惑。


这份软件最终生成的可执行程序size没有DVD那么庞大,因此,除了启动阶段的代码是直接从flash运行外,其余部分都是先将代码压缩后保存在flash中,由启动代码将其读入SDRAM内解压缩运行。DVD软件也是采用这样的思想,它将整个系统按功能模块分割成不同的section,再将这些section转换成独立的可执行的代码,按一定顺序保存的flash中,需要运行到哪个模块就将其读入SDRAM运行。这样的设计还有另外一个好处,就是可以减小保存在flash中的code size。代价是需要占用一段SDRAM空间保存这部分可执行代码。也并不是每个模块都需要独立占用一段SDRAM空间的,如果是完全独立(没有相互引用)的两个模块,就可以在SDRAM中给它们安排同一段地址,运行之前将其读入SDRAM中,相同地址范围的代码可以相互覆盖。例如,CDUSB的播放是两个独立的模块,这样就可以将它们放到同一段SDRAM空间运行。载入SDRAM中运行的程序段,其实也不一定要保存在flash中,保存在U盘或者SD卡,甚至通过UART下载理论上都是可以的,只要编写好相应的装载程序就行了。


至此,相关内容我们都已经介绍了。虽然Makefile中还有相当部分设计不够合理,也有很过重复和多余的内容,显得有些凌乱,而且整个构建过程过多的依赖dos批处理脚本,没有充分发挥出Makefile的管理功效,但是能做到这样已经是非常难能可贵了。相信能够看到这里的同志,一定是不缺乏耐心的,这些问题是不会成为障碍的。由于缺乏IC硬件和软件的相关资料,上文中某些理解只能是本人的推测,留待考证……


 


参考资料:


l         Sunplus SPHE8104L source code


l         GCC - The Complete Reference McGraw Hill


l         GNU Make


l         Using Ld


l         See MIPS Run-2nd_edition


 



『完』


 


PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条