如何使用Jlink烧录BIOS到GEC2440

1、  背景:

粤嵌教育嵌入式培训班ARM部分第一节课内容是带学生玩板子,烧BIOS(即bootloader)到flash,然后使用BIOS程序烧录linux内核镜像、根文件系统、WinCE系统NK等。拿到开发板光盘一看,烧录工具竟然是老掉牙的SJF2440···我的笔记本没有并口,也懒得去研究这个已经淘汰过时的烧录工具了,于是乎花了点时间研究了下使用Jlink烧录2440 nandflash、norflash的方法。

2、  实验环境:

2.1、Jlink:taobao买的山寨Jlink,固件版本V8,驱动版本为V4.08I。附图2.1

2.2、开发板:GEC2440_V1.1

2.3、LCD:群创7寸(480 * 272)

2.4、串口:使用PL2303 USB转串口线(我的板子DB9接头和电脑串口连接没反应···,也懒得详查是板子硬件问题还是串口线问题了)

2.5、MDK3.50:这个不用说了,用来调程序的。

3、参考资料:

http://www.rosoo.net/a/201006/9696.html。这篇资料在网上有N多版本,我也不知道哪个是原创了,总之感谢作者!

 

附图2.1, Jlink commander连接上后截图

4、原理陈述

不管是老式的并口Jtag工具,还是现在很流行的Jlink仿真器,总之都是提供一个从PC出来的Jtag接口跟目标Jtag设备连接。这里的目标Jtag设备指的就是CPU(譬如GEC2440开发板上的主芯片S3C2440)

不管是nandflash还是norflash,本身都是和CPU相连的,烧写他们只能通过CPU。即在CPU中运行flash擦除代码、flash编程代码,即可实现对flash的烧写。Bootloader的主要功能其实就是提供这些功能代码,所以一旦板子上已经烧录有bootloader的情况下,我们不必使用仿真器,只需要通过PC终端(譬如最常用的dnw)便可以烧写自己的代码bin文件到板子SDRAM运行,或者烧录他们到nor/nand flash。

问题出来了,板子上最初没有bootloader的时候怎么办呢?这就是本文主要描述的烧写bootloader的场景了。这时候外部的Jtag工具就成为必须了,不管是并口Jtag的软件模拟方式还是Jlink,都是以一个外部Jtag接口的角色,将最初的bin文件从PC机加载到板子运行,最终达到目标的。下面详细介绍Jlink工具实现烧录的细节。

其实真正的flash烧录并不是Jlink完成的,这个是大家必须首先明白的。你看开发板原理图上Jtag引脚和flash引脚有相连吗?没有。所以flash代码还是CPU运行烧录代码烧进去的。Jlink的作用仅仅是提供给CPU可以运行的代码,并且设置CPU使它从正确的地方开始运行。仅此而已。

话分两头,先说后半部分,Jlink弄给CPU用来烧录BIOS到flash的是什么代码呢?答案很有趣,就是BIOS代码本身。请大家看GEC2440的BIOS(这个BIOS在开发板光盘的目录为:开发板光盘\目标代码\目标代码\GEC2440_BIOS_V10.bin)运行的主菜单内容。其中第2功能项就是用来烧录代码到nandflash的。(之所以没有烧录到norflash的项目,是因为GEC2440开发板本身不支持norflash的启动,所以BIOS内也未实现该功能)这个第2功能选项可以将用户通过USB Port或者Serial Port事先download到SDRAM内的bin文件(这个bin文件可以是linux的image,rootfs,也可以是wince的NK.nb0,也可以是用户自己写的代码mycode.bin,当然也可以是我们的BIOS GEC2440_BIOS_V10.bin了)烧录到nandflash的指定分区内。

所以说,BIOS本身为我们烧录BIOS提供了两个有用功能:一是从PC下载GEC2440_BIOS_V10.bin到开发板的SDRAM,二是将下载的bin烧录到nandflash。所以我们唯一需要考虑的就是怎么样让这个BIOS在没有烧录前就在开发板上跑起来。这个就是重点中的关键点,也是Jlink大展神威的地方了。

