这节的移植应该是最简单的,修改代码量最少。但是我们还是要对源码进行一个简单的分析。
首先紧接上一节最后的那张图,我们已经让U-boot可以从Nand Flash启动,然后打印出来的调试信息中Flash: 对应的就是Nor Flash的大小,NAND: 就是NAND FALSH的大小。这里发现NAND居然能识别出大小,但是如果我换为Nor启动以后,NAND就又识别不出来了,这边我们先不管他,这节主要还是针对Nor Flash。
首先查一下“Flash:”是在哪里打印的,然后经过分析我们可以得到一下函数调用关系:
注意:以下函数内都经过删减方便分析,只是分析,不需要修改代码
\common\board_r.c,第370行
static int initr_flash(void)
{
ulong flash_size = 0;
bd_t *bd = gd->bd;
puts("Flash: "); //在这里打印了"Flash: "字符串
if (board_flash_wp_on())
printf("Uninitialized - Write Protect On\n");
else
flash_size = flash_init(); //下一步函数调用
print_size(flash_size, "");
putc('\n');
/* update start of FLASH memory */
#ifdef CONFIG_SYS_FLASH_BASE
bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; //设置Nor Flash基地址也就是0x00000000
#endif
/* size of FLASH memory (final value) */
bd->bi_flashsize = flash_size;
#if defined(CONFIG_OXC) || defined(CONFIG_RMU)
/* flash mapped at end of memory map */
bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size;
#elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE
bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */
#endif
return 0;
}
我们查找“Flash:”字符串,然后定位到这个函数,接着这个函数调用flash_init();对Flash初始化和识别
\drivers\mtd\cfi_flash.c,第2348行
unsigned long flash_init (void)
{
unsigned long size = 0;
int i;
/* Init: no FLASHes known */
for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
flash_info[i].flash_id = FLASH_UNKNOWN;
/* Optionally write flash configuration register */
cfi_flash_set_config_reg(cfi_flash_bank_addr(i),
cfi_flash_config_reg(i)); //进去以后宏没有定义不用管这句话
if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) //如果这种方法检测不出Nor Flash,再使用下一种flash_get_size检测
flash_get_size(cfi_flash_bank_addr(i), i);
size += flash_info[i].size;
if (flash_info[i].flash_id == FLASH_UNKNOWN) {
#ifndef CONFIG_SYS_FLASH_QUIET_TEST
printf ("## Unknown flash on Bank %d "
"- Size = 0x%08lx = %ld MB\n",
i+1, flash_info[i].size,
flash_info[i].size >> 20);
#endif /* CONFIG_SYS_FLASH_QUIET_TEST */
}
}
flash_protect_default(); //恢复Nor Flash保护状态
return (size);
}
我们先进第一种检测方法的函数看一下,是否能识别我们的Nor Flash。
\drivers\mtd\cfi_flash.c,第1783行
static int flash_detect_legacy(phys_addr_t base, int banknum)
{
flash_info_t *info = &flash_info[banknum];
if (board_flash_get_legacy(base, banknum, info)) {
/* board code may have filled info completely. If not, we
use JEDEC ID probing. */
if (!info->vendor) {
int modes[] = {
CFI_CMDSET_AMD_STANDARD, //AMD公司标准识别模式
CFI_CMDSET_INTEL_STANDARD //Inter公司标准识别模式
};
int i;
for (i = 0; i < ARRAY_SIZE(modes); i++) {
info->vendor = modes[i];
info->start[0] =
(ulong)map_physmem(base,
info->portwidth,
MAP_NOCACHE);
if (info->portwidth == FLASH_CFI_8BIT
&& info->interface == FLASH_CFI_X8X16) { //根据线宽设置解锁地址,我们的线宽是16位,所以解锁地址按照else里面的设置
info->addr_unlock1 = 0x2AAA;
info->addr_unlock2 = 0x5555;
} else {
info->addr_unlock1 = 0x5555;
info->addr_unlock2 = 0x2AAA;
}
flash_read_jedec_ids(info); //读厂家ID,设备ID
debug("JEDEC PROBE: ID %x %x %x\n",
info->manufacturer_id,
info->device_id,
info->device_id2);
if (jedec_flash_match(info, info->start[0])) //如果匹配则退出循环,不匹配就按照上面modes[]里面的模式分别读取ID
break;
else
unmap_physmem((void *)info->start[0],
info->portwidth);
}
}
switch(info->vendor) { //根据芯片设置reset指令集
case CFI_CMDSET_INTEL_PROG_REGIONS:
case CFI_CMDSET_INTEL_STANDARD:
case CFI_CMDSET_INTEL_EXTENDED:
info->cmd_reset = FLASH_CMD_RESET;
break;
case CFI_CMDSET_AMD_STANDARD:
case CFI_CMDSET_AMD_EXTENDED:
case CFI_CMDSET_AMD_LEGACY:
info->cmd_reset = AMD_CMD_RESET;
break;
}
info->flash_id = FLASH_MAN_CFI;
return 1;
}
return 0; /* use CFI */
}
这边有一个debug打印调试信息的语句,我们可以先把调试信息打开看一下。要想看打印信息,我们只需要在include\common.h 文件最上方定义一个宏#define DEBUG
就行了。
修改完文件,我们重新编译并下载到开发板后可以在串口看到这个信息:
查一下我们的芯片手册,发现我们使用的芯片MX29LV160DB他的ID就是2249,跟打印出来信息一致。
那么下面另外一个ID be ea00 0是什么意思。记不记得我们上面设置了两种读取方式,一种是ADM标准一种是Inter标准。虽然我们正确读取出了ID但是没有匹配上,for循环就按照Inter的标准接着执行了一次读取ID命令,读出了乱码。
下一步我们就要进入jedec_flash_match函数看一下,为什么匹配不成功。
\drivers\mtd\jedec_flash.c,第493行
int jedec_flash_match(flash_info_t *info, ulong base)
{
int ret = 0;
int i;
ulong mask = 0xFFFF;
if (info->chipwidth == 1)
mask = 0xFF;
for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) &&
(jedec_table[i].dev_id & mask) == (info->device_id & mask)) {
fill_info(info, &jedec_table[i], base);
ret = 1;
break;
}
}
return ret;
}
这个函数的主要功能就是把读取上来得厂家ID和设备ID跟jedec_table数组里的结构体比较,如果全部匹配,那么就把信息填进去,返回成功。
所以我们现在的工作就是在jedec_table数组里面添加一个我们芯片的结构体。
},
#endif
{
.mfr_id = (u16)MX_MANUFACT, //厂家ID
.dev_id = AM29LV160DB, //设备ID
.name = "MXIC MX29LV160DB", //名称(自定)
.uaddr = {
[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
},
.DevSize = SIZE_2MiB, //总大小
.CmdSet = P_ID_AMD_STD, //或者 CFI_CMDSET_AMD_LEGACY 两个宏是一样的
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x04000, 1),
ERASEINFO(0x02000, 2),
ERASEINFO(0x08000, 1),
ERASEINFO(0x10000, 31),
}
},
};
这个版本跟韦东山老师移植的版本不同的就是把芯片MX29LV160DB的厂家ID和设备ID都定义了出来,我们直接调用就好了。
然后是NumEraseRegions 和regions两项分别代表的意思是扇区的种类,以及扇区的大小及个数。这两个项我们就要查一下芯片手册上面的数据。
由芯片手册我们可以看到16K的扇区有1块,那么就是ERASEINFO(0x04000, 1),,8K的扇区有2块,那么就是ERASEINFO(0x02000, 2), 后面同理。
添加完我们自己芯片的结构体以后,再修改一下.h文件中最大扇区数,不然串口到时候会打印警告。
\include\configs\smdk2440.h,第136行
#define CONFIG_SYS_MAX_FLASH_SECT (128)
这样我们就都修改好了,然后把DEBUG关了,重新编译烧到开发板上验证一下。
现在U-boot已经识别出Nor Flash的大小了,再试一下是不是能用,使用flinfo
,打印一下Nor Flash信息。
取消写保护protect off all
。
擦除80000~90000之间的数据erase 80000 8ffff
复制SDRAM 0x30000000地址起10000个字节到Nor Flash地址0x80000cp.b 30000000 80000 10000
比较两者之间的数据cmp.b 30000000 80000 10000
两者数据完全一样,到这里我们的移植的完成了,而且这版的修复了2012版中的SP栈BUG,这个BUG曾经导致0x300000000~0x30001000之间的地址只能读不能写,不然程序会蹦,这是因为那一版的程序,在进入第二阶段之前把gd等数据搬到新地址,却没有重新设置SP,导致SP还是指向0x30001000。
这版是怎么修复这个BUG的可以看一下arch\arm\lib\crt0.S第118行。
当U-boot支持Nor Flash以后我们就可以通过ymodem协议烧写U-boot。一般现在的工具都支持Ymodem传输,只要稍微找一下都可以看到,这边推荐用工具Xshell 5,免费的而且有中文版,安装包我会放在文末的链接,ymodem发送功能在这个地方:
把文件下载到0x30000000开始的地址
loady 30000000
然后马上用ymodem发送文件,这个过程一定要快,不然容易失败。
后面就跟之前的USB烧录一样,解除保护,擦除,拷贝:
protect off all
erase 0 7ffff
cp.b 30000000 0 80000
推荐一个下载芯片手册的网址:链接: [http://datasheet.eeworld.com.cn/]。
补丁yizhi_4和Xshell 5安装包
链接:https://pan.baidu.com/s/1v85_5VApVgmMG9IXrwgCiQ
提取码:bb6y
注意:这边的补丁文件只是相对于上一节的u-boot,直接下载下来的u-boot打这个补丁是没有用的。