一步步学习arm开发

来源:百度文库 编辑:神马文学网 时间:2024/04/28 07:45:23
目前因为两台电脑都已经安装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,就已经完成了任务。
【2】
下一次,接着就去看看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来编写的。
第一段代码看完了,再接着看第二段代码。
【3】
/******************************************************************
* 启动代码。
** 如果不作内存初始化,就只建立堆栈,重新定位代码到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
【4】
/**************************************************************************
* 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的工作,它有一些特别的地方,如下:
【5】
/* 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上能运行的,在开发板,就不一定可以运行,就是这个原因.