歡迎您光臨本站 登入註冊首頁

S3C44B0 調試筆記-BIOS

admin @ 2014-03-25 , reply:0

概述

      由於調試耗費了我太多時間,所以記錄下來,方便以後複習,同時也希望能給初學者一點兒啟示,大家少走彎路。  &n……

       由於調試耗費了我太多時間,所以記錄下來,方便以後複習,同時也希望能給初學者一點兒啟示,大家少走彎路。

        我是去年開始聽說ARM的,可能是本人太閉塞了吧。看到后就有一種想玩的衝動,想從51升級ARM。網上都說44B0比較適合初學者。機緣巧合,21ic上看到有人叫賣44B0空板,很便宜100RMB,主板加簡易JTAG小板。做研發的都窮啊。還不錯,錢匯過去,板子第3天就回來了,就是網上流行的那個44B0 PCB。貼個圖吧,隨便找了塊和我那塊板子一模一樣的:

 

        拿到板子挺高興的,檢查了一下線路沒問題。就按對方提供的BOM單採購元器件。先把電源部分焊上,測量沒問題,再把必要的元器件焊上(CPU、SDRAM、FLASH、UART),同時還有JTAG小板。量電源和地沒有短路,上電!電源燈亮了,沒冒煙。

       下面一步就是把BIOS程序燒寫到FLASH上了,對方提供的是FLUTED,按照說明操作,燒錄失敗!這下傻了,最怕的就是這個,對於一個初學者來說,簡直是滅頂之災啊。首先懷疑CPU或FLSH是否虛焊,我的焊接水平一般,所以很值得懷疑,只好又搪了一遍。但問題依舊。又懷疑JTAG小板,仔細檢查了一下沒問題啊,跳線插插拔拔的也沒無濟於事。懷疑並口,重啟進BIOS,並口設置也沒問題。當天是沒辦法了,睡覺吧。

        第二天就開始聯繫供應商,尋求幫助,但沒什麼結果。也是,就100塊錢,還要什麼服務啊。只好自己找問題,看到原理圖上FLASH用的是SST 39VF160 ,我記得我的不一樣啊?我的BOM上寫的是AM29LV160,我又詢問了供應商,他確認了一下說他的BOM寫錯了。狂暈啊。不過還好,終於找到問題了,但換FLASH還得周末去買啊,等不及了,看看這個能不能湊合用吧,改FLUTED的FCD文件,就是根目錄那個DEFAULT.FCD。找到2塊FLASH的Datasheet對比著改,連改帶調,2個晚上,終於燒錄成功了。拔下JTAG,接上串口,打開超級終端。複位,一堆亂碼,我想應該差不多了,試著更改超級終端的設置。終於成功了,我看到BIOS的提示信息了,敲個help進去,出來一堆,當時感覺好爽,這個程序做的不錯,和DOS一個感覺了,哈哈。

        然後焊上網路部分,輸入ap,可以ping通,網路基本正常。隨後就是下載uClinux了,按照供應商的說明文檔,先把程序從0地址拷貝到0x1f0000,我運行copy命令,程序複位。重試,依舊。第一個想法就是,BIOS內部程序擦寫FLASH的函數和我的晶元不兼容,無奈,只好對比著兩塊FLASH的Datasheet修改BIOS程序。調試了一個多禮拜,依舊。這下徹底崩潰了,感覺程序應該沒有問題了。最終不得不放棄,只好換晶元,把AM29LV160換成了SST39VF160。 用最初的BIOS,但現象還是依舊,這可是怎麼回是啊?仔細想了一下,會不會因為程序代碼在FLASH里,而我又去擦除和寫FLASH導致的系統崩潰?擦寫FLASH的底層源碼都有End-Detection, 例如擦Sector的BIOS源碼,在發送完命令字后,有如下一段代碼進行判斷:

 while(1)
 {
  U16 i;

  i = *((volatile U16 *)sector)&0x40;
  if(i!=*((volatile U16 *)sector)&0x40) //D6 == D6
   continue;
  if(*((volatile U16 *)sector)&0x80) 
   break;        //D7 == 1
 }
      (CODE - 1)
        用了2種方法判斷擦除操作是否成功,看晶元資料,在這個時候,你去讀FLASH那個扇區地址的話,會得到擦除是否成功的信息,而不是得到那個扇區號所對應絕對地址的數據!也就是說在這個時候去讀特定地址sector會出錯,那麼是不是必須在擦寫操作完成後,CPU才能取指,運行程序代碼?對於我的這塊板子,我感覺是這樣的。因為它總是會DOWN在這裡。但以前碰到過其他CPU,可以在同一塊FLASH存儲程序,同時程序又可以擦寫這塊FLASH的情況。這種區別會不會是不同FLASH型號造成的?期待指點。結論:S3C44B0的FLASH(39VF160)不能在自身運行程序的同時,對自己進行擦寫操作。

       那BIOS的源碼有問題了,我該怎麼做?看來現在只能把BIOS從Flash里copy到SDRAM中才行。用move命令實現copy: move 0 0xc000000 10000。但當我run 0xc000000 時,程序重啟,為了確認程序是從哪裡執行的,我寫了一個讀pc命令rdpc(見CODE - 2)。讀出的pc值是0x41c4,顯然,程序是從0地址重啟了。這裡又讓人糊塗了,怎麼又重啟了呢?想不通了,這個疑問留在了這裡,當時沒有解決,後來拐了個彎才發現了問題。
