文档库 最新最全的文档下载
当前位置:文档库 › WinCE6.0 EBoot源码startup.s分析

WinCE6.0 EBoot源码startup.s分析

WinCE6.0 EBoot源码startup.s分析
WinCE6.0 EBoot源码startup.s分析

WinCE6.0学习之EBoot源码分析----startup.s

<> startup.s文件是S3C6410引导程序EBoot的原始入口处,该文件的内容是以汇编形式编写的,本人花了几天的时间对startup.s进行了详细的研究,翻阅了大量的书籍和网络博客。网络上有一些大牛在其博客中对源码已经进行了分析,但本人感觉里面的有些部分对初学者还是不够清楚,谁让人家是大牛呢,直接在一定高度上讲,O(∩_∩)O,不过仍然非常感谢这些牛人无私的奉献出那些知识。

<>本人是WinCE的入门选手,将沿着自己的菜鸟学习之路,将自己的一些学习心得在这里记录下来,如果能够帮到以后的人,不胜荣幸。如果有什么分析的不正确的地方,欢迎拍砖,大家讨论才能更加进步。

<>大家都知道startup.s是用来完成硬件的初始化的,具体工作如下:

<>1、设置CPU模式,使得可以无限制的访问内存和硬件,一般会设置为管理模式或者系统模式;

<>2、关闭所有的CPU中断;

<>3、关闭内存管理单元MMU和TLB;

<>4、关闭写缓冲和Cache,使得Write Buffer和Cache无效;

<>5、初始化内存控制器;

<>6、设置CPU的PLL,设置时钟;

<>7、创建堆栈;

<>8、设置并打开MMU进行地址映射,打开Cache;

<>9、如果需要,拷贝Eboot从Flash到RAM中;

<>10、跳转到C语言的main函数。

<>

INCLUDE kxarm.h

INCLUDE s3c6410.inc

INCLUDE image_cfg.inc

IMPORT OALClearUTLB

IMPORT OALFlushICache

IMPORT OALFlushDCache

IMPORT System_DisableVIC

IMPORT System_EnableIRQ

IMPORT System_SetSyncMode

IMPORT System_SetAsyncMode

IMPORT System_EnableICache

EXPORT StartUp

首先是函数引用声明,IMPORT伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,这些引用的标号在下面用到时会进行简要介绍,EXPORT 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。

;-------------------------------------------------------------------------------

; Definition for MMU table initialization

;-------------------------------------------------------------------------------

PT_1ST_BASE EQU (DRAM_BASE_PA_START+0x1000

0) ; 1st level Page Table Base Address (PHYBASE + 0x10000) save

room for interrupt vectors

PT_1ST_ENTRY_CNB EQU (DRAM_BASE_PA_START+0x40

E) ; Cached Area Page Table Entry (Cache/Unbuffer/RW), PA base

= 0x50000000

PT_1ST_ENTRY_NCNB EQU (DRAM_BASE_PA_START+0x40

2) ; Uncached Area Page Table Entry (Uncache/Unbuffer/RW), PA

base= 0x50000000

PTR_1ST_PTE EQU ((DRAM_BASE_PA_START>>16)/

4) ; Ptr to 1st PTE for0x50000000

BANK_SHIFT EQU (20)

此部分用来声明一些变量,在MMU初始化时使用。DRAM_BASE_PA_START 在PLATFORM\SMDK6410\SRC\INC\image_cfg.inc中定义,这个是DRAM的基地址,与硬件相关,S3C2410是0x30000000,S3C6410是0x50000000。

PT_1ST_BASE是一级页表的基地址,一般都选在DDR开始地址+偏移值(offset)处,WinCe的offset=64K(Linux也是,有资料ARM的页表基地址必须是64K对齐的,貌似在此处比较吻合,但是不知道为什么,有待进一步考证),所以此处在DRAM_BASE_START基础上加上了0x10000,正好64K。

PT_1ST_ENTRY_CNB和PT_1ST_ENTRY_NCNB用来设置页表项的对应的虚拟内存页的访问权限和缓冲读写属性,下面用到时再进一步解释。

PTR_1ST_PTE和BANK_SHIFT下面用到时,自然就明白什么用处了。

;------------------------------------------------------------------------------

;

; Macro for LED on SMDK Board (GPN[15:12])

;

; LED_ON for physical address domain

; VLED_ON for virtual address domain

;

;------------------------------------------------------------------------------

MACRO

LED_ON $data

ldr r10, =GPNPUD

ldr r11, [r10]

bic r11, r11, #0xFF000000 ; Pull-Up-Down Disab le

str r11, [r10]

ldr r10, =GPNDAT

ldr r11, [r10]

bic r11, r11, #0xF000

ldr r12, =$data

mov r12, r12, lsl #12 ; [15:12]

orr r11, r11, r12

str r11, [r10]

ldr r10, =GPNCON

ldr r11, [r10]

bic r11, r11, #0xFF000000

orr r11, r11, #0x55000000 ; GPN[15:12] Output .

str r11, [r10]

MEND

MACRO

VLED_ON $data

ldr r10, =vGPNPUD

ldr r11, [r10]

bic r11, r11, #0xFF000000 ; Pull-Up-Down Disab le

str r11, [r10]

ldr r10, =vGPNDAT

ldr r11, [r10]

bic r11, r11, #0xF000

ldr r12, =$data

mov r12, r12, lsl #12 ; [15:12]

orr r11, r11, r12

str r11, [r10]

ldr r10, =vGPNCON

ldr r11, [r10]

bic r11, r11, #0xFF000000

orr r11, r11, #0x55000000 ; GPN[15:12] Output .

str r11, [r10]

MEND

这部分是用来点亮LED灯的,在Eboot中可有可无,不过在这里也顺便分析一下。这里LED灯是接在GPN的管脚上,由GPNPUD、GPNDAT和GPNCON三个寄存器控制,具体功能参看一下手册吧,这三个寄存器的定义在

PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\INC\s3c6410.inc文件中。此处用MACRO 和MEND来定义了一段代码的宏,$data是宏的参数。

;------------------------------------------------------------------------------

;

; StartUp Entry

;

; Main entry point for CPU initialization.

