新闻  |   论坛  |   博客  |   在线研讨会
如何开发arm
tvb2058 | 2007-12-04 14:29:49    阅读:1289   发布文章

目前因为两台电脑都已经安装WINDOWS系统,又装了很多软件。为了没有什么风险,就装了VMWARE软件,就是虚拟机软

件,这个软件的虚拟速度确实快很多,它跑图形的操作系统也不慢。如果BOCHS,一运行图形的操作系统,就会很慢,

因此放弃了这个自由软件。如果我的电脑速度也很快,比如用AMD64位的最新CPU或许用BOCHS也不慢。

装好VMWARE软件,然后就是在里面安装LINUX,那么用什么LINUX比较好呢?目前,我是用最流行的REDHAT。并且用的

是9.0版本,这个版本确实不错。一路下来,几乎不用动什么手,就可以装完了。

吸取了上几次的经验,所以这次我选择了全部安装所有软件,硬盘的空间还有10G,所以我虚拟机硬盘就分配10G硬盘

空间。这样全部安装LINUX后,大小也才3G左右。这个LINUX已经把图形和网络,开发源程序和所有工具,全部安装完

成。像我很少使用LINUX的用户,一定不会再回到像DOS那种环境的,或许很久没有用DOS了,所以打命令总是很艰难的

,总记不住LINUX下的命令,所以我选择了图形的方式。目前在LINUX下,使用图形的方式,已经很方便了。跟WIN95的

水平已经不相上下了,如果WINDOWS真的要收我的钱,就改到LINUX下了,REDHAT9.0已经使用起来很方便了。如果是只

办公,用LINUX,已经很好用。到了2005年,LINUX确实进步了很大。REDHAT9.0已经很实用了,LINUX肯定可以成为办

公的PC标准配置了。

装好LINUX后,就要配置它的网络部份。以前几次都配置不成功,是因为不了解LINUX,或许它的帮助太少,以为每个

都是高手。现在用REDHAT9.0的中文版,总算比较了解。设置网卡的IP地址为192.168.0.2,掩码:255.255.255.0,网

关是192.168.0.1。这样就配置好了LINUX的网络了。

接着下来,就要设置VMWARE的网络部份,我选用NAT的方式。所以我找到那个VNET8那个虚拟网卡,然后设置它的IP地

址为192.168.0.8,掩码:255.255.255.0,网关是192.168.0.1。这个是在WINDOWS的网络里配置的。这样设置好之后

,在WINDOWS里,就可PING到虚拟机里的LINUX的192.168.0.2了。

在LINUX访问WINDOWS的IP是多少呢?这个问题刚开始,我也是不知道的。后来试了一下,只能PING到WINDOWS的

192.168.0.8这个地址。不过,只要PING得通这个地址,就证明LINUX和WINDOWS的网络已经设置好。

接着下来,就是解决WINDOWS与LINUX共享文件的问题。这个有很多的解决方案。我的选择是用FTP。

因为我对LINUX不了解,所以我试着配置LINUX的VSFTPD总是配置得不好,达到不我的要求,而我的要求就是简单,任

何文件都可以上传也可以下载,但LINUX缺省的方式是很安全,搞得我有时候能上传,就不能下载,有时能上传了而不

能删除文件,是因为权限太复杂,总算了解为什么WINDOWS好用,就是因WINDOWS没有那么多权限控制。

既然配置LINUX不成功,我就选择了WINDOWS的FTP服务器。拿出WINDOWS的光盘,然后添加WINDOWS的FTP服务器,在

WINDOWS后只选择两个设置,就可以设置任何人都可登录,任何文件都可上传和下载。这样太快了,还是WINDOWS方便

,全是界面的。因为目前安全是不用考虑的,用LINUX那个,要改为不安全,很难设置。

设置好WINDOWS的FTP服务器之后,就可以用LINUX后那个图形的GFTP工具连接到WINDOWS了。只要在WINDOWS的FTP目录