int ReadPC(int argc, char *argv[])
{
 U32 i;
 U16 j,k;
 
 __asm
 {
  mov i,R15
 }
 j=i/0x10000;
 k=i%0x10000;
 printf("PC Value = 0x%x \n",i);
 printf("PC Value = %d - %d \n" ,j,k);
 __asm
 {
  MRS i,CPSR
 }
 j=i/0x10000;
 k=i%0x10000;
 printf("CPSR Value = 0x%x \n",i);
 printf("CPSR Value = %d - %d \n" ,j,k);
 return 0;
}
      (CODE - 2)

      我又仔細看看供應商的說明文檔,需要先把BIOS代碼copy到0x1f0000,然後把uClinx的ROM文件下載到0xc000000位置運行。怎樣才能把BIOS程序從0地址搬移到0x1f0000?決定還用FLUTED,先把0地址的數據擦除,然後把初始地址設成2031616(FLUTED用十進位),燒錄成功,但不能啟動。是不是因為程序從0地址進入,但不能執行到0x1f0000?可是我把前面的flash都擦了啊,都是0xff,程序應該可以跑到這裡啊?看情況肯定是程序指針沒有到0x1f0000了。搞不定了,這裡耽誤了一些時間,不過後來還是從BIOS的源碼找到了突破口,我注意到了Prog命令,這段代碼讓人很費解了:
#define __ROM_SIZE 0x200000
#define BIOS_BASE (__ROM_SIZE-0x10000)
#define BIOS_LOAD (__ROM_SIZE-4)
 
 if(argc>4)
  if(strncmp(argv[4], "-no0", 4)==0)
   overwrite0 = 0;  
 
 if((prog_begin==0)&&overwrite0)
 {
  unsigned int ins;
  
  ins = *(unsigned int *)data_begin;    
  if((ins>>24)==0xea) // instruction: b xxxx, now just support b instruction!!!    bios_load_addr = ((ins&0xffffff)<<2)+8;
  else
   bios_load_addr = 4;  // other instruction, jump to 4
             
  bios_load_addr = (bios_load_addr-BIOS_LOAD-8)/4;
  bios_load_addr = (bios_load_addr&0xffffff)|0xea000000;     
  
  *(unsigned int *)data_begin = 0xea000000+(BIOS_BASE-8)/4;    
  
  modify_a0 = 1;          
 }
        (CODE - 3)
       仔細分析了一下,程序的意思就是在你燒錄0地址的時候,如果不選-no0參數,那麼程序將會把0位置的數據通過計算轉換成跳轉指令的彙編碼(0xeaxxxxxx)。替換成pc跳轉到BIOS_BASE(0x200000-0x10000=0x1f0000)的指令,終於對上號了。 *(unsigned int *)data_begin = 0xea000000+(BIOS_BASE-8)/4; arm指令是32位的,所以除4,-8是因為3級流水線,如過這裡不清楚的話,建議直接從ADS里看b指令的彙編碼。

       這下搞懂了,我就在BIOS里調用了prog函數,隨便燒了幾個位元組到0地址。讀出來看0~4分別是0xfe 0xbf 0x07 0xea。彙編碼是:0xea07bffe。,0x7bffe是指跳轉的指令數。arm是3級流水線,即pc為取指指針,pc-1是解碼指針,pc-2是執行指針。程序複位,從地址0開始執行,跳轉0x7bffe條指令。我們算一下,(0x7bffe+2)×4 =0x1f0000,也就是說,程序跳轉到了0x1f0000。
 
 
順便提一下,我的BIOS里加的讀函數,讀指定地址的數據。
int ReadData(int argc, char *argv[])
{
 int addr, size,i;
 
 if(argc<3)
 {
  puts("Please enter move addr size\naddr = read addr, size = read size\n");
  return -1;  
 }
 
 addr = strtoul(argv[1]);
 size = strtoul(argv[2]);
 if(addr==-1||size==-1)
 {
  puts("Parameter error\n");
  return -1;
 }
 if(size>0x1000)
 {
  puts("Parameter error, size must less than 1000(in hex)\n");
  return -1;
 }
 
 printf("  Address        Data    \n");
 for(i=0;i<size/2;i++)
 {
  printf("  %x        %x    \n", addr+2*i,*((unsigned short *)addr+i));
 }
 return 0;
 
}
(CODE - 4)
板子複位,居然還是沒有反映。。。。。。。。。。。。鬱悶啊。

        又怎麼了呢?想吧。一時間真的搞不懂了。後來看了一些關於ads編譯的資料,發現原來RO、RW和ZI還有文章。主要是沒有技術支持,這些東西都要自己摸索,唉。。。。。。

   
   
 
 
        ARM編譯生成的代碼中會分成兩部分,RO和RW。同時編譯會產生以下幾個重要的地址空間分配變數:(如果想知道編譯結束后,這些變數到底被賦了什麼樣的值,可以按照FIGURE-5配置ARM Linker。編譯完成之後,你就可以看到很多編譯細節,包括函數定位之類的信息。)