附图4.1 GEC2440的BIOS运行时,dnw界面显示BIOS主菜单

接着说前半部分,就是怎么样让BIOS在没有烧录的情况下在开发板上跑起来的问题了。也就是说怎么样从PC下载BIOS的bin文件到开发板的SDRAM,并且做足够的设置,让开发板能够正确的从SDRAM内的BIOS开始运行。

这里主要依靠Jlink Commander这个工具。该工具是Jlink自带工具之一,安装Jlink驱动软件之后,在安装目录SEGGER下,J-Link ARM V4.08I目录下,即可找到JlinkCommander.exe。连接好Jlink和开发板,并且给开发板上电后,打开Jlink Commander,如果硬件无误就可以看到如图2.1所示的信息。可以看到,Jlink已经检测到目标CPU了。这时只要使用Jlink提供的命令loadbin,setpc,g,h,r等即可实现加载bin文件到开发板运行的目的。

 

附图4.2 Jlink Commander

上面两部分已经解释了所有的过程,但是还有一个问题。2440内部有两处可以执行代码的区域,一个是内部sram(即steppingstone),另一个是外部SDRAM。我们的BIOS有40KB左右,当然不可能加载到sram内运行了,只能加载到SDRAM运行。但是SDRAM运行代码前必须先初始化,这就要求我们在使用Jlink加载BIOS到SDRAM前,必须先使用Jlink加载一段初始化代码(我的示例工程中叫armplat.bin)到sram运行(sram不用初始化,可以直接加载bin运行,但是代码量必须限制在4KB之内,因为sram一共只有4KB大),这段初始化代码运行时会初始化SDRAM(后面大家会看到,其实还做了其他一些事情,譬如关看门狗和设置时钟)。然后等这段代码运行后,我们再加载BIOS到SDRAM运行,BIOS运行后再烧录BIOS到nandflash,于是乎一切都顺理成章了。

 

5、代码

以下是初始化代码armplat.bin的主要代码部分。

5.1、start.S

         IMPORTxmain

         AREA  RESET, CODE, READONLY

         ENTRY

         B       Handler

Handler
ldr sp, =1024*4

         B       xmain

loop

         B       loop

         END

最最精简的启动代码,reset后直接跳转到main函数(注意这里是xmain。至于为什么是xmain而不是main,那又是另一个故事了,此处且按住不表。)

5.2、xmain.c

#define     rGPBCON          (*(volatile unsigned int*)0x56000010)

#define     rGPBDAT (*(volatile unsigned int *)0x56000014)

#define     rGPBUP    (*(volatile unsigned int *)0x56000018)

 

// Memory control

#define rBWSCON    (*(volatile unsigned *)0x48000000)   //Bus width & wait status

#define rBANKCON0  (*(volatile unsigned *)0x48000004)    //Boot ROM control

#define rBANKCON1  (*(volatile unsigned *)0x48000008)    //BANK1 control

#define rBANKCON2  (*(volatile unsigned *)0x4800000c)    //BANK2 cControl

#define rBANKCON3  (*(volatile unsigned *)0x48000010)    //BANK3 control

#define rBANKCON4  (*(volatile unsigned *)0x48000014)    //BANK4 control

#define rBANKCON5  (*(volatile unsigned *)0x48000018)    //BANK5 control

#define rBANKCON6  (*(volatile unsigned *)0x4800001c)    //BANK6 control

#define rBANKCON7  (*(volatile unsigned *)0x48000020)    //BANK7 control

#define rREFRESH   (*(volatile unsigned *)0x48000024)      //DRAM/SDRAM refresh

#define rBANKSIZE  (*(volatile unsigned *)0x48000028)      //Flexible Bank Size

#define rMRSRB6    (*(volatile unsigned *)0x4800002c)    //Mode register set for SDRAM

#define rMRSRB7    (*(volatile unsigned *)0x48000030)    //Mode register set for SDRAM

 