里,放任何文件,都可以在虚拟机的LINUX里用GFTP来拷贝入去,也可以从LINUX里拷贝文件出来了。

这样的操作系统装好之后,就是安装ARM的开发环境了。这个就是下载arm-elf的交叉编译器。这个以后再说了。

到这里,就完成LINUX的安装和文件共享的问题。这样的好处是,可以在LINUX下编译程序,可以WINDOWS上用VC等工具

写源程序和修改源程序,速度快,并且写中文的注释也很方便了。并且可以把uClinux的目标文件拿WINDOWS下用

SKYEYE等运行。如果还在虚拟机的LINUX下,再用SKYEYE的话,就会变得非常慢的。


上一次说到装好LINUX后,就要装开发环境了,因为编译在LINUX下,当然是用GCC,但这个GCC跟LINUX本身带的又不一

样。目标代码是ARM,而LINUX带的,一般是I386的目标代码。因此,一定要安装ARM的编译器。到网上下载

arm-elf-tools-20030314.sh,然后运sh ./arm-elf-tools-20030314.sh运行,就可以安装好ARM的GCC编译器了。你在

LINUX下,就可以用arm-elf-gcc -v就可测试一下,是否安装成功了。这个编译器已经包括了as,ld,gcc等三个编译器

,已经可以编译汇编和C代码,并且可以连接成目标文件。

编译器已经安装好之后,就说明整个开发环境已经建立起来,就做你想要做的事情了。至于怎么样写MAKEFILE文件,

我就不说了。因为这些都是很基础的东西,并且都是细节的东西,记不住那么多,要用到的时候再去查手册行了。并

且一个项目或许只用写一个MAKEFILE的格式,然后其它的都可以从这个基础上修改而来。我不会从头去建立一个

MAKEFILE的,那需要太多时间了。只要到其它项目里拷贝一个,自己进行修改,就可以使用。

接着下来,就要写程序,那怎么样写呢?这是一个问题。写程序的目标机器是什么呢?用什么语言来写呢?应从那里

下手呢?其实是有很多问题在这里的。

因为我以前没有学习过ARM,只学习过80X86的微机原理,我想这个ARM也是计算机,只要是一样的东西,就要用一样的

方法来学习。以前我在大学里学习微机原理的流程是这样的:学习二进制表示法,学习中央处理器组成,学习汇编,

学习CPU的内存管理,学习CPU的IO口。那么我也要用这种方法来学习才对。

二进制表示方法是一样的,所以不用学习了。那么我为什么CPU写程序呢?这个一定要搞清楚的,否则也不会怎么样写

。ARM的CPU是与X86的CPU是不一样的。它是采用了RISC设计,以前也不懂这个是什么,所以就到网上查下,在《维基

百科,自由的百科全书》里说明如下:精简指令集,计算机CPU的一种设计模式,也被称为RISC(Reduced Instruction

Set Computing 的缩写)。

早期,这种CPU指令集的特点是指令数目少,每条指令都采用标准字长、执行时间短、CPU的实现细节对于机器级程序是

可见的等等。

实际上在后来的发展中,RISC与CISC在争吵的过程中相互学习,现在的RISC指令集也达到数百条,运行周期也不再固

定...... 虽然如此,RISC设计的根本原则--针对流水线化的处理器优化--没有改变。

原来如此,RISC的一般特性是什么呢?采用RISC指令的CPU,大多数都是定长指令,采用加载与储存操作数据的办法。

就是所有要操作的数,都要保存到寄存器才能作运算,不像X86的CPU,可以直接操作内存里的数据。这个在写汇编时

需要了解的。其它的区别,对于写程序来说,是没有区别的。

ARM的CPU执行代码是三步进行的,取指---译码----执行。

 

已经把CPU大体架构学会了,当然我之前已经学习过80X86的结构,所以对于像RISC的计算机,也能想像到它是什么样

的东西。如果没有学习过《微机原理》,那最好去看看,或者去看看RISC的CPU设计,就是龙芯的设计,在清华上面有

