2008年11月1日

LDR文件格式和bin2ldr工具的修改

ADSP系列芯片通过BMODE0和BMODE1两个Pin脚来配置芯片的启动方式,ADSP内置ROM中引导代码根据这两个Pin脚的设置从相应的外部存储空间把程序加载到指定的位置去运行。因此在ADSP内置的ROM程序和用户编写的image文件之间需要统一的格式,或者说我们编译形成的image文件必须按照ROM程序的要求保存到相应的外部存储空间中,这样才能被正确的引导起来。
LDR文件的具体格式可以参考《ADSP-BF533 Blackfin® Booting Process》
bin2ldr是u-boot中用来生成LDR格式文件的一个工具。bin2ldr生成LDR文件时使用了两个应用程序段,第一段是配置SDRAM,因为只有配置好SDRAM,我们才可能将后面代码Load到内存中。第二段才是真正的image代码,这段代码是要加载到编译时指定的位置去运行的。为了在将image加载完成后ADSP能够跳到image开始的位置去执行,bin2ldr加插了一小段代码到0xFFA00000或者0xFFA08000之处,也就是ROM程序执行完后跳转之处的位置,这个位置应不同ADSP而不同。
让我们来看具体的代码。
1。先看Makefile
Makefile在编译中起着总体控制的功能,通过分析Makefile文件我们可以清楚整个的编译过程。
u-boot\tool\bin2ldr\Makefile:
#!/bin/sh
CC = gcc
CFLAG = -g
APP_FILE = ../../u-boot.bin -----> u-boot编译好的二进制image文件,也就不含其它文件格式可以直接运行的代码文件

###############################################################
----->这部分生成bin2ldr文件本身
bin2ldr: bin2ldr.o
$(CC) bin2ldr.o -o bin2ldr
bin2ldr.o: bin2ldr.c bin2ldr.h
$(CC) $(CFLAG) -c bin2ldr.c
###############################################################
----->这就是要插入到0xffa00000或0xffa08000位置的代码,它唯一的功能就是跳转到u-boot.bin在memory中的位置去执行代码
jump: jump.o
bfin-uclinux-ld -o jump -e 0x00000000 jump.o
jump.o: jump.S bin2ldr.h
bfin-uclinux-gcc $(CFLAG) -c jump.S

jump.bin: jump
bfin-uclinux-objcopy -O binary jump jump.bin
################################################################
----->将u-boot中初始化SDRAM的代码copy到这里,ROM引导程序首先加载的就是这断程序,也就是u-boot.ldr的第一段应用代码,这段代码被设置成init属性,ROM引导程序检查到是init代码块后就会马上执行它,然后再继续后面的加载动作。
init_sdram: init_sdram.o
bfin-uclinux-ld -o init_sdram -e 0x00000000 init_sdram.o
init_sdram.o:
cp ../../cpu/bf533/init_sdram.o .
init_sdram.bin: init_sdram
bfin-uclinux-objcopy -O binary init_sdram init_sdram.bin
################################################################
----->将u-boot.bin image文件copy到这里
app.bin:
cp $(APP_FILE) app.bin
################################################################
all: bin2ldr jump.bin init_sdram.bin app.bin
......

2. jump.S
这段代码很短,一看自明。
#include "bin2ldr.h"

p0.l = (APP_ENTRY & 0xFFFF);
p0.h = (APP_ENTRY >> 16);
jump (p0); ---->跳到APP_ENTRY处执行之

3. bin2ldr.c
#define INIT_FILE "init_sdram.bin" ----> 初始化SDRAM的代码
#define JUMP_FILE "jump.bin" ----->跳转到app.bin执行的代码
#define APP_FILE "app.bin" ----->编译u-boot生成的image文件
#define OUT_FILE "app.ldr" ----->bin2ldr生成的结果

---->下面这些是用于10-bytes头中标志
#define PORT_G 0x0400 >>8
#define GPIO6 0x00C0
#define ZEROFILL 0x0001
#define RESVECT 0x0002 ---->bin2ldr生成是符合BF533格式的LDR文件,如果生成BF531/BF532格式的LDR文件,这个值要设置成0x0
#define INIT 0x0008
#define IGNORE 0x0010
#define FINAL 0x8000 >>8

#define BLOCK_SIZE 0x8000 ----->块大小固定为32Kbytes

标志Flags的含义:
生成第一段应用程序段:
/*
* DXE
*/

/* Add BLOCK 0, 10 bytes head and 4 bytes data */
block[i++] = 0x40;
block[i++] = 0x00;
block[i++] = 0x80;
block[i++] = 0xff;

block[i++] = 0x04;
block[i++] = 0x00;
block[i++] = 0x00;
block[i++] = 0x00; ---->该段只有4bytes,而且置成Ignore,也就是表示这是一个DXE的开始