;

;------------------------------------------------------------------------------

LEAF_ENTRY StartUp

b ResetHandler

b . ; HandlerUndef

b . ; HandlerSWI

b . ; HandlerPabort

b . ; HandlerDabort

b . ; HandlerReserved

b . ; HandlerIRQ

b . ; HandlerFIQ

上面是startup.s的入口部分,由LEAF_ENTRY宏进行标示,进入到程序段StartUp中,由ENTRY_END标示程序段的结束。首先就跳转到ResetHandler处执行,这也是复位以后的位置,下面的跳转语句不应该进入(不过不知道为什么还要写?)。

ResetHandler

LED_ON 0x1

这条语句就是应用上面定义的LED_ON宏,点亮LED指示灯。

WinCE6.0学习之EBoot源码分析----startup.s(二)

;------------------------------------

; Enable Instruction Cache

;------------------------------------

mov r0, #0

mcr p15, 0, r0, c7, c7, 0 ; Invalidate Entire I&D Cach e

bl System_EnableICache ; Enable I Cache

上面是进行使能指令Cache,ARM中的Cache分为数据和指令两种。首先使指令Cache 和数据Cache无效(I&D Cache),通过设置CP15协处理器的C7寄存器实现(CP15总共有15个不同的寄存器,具体定义参看ARM体系结构方面的书)。System_EnableICache是通过IMPORT导入的宏,相当于一个子函数,用来实现使能指令Cache。System_EnableICache的定义在PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\SYSTEM\s3c6410_system.s文件中,该部分比较独立,也比较容易,仅仅对CP15的C1寄存器进行设置即可,如下所示,其中R1_I定义s3c6410.inc文件中。

;-------------------------

; Enable ICache

;-------------------------

LEAF_ENTRY System_EnableICache

mrc p15, 0, r0, c1, c0, 0

orr r0, r0, #R1_I

mcr p15, 0, r0, c1, c0, 0

mov pc, lr

ENTRY_END

;------------------------------------

; Peripheral Port Setup

;------------------------------------

ldr r0, =0x70000013 ; Base Addres : 0x70000 000, Size : 256 MB (0x13)

mcr p15,0,r0,c15,c2,4

此部分代码用来设置外设端口,从6410手册可以得知,外设区域的地址范围是

0x7000000~0x7FFFFFFF,地址范围为256M大小,CP15的C15是测试配置寄存器(S3C2410手册中有,但具体每一位的含义没有找到,所以此处的设置就不是很清楚了,希望知道的可以指导一下)。

;------------------------------------

; Interrupt Disable

;------------------------------------

ldr r0, =VIC0INTENCLEAR

ldr r1, =0xFFFFFFFF;

str r1, [r0]

ldr r0, =VIC1INTENCLEAR

ldr r1, =0xFFFFFFFF;

str r1, [r0]

此部分用来禁止中断,VIC0INTENCLEAR和VIC1INTENCLEAR是两个寄存器地址,在文件PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\INC\s3c6410.inc中定义,如下:

VIC0INTENCLEAR EQU (S3C6410_BASE_REG_PA_VIC0 + 0x14)

VIC1INTENCLEAR EQU (S3C6410_BASE_REG_PA_VIC1 + 0x14)

在PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\INC\s3c6410_base_regs.inc文件中定义了VIC寄存器的基地址,如下:

S3C6410_BASE_REG_PA_VIC0 EQU (0x71200000)

S3C6410_BASE_REG_PA_VIC1 EQU (0x71300000)

;------------------------------------

; Disable WatchDog Timer

;------------------------------------

ldr r0, =WTCON

ldr r1, =0x0

str r1, [r0]

此部分用来关闭看门狗,WTCON寄存器地址定义在文件

PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\INC\s3c6410.inc中,可以查阅6410的手册,如下:

WTCON EQU (0x7e004000)

;------------------------------------

; Expand Memory Port 1 to x32

;------------------------------------

ldr r0, =MEM_SYS_CFG

ldr r1, [r0]

bic r1, r1, #0x8

0 ; ADDR_EXPAND to "0"

str r1, [r0]

此部分设置扩展存储端口1,MEM_SYS_CFG同样在文件s3c6410.inc中定义(路径省略),如下,先读出MEM_SYS_CFG寄存器的值,清除第7位为0(可以查阅6410手册,不过这部分我还没有太仔细的研究,以后补充),然后再写入寄存器中。

MEM_SYS_CFG EQU (0x7e00f120)

;------------------------------------

; Disable VIC

;------------------------------------

bl System_DisableVIC 此部分关闭向量中断控制器,System_DisableVIC的定义在文件

PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\SYSTEM\s3c6410_system.s中,通过CP15的C1寄存器进行设置,如下所示,R1_VE定义在s3c6410.inc文件中。

;--------------------

; Disable VIC

;--------------------

LEAF_ENTRY System_DisableVIC

mrc p15,0,r0,c1,c0,0

bic r0,r0,#R1_VE

mcr p15,0,r0,c1,c0,0

mov pc, lr

ENTRY_END

;------------------------------------

; Enable IRQ

;------------------------------------

bl System_EnableIRQ

此部分用来使能IRQ,System_EnableIRQ的定义在文件s3c6410_system.s(省略路径)中,如下所示,首先获取到CPSR的状态,然后修改进行修改,最后再赋值给CPSR。这里需要说明的是cpsr_cxsf,CPSR被分成了四个区域,“c”代表的是控制位[7:0],“x”

代表的扩展为[15:8],“s”代表的是状态位[23:16],“f”代表的是标志位[31:24],四个域也可以单独使用,表示只修改CPSR该域的值。

;--------------------

; Enable IRQ

;--------------------

LEAF_ENTRY System_EnableIRQ

mrs r0,cpsr

bic r0,r0,#I_Bit

msr cpsr_cxsf,r0

mov pc, lr

ENTRY_END

;------------------------------------

; Clear DRAM

;------------------------------------