很多关于龙芯的设计文档,这些都是CPU的功能。

为什么要学习汇编呢?现在不是大多数用C编写的了吗?是的,目前嵌入式软件大多数都是用C开发的。其实学习汇编

,是为了更好了解CPU的处理,这个只是为了了解,为了更深入的了解,而不是用它来开发大部份的应用。当然它也是

有用的地方,比如BOOTLOADER这样的程序,没有汇编,是不可能完成的。还有那些有特殊的指令,没有汇编也是不可

能写得出来的。目前,我学习汇编,就是为了写BOOTLOADER。我也没有为了全部去学习它,而去学习,而是拿

BOOTLOADER的源程序出来,从上面了解汇编,看懂别人的BOOTLOADER,或者能修改它的汇编,就算学习完成,而不必

要每条指令都去死记它,去了解它。只要BOOTLOADER中有的指令,才去看它,去了解它,把一个BOOTLOADER看完下来

,就相当于把汇编学习完成,而不必要自己动手去新写什么汇编,只要能修改目前已经有的BOOTLOADER,就已经完成

了任务。

下一次,接着就去看看U-BOOT的源程序了。当然是基于S3C44B0的BOOTLOADER进行的。


上一次说到要学习UBOOT的代码,但在看之前,首先要知道目标机器的编程资源,这里的资源,是指S3C44B0所提供的

运行程序的资源,对任何嵌入式软件开发,都首先要对硬件有一个很好的了解,这跟PC机的编程是大不一样的。因为

PC机都已经发展了30多年,但整个编程的体系是没有很大的变化,就是说现在的PC都是在虚拟机上编写了,跟硬件打

交道的机会很少,所以不用去了解它。但是在嵌入式的软件里,每样硬件都是千差万别的,所以一定要去看原版的

S3C44B0说明手册,一定要看英文原版的,不能看那种中文版的,哈哈,为什么要看英文原版的呢?第一,每个CPU都

有很多特别寄存器,而这些寄存器都是用英文缩写的,看中文,就不知道它是什么意思了,并且还要死记,没有英文

整句好记。第二,中文是经过翻译的,并且都不是三星厂家进行翻译,都是一些业余水平的人来做,很难保证按原文

的意思进行。有一次,我看中文的S3C44B0资料找特别寄存器,就找不到,我说为什么没有呢,原来别人都不翻译那些

,结果在英文原版中,一看就找到了。因此,喜欢看英文原版的。并且只要学习过几个CPU之后,发现嵌入式软件就那

几样东西了。

对开发软件的人来说,最重要的东西是什么呢?哈哈,当然是存储器和寄存器了。每次拿到开发板之后,一定要去了

解存储器是怎么样分配的,是从什么地址开始,存储器有多大。是什么样的存储器,是FLASH的,还是SDRAM的。FLASH

的存储器映射在S3C44B0的什么地址空间呢?SDRAM又是映射在什么地址空间呢?一定要把这些问题搞清楚,否则就很

难对它开发软件。所以看S3C44B0的手册时,就要注意看它的存储管理。目前,S3C44B0的存储器管理,分为很多BANK

的。BANK0一般用来映射FLASH存储器,并且在BANK0后面一段空间里进行特别寄存器映射,它的空间是4M大小。并且一

般的开发板,都是把FLASH存储器映射在0x0000_0000 到 0x001F_FFFF,这里的地址空间,就是2M大小。把SDRAM映射在

BANK6里面,地址空间就是0x0c00_0000 到 0x0c7f_ffff,这里就是8M的SDRAM了。

特别寄存器的地址空间是从 0x01c0_0000到0x0200_0000的4M空间里,这里的寄存器,大多数都是跟IO有关的。比如串

行通讯,DMA,PWM,看门狗,IO口等等。

看懂了存寄器和存储器,就可以进行汇编练习。当然S3C44B0它还有ARM标准的寄存器,共有37个吧。这些是所有ARM的

CPU都具有的。

