根据芯片手册,S5P6818的启动模式有很多种:
因为我的板子是使用SD/eMMC boot的,所以对此模式深入分析, internal ROM Boot模式也是大同小异的。
(1). 由芯片手册可知SD卡的启动过程:板子上电后, 是从SD卡的第二个扇区开始自动复制56kb的代码数据到芯片的内部的SRAM,然后从0xFFFF0000出开始运行。从图中可以看到芯片在复制SD卡中的代码时,自动得忽略了512字节的数据,这是因为这512字节的数据是SD卡的MBR主引导记录,也是SD卡的第一个扇区。
(2).芯片把SD卡的56kb数据复制过来,然后在0xFFFF0000处开始执行,(如果复制过来的是uboot的部分代码,那么执行的操作一般有: 1.做一些芯片的初始化 和芯片外设必要的初始化(如SDRAM); 2.把SD卡上的代码全部复制到内存SDRAM中,然后跳转到SDRAM中继续执行。)但是在此主要分析 芯片启动过程的最基本最主要的原来,uboot的启动分析将在后续详细介绍。
注意:
在复制512bytes后,芯片会对复制的代码进行数字签名的检查,具体的做法就是检查这512bytes中的最后16个字节是否为0x4849534E。若是,则为正确代码,跳转到内部RAM即地址0xFFFF0000处运行;否则,选择下一个外部存储器进行判断测试。我使用UltraEdit打开可以正常工作的uboot二进制文件,可以发现代码里面确实是这样的:
知道这个原理以后,我们就可以在这芯片内的56kb的SRAM做任何想做的事情了。下面开始写一个汇编裸机的点亮led灯的程序。
点亮led灯程序的思路为:
(1)根据开发板的电路图和芯片手册,使用汇编语言设计点亮led灯的程序
(2)编译链接成leds.bin文件
(3)在虚拟机Ubuntu中制作添加boot header的工具,然后使用工具为leds.bin添加boot header,生成芯片可以识别的bootleds.bin文件
(4)制作SD启动卡
下面为具体的操作过程:
1) 我使用粤嵌的开发板,根据原理图和芯片手册,点亮D7灯。
代码文件:leds.S, main.c, Makefile, leds.lds
leds.S:
/*leds.S*/
//GPIOE13 ---- D7
#define GPIOEOUT 0xC001E000
#define GPIOEOUTENB 0xC001E004
#define GPIOEALTFN0 0xC001E020
#define GPIOEALTFN1 0xC001E024
#define CONFIG_SP_BASE 0xFFFFE000 //栈深 最多不超过56kb
.text
.global _start
_start:
/* disable watchdog */
ldr r0, =0xC0019000
mov r1, #0
str r1, [r0] //加一个[]表示一个内存地址
/* 配置GPIOE13为gpio模式 此处也可使用c语言实现*/
mov r1, #3
mov r0, r1, lsl#26
mvn r1, r0
ldr r0, =GPIOEALTFN0 //把GPIOEALTFN0地址保存到r0
ldr r2, [r0] //读取GPIOEALTFN0地址中的值
and r2, r2, r1 //r2 = r2&r1
str r2, [r0]
/* 配置GPIOE13为输出模式 此处也可使用c语言实现*/
mov r1, #1
mov r0, r1, lsl#13
ldr r1, =GPIOEOUTENB
ldr r2, [r1]
orr r2, r0, r2
str r2, [r1]
/*设置栈*/
ldr sp, =CONFIG_SP_BASE
bl main //调用c函数
halt_loop:
b halt_loop
main.c:
/*main.c*/
//GPIOE13 ---- D7
#define GPIOEOUT (*(volatile unsigned int *)0xC001E000)
#define GPIOEOUTENB (*(volatile unsigned int *)0xC001E004)
#define GPIOEALTFN0 (*(volatile unsigned int *)0xC001E020)
#define GPIOEALTFN1 (*(volatile unsigned int *)0xC001E024)
#define DELAY_TIME 0X400000
void delay(int val)
{
volatile int i=val;
while(i--);
}
void main(void)
{
while(1)
{
GPIOEOUT &= ~(1<<13);//GPIOE13 output 0
delay(DELAY_TIME);
GPIOEOUT |= (1<<13);//GPIOE13 output 1
delay(DELAY_TIME);
}
}
Makefile:
leds.bin: main.o leds.o
arm-none-linux-gnueabi-ld -Tleds.lds -o leds.elf $^
arm-none-linux-gnueabi-objcopy -O binary leds.elf leds.bin
arm-none-linux-gnueabi-objdump -D leds.elf > leds.dis
%.o : %.S
arm-none-linux-gnueabi-gcc -o $@ $< -c -nostdlib
%.o : %.c
arm-none-linux-gnueabi-gcc -o $@ $< -c -nostdlib -O3
clean:
rm *.o *.elf *.bin *.dis -f
leds.lds:
SECTIONS {
. = 0x00;
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
2) 生成bin文件
make clean
make
3) 添加boot header
查看资料,使用原来的工具生成的 启动程序的结构为
图中的nsih.txt代表使用工具mk6818解析nsih.txt 文件得到的有效二进制数据; 2ndboot我查不到它的生成方式, 应该是官方资料直接提供的;而且nsih.txt和2ndboot这个文件已经对芯片做了一些初始化工作,比如:初始化芯片的内存控制器, 初始化外部SDRAM,初始化时钟等等, 以至于我在uboot中的自搬运代码之前看不到有对外部SDRAM的初始化操作。
mk6818的源码:
/*mk6818.c*/
#include
#include
#include
#include
#define BLKSIZE (512)
#define SECBOOT_NSIH_POSITION (1)
#define SECBOOT_POSITION (2)
#define BOOTLOADER_NSIH_POSITION (64)
#define BOOTLOADER_POSITION (65)
struct nand_bootinfo_t
{
uint8_t addrstep;
uint8_t tcos;
uint8_t tacc;
uint8_t toch;
uint32_t pagesize;
uint32_t crc32;
};
struct spi_bootinfo_t
{
uint8_t addrstep;
uint8_t reserved0[3];
uint32_t reserved1;
uint32_t crc32;
};
struct sdmmc_bootinfo_t
{
uint8_t portnumber;
uint8_t reserved0[3];
uint32_t reserved1;
uint32_t crc32;
};
struct sdfs_bootinfo_t
{
char bootfile[12];
};
union device_bootinfo_t
{
struct nand_bootinfo_t nandbi;
struct spi_bootinfo_t spibi;
struct sdmmc_bootinfo_t sdmmcbi;
struct sdfs_bootinfo_t sdfsbi;
};
struct ddr_initinfo_t
{
uint8_t chipnum;
uint8_t chiprow;
uint8_t buswidth;
uint8_t reserved0;
uint16_t chipmask;
uint16_t chipbase;
uint8_t cwl;
uint8_t wl;
uint8_t rl;
uint8_t ddrrl;
uint32_t phycon4;
uint32_t phycon6;
uint32_t timingaref;
uint32_t timingrow;
uint32_t timingdata;
uint32_t timingpower;
};
struct boot_info_t
{
uint32_t vector[8]; // 0x000 ~ 0x01C
uint32_t vector_rel[8]; // 0x020 ~ 0x03C
uint32_t deviceaddr; // 0x040
uint32_t loadsize; // 0x044
uint32_t loadaddr; // 0x048
uint32_t launchaddr; // 0x04C
union device_bootinfo_t dbi; // 0x050 ~ 0x058
uint32_t pll[4]; // 0x05C ~ 0x068
uint32_t pllspread[2]; // 0x06C ~ 0x070
uint32_t dvo[5]; // 0x074 ~ 0x084
struct ddr_initinfo_t dii; // 0x088 ~ 0x0A8
uint32_t axibottomslot[32]; // 0x0AC ~ 0x128
uint32_t axidisplayslot[32]; // 0x12C ~ 0x1A8
uint32_t stub[(0x1F8 - 0x1A8) / 4]; // 0x1AC ~ 0x1F8
uint32_t signature; // 0x1FC "NSIH"
};
static int process_nsih(const char * filename, unsigned char * outdata)
{
FILE * fp;
char ch;
int writesize, skipline, line, bytesize, i;
unsigned int writeval;
fp = fopen(filename, "r+b");
if(!fp)
{
printf("Failed to open %s file.\n", filename);
return 0;
}
bytesize = 0;
writeval = 0;
writesize = 0;
skipline = 0;
line = 0;
while(0 == feof(fp))
{
ch = fgetc (fp);
if (skipline == 0)
{
if (ch >= '0' && ch <= '9')
{
writeval = writeval * 16 + ch - '0';
writesize += 4;
}
else if (ch >= 'a' && ch <= 'f')
{
writeval = writeval * 16 + ch - 'a' + 10;
writesize += 4;
}
else if (ch >= 'A' && ch <= 'F')
{
writeval = writeval * 16 + ch - 'A' + 10;
writesize += 4;
}
else
{
if(writesize == 8 || writesize == 16 || writesize == 32)
{
for(i=0 ; i>= 8;
}
}
else
{
if (writesize != 0)
printf("Error at %d line.\n", line + 1);
}
writesize = 0;
skipline = 1;
}
}
if(ch == '\n')
{
line++;
skipline = 0;
writeval = 0;
}
}
printf("NSIH : %d line processed.\n", line + 1);
printf("NSIH : %d bytes generated.\n", bytesize);
fclose(fp);
return bytesize;
}
static char * to_readable_msg(char * buf, int len)
{
static char msg[4096];
int i, n;
for(i = 0; i < len; i++)
{
n = i % 5;
if(n == 0)
buf[i] ^= 0x24;
else if (n == 1)
buf[i] ^= 0x36;
else if (n == 2)
buf[i] ^= 0xAC;
else if (n == 3)
buf[i] ^= 0xB2;
else if (n == 4)
buf[i] ^= 0x58;
}
memset(msg, 0, sizeof(msg));
memcpy(msg, buf, len);
return msg;
}
/*
* "Copyright(c) 2011-2014 http://www.9tripod.com\n"
*/
char msg_copyright[] = { 0x67, 0x59, 0xdc, 0xcb, 0x2a, 0x4d, 0x51, 0xc4, 0xc6,
0x70, 0x47, 0x1f, 0x8c, 0x80, 0x68, 0x15, 0x07, 0x81, 0x80, 0x68, 0x15,
0x02, 0x8c, 0xda, 0x2c, 0x50, 0x46, 0x96, 0x9d, 0x77, 0x53, 0x41, 0xdb,
0x9c, 0x61, 0x50, 0x44, 0xc5, 0xc2, 0x37, 0x40, 0x18, 0xcf, 0xdd, 0x35,
0x2e, };
/*
* "Forum: http://xboot.org\n"
*/
char msg_forum[] = { 0x62, 0x59, 0xde, 0xc7, 0x35, 0x1e, 0x16, 0xc4, 0xc6, 0x2c,
0x54, 0x0c, 0x83, 0x9d, 0x20, 0x46, 0x59, 0xc3, 0xc6, 0x76, 0x4b, 0x44,
0xcb, 0xb8, };
/*
* "Tel: 0755-33133436\n"
*/
char msg_tel[] = { 0x70, 0x53, 0xc0, 0x88, 0x78, 0x14, 0x01, 0x99, 0x87, 0x75,
0x17, 0x05, 0x9d, 0x81, 0x6b, 0x10, 0x05, 0x9a, 0xb8, };
int main(int argc, char *argv[])
{
FILE * fp;
struct boot_info_t * bi;
unsigned char nsih[512];
char * buffer;
int length, reallen;
int nbytes, filelen;
//printf("%s", to_readable_msg(msg_copyright, sizeof(msg_copyright)));
//printf("%s", to_readable_msg(msg_forum, sizeof(msg_forum)));
//printf("%s", to_readable_msg(msg_tel, sizeof(msg_tel)));
if(argc != 5)
{
printf("Usage: mk6818 <2ndboot> \n");
return -1;
}
if(process_nsih(argv[2], &nsih[0]) != 512)
return -1;
length = 32 * 1024 * 1024;
buffer = malloc(length);
memset(buffer, 0, length);
/* 2ndboot nsih */
memcpy(&buffer[(SECBOOT_NSIH_POSITION - 1) * BLKSIZE], &nsih[0], 512);
/* 2ndboot */
fp = fopen(argv[3], "r+b");
if(fp == NULL)
{
printf("Open file 2ndboot error\n");
free(buffer);
return -1;
}
fseek(fp, 0L, SEEK_END);
filelen = ftell(fp);
fseek(fp, 0L, SEEK_SET);
nbytes = fread(&buffer[(SECBOOT_POSITION - 1) * BLKSIZE], 1, filelen, fp);
if(nbytes != filelen)
{
printf("Read file 2ndboot error\n");
free(buffer);
fclose(fp);
return -1;
}
fclose(fp);
/* fix 2ndboot nsih */
bi = (struct boot_info_t *)(&buffer[(SECBOOT_NSIH_POSITION - 1) * BLKSIZE]);
/* ... */
/* bootloader nsih */
memcpy(&buffer[(BOOTLOADER_NSIH_POSITION - 1) * BLKSIZE], &nsih[0], 512);
/* bootloader */
fp = fopen(argv[4], "r+b");
if(fp == NULL)
{
printf("Open file bootloader error\n");
free(buffer);
return -1;
}
fseek(fp, 0L, SEEK_END);
filelen = ftell(fp);
reallen = (BOOTLOADER_POSITION - 1) * BLKSIZE + filelen;
fseek(fp, 0L, SEEK_SET);
nbytes = fread(&buffer[(BOOTLOADER_POSITION - 1) * BLKSIZE], 1, filelen, fp);
if(nbytes != filelen)
{
printf("Read file bootloader error\n");
free(buffer);
fclose(fp);
return -1;
}
fclose(fp);
/* fix bootloader nsih */
bi = (struct boot_info_t *)(&buffer[(BOOTLOADER_NSIH_POSITION - 1) * BLKSIZE]);
bi->deviceaddr = 0x00008000;
bi->loadsize = ((filelen + 512 + 512) >> 9) << 9;
bi->loadaddr = 0x43C00000;
bi->launchaddr = 0x43C00000;
/* destination */
fp = fopen(argv[1], "w+b");
if(fp == NULL)
{
printf("Destination file open error\n");
free(buffer);
return -1;
}
nbytes = fwrite(buffer, 1, reallen, fp);
if(nbytes != reallen)
{
printf("Destination file write error\n");
free(buffer);
fclose(fp);
return -1;
}
free(buffer);
fclose(fp);
printf("Generate destination file: %s\n", argv[1]);
return 0;
}
执行./mk6818 bootleds.bin nsih.txt 2ndboot leds.bin, 就可以生成包含boot header的bin文件bootleds.bin
4) 制作SD启动卡
在ubuntu下制作
(1)cat /proc/partitions 查看系统原有的设备节点
major minor #blocks name
8 0 52428800 sda
8 1 51427328 sda1
8 2 1 sda2
8 5 998400 sda5
11 0 1048575 sr0
(2)插入SD卡到Ubuntu系统,再次执行 cat /proc/partitions
major minor #blocks name
8 0 52428800 sda
8 1 51427328 sda1
8 2 1 sda2
8 5 998400 sda5
11 0 1048575 sr0
8 16 30289920 sdb
8 17 38758400 sdb1
经过对比可见,SD卡的设备名称为sdb
(3)修改一下SD卡的分区
sudo fdisk /dev/sdb
返回:
Command (m for help):
输入 d 并回车, 删除所有分区。 返回:
Selected partition 1
Command (m for help):
输入 w 并回车, 保存所有已经修改的分区信息。 返回:
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks
拨掉 SD 卡, 再插入 PC 机上, 输入如下命令查看现有的设备节点:
cat /proc/partitions
返回:
major minor #blocks name
8 0 52428800 sda
8 1 51427328 sda1
8 2 1 sda2
8 5 998400 sda5
11 0 1048575 sr0
8 16 30289920 sdb
至此, SD 卡原分区/dev/sdb1 被删除。
插上SD卡, 并执行:sudo ./GEC6818-sdmmc.sh /dev/sdb bootleds.bin
这样SD卡启动卡已经制作完成了。直接插到开发板的SD卡槽,上电执行,就会看到D7在闪烁了。
全部相关文件链接:链接 https://pan.baidu.com/s/15NNpWuebL-4TNe_0mSIR0Q 提取码:dqq2