[ CLEAR_DRAM_ON_EBOOT

mov r1, #0

mov r2, #0

mov r3, #0

mov r4, #0

mov r5, #0

mov r6, #0

mov r7, #0

mov r8, #0

ldr r0, =IMAGE_NK_PA_STAR

T ; Start address (Physica l 0x5010.0000)

ldr r9, =(DRAM_SIZE-IMAGE_N K_OFFSET) ; 127 MB of RAM (1MB + 127MB)

10

stmia r0!, {r1-r8}

subs r9, r9, #32

bne %B10

]

此部分用来清空DRAM空间,代码用方括号括起来,CLEAR_DRAM_ON_EBOOT宏如果为TRUE,那么下面的代码将被执行,如果为FALSE,则不执行。 CLEAR_DRAM_ON_EBOOT 宏在文件s3c6410中被定义,如下:

GBLL CLEAR_DRAM_ON_EBOOT

CLEAR_DRAM_ON_EBOOT SETL {FALSE}

上面的汇编再解释一下,GBLL伪指令用于定义一个全局的逻辑变量,而SETL伪指令用于给一个逻辑变量赋值,所以上面方括号中的代码是不执行的。

但仍然介绍一下里面的代码操作,首先给R1到R8的8个寄存器赋值为0,IMAGE_NK_PA_START 在PLATFORM\SMDK6410\SRC\INC\image_cfg.inc文件中定义,如下:

IMAGE_NK_OFFSET EQU (0x00100000)

IMAGE_NK_PA_START EQU(DRAM_BASE_PA_START+IMAGE_NK_OFFSET) DRAM_BASE_PA_START在上面的介绍中已经给出,是0x50000000,所以R0=0x50100000,是DRAM的起始地址(是否应该是0x50000000或者0x50010000),DRAM_SIZE也在image_cfg.inc 文件中定义,如下:

DRAM_SIZE EQU (0x08000000)

所以R9=128M-1M=127M,表明清空的DRAM的大小是127M,stmia指令将R1-R8的8个寄存器的值写入以R0开始的8个对应地址中,之后R9表示的剩余DRAM大小减去32字节(一个寄存器4字节,总共8个),如果R9不为0,则跳转到标号10处,继续清空DRAM。WinCE6.0学习之EBoot源码分析----startup.s(三)下面将详细叙述MMU的设置,也是本人花费时间最多的一部分内容,无论是2410、6410甚至是Cortex-A8核的ARM,MMU的设置基本都一样,所以移植时这部分可以直接搬过来,只需要更改全局内存映射表的映射关系即可。

先说说为什么在EBoot要设置MMU?其实有大牛们讨论过这个话题,在系统启动时会对页表进行重新映射,包括二级页表的设置,而在EBoot中只进行了一级页表的设置,最后也没有给出明确的答案,有的说是WinCE规定的,这里先不追究了,等以后研究了系统启动后的代码,再来讨论这个问题。OAL作为WinCE的开始需要启用虚拟内存,需要为MMU设置正确的页表进行地址映射,另外WinCE编译系统产生的二进制文件.bib使用的内存地址都是虚拟地址,这些虚拟地址是编译系统对二进制代码进行地址重定位的重要依据,所以在Eboot

中对MMU进行设置,定义内存映射表的依据是全局内存映射表,在

PLATFORM\SMDK6410\SRC\INC\oemaddrtab_cfg.inc文件中定义,内容如下:

g_oalAddressTable

; mDDR 128 MB

;DCD 0x80000000, 0x50000000, 64 ; 64 MB DRAM

[ SMDK6410_X5D

DCD 0x80000000, 0x60000000, 64 ; 64 MB DRAM

|

DCD 0x80000000, 0x50000000, 128 ; 128 MB DRAM

]

DCD 0x90000000, 0x70000000, 4 ; SROM SFR

DCD 0x00000000, 0x00000000, 0 ; end of table 它是以g_oalAddressTable宏开始的,DCD用来分配一片连续的字存储单元,第一个参数是虚拟地址值,第二个参数是对应的物理地址值,第三个参数指明分配的大小,在映射表的最后,将三个参数的值均设为0,表示是映射表的结束。下面看startup.s文件中的源码。

;------------------------------------

; Initialize MMU Table

;------------------------------------

;----------------------------

; Compute physical address of the OEMAddressTable.

20

add r11, pc, #g_oalAddressTable -(. + 8)

ldr r10, =PT_1ST_BASE ; (r1 0) = 1st level page table

上面的代码将R11赋值为内存映射表的地址,R10赋值为页表存储的地址,PT_1ST_BASE 在最开始处已经进行了定义,是页表基地址0x50010000,解释一下R11的赋值语句。ARM 处理器是流水线结构,允许指令预取,所以PC一般等于当前执行指令下面的第2条指令的地址,即PC=当前指令地址值+8字节,而“.”代表的是当前指令的地址值,PC=(. + 8),这样R11获得的是g_oalAddressTable的地址。那为什么不直接mov r11

#g_oalAddressTable进行赋值呢?确实不能这样,这样可以编译通过,但是执行不通过,只能采用和PC的相对值来换算。

add r10, r10, #0x2000 ; (r10) = ptr to 1s t PTE for "unmapped space"

这一条代码,将R10的值增加了0x2000,因为第1级MMU的入口是地址值的高14为(过滤掉低18位),g_oalAddressTable中看出,DRAM的起始虚拟地址为0x80000000,而

0x80000000>>18=0x2000,正好是0x80000000地址对应的第1级MMU入口的偏移值,也就是说R10现在存储的是0x80000000虚拟地址对应的页表的存储地址。那么为什么要右移18位?第1级页表将4GB的地址空间划分为多个1MB的段,对应的就有4096个页表项,而ARM 地址映射时,虚拟地址被分为两部分:高位+低位,高位用来表示虚拟地址对应的页表针对页表首地址的偏移值,而低位表示在页表1MB地址空间中的偏移量,页表中的一个页表项可以描述4字节的虚拟页(因为ARM是32位的,每次都是读取4字节的数据),那么1MB的空间就需要256KB个这样的页表项才可以描述(256K*4=1M),而要表示256KB的大小,需要虚拟地址的低18位,剩下的高14位就可以用来计算虚拟地址对应页表相对于页表首地址的偏移量了。

mov r0, #0x0E ; (r0) = PTE for 0: 1MB cachable bufferable