把CPU和S3C44B0特别的功能深入了解之后,就可以去看UBOOT代码。

 

目前我的引导程序是在UBOOT1.1.2上修改过来的,大家可以下载UBOOT的源程序,也许你们会问在那里有下呢?这个问

题不用我回答了,用GOOGLE,直接找u-boot,就可以找到了。

由于这个引导程序是从u-boot1.1.2修改过来的,所以还是采用uboot的编译工具,它就是

arm-linux-tools-20031127.tar.gz,这个可以uboot的网站下载,然后在linux下安装好,就可以编译引导程序。

编译命令主要:

make clean 是清空所有临时文件,一般是用来清空目录,用来重新编译程序。
make XXXX_config 是编译本引导程序,XXXX是自己定义的开发板,在board目录下。
make backup 是备份引导程序的源程序。

UBOOT编译环境建立起来,就可以去修改和学习UBOOT的源程序,就可以开发自己的boot。源程序已经下载,就可以解

开压缩包,在目录u-boot-1.1.3,因目前最新的UBOOT是1.1.2,那个1.1.3是我自己的UBOOT源程序,如果自己想改为

1.1.3名称,在LINUX下编译是有点问题的,自己去试试吧。哈哈。。。

接着,我到目录cpu\s3c44b0,这个目录,就是S3C44B0的CPU引导程序,最开始运行的代码就是从这里开始的。那我看

代码,也要从这里开始。

看一下这个目录,包括文件有:

G:\Downloads\lichee\lichee\boot\src\u-boot-1.1.3\cpu\s3c44b0 的目录

2005-07-18  12:47             .
2005-07-18  12:47             ..
2005-07-16  04:35             4,154 .depend
2004-02-24  08:16             1,066 config.mk
2004-10-18  05:12             9,878 cpu.c
2004-02-24  08:16             4,843 interrupts.c
2004-02-24  08:16             1,303 Makefile
2004-10-18  05:12             4,378 serial.c
2005-07-17  23:48             4,820 start.S
2005-07-17  23:47             4,784 start.S.bak

有好几个文件,它们的作用,大家自己先想想,不懂的再问我,目前我要开始看start.S文件,这个最开始运行的文件

这个文件是汇编写的,但它是经过c的预处理的,所以像在头文件.h中的宏,也可以使用的,这就是GCC的博大之处。

大家看到我的目录,也许很奇怪,为什么会有“lichee”这个名称?我来告知各位,这个是我起的名称,叫做“荔枝

”,我的BOOT代码和我的uClinux的代号,都叫“荔枝”。吃过“荔枝”的人,都知道外面红红的,里面的肉是白的,

清甜可口。我的BOOT和OS都是外面看起来很好看,里面的功能,就是难看了,但要深入去了解,才会尝到新鲜的味道

好了,下面就要开始品尝我的“荔枝”了,第一段代码:

/** S3C44B0 CPU启动代码。* 蔡军生 2005/07/17 **/

/** 包含配置文件。*/

#include    /*添加版本说明.2005/07/16*/
#include     /** 中断向量表.*/

/* 声明标量是全局函数,CPU加电启动后,就从这里执行代码。 */


.globl _start
_start: b          reset                   /* 跳到复位中断开始位置。 */
        add        pc, pc, #0x0c000000
        add        pc, pc, #0x0c000000
        add        pc, pc, #0x0c000000
        add        pc, pc, #0x0c000000
        add        pc, pc, #0x0c000000
        add        pc, pc, #0x0c000000
        add        pc, pc, #0x0c000000
        .balignl 16,0xdeadbeef

上面这段代码,看懂了吗?如果有什么不懂,就发问题过来。

这里用到ARM指令,b指令就是无条件地跳到reset的地方运行,reset是一个标记,并且它是相对量。

add        pc, pc, #0x0c000000,这句用到了4G空间跳转技术,因为ARM的CPU下一次值是根据PC的值来进行的。这里

修改了PC的值,就相当跳到对应的地址运行了。又由ARM的指令的宽度是4字节的,所以这里的PC值就是0x0c000004。