// CLOCK & POWER MANAGEMENT

#define rLOCKTIME   (*(volatile unsigned *)0x4c000000)   //PLL lock time counter

#define rMPLLCON    (*(volatile unsigned *)0x4c000004)  //MPLL Control

#define rUPLLCON    (*(volatile unsigned *)0x4c000008)   //UPLL Control

#define rCLKCON     (*(volatile unsigned *)0x4c00000c)   //Clock generator control

#define rCLKSLOW    (*(volatile unsigned *)0x4c000010)   //Slow clock control

#define rCLKDIVN    (*(volatile unsigned *)0x4c000014)    //Clock divider control

#define rCAMDIVN    (*(volatile unsigned *)0x4c000018) //USB, CAM Clock divider control

 

// WATCH DOG TIMER

#define rWTCON   (*(volatile unsigned *)0x53000000)        //Watch-dog timer mode

#define rWTDAT   (*(volatile unsigned *)0x53000004)         //Watch-dog timer data

#define rWTCNT   (*(volatile unsigned *)0x53000008)        //Eatch-dog timer count

 

#define rINTMSK     (*(volatile unsigned *)0x4a000008)   //Interrupt mask control

 

// 延迟函数,用来实现LED闪烁

void delay(void)

{

         inti;

         for(i=0;i<500000;i++);

}

// 关所有中断

void disable_interrupt(void)

{

         rINTMSK= 0xffffffff;

}

// 关看门狗

void wdt_disable(void)

{

         rWTCON= 0x00;

}

// MPLL初始化,将主时钟设置为405MHz

void mpll_init(void)

{

  rMPLLCON = (127<<12) | (2<<4) | (1<<0);

  rCLKDIVN = (0x3<<1)|(0x1<<0);

}

// SDRAM初始化函数,这里的初始化参数来源于EXT_RAM.ini

void sdram_init(void)

{

         rBWSCON= 0x22000000;

         rBANKCON6= 0x00018005;

         rBANKCON7= 0x00018005;   

         rREFRESH= 0x008404F3;

         rBANKSIZE= 0x00000032;

         rMRSRB6= 0x00000020;

         rMRSRB7= 0x00000020;

}

// 主函数,依次调用上面各个功能函数,并最终以GPB5接得LED1闪烁作为成功执行的标// 志。

void xmain(void)

{

         disable_interrupt();

         wdt_disable();

         mpll_init();

         sdram_init();

 

         //GPBCON[11:10]=01

         rGPBCON&= ~(0x3<<10);

         rGPBCON|=(0x1<<10);

         rGPBUP|=(0x1<<5);

        

         while(1)

         {

                   //GPBDAT[5]=1

                   rGPBDAT|=(0x1<<5);

                   //DELAY

                   delay();

                   //GPBDAT[5]=0

                   rGPBDAT&=~(0x1<<5);

                   //DELAY

                   delay();

         }

}

6、操作和实验现象

6.1、在MDK3.5中建立工程,编译以上代码。注意在选项->Target->ReadonlyMemory设置中,ROM1的start一定要设置为0x00000000,size设置为0x1000。因为我们的代码将来是要加载到内部sram中运行的,而且我们是选择从nandflash启动,nand启动时sram就是被映射到0x0地址的。选项栏中其他选项这里就不详细说了,详见我另一篇教程《Jlink+SDRAM裸奔2440教程》。

6.2、准备好刚才编译好得到的初始化代码armplat.bin,和GEC2440_BIOS_V10.bin,将之copy到D盘根目录下(也可以是其他盘根目录,选择根目录是因为在Jlink Commander的命令行下目录都是要手输的,根目录可以好输入一点,呵呵),将GEC2440_BIOS_V10.bin改名为boot.bin(原因同上,输入的时候减少一个一个扣字母的痛苦)。

6.3、启动Jlink Commander,看到附图2所示页面即表示Jlink Commander连接正确。若显示一些其他错误信息,譬如找不到目标Jtag设备、不能挂起CPU等等,请尝试:第一检查硬件连接,开发板是否已经上电,Jlink连线是否正确;第二,键入r命令尝试复位CPU。