orr r0, r0, #0x400 ; set kernel r/w permis sion

上面的两条代码用来设置页表项的高速缓冲、写缓冲以及读写属性,具体的设置请参看CP15协处理器C1寄存器的功能,以后有时间会整理一篇关于CP15协处理器的说明。其实读者如果仔细看会发现,设置后R0=0x40E,和文件开始声明的PT_1ST_ENTRY_CNB变量的值是一样的,所以也可以用mov r0 PT_1ST_ENTRY_CNB来代替两条语句,但是本人觉得用两条语句更能表明设置了什么,语义更明确。

25

mov r1, r11 ; (r1) = ptr to MemoryMap array

30

ldr r2, [r1], #4 ; (r2) = virtual addr ess to map Bank at

ldr r3, [r1], #4 ; (r3) = physical ad dress to map from

ldr r4, [r1], #4 ; (r4) = num MB to map

首先将R11也就是g_oalAddressTable指向的全局内存映射表的地址赋给R1(这就是高手的代码书写习惯,不会直接操作R11),然后依次将R1指向的地址处的数据赋值给R2、R3和R4寄存器,“#4”是表示每次操作完后,R1的地址值增加4字节,指向下一个数据,其实对照oemaddrtab_cfg.inc的映射表,很容易发现,R2获得的是虚拟地址的值,R3获得的是对应物理地址的值,R4则是这段存储空间的大小。

cmp r4, #0 ; End of table?

beq %F40

上面两条代码用来判断是否到了oemaddrtab_cfg.in中映射表的结尾,上面已经介绍过,映射表的最后一条的三个参数全部都为0,标识映射表的结束。beq是一条跳转语句,表示如果相等,则跳转,%F40表明向后寻找名称为40的标号(F=After)。

WinCE6.0学习之EBoot源码分析----startup.s(四)

ldr r5, =0x1FF00000

and r2, r2, r5 ; VA needs 51 2MB, 1MB aligned.

原来R2中存储的是虚拟地址值(如0x80000000),通过上面的代码,将R2中数据的[29-21]位置为1,前面已经说过将空间划分成了多个1MB的段,那么就必须以1MB对齐,而且WinCE最多只能支持512MB的物理内存,所以虚拟地址的合法区间是

0x80000000~0x9FFFFFFF,正好是512MB的大小(这段是带缓冲的虚拟地址,相应的不带缓冲的虚拟地址是0xA0000000~0xBFFFFFFF)。R2中的数据正是虚拟地址对应的页表相对于页表首地址的偏移地址,也就是R2用来计算该虚拟地址对应的页表的存储地址,从下面的代码会看出这点。

ldr r5, =0xFFF00000

and r3, r3, r5 ; PA needs 4G B, 1MB aligned.

原来R3中存储的是物理地址的地址值,页表项的内容由两部分组成:物理地址的高12位(高12位)+缓冲读写属性(低12位),这里的代码正是保留了物理地址的高12位,清空低20位,正是为了形成页表项内容进行准备。

add r2, r10, r2, LSR #18

add r0, r0, r3 ; (r0) = PTE f or next physical page

这两条代码正是在前面计算得基础上,用R2中虚拟地址的高14位的偏移量+R10中存储的页表的基地址,从而将该虚拟地址对应的页表要存储的地址计算出来,并赋值给R2。而R0存储的数据是关于页表对应的虚拟页的缓冲读写属性,再加上R3中存储的物理地址的高12位,从而形成了相应页表项的内容,并赋值给R0。

35

str r0, [r2], #4

add r0, r0, #0x00100000 ; (r0) = PTE for next p hysical page

sub r4, r4, #1 ; Decrement number of MB left

cmp r4, #0

bne %B35 ; Map next MB

上面的第一条代码,通过str指令将R0中存储的页表项内容,写入R2指向的地址中,同时R2指向下一个地址。接下来将页表项的内容增加1MB的大小,从

oemaddrtab_cfg.inc的映射表可以看出,最后一个参数表示的分配存储空间的大小,是以MB为单位,所以R0指向的是下一个物理地址页(单位1M)的页表项的内容。之后将R4中空间大小的值减1,表示剩余要构造的页表数目,如果不为0,则跳转到标号35处,继续构造页表,知道这一个页表条目完成为止(或者说这一段存储空间全部映射完成为止)。

bic r0, r0, #0xF0000000 ; Clear Section Base Add ress Field

bic r0, r0, #0x0FF00000 ; Clear Section Base Add ress Field

b %B30 ; Get next element

这部分代码简单的说就是清空R0中的高12位,即清空上一个物理地址的高12位,因为要用来存储下一个物理地址的高12位,同时保留低20位中的缓冲读写属性,完成后跳转到标号30处,继续读取全局内存映射表的内容,构造地址映射的页表。其中%B30表示向前寻找标号为30的地址(B=Before)。

40

tst r0, #8

bic r0, r0, #0x0C ; clear cachable & bufferable bits in PTE

add r10, r10, #0x0800 ; (r10) = ptr to 1s t PTE for "unmapped uncached space"

bne %B25 ; go setup PTEs fo r uncached space

sub r10, r10, #0x3000 ; (r10) = restore a ddress of 1st level page table

一般WinCE会根据全局内存映射表进行两份虚实映射,一份带缓冲的虚拟地址和不带缓冲的虚拟地址。到这里的时候,说明0x80000000~0x9FFFFFFF这段512M的虚拟地址的页表已经构造完成了,这段地址是带缓冲的,下面需要对不带缓冲的虚拟地址构造页表。

第一条tst语句表示判断R0的位[7]的值是否为1,从而影响bne跳转语句的结果。R0的第7位在页表项内容中表示的是缓冲的属性,如果为1,表明还没有开始构造不带缓冲的虚拟地址对应的页表,如果不为0,说明不带缓冲的虚拟地址对应的页表也已经构造完成了(是不是有点晕啊,呵呵,因为当第二次循环运行这条语句的时候,已经就完成了不带缓冲的页表的构造,不行你自己走一遍代码就知道了)。第二条bic的语句很简单,就是为了将高速缓冲和写缓冲的属性值去掉。第三条语句有点意思,其实就是为了将R10指向存储不带缓冲的虚拟地址对应的页表的存储地址(0xA0000000),但是采用的方法是计算偏移量,当前