如果这里直接用b指令是不行的,因为它不超出32M的寻址空间,只有修改PC值才可以达到目的,因此采用ADD指令来修

改PC值。

.balignl 16,0xdeadbeef

上面这句,就是填充多少字节在后面。

整段代码实现建立中断向量表,这个根据ARM的CPU来编写的。


第一段代码看完了,再接着看第二段代码。


/******************************************************************
* 启动代码。
** 如果不作内存初始化,就只建立堆栈,重新定位代码到RAM位置。
* 然后就可以跳到第二阶段的代码运行了。
**********************************************************************/

/* 保存变量的数据区 */
_TEXT_BASE:
        .word        TEXT_BASE
        .globl _armboot_start
_armboot_start:
        .word _start
/** These are defined in the board-specific linker script.*/
        .globl _bss_start
_bss_start:
        .word __bss_start
        .globl _bss_end
_bss_end:
        .word _end

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START

IRQ_STACK_START:
        .word        0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
        .word 0x0badc0de
#endif

上面这段代码,主要保存一些全局变量,用于BOOT程序从FLASH拷贝到RAM,或者其它的使用。还有一些变量的长度是

通过连接脚本里得到,实际上由编译器算出来的。

看了数据区,这次要看从引导那里跳到这里执行时,运行什么东西了。
/** 实际运行的复位代码。从一开始运行的代码,就跳到这里运行。*/
reset:
       /* * 设置cpu运行在SVC32模式。*/
        mrs        r0,cpsr
        bic        r0,r0,#0x1f
       orr        r0,r0,#0x13
       msr        cpsr,r0

具体分析如下:
/** 实际运行的复位代码。从一开始运行的代码,就跳到这里运行。*/

reset:
        /** 设置cpu运行在SVC32模式。S3C44B0共有7种模式。   */
       mrs        r0,cpsr
取得当前程序状态寄存器cpsr到r0。
        bic        r0,r0,#0x1f
这里使用位清除指令,把中断全部清除,只置位模式控制位。
        orr        r0,r0,#0x13
计算为超级保护模式。
        msr        cpsr,r0
设置cpsr为超级保护模式。
通过设置ARM的CPSR寄存器,让CPU运行在操作系统模式,为后面进行其它操作作好准备了。后面的代码如下:
/*       * 当是从FLASH启动时,就要进行内存测试,当
         * 是从RAM启动时,一般就是开发本源程序时,就
         * 可以跳过。
         *
         */
#ifdef CONFIG_INIT_CRITICAL
        bl        cpu_init_crit
       /*
         * 在重新定位之前,要进行RAM访问时间测试,因为每个开发
         * 都是不一样的。
         * 可以在文件memsetup.S里看到它的说明。        
        */
        bl        memsetup
#endif

/* 进行重定位 */
relocate:        /* 重定位Boot代码到RAM内存,比如从FLASH移到RAM */
adr      r0, _start                /* 把_start的相对地址移到r0    */
ldr     r1, _TEXT_BASE    /* 把_TEXT_BASE地址,就是BOOT在RAM中运行地址 */
cmp     r0, r1         /*  比较两个地址是否相同,如果相同,就已经在RAM运行,否则就是FLASH中运行。*/
       beq     stack_setup

  /* 是在FLASH中运行,要把FLASH中的BOOT代码移到RAM中,然后再运行. */
       ldr        r2, _armboot_start
       ldr        r3, _bss_start
       sub        r2, r3, r2                /* r2保存引导代码大小    */
       add        r2, r0, r2                /* r2保存引导代码最后地址         */
copy_loop:
        ldmia        r0!, {r3-r10}                /* 从源地址[r0]读取8个字节到寄存器,每读一个就更新一

次r0地址 */
        stmia        r1!, {r3-r10}                /* 拷贝寄存器r3-r10的值保存到 [r1]指明的地址,每写一

个字节,就增加1.    */
        cmp        r0, r2                        /* 判断是否拷贝到[r2]地址,就是引导代码结束位置。   */
        ble        copy_loop       /* 循环拷贝 */