|Image$$RO$$Base| :Read Only 代碼部分的起始地址。FIGURE – 3的RO BASE。
|Image$$RO$$Limit| :Read Only 代碼部分的結束地址。
|Image$$RW$$Base| : Read Write 代碼可讀寫部分的起始地址。FIGURE – 3的RWBASE。
|Image$$ZI$$Base|  :這裡是程序中用到的一些變數使用的空間。
|Image$$ZI$$Limit| :變數結束的地址。

再看一段程序,BIOS中的初始化RAM的一部分程序:
 IMPORT InitSystem
 bl InitSystem 
 
 adr r0, ResetEntry
 ldr r1, BaseOfROM
 cmp r0, r1
 ldreq r0, TopOfROM
 beq InitRamData
   
 ldr r2, =CopyProcBeg
 sub r1, r2, r1
 add r0, r0, r1 
 ldr r3, =CopyProcEnd 

 ldmia r0!, {r4-r7}
 stmia r2!, {r4-r7}
 cmp r2, r3
 bcc %B0 
 
 ldr r3, TopOfROM  
 ldr pc, =CopyProcBeg
 
;***********************************************
CopyProcBeg 

 ldmia r0!, {r4-r11}
 stmia r2!, {r4-r11}
 cmp r2, r3
 bcc %B0 
CopyProcEnd
 
 sub r1, r2, r3
 sub r0, r0, r1  
 
InitRamData 
 ldr r2, BaseOfBSS
 ldr r3, BaseOfZero 
0
 cmp r2, r3
 ldrcc r1, [r0], #4
 strcc r1, [r2], #4
 bcc %B0 

 mov r0, #0
 ldr r3, EndOfBSS

 cmp r2, r3
 strcc r0, [r2], #4
 bcc %B1   
        
 ldr pc, GotoMain 

GotoMain DCD $MainEntry

;***********************************************
 IMPORT |Image$$RO$$Base| ; ROM code start 
 IMPORT |Image$$RO$$Limit| ; RAM data starts after ROM program
 IMPORT |Image$$RW$$Base| ; Pre-initialised variables
 IMPORT |Image$$ZI$$Base| ; uninitialised variables
 IMPORT |Image$$ZI$$Limit| ; End of variable RAM space


BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|

  (CODE - 5)
      ResetEntry是FIGURE-4中的 Image Entry Point。即程序入口地址。可以看出,上面程序的主要功能是將RO中的數據搬移至RW,同時將ZI中的數據清零。

      這就對了,我最初的RO和ResetEntry都設至成了0,這樣的話,數據搬移的時候就把已經被我擦除的Flash空間中的數據(一連串的0xff )搬到了RW段,所以導致程序無法正常運行了。我把RO和ResetEntry都設至成了0x1f0000,RW設成0xc700000。編譯。然後再燒錄到FLASH的0x1f0000。重啟,OK啦!板子跑起來了,用rdpc命令,讀出pc值是0x1f41c4(FIGURE - 6)。完全正確。不過還是BIOS程序,只不過換了個地址跑而已。不過這下明白了,前面提到的直接copy到0xc000000不能運行的問題也解開了。

 
 

       重新設置了R0和ResetEntry都設至成了0xc000000,RW不變。然後編譯成bin。從串口下載到板子的0xc000000處,然後run 0xc000000 。哈哈,跑起來了。rdpc返回值是0xc0041c4。正確。

    下一步,是把uClinux的image下載到0xc000000處。在整個調試過程中,我的TCP/IP網路部分不知道什麼時候壞掉了,看來還得抽時間去買個8019了。所以只好用串口下載了。串口下載了好幾次,數據都不夠,通訊會丟數據。最後不得不用FLUTED了,雖說慢點兒,但還是很好用啊(700多K,燒了50分鐘吧)。image文件被燒到了0x100000處。然後重啟進入0x1f0000處的BIOS程序,運行move 100000 c000000 100000 。然後run c000000。
 
 
    呵呵,啟來了。不過後面報錯了,看來我要裝linux然後做修改了,到這裡也算一個段落了,所以做個總結吧。

    我的一點點心得,裡面可能很多漏洞的地方,我也是個初學者,希望高手能夠指點。


           Robertchain@163.com


[admin via 研發互助社區 ] S3C44B0 調試筆記-BIOS已經有2858次圍觀

http://cocdig.com/docs/show-post-42737.html