R10中存储的是带缓冲的虚拟地址对应的页表的存储地址(0x80000000),而0xA0000000于0x8000000相差0x20000000,那么把0x20000000>>18得到的结果就是0x0800,所以在

R10的基础加上0x0800就是页表应该存储的地址(其实就是依据虚拟地址高14位代表的是相对于页表首地址的偏移量,所以也可以直接用0xA0000000>>18位来计算页表的存储地址,不信你试试,结果是一样的)。第四条语句就简单了,向前跳转到标号25处,像缓冲虚拟地址的页表构造的方法对不带缓冲虚拟地址的页表进行构造。第五条语句为下面的代码做准备,将R10指向的地址重新指向页表基地址,即PT_1ST_BASE,网上有人说这句有问题,其实没有问题,不过这一句确实没什么用处,因为下面对R10进行了重新赋值,即将

PT_1ST_BASE重新赋给了R10。不过为了理解明白,本人还是解释一下这句是怎么计算出来的?首先在最开始R10增加了0x2000,上这里有两次增加了0x0800(为什么是两次呢?好好看代码,上面的第三条语句会被执行两次),加起来正好是0x3000,所以减去这个值得到的正是页表的基地址。

;----------------------------------------------

; Setup mmu to map (VA == 0) to (PA == 0x30000000).

; cached area

ldr r0, =PT_1ST_BASE ; PTE entry f or VA = 0

ldr r1, =PT_1ST_ENTRY_CNB ; Cache/Unbuffer/RW str r1, [r0]

上面是将0x0的带缓冲的虚拟地址映射到0x30000000的物理地址处, 0x0的虚拟地址对应的页表的存储地址就在PT_1ST_BASE处(0x0>>18得到的是偏移量,说明就在页表基地址处)。第二条语句是设置页表项的内容:物理地址的高12位+缓冲读写属性。第三条语句str将页表项的内容写入到对应的页表存储地址处。

; uncached area.

add r0, r0, #0x0800 ; PTE entry for VA = 0x02000000

ldr r1, =PT_1ST_ENTRY_NCNB ; Uncache/Unbuffer /RW

str r1, [r0]

这部分用来映射不带缓冲的虚拟地址0x20000000(后面的注释不对)到物理地址

0x30000000,上之前分析的一样,采用计算偏移量的方法,第二条和第三条语句和上一部分的一样,这里不再赘述。

WinCE6.0学习之EBoot源码分析----startup.s(五)

; Comment:

; The following loop is to direct map RAM VA == PA. i.e.

; VA == 0x50XXXXXX => PA == 0x50XXXXXX for S3C6410

; Fill in 8 entries to have a direct mapping for DRAM

ldr r10, =PT_1ST_BASE ; Restore address of 1st level page table

ldr r0, =DRAM_BASE_PA_START

这部分开始建立虚拟地址到相等的物理地址的映射,因为是S3C6410,所以是0x50000000,如果是2410,则为0x30000000。首先将R10指向页表首地址(验证了前面那句减去0x30000000语句是没用的),R0存储的是0x50000000,既可以当做物理地址操作,也可以当做虚拟地址操作(因为是相等的地址映射)。

add r10, r10, #PTR_1ST_PTE ; (r10) = ptr to 1st PTE for 0x50000000

这条语句用来计算虚拟地址0x50000000对应的页表的存储地址,并赋值给R10。

PTR_1ST_PTE在文件最开始处已经进行了定义,其实定义相当于将0x50000000>>18,用高14位计算相对于页表基地址的偏移量。

add r0, r0, #0x1E ; 1MB cachable b ufferable

orr r0, r0, #0x400 ; set kernel r/w permis sion

mov r1, #0

; mov r3, #64 ; 64MB DRA M

mov r3, #128 ; 128MB DRAM 前两条语句用来设置页表对应页的缓冲读写属性,第四条语句,将DRAM的大小赋值给R3寄存器,该值的大小选择就是根据全局内存映射表中的DRAM设置的大小。该值不会大于512,因为WinCE最大支持512MB的物理内存。

45

mov r2, r1 ; (r2) = virtual a ddress to map Bank at

cmp r2, #0x20000000:SHR:BANK_SHIFT

add r2, r10, r2, LSL #BANK_SHIFT-18

第一条和第二条语句要结合起来看,R2开始是0,cmp的比较就是为了保证DRAM的大小不超过512M,注释中的Bank大小就是1MB,BANK_SHIFT在最开始定义,大小为20,而0x20000000>>20=512。cmp指令影响的状态位,在后面的代码判断时使用。第三条语句其实还是计算对应的页表的存储地址的,LSL #BANK_SHIFT-18结合前面一条语句,相当于将

R2>>18,这下就回到了前面介绍的,将高14位作为页表基地址的偏移量。

strlo r0, [r2]

add r0, r0, #0x00100000 ; (r0) = PTE for next p hysical page

subs r3, r3, #1

add r1, r1, #1

bgt %B45

第一条语句是将R0中的页表项写入R2指向的地址,但是没有str指令,而是strlo 指令,因为这里有不同的地方,strlo实现的效果是,如果还没有超过512MB,那么像str

指令一样,如果超过512MB,则不会往地址中写数据,忽略此次操作。个人认为,这样写的用意在于,当以后DRAM大小变化的时候,只需要该一个值,其他部分不变,而且如果设置的大于512MB,可以实现保护。第二条到第四条语句和前面介绍的部分类似。最后一条语句就是根据上面cmp的比较结果进行跳转的,bgt表示如果大于则跳转。

ldr r10, =PT_1ST_BASE ; (r10) = restore address of 1st lev el page table

该语句重新将R10指向页表基地址,因为下面会用到它。

; The page tables and exception vectors are setup.

; Initialize the MMU and turn it on.

mov r1, #1

mcr p15, 0, r1, c3, c0, 0 ; setup access to domai n 0

mcr p15, 0, r10, c2, c0, 0