/*拷贝中断向量表,实际是建立起二级中断向量表,当CPU中断时,先运行FLASH中断,接着就转移到实际中向表执行

中断程序。*/
        adr        r0, real_vectors
        add        r2, r0, #1024
        ldr        r1, =0x0c000000
        add        r1, r1, #0x08
vector_copy_loop:
        ldmia        r0!, {r3-r10}
        stmia        r1!, {r3-r10}
        cmp        r0, r2
        ble        vector_copy_loop
        /* 建立起堆栈        */
stack_setup:
        ldr        r0, _TEXT_BASE     /* upper 128 KiB: relocated uboot   */
        sub   r0, r0, #CFG_MALLOC_LEN    /* malloc area      */
        sub        r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo        */
#ifdef CONFIG_USE_IRQ
        sub        r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
        sub        sp, r0, #12                /* leave 3 words for abort-stack    */
        ldr        pc, _start_armboot /* 已经准备好了堆栈,就可跳到C写的代码里,由于我的代码是ARM,就是

跳到lib_arm\board.c(208):void start_armboot (void)中运行。 */

_start_armboot:        .word start_armboot

/**************************************************************************
* CPU_init_critical临界区寄存器
* 设置一些重要的寄存器,并进行内存测试。
*************************************************************************
*/

#define INTCON (0x01c00000+0x200000) /* 中断控制器 */
#define INTMSK (0x01c00000+0x20000c) /* 中断控制屏蔽寄存器 */
#define LOCKTIME (0x01c00000+0x18000c)
#define PLLCON (0x01c00000+0x180000)
#define CLKCON (0x01c00000+0x180004)
#define WTCON (0x01c00000+0x130000)

cpu_init_crit:

        /* 关闭看门狗 */
        ldr         r0, =WTCON
        ldr        r1, =0x0
        str        r1, [r0]

        /** 清除所有中断位,设置INTMRs实现。*/
        ldr        r1,=INTMSK
        ldr        r0, =0x03fffeff
        str        r0, [r1]
        ldr        r1, =INTCON
        ldr        r0, =0x05
        str        r0, [r1]

        /* 设置时钟控制寄存器 */
        ldr        r1, =LOCKTIME
        ldrb        r0, =800
        strb        r0, [r1]

        /* 设置锁相环,控制CPU运行速度。 */
        ldr        r1, =PLLCON

#if CONFIG_S3C44B0_CLOCK_SPEED==66
        ldr        r0, =0x34031         /* 66MHz (Quartz=11MHz) */

#elif CONFIG_S3C44B0_CLOCK_SPEED==75
        ldr        r0, =0x610c1 /*B2: Xtal=20mhz Fclk=75MHz  */
#else

# error CONFIG_S3C44B0_CLOCK_SPEED undefined

#endif
        str        r0, [r1]
        ldr        r1,=CLKCON
        ldr        r0, =0x7ff8
        str        r0, [r1]

        /* 调用子函数返回 */
        mov        pc, lr

/*************************************************/
/*        实际的中断向量表        */
/*************************************************/

real_vectors:
        b        reset
        b        undefined_instruction
        b        software_interrupt
        b        prefetch_abort
        b        data_abort
        b        not_used
        b        irq
        b        fiq
/*************************************************/

undefined_instruction:
        mov        r6, #3
        b        reset

software_interrupt:
        mov        r6, #4
        b        reset

prefetch_abort:
        mov        r6, #5
        b        reset

data_abort:
        mov        r6, #6
        b        reset

not_used:
        /* we *should* never reach this */
        mov        r6, #7
        b        reset
irq:
        mov        r6, #8
        b        reset
fiq:
        mov        r6, #9
        b        reset

把引导的汇编看完,已经准备C的运行环境,下面就开始学习C的源程序,从start.S文件里到跳文件lib_arm\board.c里运

行.

