一.丢失引导:
华恒论坛在新版本的ppcboot中的说明如下:
近来不断有客户反映,s3c2410的一些开发板有时会莫名的丢失引导。一开始我们以为是客户不熟悉操作,不小心误操作的结果,但经过一段时间的多方检测,发现这里不仅仅是误操作这么简单;我们通过对这方面的问题收集,并结合客户反馈的若干想法和建议,我们总结后发现这不单单是s3c2410相关开发板的问题,还包括ppc8250、xscale425、s3c2440、mxl9328、arm9200等,都有类似问题出现过。通过对这类板卡的分析发现这类板卡都有一个共性,就是他们的rom存储都是采用的intel e28f128 这款flash;再者了解到e28f128这款flash的控制时序相对比较简单很可能也是导致这个问题发生重要原因。
更改后的ppcboot.bin在当前的使用上做如下调整
原先的引导、内核、文件系统的起始地址分别为1000000、1040000、1140000,现在分为0、40000、140000
……
以一下所提到的所有问题已经得到了彻底的解决,尤其让我们高兴的是早期对intel的这款e28f128 flash 始终支持的不是很好(这块flash往往擦写几十次可能就坏了,这实际上是这块flash的操作时序太简单所致,这也是采用这款flash的所有商家的共同问题。),我们通过多方努力,今天终于做到了性能上的突破。
现有的HHARM2410使用的是3 Volt Intel StrataFlash Memory device, 该Flash Memory提供安全模式(Security Mode),可以保护Flash中的数据防止被意外删除或重写。但是现在的 ppcboot for HHARM2410 版本并没有实现此保护功能,造成的后果是某些重要代码烧写到Flash中之后,如果后续程序跑飞,很可能会冲掉之前烧入的重要代码。这类错误很难发现而且后果通常比较严重。
针对以上问题,对现有的ppcboot for HHARM2410代码进行改进,使之可以实现保护Flash中的数据防止被意外删除或重写的功能就显得十分必要。
……
那块板的ppcboot烧写基地址是0x1000000,那么就是没有写保护的旧版了……
但不太清楚到底什么情况下可以改写flash的东西(NOR flash是可以随机读写的,但擦除只能以block为单位进行——这是否意味着,以正常方式对某个地址进行写操作,并不存在判断该地址数据是否为0xff的过程,只要有为"1"的bit就可能可以改变对应字节的内容呢?)
在丢失引导之前曾经执行过loadb 8000(之前设置了base 30000000,然而这个设置居然是对loadb无效的,而loadb命令在下载前也不会提示,所以下载完看到统计信息才知道地址错了),不知道是不是就是这个指令冲掉了引导……
而更神奇的是,后来我把ppcboot烧回去了,发现不但ppcboot没有了,kernel、initrd、jffs2等所有分区都被破坏了(某些字节被改写),难道是丢失引导后ppcboot跑飞,到处乱写了东西?
二.JTAG:核心板的JTAG connector是10pin的那种,而且size特别小,由于找不到这种插头,最后同事硬是用两个差不多孔距的插头插了上去,把那几根线引到了另一个大的20pin connector上。DB25-JTAG用的是同时有Wiggler、STD、S3C2410三种接口的那个转换板。一开始接的是Wiggler,调试代理能认到CPU,H-JTAG也能,但就是sjf2410死活认不出来。开始怀疑是延串口长线、giveio甚至是系统的问题(调试用机,啥问题都有可能出现),但用一个测试程序去测,giveio是正常的,而且换到linux下甚至到另外一台win2k下,sjf2410都依然固执地不认cpu。后来再认真看看sjf2410的文档才醒悟过来可能是db25那边的Pin Assignment的问题。另外,后来才看到那个转接板配套的ARM9开发板的文档里其实已经指出,使用sjf2410时要把20针排线接到S3C2410的接口上……其实还是自己以前对JTAG的认识不够全面造成的问题,还好最终能够及时发现。
三.SJF2410 & 28F128J3A:解决JTAG的问题后,SJF2410在dos下总算能认cpu了(可是Linux下面依然认不出来。。。难道华恒的程序改了Pin Assignment?或者系统本身的问题?),然而在检测Flash型号的时候,ID认出来0x89,但Device类型却认不出来。对照strata32.c和28F128J3A的datasheet,读取Identifier Codes的方法应该是先向任意地址写0x90(见Datasheet的Table 4. Command set definitions),然后读地址00000,可得到ID号0x89,而地址00001则读到Device Code,128Mbit对应的是0x18(见Table 5. Identifier Codes)。Table 5中还提到,那两个地址的最低位是A1,A0在x16模式(即16位模式)下被忽略了。Strata的代码原来是这么写的:
int Strata_CheckDevice(int targetAddr)
{
//_RESET();
_WR(targetAddr, 0x00900090);
return _RD(targetAddr+0x4);
}
试着把0x4改成0x2,果然可以读出来了,接着就自动开始了烧写。然而烧完后用内存读写功能查看,发现烧进去的东西每隔两个字节就是两个0xFF,即11 22 FF FF 55 66 FF FF 99 AA这样。再仔细阅读一下Strata.c,才发现原来这个程序对应的硬件配置是16x2,即用2片flash接成32bit的形式,一片是低16bit一片是高16bit,sjf2410也是以32bit的宽度进行读写。例如上面的_WR(targetAddr, 0x00900090);实际上就是向targetAddr开始的4个字节写入数据,也就是分别向两片flash写入16bit数0x0090(其实这从代码的注释中也可以看出来)。targetAddr仍然是按字节编码的,对于16x2的配置,每次写入32bit,地址就要+4,因此用这样的代码对16x1的配置进行烧写,就会每次只烧入了低16位,高16位没烧,而地址的增量仍然是4,于是就会出现每2字节间隔2个ff的现象。
也就是说,16x2的地址分布是这样的(假设基地址是00000000):
---mem-------flash--
0000 0000 - 0:000000
0000 0001 - 0:000001
0000 0002 - 1:000000
0000 0003 - 1:000001
0000 0004 - 0:000002
0000 0005 - 0:000003
0000 0006 - 1:000002
0000 0007 - 1:000003
....
由于flash是16位,所以其地址线的A0被忽略,也就是说000000和000001实际上都是同一个地址,左移一位后都是00000。
而16x1的地址分布是:
---mem-------flash--
0000 0000 - 0:000000
0000 0001 - 0:000001
0000 0002 - 0:000002
0000 0003 - 0:000003
0000 0004 - 0:000004
0000 0005 - 0:000005
0000 0006 - 0:000006
0000 0007 - 0:000007
....
所以,把烧写部分的代码改写成这样就行了:
for (i=0; i<targetSize; i+=2)
{
Strata_ProgFlash(i+targetAddress+targetOffset, (*((U16 *)(srcAddr+i)))&0xffff);
//if((i%0x100)==0xfc)
if((i&0xff)==0xfe)
printf("[%x]",i+2);
}
读数也一样,根据datasheet,读Device Code的地址是0x00001,加上忽略掉的A0,就是0x00001 << 1。而在16x2的配置下,根据上面的地址应设表,0x0000 0002和0x0000 0003实际上对应高16bit的0x00000地址(忽略A0),0x0000 0004和0x0000 0005对应的才是低16bit flash的0x00001地址(忽略A0)。因此16x2读Device Code是_RD(targetAddr+0x4),16x1应该改成_RD(targetAddr+0x2)。
至于擦除,由于是按block进行的,根据28F128J3A的datasheet,它分为128个block,每个block为1Mbit,即128kb(0x20000字节),所以擦除部分的代码应该为:
for(i=0;i<targetSize;i+=0x20000)
{
//Strata_ClearBlockLock(targetAddress+targetOffset+i);
Strata_EraseSector(targetAddress+targetOffset+i);
}
当然,Strata_EraseSector也要进行修改,不然擦除的时候就可能跳不出里面那个while循环:
void Strata_EraseSector(int targetAddress)
{
unsigned long ReadStatus;
unsigned long bSR5; // Erase and Clear Lock-bits Status, lower 16bit, 8MB Intel Strate Flash ROM
unsigned long bSR5_2; // Erase and Clear Lock-bits Status, higher 16bit, 8MB Intel Strate Flash ROM
unsigned long bSR7; // Write State Machine Status, lower 16bit, 8MB Intel Strate Flash ROM
unsigned long bSR7_2; // Write State Machine Status, higher 16bit, 8MB Intel Strate Flash ROM
//_RESET();
// _WR(targetAddress, 0x00200020);
// _WR(targetAddress, 0x00d000d0);
_WR(targetAddress, 0x0020); // Block Erase, First Bus Cycle, targetAddress is the address withint the block
_WR(targetAddress, 0x00d0); // Block Erase, Second Bus Cycle, targetAddress is the address withint the block
//_RESET();
_WR(targetAddress, 0x0070); // Read Status Register, First Bus Cycle, targetAddress is any valid address within the device
ReadStatus=_RD(targetAddress); // Read Status Register, Second Bus Cycle, targetAddress is any valid address within the device
bSR7=ReadStatus & (1<<7); // lower 16-bit 8MB Strata
//bSR7_2=ReadStatus & (1<<(7+16));// higher 16-bit 8MB Strata
//while(!bSR7 || !bSR7_2)
while(!bSR7)
{
_WR(targetAddress, 0x0070);
ReadStatus=_RD(targetAddress);
bSR7=ReadStatus & (1<<7);
//bSR7_2=ReadStatus & (1<<(7+16));
//printf("wait\n");
}
_WR(targetAddress, 0x0070); // When the block erase is complete, status register bit SR.5 should be checked.
// If a block erase error is detected, the status register should be cleared before
// system software attempts correct actions.
ReadStatus=_RD(targetAddress);
bSR5=ReadStatus & (1<<5); // lower 16-bit 8MB Strata
//bSR5_2=ReadStatus & (1<<(5+16)); // higher 16-bit 8MB Strata
//if (bSR5==0 && bSR5_2==0)
if (bSR5==0)
{
printf("Block @%xh Erase O.K. \n",targetAddress);
}
else
{
//printf("Error in Block Erasure!!\n");
_WR(targetAddress, 0x0050); // Clear Status Register
error_erase=1; // But not major, is it casual ?
}
_RESET(); // write 0xffh(_RESET()) after the last opoeration to reset the device to read array mode.
}
另外,烧写flash可能初始化时需要对cpu的某些寄存器进行设置。在FlashPGM里,虽然可以识别cpu,但烧写总是不顺利,应该是cpu寄存器没有设置好的原因。由于时间关系,没有对这个问题进行深入研究……
由于sjf2410开放源代码,因此很容易就可以将其修改得适应自己的硬件设置,也可以很容易地增加功能。例如我在内存读写那里加入了一个df指令,用来把flash的内容dump出来(当然,如果能进linux,直接把mtd设备内容dump出来保存到u盘上是最快的做法)。
--- sjf2410_ori/mem_rdwr.c 2002-11-15 08:29:02.000000000 +0800
+++ sjf2410/mem_rdwr.c 2006-09-18 19:34:04.000000000 +0800
@@ -19,7 +19,7 @@
{
int i,j;
char command[128];
- U32 addr;
+ U32 addr, len;
U8 dataByte;
U16 dataHW;
U32 dataWord;
@@ -49,6 +49,7 @@
printf("| wb <hex_addr> <hex_data>: write, byte data |\n");
printf("| wh <hex_addr> <hex_data>: write, half-word data |\n");
printf("| ww <hex_addr> <hex_data>: write, word data |\n");
+ printf("| df <hex_addr> <len> : dump to file |\n");
printf("+-------------------------- NOTE ----------------------------+\n");
printf("| 1. nGCS6,7 SDRAM read/write isn't supported now. |\n");
printf("| 2. example: >bs 2 16 1 |\n");
@@ -76,10 +77,47 @@
break;
case 'd':
+ if(command[1] == 'f'){
+ FILE *fp;
+ char filename[FILENAME_MAX];
+ scanf("%x %x",&addr, &len);
+ addr=addr&0xfffffffe; //ignore A[0]
+ bank=S2410_Addr2Bank(addr);
+ i = addr + len;
+ j = 0;
+ printf("Please wait...\n");
+ sprintf(filename,"dump_%X_%X",addr,len);
+ fp = fopen(filename,"wb");
+ if(fp==NULL){
+ printf("Failed to open %s for writing!\n",filename);
+ break;
+ }
+ while(addr < i){
+ if (j != ((addr>>8)&1)){
+ j = ((addr>>8)&1);
+ printf("[%8x]",addr);
+ fflush(fp);
+ }
+ //error=MRW_RdByte(addr++,&dataByte);
+ error=MRW_RdHW(addr,&dataHW);
+ addr+=2;
+ if(error!=0)
+ printf("?? ");
+ else{
+ //printf("%04x ",dataHW);
+ fputc(dataHW&0xff,fp);
+ fputc((dataHW>>8)&0xff,fp);
+ }
+ //printf(".");
+ }
+ printf("\nDumped to %s\n",filename);
+ fclose(fp);
+ break;
+ }
scanf("%x",&addr);
addr=addr&0xfffffff0;
bank=S2410_Addr2Bank(addr);
- for(i=0;i<4;i++)
+ for(i=0;i<8;i++)
{
printf("%8x:",addr);
for(j=0;j<16;j++)
其实我还打算加入烧写后自动为block加上写保护的功能的,不过由于现在不能碰那块板,于是只好放弃……反正新版本的ppcboot也可以方便地进行写保护嘛……
四.PPCBoot & Terminal software:前面说了,ppcboot可以用loadb命令,通过串口以kermit协议下载.bin文件(但要注意那该死的base设置无效的bug)。由于windows自带的超级终端显示缓存的内容经常会乱掉,所以一直用的是Tera Term。但通过Tera Term把kernel下载到sdram中运行却怎么也跑不起来。经过观察,发现Tera term通过kermit传文件居然会丢包,于是后来只好改回超级终端,虽然传输速率比Tera Term低,但是却不会发生丢包的现象。
现在,至少有3种烧写flash的方法了:jtag、ppcboot、进入linux后写mtd设备
* jtag仅在丢失引导后重新烧写ppcboot使用,因为比较慢。
* 能够进入ppcboot后,就可以通过loadb从串口接收kernel和initrd(因为底板没有网口,不然就用tftp了),分别把它们放到30008 000和3080 0000的sdram空间(sdram接的片选是nGSC6,因此地址空间为0x3000 0000~0x37FF FFFF,具
体说明可以在华恒的手册上查到),然后bootm或者go 30008000就可以启动kernel进入系统。
* 进入系统后,如果kernel正常,可以通过u盘把jffs2和cramfs两个分区的img文件字节烧入相应flash分区。过程很简单:先umount掉/dev/mtdblock/4和/dev/mtdblock/5的挂载,然后把img文件复制到tmpfs中,再dd if=xxx.img of=/dev/mtd/4这样的命令烧写,烧写完后就可以重新挂载了。不过这里有个小问题,那块板最后两个分区默认下分别是jffs2和cramfs,如果用其它分区格式烧写,再挂载的时候,系统仍然会把它们识别成jffs2和cramfs(跟fstab无关的),必须用-t参数手动指定分区格式才能正常挂载。还有,mtdblock/4之前的分区都是不可写的,这意味着不能在linux下面通过写这几个分区来烧写ppcboot、kernel和ramdisk,应该能通过修改kernel去除这一限制(flash分区表是写在kernel里的,那么应该也可以通过kernel来让mtd分区只读)。
现在不知道是ppcboot还是kernel的问题,在默认情况下插入u盘会提示"usb.c: USB device not accepting new address",而启动后马上进入ppcboot,然后cp 1040000 30008000 e0000,cp 1140000 30800000 210000把kernel和ramdisk装入内存(ramdisk一定也要在内存中。另外,好玩的是,reset键复位并不会清除sdram的内容,所以cp之前可以先md一下看看内存中之前复制过去的ramdisk是否还健在——kernel基本上是不在的了,进入系统后30008000的内容就变了),接着go 30008000或者bootm进入系统才可以正常使用u盘。
另外,ppcboot在fc4下编译会出错,在华恒的论坛上,有人
指出在ppcboot-2.0.0/tools/gdb/astest.c
找到下面代码
printf("\tcooked_size=%ld, raw_size=%ld, output_offset=%ld\n",
(long)sect->_cooked_size , (long)sect->_raw_size,
(long)sect->output_offset);
改成
printf("\tcooked_size=%ld, raw_size=%ld, output_offset=%ld\n",
(long)sect->size, (long)sect->rawsize,
(long)sect->output_offset);
看了一下/usr/includ/bfd.h,应该就是2.6的typedef struct bfd_section定义改变了的原因。
五.Kernel:kernel方面没进行太多的研究,所以笔记不多:
* USB Support那里的Maximum port(s) of RootHub要改成1,不然kernel会像疯子一样不停地抱怨连不上其它的port。
* General Setup的 Compressed boot loader in ROM/flash,选上之后似乎ppcboot就boot不了编译出来的kernel了(未确认,似乎而已,很可能那次boot不了是因为其它原因,只有等以后有机会并且可以用tftp传kernel的时候再进一步确认了。)
* 音频方面,Kernel那里只选上了OSS sound modules,声音驱动实际上是进入系统后用insmod /usr/2410audio.o另外加载的。其实很多设备的驱动都是进入系统后才挂的,可以参考华恒的手册。
* flash分区表在/drivers/mtd/maps/s3c2410_llg.c文件,可能可以通过修改这个文件来设置分区是否只读。