摘要:該文描述了如何使用JTAG口直接操作ARM CPU寫FLASH芯片的流程。對致力于嵌入式操作系統學習人員有所幫助。
關鍵詞:嵌入式;ARM;JTAG
中圖分類號:TP338文獻標識碼:A文章編號:1009-3044(2008)27-2099-02
Write BOOTLOADER to FLASH with ARM JTAG
TANG De-jun
(Ningbo Yi-zhou Investment Group Co., Ltd.,Ningbo 315100,China)
Abstract: This dissertation describe how to write program of bootloader to boot flash. Embedded operating system dedicated to the study staff help.
Key words: ARM; JTAG; embedded system
在ARM7/ARM9的應用中,一個最重要的步驟是Bootloader如何寫入。目前,大部分人使用編程器來寫入Bootloader程序到啟動FLASH芯片中,然后把啟動FLASH芯片插入芯片座來達到啟動的目的。這種方法有以下幾個麻煩:1) 需要焊接一個FLASH芯片的插座,FLASH芯片型號根據封裝分有PLCC和TSOP和FPGA三種,PLCC是使用最廣泛的用作啟動芯片的封裝,TSOP和FPGA插座在實際生產過程中非常麻煩;2) 啟動FLASH容量不夠,大部分應用需要增加一片操作系統和應用程序存放的FLASH芯片;3) 生產工序工位增加了,貼片生產時需要增加一個工位來預先寫入程序到FLASH芯片。本文向廣大嵌入式研發工程師提供一種通用ARM芯片的JTAG接口,在沒有啟動程序或有程序的情況下,重新寫入啟動程序的方法。
要使用ARM JTAG接口,需要一個JTAG連接器到計算機。獲取JTAG連接器的方法有很多,這不是本文的重點,這里不多描述JTAG連接器。
有了ARM JTAG連接器后,根據JTAG連接的方法,連接的到計算機。我們使用最簡單、連接到計算機并口的標準JTAG連接器來作介紹。
使用HJATG軟件進行和目標ARM CPU的通信調試(HJATG軟件可以在www.hjtag.com免費獲取)。
在計算機上安裝H-JTAG,給目標板加電,在計算機上執行H-JTAG 。
設置電纜類型按圖標 :JTAG類型有兩種,根據電纜類型選擇sdt JTAG(標準JTAG接口)或wiggler電纜,一般使用sdt JTAG。
設置使用并行口的端口:按圖標 選擇LPT1或LPT2,一般選擇LPT1。
設置CPU的目標類型:在CPU自動選擇沒有檢測到時,按圖標 適當選擇ARM7或ARM9,選擇總線模式Little Endian(地位字節在前)或Big Endian(高位字節在前),根據硬件設定一般選擇Little Endian。
按圖標 ,應當檢測處CPU的型號代碼,不同的CPU型號,檢測處的CPU型號代碼不同,意味不同的ARM內核,檢測KS8695X所示代碼是ARM9 0x00922F0F。
安裝ARM Developer Suite(ADS1.2)。
執行AXD Debugger,在Options|Configure Target…下配置連接目標板所使用的動態庫文件,選擇ADD按紐,找到H-JTAG,找到文件H-JTAG.dll,選擇確定,回到配置界面。
選擇OK按紐,回到操作界面,選擇Processor Views | Memory菜單,打開內存顯示窗口,鼠標右鍵點擊內存顯示窗口,在彈出菜單中選擇Size中選擇32bits,即使用32位數據顯示格式。在地址欄內填入相應地址,應能顯示相應地址值。注意:在有MMU控制器的CPU,一旦啟用MMU,內存單元受到保護,顯示地址為邏輯地址,不是物理地址,也無法操作相應的寄存器物理地址。
解讀處理器資料,找到可以通過JTAG控制的內存空間。如處理器內有可以控制的內部RAM,設置相應寄存器,使RAM可以讀寫(一般內部SRAM大部分為指令CACHE和數據CACHE,通過部分寄存器可以時SRAM設定作為內部RAM使用,大部分ARM7內都有SRAM)。
例如:S3C4510B設置SYSCFG用以配置內部RAM,令SYSCFG = 0xE7FFFF86,即配置內部SRAM到地址0x03ffe0000
又例如:在W90N740中CAHCON寄存器,設置內部SRAM用途,所幸的是缺省內部SRAM可用,在地址0xFFE00000開始。
在內部有MMU單元的CPU,內部cache大都不可能設定為內部RAM,需要通過設定外部的SDRAM來成為可用的RAM空間,下面以KS8695X為例來說明如何設定SDRAM成為JTAG調試程序的可用RAM空間。
在KS8695X處理器中,復位后SYSCFG寄存器在地址0x03ff0000,這個寄存器地址沒有改變的話,整個寄存器組都在0x03ff0000~0x03ffffff之間。
禁止一切中斷令寄存器INTEN(0x03ffE024) = 0 。
配置啟動FLASH芯片的地址范圍到0x02800000~0x02C00000之間,4M字節地址范圍,ROMCON0(0x03ff4010) = 0xAFE80070,ERGCON(0x03ff4020) = 0x00000002。
配置SDRAM在0x00000000~0x01000000地址范圍(16M字節),寬度為32位數據,4個Bank,令SDCON0(0x03ff4030) = 0x3FC0000E。
配置 RAS CAS反應時間SDGCON(0x03ff4038) = 0x0000000A。
發送一個空命令到SDRAM,令SDBCON(0x03FF403C) = 0x00030000。
發送一個預先充電命令到SDRAM,令SDBCON(0x03FF403C) = 0x00010000。
設置SDRAM刷新時間,REFTIM (0x03FF4040) = 0x000000168。
配置SDRAM命令寄存器,令SDBCON(0x03FF403C) = 0x00020033。
到此SDRAM配置完成,可以使用了。
在地址0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000地址寫入測試數據,確認硬件沒有錯誤。如果同一單元SDRAM訪問有時有錯誤,可以修改寄存器CLKCON(0x03FF0004)降低總線頻率,保證SDRAM訪問正確。以上配置寄存器可以通過執行下面的配置文件來解決。
現在我們可以在SDRAM空間內執行匯編程序了。直接執行C語言程序,現在還不行,因為C語言需要的各類參數還沒有初始化。在0x8000位置執行下面的匯編程序:
;# Enter UNDEF mode and set up the UNDEF stack pointer
msr cpsr_c, #MODE_UNDEF | I_BIT | F_BIT
ldr sp, =UNDEF_STACK
;# Enter FIQ mode and set up the FIQ stack pointer
msr cpsr_c, #MODE_FIQ | I_BIT | F_BIT
ldr sp, =FIQ_STACK
;# Enter IRQ mode and set up the IRQ stack pointer
msr cpsr_c, #MODE_IRQ | I_BIT | F_BIT
ldr sp, =IRQ_STACK
;# Enter SVC mode and set up the SVC stack pointer
msr cpsr_c, #MODE_SVC | I_BIT | F_BIT
ldr sp, =SVC_STACK
;# Enter SYS mode and set up the SYS stack pointer for temperary use
msr cpsr_c, #MODE_SYS | I_BIT | F_BIT
ldr sp, =RAM_LIMIT_TMP
;# Enter ABT mode and set up the ABT stack pointer
msr cpsr_c, #MODE_ABT | I_BIT | F_BIT
ldr sp, =ABT_STACK
執行完這個文件后,各種模式下堆棧指針就可以安全使用了,這時C語言程序已經可以直接在SDRAM中執行了。
以下面程序為主體,可以直接通過程序在FLASH的任意位置寫任何內容了,在FLASH的起始區域寫入Bootloader當然也是可以的:
size=(int)((unsigned int)(bootloader_tail)-(unsigned int)(bootloader_entry));
uprintf(\"bin size is %d bytes\\",size);
nextAddress = FLASH_BASE;//+0x40000;//linux zImage
if(size % 4 !=0)
i =(size(~0x3))+0x4; //word aligment
else
i = size;
src=(unsigned int)bootloader_entry;
dest=(unsigned int)nextAddress;
uprintf(\"Address:%08x\",dest);
while(i)
{
uputchar('.');
blockSize=flash[flash_type].BlockSize(dest);
flash[flash_type].BlockErase(dest, blockSize);
if( i < blockSize )blockSize=i; // Check if > a block size
flash[flash_type].BlockWrite(dest, (UCHAR *)src, blockSize);
src+=blockSize;
dest+=blockSize;
i-=blockSize;
}
uprintf(\" OK!\\");
uprintf(\"Verifing \");
// Verify program
if(size % 4 !=0)
i =(size(~0x3))+0x4; //word aligment
else
i = size;
src=(unsigned int)bootloader_entry;
dest=(unsigned int)nextAddress;(下轉第2106頁)
(上接第2100頁)
blockSize=flash[flash_type].BlockSize((unsigned int)src);
blockSize=blockSize-1;
j = 0;
while(i)
{
if( (iblockSize)==0x0 )uputchar('.');
if( *((volatile unsigned int *)src) != *((volatile unsigned int *)dest) )
{
uprintf(\"\ERROR: A:0x%08x W:0x%08x R:0x%08x!!\\",dest,*((volatile unsigned int *)src),*((volatile unsigned int*)dest));
break;
}
src+=4;
dest+=4;
i-=4;
}
if(i==0) uprintf(\" OK!\\");
通過ARM CPU的JTAG接口控制ARM CPU直接操作CPU的外圍電路,可以讓我們在調試硬件時,能夠在32位復雜的系統下,很清除的知道外圍電路的工作情況,能一步一步排除各種硬件錯誤,直到系統能夠穩定的工作。使用JTAG接口寫入Bootloader只是其中的一個特例。當然這種方法也不是萬能的,外設要求CPU反應速度快時就無法滿足要求了。
實際我們通過JTAG接口來控制ARM CPU來調試的程序,一般不能太大。因為JTAG傳輸電纜會有誤碼,太大的程序很難保證它的可靠性。
希望本文能為致力于嵌入式操作系統學習人員,提供一點點幫助。
參考文獻:
[1] 杜春雷.ARM體系結構與編程[M].北京:清華大學出版社,2002:115-165,262-288.
[2] 張曉林.嵌入式系統設計與實踐[M].北京:航空航天大學出版社,2006:72-93.
[3] 趙星寒,周春來,劉濤.ARM開發工具ADS原理與應用[M].北京:航空航天大學出版社,2006:127-189.
[4] 陳渝,李明,楊曄.源碼開放的嵌入式系統軟件分析與實踐[M].北京:航空航天大學出版社,2004:33-78.
[5] 田澤.嵌入式系統開發與應用[M].北京航空航天大學出版社,2005:74-94.
注:“本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文。”