前面设置好页表以后,这里开始初始化MMU,并打开MMU。上面代码设置的是域0的访问控制属性,同时将页表的基地址存储到CP15协处理器的C2寄存器中。

mcr p15, 0, r0, c8, c7, 0 ; flush I+D TLBs

这条代码用来无效整个指令Cache、数据Cache和TLB,相当于清空所有MMU的相关空间。

mrc p15, 0, r1, c1, c0, 0

orr r1, r1, #0x0071 ; Enable MMU

orr r1, r1, #0x0004 ; Enable the Data Cac he

第一条语句读取CP15的C1寄存器的数据到R1中,下面两条用来设置MMU的标志,使能MMU,使能数据Cache。

ldr r0, =VirtualStart

cmp r0, #0 ; make sure no s tall on "mov pc,r0" below

mcr p15, 0, r1, c1, c0, 0

mov pc, r0 ; & jump to new virtual address

nop

首先R0存储标号VirtualStart的地址,它是一个虚拟地址,因为它出现在启用虚拟地址之后。确保标号VirtualStart的虚拟地址不为0,因为下面有mov pc r0,语句,不可以给pc赋值0。第三天语句将R1中的数据写入CP15的C1寄存器中,设置MMU标志,并启用MMU,最后通过直接给PC赋值,跳转到VirtualStart标号处。

WinCE6.0学习之EBoot源码分析----startup.s(六)

;-----------------------------------------------

; MMU Enabled and Virtual Address is Valid from here

;-----------------------------------------------

VirtualStart

;--------------------------------------------------

; Initialize Stack

; Stack size and location information is in "image_cfg.inc"

;--------------------------------------------------

mrs r0, cpsr

bic r0, r0, #Mode_MASK

orr r1, r0, #Mode_IRQ | NOINT

msr cpsr_cxsf, r1 ; IRQMode ldr sp, =IRQStack_VA ; IRQStack

跳转到VirtualStart标号处后,先初始化堆栈。第一条语句mrs获取CPSR状态寄存器的值,并存储到R0中。Mode_MASK、Mode_IRQ、NOINT都在文件

PLATFORM\COMMON\SRC\SOC\S3C6410_SEC_V1\OAL\INC\s3c6410.inc中定义。第二条语句用来清空表示处理器模式的低5位,第三条语句设置处理器模式为IRQ模式,同时禁止IRQ 和FRQ中断。第四条语句执行前面的设置。IRQStack_VA在文件image_cfg.inc中定义,第五条语句将IRQ堆栈的虚拟地址保存在SP。

bic r0, r0, #Mode_MASK | NOINT

orr r1, r0, #Mode_SVC

msr cpsr_cxsf, r1 ; SVCMode ldr sp, =SVCStack_VA ; SVCStack

Xmodem协议详解以及源代码剖析