/*引导程序从汇编start.S里跳到这里执行。蔡军生 2005/07/19*/

void start_armboot (void)
{
        /* 声明一个全局指针,它是指向一个数据结构,用于保存参数。
        并且它占用r8寄存器,用它来保存内存地址,达到全局使用目的。
        */
        DECLARE_GLOBAL_DATA_PTR;
        ulong size;
        init_fnc_t **init_fnc_ptr;
        char *s;

#if defined(CONFIG_VFD) defined(CONFIG_LCD) unsigned long addr;

#endif
        /* gd指针可写,因为已经分配一个寄存器给它作为变量。
        这里就相当于把后面算出来的地址保存到r8寄存器.
        */
        gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

       /* 下面一句是阻止3.4以上版本的GCC进行代码优化,把后面的代码删除掉。 */

        __asm__ __volatile__("": : :"memory");

        /* 清空gd指向的结构 */
        memset ((void*)gd, 0, sizeof (gd_t));
        /*  */
        gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
        memset (gd->bd, 0, sizeof (bd_t));
        monitor_flash_len = _bss_start - _armboot_start;

这一段准备好保存参数的全局变量区.
后面就是一系列的初始化和获取正确的参数.

/* 用循环调用所有初始化函数 */
        for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
        {
               if ((*init_fnc_ptr)() != 0)
               {
                      /* 当每个函数初始化失败时,就会挂机在这里。 */
                        hang();
                }
        }
上次说到在函数指针数组里,不断地调用所有初始化函数进行初始化,下面就来仔细地分析一下,它们到底是做什么

的,做什么样的初始化,怎么样为后面做好运行的准备工作。看到第一个初始化函数,就是CPU初始化(cpu_init),

这个函数是在cpu\s3c44b0\cpu.c里,它的作用就是进行S3C44B0初始化工作。看到这个函数内容如下:

/* CPU初始化。蔡军生 2005/07/23*/
int cpu_init (void)
{
        /* 清空缓冲区 */
        icache_enable();
        return 0;
}
它在里面调用了函数icache_enable(),它就是用来初始化S3C44B0的缓冲区,并且启用CPU缓冲区。因为CPU在加电之

后,它的初始化值是不启用内部的8K缓冲区的,必须由程序进行设置。接着看看那个调用的函数又是怎么样初始化内

部缓存区的呢?

/* CPU内存的缓冲初始化。蔡军生 2005/07/23*/
void icache_enable (void)
{
        ulong reg;
        /* 清空内存的缓冲区.  */
       s3c44b0_flush_cache();
        /* 初始化缓冲区,设置非缓冲区的起始地址和结束地址。第一个寄存器指明下面的地址不要缓存,低16位是起始地址,高16位是结束地址。并且空间大小都是以4K为界。0x0000:0000 - 0x0C00:0000*/

        NCACHBE0 = 0xC0000000;
        NCACHBE1 = 0x00000000;

        /* 设置SYSCFG寄存器启用8K缓冲区。 */

  reg = SYSCFG;
  reg = 0x00000006; /* 8kB */

        SYSCFG = reg;
}

在这个函数里,第一个先调用函数是进行缓冲区清0的工作,它有一些特别的地方,如下:

/* CPU的内部缓冲初始化。蔡军生 2005/07/23*/

static void s3c44b0_flush_cache(void)
{
        volatile int i;
       /* 清空缓冲区,每次要按4个32位来读写,所以要加16. */
       for( i = 0x10002000; i < 0x10004800; i += 16 )
        {
                *((int *)i)=0x0;
        }
}

它用一个for循环进行内部的缓冲区初始化,由于S3C44B0决定每次读写都是按16字节进行的。因此,这里的i就是不断

地加16个字节。不过,这里为什么不清除0x10000000到0x10001fff区域呢?这个我也没有搞清楚,等我有空试试清除

有没有问题!

到现在为止,缓冲区已经清空,就要设置那些内存区域是不要进行缓存的。因为不是所有内存都需要进行缓冲的,比

如读取外面的IO,就不需要进行缓冲;读取FLASH也不需要。因此,设置第一个非缓冲区的起始地址为NCACHBE0 =

0xC0000000,这个值里的低16位是起始地址0x0000,它的32位地址就是从0x00000000开始。它的高16位是结束地址

0Xc000,它的32位地址就是从0Xc0000000结束。最后就是通过设置SYSCFG寄存器的[2:1]位的值为11,就启用了8K内存

数据和指令缓冲区。到这里为止,就已经设置好CPU的缓冲区初始化和启用。

由于前几次,把编译好的UBOOT写到FLASH老是运行不了。那么怎么办呢?思考了很久,也查看了源程序,还是没有发

现问题。也许那个UBOOT的源程序太大,有很多的编译开关,还有很多驱动程序选择,所以一头雾水,不知怎么办好。

到了这个时候,只有从头开始跟踪了。就是尽可能地跟踪程序运行到那里。因而依次地进行下面的检查:
1. 检查设置的倍频是否对。
2. 检查程序写到FLASH的数据是否对。
3. 检查设置RAM是否对。
4. 检查设置串口的波特率是否对。

一般进行四部份检查就找出大体的问题。我首先查了一下,我的开发板上的晶振是10MHz输入,那么我要倍频到60MHz

,所以就要计算它的倍频系数。当然最快的办法,就是用三星提供的PLLSET.EXE工具,五秒钟就搞定了。经过检查,

发现我的倍频系数不对,所以重新设置倍频系数。一般设置过倍频系后,都要调整串口波特率、SDRAM的刷新频率。设

置好倍频系数后,接着,就要计算串口的波特率,这个三星的S3C44B0的手册有计算公式。比如9600,就是拿60MHz进

行分频得到的。

这次设置好后这些东西之后,就把UBOOT编译后,再写到FLASH运行,还是不行。偶都在痛苦中,为什么还不行呢。后

来我想想,把UBOOT的编译地址改到0x00000000运行,就是在FLASH运行,不拷贝到SDRAM中运行。结果是可以运行一段

,串口有东西输出来,我的设置的LED也可以显示。蜂鸣器也不响。说明这个串口的设置已经对,那么系统的倍频也对

了。但UBOOT的堆栈我没有改,所以只能跑到没有使用到堆栈的代码。接着,我还需要改回到0x0C100000的基地址运行

。经过用ADS中的AXD调试,单步跟踪。又是一件痛苦的事情,发现AXD只能单步跟踪,设置断点,或者其它调试都不行

,如果在后面设置了断点,选择运行,发现到断点,还停不下来的。

呵呵。。。。。。。调试就是这样的,不是样样都顺手。既然只能单步运行,就老老实实地单步调试了,经过30多分

钟的单步运行,F8都已经按得手软。最后才跟踪到出错的地址,发现内存设置不对。主要是SDRAM的设置不对,查看了

加载地址的出错了。发现了这个加载地址出错,偶也没有更好的办法解决之前,就只好把算好的值,依次地用ldr加载

到r1-r13,共13个寄存器里。接着让AXD全速运行,程序就可运行了。原来出错就是没有把SDRAM的参数加载正确,让

我调试了三天。

通过一个多小时的调试,串口可以显示了,UBOOT的很多命令也可运行了。但还没有调通USB口,也没有网络接口。后

面的工作,就是先调通网络接口。我的开发板用的网络芯片是RTL8019AS,这个我也没有怎么看过,得好好找点资料,

了解了解这个IC,然后找一份LINUX的RTL8019的驱动程序出来看看,再决定怎么样调通RTL8019,由于RTL8019要用到

中断,就要先检查S3C44B0设置,否则到最后都没有办法调试。

通过开发板的调试,与SKYEYE相比,主要的区别是在SDRAM的频率,串口的频率,还有中断的功能,都有很大的不同。

因此,在SKYEYE上能运行的,在开发板,就不一定可以运行,就是这个原因

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客