6.4、设置Jlink速度。Jlink Commander连接后,默认速度为5KHz,这样下载速度很慢,因此我们使用speed12000 命令将Jlink速度设置为12MHz。

6.4、开始下载armplat.bin到sram运行。首先键入loadbin d:\armplat.bin 0x0,成功后,再次输入setpc 0x0,成功后再次输入g,此时armplat.bin 已经在内部sram内跑起来了,此时开发板上的LED1应该已经在闪烁了。

6.5、键入h命令挂起CPU。注意这一步很重要,如果忽略了此步骤,下面在下载boot.bin时会提示无法挂起CPU。

6.6、键入loadbin d:\boot.bin 0x33000000,成功后再次键入setpc 0x33000000,成功后再次键入g,完成。

注意:在最后一个g之前,一定要提前打开dnw(dnw在光盘/实用工具/串口工具dnw目录下),并且使用串口连接上开发板。因为g命令执行后,SDRAM中的BIOS会立即执行,如果连接了dnw就能在dnw的终端内看到BIOS执行时打印的信息(入附图4.1所示)。此时5s钟内如果不通过dnw做出选择,即会自动进入启动内核(nandflash内烧录的linux或wince内核)流程。那么我们这次辛辛苦苦的把BIOS下载的SDRAM就算是白费了,又得从6.1开始从新来过了。

附图6.1,D:\下置入boot.bin和armplat.bin

 

附图6.2,r命令复位CPU

附图6.3,speed命令设置Jlink速度为12MHz

附图6.4, 加载并运行armplat.bin

附图6.5,加载并运行boot.bin

7、最后的说明

7.1、通过dnw烧录BIOS、烧录image等操作请参考开发板光盘下/用户手册/GEC2440开发板用户手册.PDF,文档内说的很详细了。大家刚拿到开发板时都是有预烧录BIOS的,建议先不要破坏BIOS,按照这份文档所说先利用BIOS烧录别的bin做实验,待熟悉BIOS和dnw的使用后再做本实验。

7.2、dnw支持串口和USB两种download方式,但是我们一般会使用USB方式,因为速度会快一些。USB download要求要求连接USB线,并且要安装USB驱动。注意这个USB驱动是在dnw打开并且BIOS运行后选择了0号功能以后,PC端才会跳出发现新硬件的窗口,此时可以手动选择USB驱动文档进行安装。Dnw的USB驱动在开发板光盘内USB驱动程序文件夹内。

7.3、我在做实验时发现,dnw的configuration->Option页面中,USB Port->Download address栏目中输入0x30100000(dnw的默认值)时,linux的image和rootfs下载OK并且可以运行,但是WinCE的NK下载后运行不起来。后来反复实现发现将Download address改为0x30200000即可。猜测应该和SDRAM的空间分配使用有关,暂时没有时间去研究了。
7.4、后来在实验时发现另一个很有意思的问题。我上面列出的例程是我初次实验时写出的,一些初始化代码均使用了c函数,实验是ok的。(但是我实际上刚开始在start.S中是没有 ldr sp, =1024*4的,这个是应该加上的。但是当时没加,结果却是是对的···)
后来在课堂上现写了这个项目的代码,并且使用了汇编代码做初始化。实际就是把上面各个c函数中得内容直接copy到xmain中,结果下载后死活不能成功引导boot。查了几个小时后来发现了两个问题:一是在时钟初始化之后没有加delay,二是在sdram初始化之后没有加delay。
2440手册上有明确提出,当MPLL改变时必须添加至少7个nop以使修改后的时钟稳定。在使用c函数时,因为c调用和返回有一定开销,因此这个细节没有注意并没有造成错误,但是一旦使用汇编代码就会立刻显现出问题。
关于sdram初始化之后要加delay,我倒是没有在手册上找到明确说明。但是实验证明,sdram初始化之后确实也需要加延迟。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