研究 Xmodem 协议必看的 11个问题 Xmodem 协议作为串口数据传输主要的方式之一,恐怕只有做过 bootloader 的才有机会接触一下, 网上有关该协议的内容要么是英语要么讲解不详细。笔者以前写 bootloader 时研究过 1k-Xmodem ,参考了不少相关资料。这里和大家交流一下我对 Xmodem 的理解,多多指教! 1. Xmodem 协议是什么? XMODEM协议是一种串口通信中广泛用到的异步文件传输协议。分为标准Xmodem 和 1k-Xmodem 两种,前者以 128字节块的形式传输数据,后者字节块为 1k 即 1024字节,并且每个块都使用一个校验和过程来进行错误检测。在校验过程中如果接收方关于一个块的校验和与它在发送方的校验和相同时,接收方就向发送方发送一个确认字节 (ACK。由于 Xmodem 需要对每个块都进行认可, 这将导致性能有所下降, 特别是延时比较长的场合, 这种协议显得效率更低。 除了 Xmodem ,还有 Ymodem , Zmodem 协议。他们的协议内容和 Xmodem 类似,不同的是 Ymodem 允许批处理文件传输,效率更高; Zmodem 则是改进的了Xmodem ,它只需要对损坏的块进行重发,其它正确的块不需要发送确认字节。减少了通信量。 2. Xmodem 协议相关控制字符 SOH 0x01 STX 0x02 EOT 0x04 ACK 0x06 NAK 0x15

CAN 0x18 CTRLZ 0x1A 3.标准 Xmodem 协议(每个数据包含有 128字节数据帧格式 _______________________________________________________________ | SOH | 信息包序号 | 信息包序号的补码 | 数据区段 | 校验和 | |_____|____________|___________________|__________|____________| 4. 1k-Xmodem (每个数据包含有 1024字节数据帧格式 _______________________________________________________________ | STX | 信息包序号 | 信息包序号的补码 | 数据区段 | 校验和 | |_____|____________|___________________|__________|____________| 5.数据包说明 对于标准 Xmodem 协议来说,如果传送的文件不是 128的整数倍,那么最后一个数据包的有效内容肯定小于帧长,不足的部分需要用 CTRL- Z(0x1A来填充。这里可能有人会问,如果我传送的是 bootloader 工程生成的 .bin 文件, mcu 收到后遇到0x1A 字符会怎么处理?其实如果传送的是文本文件,那么接收方对于接收的内容是很容易识别的,因为 CTRL-Z 不是前 128个 ascii 码, 不是通用可见字符, 如果是二进制文件, mcu 其实也不会把它当作代码来执行。哪怕是 excel 文件等,由于其内部会有些结构表示各个字段长度等,所以不会读取多余的填充字符。否则 Xmodem太弱了。对于 1k-Xmodem ,同上理。 6.如何启动传输?

LWIP协议栈的分析和设计

---《计算机网络与控制》论文 LWIP协议栈的分析

摘要 近些年来,随着互联网和通讯技术的迅猛发展,除了计算机之外,大量的嵌入式设备也需求接入网络。目前,互联网中使用的通讯协议基本是TCP/IP协议族,可运行于不同的网络上,本文研究的就是嵌入式TCP/IP协议栈LWIP。文章首先分析了LWIP的整体结构和协议栈的实现,再介绍协议栈的内存管理,最后讲解协议栈应用程序接口。 关键词: 嵌入式系统;协议;LWIP;以太网 Abstract With the rapid development of internet and communication technology, Not only computers but also embeded equipments are need to connect networks. At present, the basic communication protocol using in internet is TCP/IP, it can run in different network. This paper analyses the Light-Weight TCP/IP. The process model of a protocol implementation and processing of every layer are described first, and then gives the detailed management of Buffer and memory. At last, a reference lwIP API is given. Key words: Embedded System, Protocol, Light weight TCP/IP,Ethernet 引言

lwip各层协议栈详解

竭诚为您提供优质文档/双击可除lwip各层协议栈详解 篇一:lwip协议栈源码分析 lwip源码分析 -----caoxw 1lwip的结构 lwip(lightweightinternetprotocol)的主要模块包括:配置模块、初始化模块、netif模块、mem(memp)模块、netarp模块、ip模块、udp模块、icmp模块、igmp模块、dhcp模块、tcp模块、snmp模块等。下面主要对我们需要关心的协议处理进行说明和梳理。配置模块: 配置模块通过各种宏定义的方式对系统、子模块进行了配置。比如,通过宏,配置了mem管理模块的参数。该配置模块还通过宏,配置了协议栈所支持的协议簇,通过宏定制的方式,决定了支持那些协议。主要的文件是opt.h。 初始化模块: 初始化模块入口的文件为tcpip.c,其初始化入口函数为: voidtcpip_init(void(*initfunc)(void*),void*arg)

该入口通过调用lwip_init()函数,初始化了所有的子模块,并启动了协议栈管理进程。同时,该函数还带有回调钩子及其参数。可以在需要的地方进行调用。 协议栈数据分发管理进程负责了输入报文的处理、超时处理、api函数以及回调的处理,原型如下: staticvoidtcpip_thread(void*arg) netif模块: netif模块为协议栈与底层驱动的接口模块,其将底层的一个网口设备描述成协议栈的一个接口设备(netinterface)。该模块的主要文件为netif.c。其通过链表的方式描述了系统中的所有网口设备。 netif的数据结构描述了网口的参数,包括ip地址、mac 地址、link状态、网口号、收发函数等等参数。一个网口设备的数据收发主要通过该结构进行。 mem(memp)模块: mem模块同一管理了协议栈使用的内容缓冲区,并管理pbuf结构以及报文的字段处理。主要的文件包括mem.c、memp.c、pbuf.c。 netarp模块: netarp模块是处理arp协议的模块,主要源文件为etharp.c。其主要入口函数为: err_tethernet_input(structpbuf*p,structnetif*netif)

LwIP协议栈源码详解

LwIP协议栈源码详解 ——TCP/IP协议的实现 Created by.. 老衲五木 at.. UESTC Contact me.. for_rest@https://www.wendangku.net/doc/1119152959.html, 540535649@https://www.wendangku.net/doc/1119152959.html,

前言 最近一个项目用到LwIP,恰好看到网上讨论的人比较多,所以有了写这篇学习笔记的冲动,一是为了打发点发呆的时间,二是为了吹过的那些NB。往往决定做一件事是简单的,而坚持做完这件事却是漫长曲折的,但终究还是写完了,时间开销大概为四个月,内存开销无法估计。。 这篇文章覆盖了LwIP协议大部分的内容,但是并不全面。它主要讲解了LwIP协议最重要也是最常被用到的部分,包括内存管理,底层网络接口管理,ARP层,IP层,TCP层,API 层等,这些部分是LwIP的典型应用中经常涉及到的。而LwIP协议的其他部分,包括UDP,DHCP,DNS,IGMP,SNMP,PPP等不具有使用共性的部分,这篇文档暂时未涉及。 原来文章是发在空间中的,每节每节依次更新,后来又改发为博客,再后来就干脆懒得发了。现在终于搞定,于是将所有文章汇总。绞尽脑汁的想写一段空前绝后,人见人爱的序言,但越写越觉得像是猫儿抓的一样。就这样,PS:由于本人文笔有限,情商又低,下里巴人一枚,所以文中的很多语句可能让您很纠结,您可以通过邮箱与我联系。共同探讨才是进步的关键。 最后,欢迎读者以任何方式使用与转载,但请保留作者相关信息,酱紫!码字。。。世界上最痛苦的事情莫过于此。。。 ——老衲五木

目录 1 移植综述------------------------------------------------------------------------------------------------------4 2 动态内存管理------------------------------------------------------------------------------------------------6 3 数据包pbuf--------------------------------------------------------------------------------------------------9 4 pbuf释放---------------------------------------------------------------------------------------------------13 5 网络接口结构-----------------------------------------------------------------------------------------------16 6 以太网数据接收--------------------------------------------------------------------------------------------20 7 ARP表-----------------------------------------------------------------------------------------------------23 8 ARP表查询-----------------------------------------------------------------------------------------------26 9 ARP层流程-----------------------------------------------------------------------------------------------28 10 IP层输入-------------------------------------------------------------------------------------------------31 11 IP分片重装1--------------------------------------------------------------------------------------------34 12 IP分片重装2--------------------------------------------------------------------------------------------37 13 ICMP处理-----------------------------------------------------------------------------------------------40 14 TCP建立与断开----------------------------------------------------------------------------------------43 15 TCP状态转换-------------------------------------------------------------------------------------------46 16 TCP控制块----------------------------------------------------------------------------------------------49 17 TCP建立流程-------------------------------------------------------------------------------------------53 18 TCP状态机----------------------------------------------------------------------------------------------56 19 TCP输入输出函数1-----------------------------------------------------------------------------------60 20 TCP输入输出函数2-----------------------------------------------------------------------------------63 21 TCP滑动窗口-------------------------------------------------------------------------------------------66 22 TCP超时与重传----------------------------------------------------------------------------------------69 23 TCP慢启动与拥塞避免-------------------------------------------------------------------------------73 24 TCP快速恢复重传和Nagle算法-------------------------------------------------------------------76 25 TCP坚持与保活定时器-------------------------------------------------------------------------------80 26 TCP定时器----------------------------------------------------------------------------------------------84 27 TCP终结与小结----------------------------------------------------------------------------------------88 28 API实现及相关数据结构-----------------------------------------------------------------------------91 29 API消息机制--------------------------------------------------------------------------------------------94 30 API函数及编程实例-----------------------------------------------------------------------------------97

lwip协议栈源码分析

LWIP源码分析 ----- caoxw 1 LWIP的结构 LWIP(Light weight internet protocol)的主要模块包括:配置模块、初始化模块、NetIf 模块、mem(memp)模块、netarp模块、ip模块、udp模块、icmp 模块、igmp模块、dhcp 模块、tcp模块、snmp模块等。下面主要对我们需要关心的协议处理进行说明和梳理。 配置模块: 配置模块通过各种宏定义的方式对系统、子模块进行了配置。比如,通过宏,配置了mem管理模块的参数。该配置模块还通过宏,配置了协议栈所支持的协议簇,通过宏定制的方式,决定了支持那些协议。主要的文件是opt.h。 初始化模块: 初始化模块入口的文件为tcpip.c,其初始化入口函数为: void tcpip_init(void (* initfunc)(void *), void *arg) 该入口通过调用lwip_init()函数,初始化了所有的子模块,并启动了协议栈管理进程。同时,该函数还带有回调钩子及其参数。可以在需要的地方进行调用。 协议栈数据分发管理进程负责了输入报文的处理、超时处理、API函数以及回调的处理,原型如下: static void tcpip_thread(void *arg) NetIf模块: Netif模块为协议栈与底层驱动的接口模块,其将底层的一个网口设备描述成协议栈的一个接口设备(net interface)。该模块的主要文件为netif.c。其通过链表的方式描述了系统中的所有网口设备。 Netif的数据结构描述了网口的参数,包括IP地址、MAC地址、link状态、网口号、收发函数等等参数。一个网口设备的数据收发主要通过该结构进行。 Mem(memp)模块: Mem模块同一管理了协议栈使用的内容缓冲区,并管理pbuf结构以及报文的字段处理。主要的文件包括mem.c、memp.c、pbuf.c。 netarp模块: netarp模块是处理arp协议的模块,主要源文件为etharp.c。其主要入口函数为: err_t ethernet_input(struct pbuf *p, struct netif *netif) 该入口函数通过判断输入报文p的协议类型来决定是按照arp协议进行处理还是将该报文提交到IP协议。如果报文是arp报文,该接口则调用etharp_arp_input,进行arp请求处理。 如果是ip报文,该接口就调用etharp_ip_input进行arp更新,并调用ip_input接口,将报文提交给ip层。 在该模块中,创建了设备的地址映射arp表,并提供地址映射关系查询接口。同时还提供了arp报文的发送接口。如下:

LwIP协议栈开发嵌入式网络的三种方法分析

LwIP协议栈开发嵌入式网络的三种方法分析 摘要轻量级的TCP/IP协议栈LwIP,提供了三种应用程序设计方法,且很容易被移植到多任务的操作系统中。本文结合μC/OS-II这一实时操作系统,以建立TCP服务器端通信为例,分析三种方法以及之间的关系,着重介绍基于raw API的应用程序设计。最后在ST公司STM32F107微处理器平台上验证,并给出了测试结果。 关键词LwIP协议栈;μC/OS-II;嵌入式网络;STM32F107; 随着嵌入式系统功能的多样化以及网络在各个领域的中的广泛应用,具备网络功能的嵌入式设备拥有更高的使用价值和更强的通用性。然而大部分嵌入式设备使用经济型处理器,受内存和速度限制,资源有限,不需要也不可能完整实现所有的TCP/IP协议,有时只需要满足实际需求就行。LwIP是由瑞典计算机科学研究院开发的轻量型TCP/IP协议栈,其特点是保持了以太网的基本功能,通过优化减少了对存储资源的占用。LwIP是免费、开源的,任何人可以使用,能够在裸机的环境下运行,当然设计的时候也考虑了将来的移植问题,可以很容易移植到多任务操作系统中。本文介绍了以ARM微处理器STM32F107和PHY接口DP83848为平台,构建的嵌入式系统中,采用LwIP和嵌入式操作系统μC/OS-II,使用协议栈提供的三种应用程序接口,实现嵌入式设备的网络通信功能。 1LwIP和μC/OS-II介绍 1.1 LwIP协议栈 LwIP协议是瑞士计算机科学院的Adam Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。LwIP含义是light weight(轻型)IP协议,在实现时保持了TCP协议的主要功能基础上减少对RAM的占用,一般它只需要几十K的RAM和40K左右的ROM 就可以运行,这使LwIP协议栈很适合在低端嵌入式系统中使用。 LwIP协议栈的设计才用分层结构的思想,每一个协议都作为一个模块来实现,提供一些与其它协议的接口函数。所有的TCP/IP协议栈都在一个进程当中,这样TCP/IP协议栈就和操作系统内核分开了。而应用程序既可以是单独的进程也可以驻留在TCP/IP进程中,它们之间利用ICP机制进行通讯。如果应用程序是单独的线程可以通过操作系统的邮箱、消息队列等,与协议栈进程通讯。如果应用程序驻留在协议栈进程中,则应用程序可以通过内部回调函数和协议栈进程通讯。 1.2 μC/OS-II实时操作系统 μC/OS-II是一个源码公开、可移植、可固化、可裁剪及占先式的实时多任务操作系统,是专门为嵌入式应用设计的实时操作系统内核,已广泛的应用在各种嵌入式系统中。 μC/OS-II是多任务系统,内核负责管理各个任务,每个任务都有其优先级,μC/OS-II 最多可以管理64个任务,其每个任务都拥有自己独立的堆栈。μC/OS-II提供了非常丰富的系统服务功能,比如信号量、消息邮箱、消息队列、事件标志、内存管理和时间管理等,这些功能可以帮助用户实现非常复杂的应用。 1.3 LwIP协议栈移植到μC/OS-II LwIP协议栈在设计的时候就考虑到了将来的移植问题,因此把所有与硬件、操作系统、编译器有关的部分都全部独立起来,形成了一个操作系统模拟层。操作系统模拟层用进程间的信号量、邮箱机制处理通信问题,而μC/OS-II是一个基于任务调度的嵌入式实时操作系

相关文档