#if (BFIN_BOOT_MODE == BF537_UART_BOOT)
block[i++] = (GPIO6|IGNORE|RESVECT);
block[i++] = PORT_G;
#else
block[i++] = (IGNORE|RESVECT); ---->注意这里的IGNORE标记
block[i++] = 0;
#endif
dxe_temp1 = i;
block[i++] = 0x00;
dxe_temp2 = i;
block[i++] = 0x00;
dxe_temp3 = i;
block[i++] = 0x00;
dxe_temp4 = i;
block[i++] = 0x00;


/* Add BLOCK 1, init block */
block[i++] = 0x00; /* create head of init block */
block[i++] = 0x00;
block[i++] = 0xa0;
block[i++] = 0xff; ---->ROM程序执行后跳转的位置BF533是0xffa00000,如果是BF531/BF532需要改成0xffa08000
block_temp1 = i;
block[i++] = 0x00; /* count of init block,init to ZERO */
block_temp2 = i;
block[i++] = 0x00;
block_temp3 = i;
block[i++] = 0x00;
block_temp4 = i;
block[i++] = 0x00;
#if (BFIN_BOOT_MODE == BF537_UART_BOOT)
block[i++] = (GPIO6|INIT|RESVECT);
block[i++] = PORT_G;
#else
block[i++] = (INIT|RESVECT); /* flag of init block */ ---->init块,ROM引导程序加载完该块后立即先执行它
block[i++] = 0x00;
#endif

temp = i; /* index for the data of init block */
while( (ch=fgetc(init_fd))!=EOF ) ----->复制SDRAM初始化代码
block[i++] = ch;

init_n = i-temp;


block[block_temp1] = init_n & 0xFF; /* re-write the count of init_block */
block[block_temp2] = (init_n & 0xFF00) >>8;
block[block_temp3] = (init_n & 0xFF0000) >> 16;
block[block_temp4] = (init_n & 0xFF000000) >> 24;

dxe_n = i - 0x0e; ---->该DXE的大小要减掉头IGNORE块的14个字节
block[dxe_temp1] = dxe_n & 0xFF;
block[dxe_temp2] = (dxe_n & 0xFF00) >>8;
block[dxe_temp3] = (dxe_n & 0xFF0000) >>16;
block[dxe_temp4] = (dxe_n & 0xFF000000) >> 24;

生成第二段应用程序段:
/*
* DXE 1
*/

temp = i; /* save the start index of DXE 1*/
/* Add BLOCK 0, 10 bytes head and 4 bytes data */
block[i++] = 0x40;
block[i++] = 0x00;
block[i++] = 0x80;
block[i++] = 0xff;

block[i++] = 0x04;
block[i++] = 0x00;
block[i++] = 0x00;
block[i++] = 0x00;

#if (BFIN_BOOT_MODE == BF537_UART_BOOT)
block[i++] = (GPIO6|IGNORE|RESVECT);
block[i++] = PORT_G;
#else
block[i++] = (IGNORE|RESVECT); ---->同上这block也只是表示一个新的DXE的开始
block[i++] = 0;
#endif

dxe_temp1 = i;
block[i++] = 0x00;
dxe_temp2 = i;
block[i++] = 0x00;
dxe_temp3 = i;
block[i++] = 0x00;
dxe_temp4 = i;
block[i++] = 0x00;


/* Add BLOCK 1, jump block */
block[i++] = 0x00;
block[i++] = 0x00;
block[i++] = 0xa0;
block[i++] = 0xff; ---->ROM程序执行后跳转的位置BF533是0xffa00000,如果是BF531/BF532需要改成0xffa08000

block_temp1 = i;
block[i++] = 0x00;
block_temp2 = i;
block[i++] = 0x00;
block_temp3 = i;
block[i++] = 0x00;
block_temp4 = i;
block[i++] = 0x00;

#if (BFIN_BOOT_MODE == BF537_UART_BOOT)
block[i++] = (GPIO6|RESVECT);
block[i++] = PORT_G;
#else
block[i++] = RESVECT; ---->注意这里并没有INIT标记,要全部代码加载完后ROM引导程序才跳转到这里
block[i++] = 0;
#endif
jump_n = i;
while( (ch=fgetc(jump_fd))!=EOF) ---->复制jump代码到ROM引导程序跳转的开始位置
block[i++] = ch;
jump_n = i - jump_n;

block[block_temp1] = jump_n & 0xFF; /* re-write the count of init_block */
block[block_temp2] = (jump_n & 0xFF00) >>8;
block[block_temp3] = (jump_n & 0xFF0000) >> 16;
block[block_temp4] = (jump_n & 0xFF000000) >> 24;

/* Add BLOCK 2, app block */
app_n = 0;
while( (ch=fgetc(app_fd))!=EOF) ----->下面这些代码就是真正的u-boot image程序了,分成多个block加载,没什么特别的,后面代码不再介绍了
app[app_n++] = ch;

最终生成结果可以使用ldr-utils查看,比如:
bfin-uclinux-ldr -s u-boot.ldr

一切皆明了,原来ADI的ROM程序就是这么干的!

没有评论: