(1)GBLL 伪指令用于定义一个全局的逻辑变量,并初始化为{False}。
GBLL BOOTLOADER
BOOTLOADER SETL {TRUE}
(2)GET(或 INCLUDE)
GET 伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。可以使用 INCLUDE 代替 GET。
INCLUDE ..\\..\\kernel\\oal\\startup.s
(3)IMPORT 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。
IMPORT BootloaderMain
IMPORT MMUSetup
(4)BL 带返回的跳转指令
(5)BEQ表示“相等则跳转”,即当CPSR中的Z标志置位时发生跳转
B Label ;程序无条件跳转到标号Label处执行
CMP R1,#0 ;当CPSR寄存器中的Z条件码置位时,程序跳转到标号Label处执行
BEQ Label
(6)LDR 指令的格式为:
LDR{条件} 目的寄存器,<存储器地址>
LDR 指令用于从存储器中将一个 32 位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取 32 位的字数据到通用寄存器,然后对数据进行处理。当程序计数器 PC 作为目的寄存器时,指令从存储器中读取的字数据被当作目的地址,从而可以实现程序流程的跳转。
指令示例:
LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。
LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR R0,[R1,R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1
LDR R0,[R1,#8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址R1+8写入R1。
LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
(7)STR 指令的格式为:
STR{条件} 源寄存器,<存储器地址>
STR 指令用于从源寄存器中将一个 32 位的字数据传送到存储器中。与LDR对应
(8)采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多 16 个通用寄存器的值。以下指令:
LDMIA R0,{R1,R2,R3,R4} ;R1←[R0]
;R2←[R0+4]
;R3←[R0+8]
;R4←[R0+12]
该指令的后缀 IA表示在每次执行完加载/存储操作后,R0 按字长度增加,因此,指令可将连续存储单元的值传送到 R1~R4。
(9)SBC 指令的格式为:
SBC{条件}{S} 目的寄存器,操作数 1,操作数 2
SBC指令用于把操作数1减去操作数 2,再减去 CPSR 中的C 条件标志位的反码,并将结果存放到目的寄存器中。操作数1应是一个寄存器,操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令使用进位标志 来表示借位,这样就可以做大于 32 位的减法。注意不要忘记设置 S后缀来更改进位标志。该指令可用于有符号数或无符号数的减法运算。
指令示例:
SUBS R0,R1,R2 ; R0 = R1 - R2 - !C,并根据结果设置CPSR的进位标志位
(10)BX 带状态切换的跳转指令
(11)MCR 指令的格式为:
MCR{条件} 协处理器编码,协处理器操作码 1,源寄存器,目的寄存器 1,目的寄存器 2,协处理器操作码 2
MCR 指令用于将 ARM 处理器寄存器中的数据传送到协处理器寄存器中,若协处理器不能成功完成操作,则产生未定义指令异常。其中协处理器操作码1和协处理器操作码2为协处理器将 要执行的操作,源寄存器为 ARM 处理器的寄存器,目的寄存器1和目的寄存器2均为协处理器的寄存器。
指令示例:
MCR P3,3,R0,C4,C5,6 ;该指令将ARM处理器寄存器R0中的数据传送到协处理器P3的寄存器C4和C5中。
(12)CMP 指令的格式为:
CMP{条件} 操作数 1,操作数 2
CMP 指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,同时更新 CPSR 中条件标志位的值。该指令进行一次减法运算,但不存储结果,只更改条件标志位。标志位表示的是操作数 1 与操作数 2 的关系(大、小、相等),例如,当操作数 1 大于操作操作数 2,则此后的有 GT 后缀的指令将可以执行。
指令示例:
CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位
CMP R1,#100 ;将寄存器R1的值与立即数100相减,并根据结果设置CPSR的标志位
(13)批量数据加载/存储指令LDM(或 STM)指令的格式为:
LDM(或 STM){条件}{类型} 基址寄存器{!},寄存器列表{∧}
LDM(或 STM)指令用于从由基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据,该指令的常见用途是将多个寄存器的内容入栈(SDM)或出栈(LDM)。其中,{类型}为以下几种情况:
IA 每次传送后地址加 1;
IB 每次传送前地址加 1;
DA 每次传送后地址减 1;
DB 每次传送前地址减 1;
FD 满递减堆栈;
ED 空递减堆栈;
FA 满递增堆栈;
EA 空递增堆栈;
{!}为可选后缀,若选用该后缀,则当数据传送完毕之后,将最后的地址写入基址寄存器,否则基址寄存器的内容不改变。
STMFD R13!,{R0,R4-R12,LR} ;将寄存器列表中的寄存器(R0,R4 到R12,LR)存入堆栈
LDMFD R13!,{R0,R4-R12,PC} ;将堆栈内容恢复到寄存器(R0,R4到R12,LR)
(14)ORR 指令的格式为:
ORR{条件}{S} 目的寄存器,操作数 1,操作数 2
ORR 指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数 1
应是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数 1 的某些位。
指令示例:
ORR R0,R0,#3 ; 该指令设置R0的0、1位,其余位保持不变。
(15)BIC 指令的格式为:
BIC{条件}{S} 目的寄存器,操作数 1,操作数 2
BIC指令用于清除操作数1 的某些位,并把结果放置到目的寄存器中。操作数 1 应是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即数。操作数 2 为 32 位的掩码,如果在掩码中设置了某一位,则清除这一位。未设置的掩码位保持不变。
指令示例:
BIC R0,R0,#%1011 ; 该指令清除 R0 中的位 0、1、和 3,其余的位保持不变。
(16)
ADR(小范围的地址读取伪指令)
ADRL(中等范围的地址读取伪指令)
LDR(大范围的地址读取伪指令)
ldr r0, =0xFFFFC000
用于将基于PC的地址或基于寄存器的地址读取到寄存器中。
///伪指令通过汇编编译器替换成对应的ARM/Thumb 指令。
以下指令的疑问:
ldr r2, =(EbootImageSize/16) // 加小括号代表什么?-----地址空间里存的数据
Showing posts with label hardware. Show all posts
Showing posts with label hardware. Show all posts
2009-12-10
常用ARM汇编指令(quote)
ARM指令集可以分为六大类,分别为数据处理指令、Load/Store指令、跳转指令、程序状态寄存器处理指令、协处理器指令和异常产生指令。
ARM指令使用的基本格式如下:
〈opcode〉{〈cond〉}{S} 〈Rd〉,〈Rn〉{,〈operand2〉}
opcode 操作码;指令助记符,如LDR、STR等。
cond 可选的条件码;执行条件,如EQ、NE等。
S 可选后缀;若指定“S”,则根据指令执行结果更新CPSR中的条件码。
Rd 目标寄存器。
Rn 存放第1操作数的寄存器。
operand2 第2个操作数
arm的寻址方式如下:
立即寻址
寄存器寻址
寄存器间接寻址
基址加偏址寻址
堆栈寻址
块拷贝寻址
相对寻址
这里不作详细描述,可以查阅相关文档。
数据处理指令
Load/Store指令
程序状态寄存器与通用寄存器之间的传送指令
转移指令
异常中断指令
协处理器指令
在S3C2410、S3C2440的数据手册中对各种汇编指令有详细的描述;这里只对较常见的作写介绍。
1、相对跳转指令:b、bl
这两条指令的不同之处在于bl指令除了跳转之外,还将返回地址(bl的下一条指令的地址)保存在lr寄存器中。
这两条指令的可跳转范围是当前指令前后32M。
2、数据传送指令mov,地址读取伪指令ldr
mov指令可以把一个寄存器的值赋给另外一个寄存器,或者把一个常数赋给寄存器。
mov传送的常数必须能用立即数来表示。当不能用立即数表示时,可以用ldr命令来赋值。
ldr是伪命令,不是真实存在的指令,编译器会把它扩展成真正的指令;如果该常数能用“立即数”来表示,则使用mov指令,否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
3、内存访问指令 ldr、str、ldm、stm
ldr既可以指低至读取伪指令,也可以是内存访问指令。当他的第二个参数前面有'='时标伪指令,否则表内存访问指令。
ldr指令从内存中读取数据到寄存器,str指令把寄存器的指存储到内存中,他们的操作数都是32位的。
多寄存器传送指令可以用一条指令将16个可见寄存器(R0~R15)的任意子集合(或全部)存储到存储器或从存储器中读取数据到该寄存器集合中。与单寄存器存取指令相比,多寄存器数据存取可用的寻址模式更加有限。多寄存器存取指令的汇编格式如下:
LDM/STM{<cond>}<add mode> Rn{!}, <registers>
4、加减指令 add、sub
5、程序状态寄存器的访问指令msr,mrs
ARM指令中有两条指令,用于在状态寄存器和通用寄存器之间传送数据。修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。 这两条指令分别是:
状态寄存器到通用寄存器的传送指令(MRS)
通用寄存器到状态寄存器的传送指令(MSR)
其汇编格式如下:
MRS{<cond>} Rd,CPSR|SPSR
其汇编格式如下:
MSR{<cond>} CPSR_f | SPSR_f,#<32-bit immediate>
MSR{<cond>} CPSR_<field> | SPSR_<field>,Rm
6、异常中断指令
异常中断指令可以分为一下两种:
软件中断指令(SWI)
断点指令(BKPT—仅用于v5T体系)
软件中断指令SWI用于产生SWI异常中断,用来实现在用户模式下对操作系统中特权模式的程序的调用;断点中断指令BKPT主要用于产生软件断点,供调试程序用。
7、其他伪指令
'.extern' 定义一个外部符号(可以是变量也可以是函数),上面的代码表示表文本文件中引用的main是一个外部函数。
'.text'表示下面的语句都属于代码段
'.global'将本文件中的某个程序标号定义为全局的,如‘_start’就是个全局函数
ARM指令使用的基本格式如下:
〈opcode〉{〈cond〉}{S} 〈Rd〉,〈Rn〉{,〈operand2〉}
opcode 操作码;指令助记符,如LDR、STR等。
cond 可选的条件码;执行条件,如EQ、NE等。
S 可选后缀;若指定“S”,则根据指令执行结果更新CPSR中的条件码。
Rd 目标寄存器。
Rn 存放第1操作数的寄存器。
operand2 第2个操作数
arm的寻址方式如下:
立即寻址
寄存器寻址
寄存器间接寻址
基址加偏址寻址
堆栈寻址
块拷贝寻址
相对寻址
这里不作详细描述,可以查阅相关文档。
数据处理指令
Load/Store指令
程序状态寄存器与通用寄存器之间的传送指令
转移指令
异常中断指令
协处理器指令
在S3C2410、S3C2440的数据手册中对各种汇编指令有详细的描述;这里只对较常见的作写介绍。
1、相对跳转指令:b、bl
这两条指令的不同之处在于bl指令除了跳转之外,还将返回地址(bl的下一条指令的地址)保存在lr寄存器中。
这两条指令的可跳转范围是当前指令前后32M。
b funa |
2、数据传送指令mov,地址读取伪指令ldr
mov指令可以把一个寄存器的值赋给另外一个寄存器,或者把一个常数赋给寄存器。
mov r1, r2 |
mov传送的常数必须能用立即数来表示。当不能用立即数表示时,可以用ldr命令来赋值。
ldr是伪命令,不是真实存在的指令,编译器会把它扩展成真正的指令;如果该常数能用“立即数”来表示,则使用mov指令,否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
ldr r1, = 1024 |
3、内存访问指令 ldr、str、ldm、stm
ldr既可以指低至读取伪指令,也可以是内存访问指令。当他的第二个参数前面有'='时标伪指令,否则表内存访问指令。
ldr指令从内存中读取数据到寄存器,str指令把寄存器的指存储到内存中,他们的操作数都是32位的。
ldr r1, [r2, #4] /*将地址为r2+4的内存单元数据读取到r1中*/ |
LDM/STM{<cond>}<add mode> Rn{!}, <registers>
4、加减指令 add、sub
add r1, r2, #1 /*r1=r2+1*/ |
ARM指令中有两条指令,用于在状态寄存器和通用寄存器之间传送数据。修改状态寄存器一般是通过“读取-修改-写回”三个步骤的操作来实现的。 这两条指令分别是:
状态寄存器到通用寄存器的传送指令(MRS)
通用寄存器到状态寄存器的传送指令(MSR)
其汇编格式如下:
MRS{<cond>} Rd,CPSR|SPSR
其汇编格式如下:
MSR{<cond>} CPSR_f | SPSR_f,#<32-bit immediate>
MSR{<cond>} CPSR_<field> | SPSR_<field>,Rm
msr cpsr, r0 /*复制r0到cpsr中*/ |
异常中断指令可以分为一下两种:
软件中断指令(SWI)
断点指令(BKPT—仅用于v5T体系)
软件中断指令SWI用于产生SWI异常中断,用来实现在用户模式下对操作系统中特权模式的程序的调用;断点中断指令BKPT主要用于产生软件断点,供调试程序用。
7、其他伪指令
.extern main |
'.text'表示下面的语句都属于代码段
'.global'将本文件中的某个程序标号定义为全局的,如‘_start’就是个全局函数
ARM汇编学习的总结(quote)
ARM汇编学习的总结
ARM汇编指令的一些总结
ARM汇编指令很多,但是真正常用的不是很多,而且需要认真琢磨的又更少了。
比较有用的是MOV B BL LDR STR
还是通过具体汇编代码来学习吧。
@ disable watch dog timer
mov r1, #0x53000000 //立即数寻址方式
mov r2, #0x0
str r2, [r1]
MOV没有什么好说的,只要掌握几个寻址方式就可以了,而且ARM的寻址方式比386的简单很多。立即数寻址方式,立即数要求以“#”作前缀,对于十六进制的数,还要求在#后面加上0x或者&。0x大家很好理解。有一次我碰到了&ff这个数,现在才明白跟0xff是一样的。
STR是比较重要的指令了,跟它对应的是LDR。ARM指令集是加载/存储型的,也就是说它只处理在寄存器中的数据。那么对于系统存储器的访问就经常用到STR和LDR了。STR是把寄存器上的数据传输到指定地址的存储器上。它的格式我个人认为很特殊:
STR(条件) 源寄存器,<存储器地址>
比如 STR R0, [R1] ,意思是R0-> [R1],它把源寄存器写在前面,跟MOV、LDR都相反。
LDR应该是非常常见了。LDR就是把数据从存储器传输到寄存器上。而且有个伪指令也是LDR,因此我有个百思不得其解的问题。看这段代码:
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_F
ldr r2,=0x55aa // 0x55aa是个立即数啊,前面加个=干什么?
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP]
mov r2, #0x00
str r2, [r1, #oGPIO_DAT]
对于当中的ldr 那句,我就不明白了,如果你把=去掉,是不能通过编译的。我查了一些资料,个人感觉知道了原因:这个=应该表示LDR不是ARM指令,而是伪指令。作为伪指令的时候,LDR的格式如下:
LDR 寄存器, =数字常量/Label
它的作用是把一个32位的地址或者常量调入寄存器。嗬嗬,那大家可能会问,
“MOV r2,#0x55aa”也可以啊。应该是这样的。不过,LDR是伪指令啊,也就是说编译时编译器会处理它的。怎么处理的呢?——规则如下:如果该数字常量在MOV指令范围内,汇编器会把这个指令作为MOV。如果不在MOV范围中,汇编器把该常量放在程序后面,用LDR来读取,PC和该常量的偏移量不能超过4KB。
这么一说,虽然似懂非懂,但是能够解释这个语句了。
然后说一下跳转指令。ARM有两种跳转方式。
(1) mov pc <跳转地址〉
这种向程序计数器PC直接写跳转地址,能在4GB连续空间内任意跳转。
(2)通过 B BL BLX BX 可以完成在当前指令向前或者向后32MB的地址空间的跳转(为什么是32MB呢?寄存器是32位的,此时的值是24位有符号数,所以32MB)。
B是最简单的跳转指令。要注意的是,跳转指令的实际值不是绝对地址,而是相对地址——是相对当前PC值的一个偏移量,它的值由汇编器计算得出。
BL非常常用。它在跳转之前会在寄存器LR(R14)中保存PC的当前内容。BL的经典用法如下:
bl NEXT ; 跳转到NEXT
……
NEXT
……
mov pc, lr ; 从子程序返回。
最后提一下Thumb指令。ARM体系结构还支持16位的Thumb指令集。Thumb指令集是ARM指令集的子集,它保留了32位代码优势的同时还大大节省了存储空间。由于Thumb指令集的长度只有16位,所以它的指令比较多。它和ARM各有自己的应用场合。对于系统性能有较高要求,应使用32位存储系统和ARM指令集;对于系统成本和功耗有较高要求,应使用16位存储系统和ARM指令集。
对ARM异常(Exceptions)的理解
分类:技术笔记
毕设笔记
1.对ARM异常(Exceptions)的理解
所有的系统引导程序前面中会有一段类似的代码,如下:
| .globl _start ;系统复位位置 _start: b reset ;各个异常向量对应的跳转代码 ldr pc, _undefined_instruction ;未定义的指令异常 ldr pc, _software_interrupt ;软件中断异常 ldr pc, _prefetch_abort ;内存操作异常 ldr pc, _data_abort ;数据异常 ldr pc, _not_used ;未使用 ldr pc, _irq ;慢速中断异常 ldr pc, _fiq ;快速中断异常 |
从中我们可以看出,ARM支持7种异常。问题时发生了异常后ARM是如何响应的呢?第一个复位异常很好理解,它放在0x0的位置,一上电就执行它,而且我们的程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。那么怎么会执行到后面几个异常处理函数呢?
看看书后,明白了ARM对异常的响应过程,于是就能够回答以前的这个疑问。
当一个异常出现以后,ARM会自动执行以下几个步骤:
(1)把下一条指令的地址放到连接寄存器LR(通常是R14),这样就能够在处理异常返回时从正确的位置继续执行。
(2)将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中。从异常退出的时候,就可以由SPSR来恢复CPSR。
(3) 根据异常类型,强制设置CPSR的运行模式位。
(4)强制PC(程序计数器)从相关异常向量地址取出下一条指令执行,从而跳转到相应的异常处理程序中。
至于这些异常类型各代表什么,我也没有深究。因为平常就关心reset了,也没有必要弄清楚。
ARM规定了异常向量的地址:
b reset ; 复位 0x0
ldr pc, _undefined_instruction ;未定义的指令异常 0x4
ldr pc, _software_interrupt ;软件中断异常 0x8
ldr pc, _prefetch_abort ;预取指令 0xc
ldr pc, _data_abort ;数据 0x10
ldr pc, _not_used ;未使用 0x14
ldr pc, _irq ;慢速中断异常 0x18
ldr pc, _fiq ;快速中断异常 0x1c
这样理解这段代码就非常简单了。碰到异常时,PC会被强制设置为对应的异常向量,从而跳转到相应的处理程序,然后再返回到主程序继续执行。
这些引导程序的中断向量,是仅供引导程序自己使用的,一旦引导程序引导Linux内核完毕后,会使用自己的中断向量。
嗬嗬,这又有问题了。比如,ARM发生中断(irq)的时候,总是会跑到0x18上执行啊。那Linux内核又怎么能使用自己的中断向量呢?原因在于Linux内核采用页式存储管理。开通MMU的页面映射以后,CPU所发出的地址就是虚拟地址而不是物理地址。就Linux内核而言,虚拟地址0x18经过映射以后的物理地址就是0xc000 0018。所以Linux把中断向量放到0xc000 0018就可以了。
另外,说一下MMU。说句实话,还不是很明白这个MMU机理。参加Intel培训的时候,李眈说了MMU的两个主要作用:
(1)安全性:规定访问权限
(2) 提供地址空间:把不连续的空间转换成连续的。
第2点是不是实现页式存储的意思?
2005年6月9日晚
补充一下: 05/06/14
.globl _start ;系统复位位置
_start: b reset ;各个异常向量对应的跳转代码
ldr pc, _undefined_instruction ;未定义的指令异常
……
_undefined_instruction :
.word undefined_instruction
也许有人会有疑问,同样是跳转指令,为什么第一句用的是 b reset;
而后面的几个都是用ldr?
为了理解这个问题,我们以未定义的指令异常为例。
当发生了这个异常后,CPU总是跳转到0x4,这个地址是虚拟地址,它映射到哪个物理地址
取决于具体的映射。
ldr pc, _undefined_instruction
相对寻址,跳转到标号_undefined_instruction,然而真正的跳转地址其实是_undefined_instruction的内容——undefined_instruction。那句.word的相当于:
_undefined_instruction dw undefined_instruction (详见毕设笔记3)。
这 个地址undefined_instruction到底有多远就难说了,也许和标号_undefined_instruction在同一个页面,也许在很 远的地方。不过除了reset,其他的异常是MMU开始工作之后才可能发生的,因此undefined_instruction 的地址也经过了MMU的映射。
在刚加电的时候,CPU从0x0开始执行,MMU还没有开始工作,此时的虚拟地址和物理地址相同;另一方面,重启在MMU开始工作后也有可能发生,如果reset也用ldr就有问题了,因为这时候虚拟地址和物理地址完全不同。
因此,之所以reset用b,就是因为reset在MMU建立前后都有可能发生,而其他的异常只有在MMU建立之后才会发生。用b reset,reset子程序与reset向量在同一页面,这样就不会有问题(b是相对跳转的)。如果二者相距太远,那么编译器会报错的。
.globl _start ;系统复位位置
_start: b reset ;各个异常向量对应的跳转代码
ldr pc, _undefined_instruction ;未定义的指令异常
……
_undefined_instruction :
.word undefined_instruction
也许有人会有疑问,同样是跳转指令,为什么第一句用的是 b reset;
而后面的几个都是用ldr?
为了理解这个问题,我们以未定义的指令异常为例。
当发生了这个异常后,CPU总是跳转到0x4,这个地址是虚拟地址,它映射到哪个物理地址
取决于具体的映射。
ldr pc, _undefined_instruction
相对寻址,跳转到标号_undefined_instruction,然而真正的跳转地址其实是_undefined_instruction的内容——undefined_instruction。那句.word的相当于:
_undefined_instruction dw undefined_instruction (详见毕设笔记3)。
这 个地址undefined_instruction到底有多远就难说了,也许和标号_undefined_instruction在同一个页面,也许在很 远的地方。不过除了reset,其他的异常是MMU开始工作之后才可能发生的,因此undefined_instruction 的地址也经过了MMU的映射。
在刚加电的时候,CPU从0x0开始执行,MMU还没有开始工作,此时的虚拟地址和物理地址相同;另一方面,重启在MMU开始工作后也有可能发生,如果reset也用ldr就有问题了,因为这时候虚拟地址和物理地址完全不同。
因此,之所以reset用b,就是因为reset在MMU建立前后都有可能发生,而其他的异常只有在MMU建立之后才会发生。用b reset,reset子程序与reset向量在同一页面,这样就不会有问题(b是相对跳转的)。如果二者相距太远,那么编译器会报错的。
2009-12-04
SPI Protocol Introduction(quote)
SPI概述
SPI:高速同步串行口。3~4线接口,收发独立、可同步进行.
SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片 的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协 议,比如AT91RM9200.
SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASHRAM、网络控制器、LCD显示 驱动器、A/D转换器和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCK)、主机输 入/从机输出数据线MISO、主机输出/从机输入数据线MOST和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT或INT、有的 SPI接口芯片没有主机输出/从机输入数据线MOSI)。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。
(1)SDO – 主设备数据输出,从设备数据输入
(2)SDI – 主设备数据输入,从设备数据输出
(3)SCLK – 时钟信号,由主设备产生
(4)CS – 从设备使能信号,由主设备控制
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。
接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的 原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变 (上沿和下沿为一次),就可以完成8位数据的传输。
要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式 有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设 备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为 SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上 沿或下沿采集有不同定义,具体请参考相关器件的文档。
在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。
最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
AT91RM9200的SPI接口主要由4个引脚构成:SPICLK、MOSI、MISO及 /SS,其中SPICLK是整个SPI总线的公用时钟,MOSI、MISO作为主机,从机的输入输出的标志,MOSI是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。/SS是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS管脚的电平低的是从机,相反/SS管脚的电平高的是主机。 在一个SPI通信系统中,必须有主机。SPI总线可以配置成单主单从,单主多从,互为主从。
SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,说NPCS0~3接4-16译码器,这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。
SPI协议举例
SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
假设下面的8位寄存器装的是待发送的数据10101010,上升沿发送、下降沿接收、高位先发送。
那么第一个上升沿来的时候 数据将会是sdo=1;寄存器=0101010x。下降沿到来的时候,sdi上的电平将所存到寄存器中去,那么这时寄存器=0101010sdi,这样在 8个时钟脉冲以后,两个寄存器的内容互相交换一次。这样就完成里一个spi时序。
举例:
假设主机和从机初始化就绪:并且主机的sbuff=0xaa,从机的sbuff=0x55,下面将分步对spi的8个时钟周期的数据情况演示一遍:假设上升沿发送数据
这样就完成了两个寄存器8位的交换,上面的上表示上升沿、下表示下降沿,sdi、sdo相对于主机而言的。其中ss引脚作为主机的时候,从机可以把它拉底 被动选为从机,作为从机的是时候,可以作为片选脚用。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根 据主机的命令准备数据,主机在下一个8位时钟周期才把数据读回来。 SPI 总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束 中断标志;写冲突保护;总线竞争保护等。下图示出SPI总线工作的四种方式,其中使用的最为广泛的是SPI0和SPI3方式 (实线表示):
SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传 输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿 (上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。
SPI总线包括1根串行同步时钟信号线以及2根数据线。
SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传 输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿 (上升或下降)数据被采样。SPI主模块和与之通信的外设音时钟相位和极性应该一致。SPI接口时序如图3、图4所示。
补充:
上文中最后一句话:SPI主模块和与之通信的外设备时钟相位和极性应该一致。个人理解这句话有2层意思:其一,主设备SPI时钟和极性的配置应该由外设来 决定;其二,二者的配置应该保持一致,即主设备的SDO同从设备的SDO配置一致,主设备的SDI同从设备的SDI配置一致。因为主从设备是在SCLK的 控制下,同时发送和接收数据,并通过2个双向移位寄存器来交换数据。工作原理演示如下图:
上升沿主机SDO发送数据1,同时从设备SDO发送数据0;紧接着在SCLK的下降沿的时候从设备的SDI接收到了主机发送过来的数据1,同时主机也接收到了从设备发送过来的数据0.
SPI协议心得
SPI接口时钟配置心得:
在主设备这边配置SPI接口时钟的时候一定要弄清楚从设备的时钟要求,因为主设备这边的时钟极性和相位都是以从设备为基准的。因此在时钟极性的配置上一定 要搞清楚从设备是在时钟的上升沿还是下降沿接收数据,是在时钟的下降沿还是上升沿输出数据。但要注意的是,由于主设备的SDO连接从设备的SDI,从设备 的SDO连接主设备的SDI,从设备SDI接收的数据是主设备的SDO发送过来的,主设备SDI接收的数据是从设备SDO发送过来的,所以主设备这边 SPI时钟极性的配置(即SDO的配置)跟从设备的SDI接收数据的极性是相反的,跟从设备SDO发送数据的极性是相同的。下面这段话是Sychip Wlan8100 Module Spec上说的,充分说明了时钟极性是如何配置的:
The 81xx module will always input data bits at the rising edge of the clock, and the host will always output data bits on the falling edge of the clock.
意思是:主设备在时钟的下降沿发送数据,从设备在时钟的上升沿接收数据。因此主设备这边SPI时钟极性应该配置为下降沿有效。
又如,下面这段话是摘自LCD Driver IC SSD1289:
SDI is shifted into 8-bit shift register on every rising edge of SCK in the order of data bit 7, data bit 6 …… data bit 0.
意思是:从设备SSD1289在时钟的上升沿接收数据,而且是按照从高位到地位的顺序接收数据的。因此主设备的SPI时钟极性同样应该配置为下降沿有效。
时钟极性和相位配置正确后,数据才能够被准确的发送和接收。因此应该对照从设备的SPI接口时序或者Spec文档说明来正确配置主设备的时钟。
SPI:高速同步串行口。3~4线接口,收发独立、可同步进行.
SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片 的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协 议,比如AT91RM9200.
SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。外围设置FLASHRAM、网络控制器、LCD显示 驱动器、A/D转换器和MCU等。SPI总线系统可直接与各个厂家生产的多种标准外围器件直接接口,该接口一般使用4条线:串行时钟线(SCK)、主机输 入/从机输出数据线MISO、主机输出/从机输入数据线MOST和低电平有效的从机选择线SS(有的SPI接口芯片带有中断信号线INT或INT、有的 SPI接口芯片没有主机输出/从机输入数据线MOSI)。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)。
(1)SDO – 主设备数据输出,从设备数据输入
(2)SDI – 主设备数据输入,从设备数据输出
(3)SCLK – 时钟信号,由主设备产生
(4)CS – 从设备使能信号,由主设备控制
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。这就允许在同一总线上连接多个SPI设备成为可能。
接下来就负责通讯的3根线了。通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的 原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变 (上沿和下沿为一次),就可以完成8位数据的传输。
要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。这样传输的特点:这样的传输方式 有一个优点,与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设 备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。SPI还是一个数据交换协议:因为 SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上 沿或下沿采集有不同定义,具体请参考相关器件的文档。
在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。
最后,SPI接口的一个缺点:没有指定的流控制,没有应答机制确认是否接收到数据。
AT91RM9200的SPI接口主要由4个引脚构成:SPICLK、MOSI、MISO及 /SS,其中SPICLK是整个SPI总线的公用时钟,MOSI、MISO作为主机,从机的输入输出的标志,MOSI是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。/SS是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS管脚的电平低的是从机,相反/SS管脚的电平高的是主机。 在一个SPI通信系统中,必须有主机。SPI总线可以配置成单主单从,单主多从,互为主从。
SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,说NPCS0~3接4-16译码器,这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。
SPI协议举例
SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。
假设下面的8位寄存器装的是待发送的数据10101010,上升沿发送、下降沿接收、高位先发送。
那么第一个上升沿来的时候 数据将会是sdo=1;寄存器=0101010x。下降沿到来的时候,sdi上的电平将所存到寄存器中去,那么这时寄存器=0101010sdi,这样在 8个时钟脉冲以后,两个寄存器的内容互相交换一次。这样就完成里一个spi时序。
举例:
假设主机和从机初始化就绪:并且主机的sbuff=0xaa,从机的sbuff=0x55,下面将分步对spi的8个时钟周期的数据情况演示一遍:假设上升沿发送数据
这样就完成了两个寄存器8位的交换,上面的上表示上升沿、下表示下降沿,sdi、sdo相对于主机而言的。其中ss引脚作为主机的时候,从机可以把它拉底 被动选为从机,作为从机的是时候,可以作为片选脚用。根据以上分析,一个完整的传送周期是16位,即两个字节,因为,首先主机要发送命令过去,然后从机根 据主机的命令准备数据,主机在下一个8位时钟周期才把数据读回来。 SPI 总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于 CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束 中断标志;写冲突保护;总线竞争保护等。下图示出SPI总线工作的四种方式,其中使用的最为广泛的是SPI0和SPI3方式 (实线表示):
SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传 输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿 (上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。
SPI总线包括1根串行同步时钟信号线以及2根数据线。
SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传 输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿 (上升或下降)数据被采样。SPI主模块和与之通信的外设音时钟相位和极性应该一致。SPI接口时序如图3、图4所示。
补充:
上文中最后一句话:SPI主模块和与之通信的外设备时钟相位和极性应该一致。个人理解这句话有2层意思:其一,主设备SPI时钟和极性的配置应该由外设来 决定;其二,二者的配置应该保持一致,即主设备的SDO同从设备的SDO配置一致,主设备的SDI同从设备的SDI配置一致。因为主从设备是在SCLK的 控制下,同时发送和接收数据,并通过2个双向移位寄存器来交换数据。工作原理演示如下图:
上升沿主机SDO发送数据1,同时从设备SDO发送数据0;紧接着在SCLK的下降沿的时候从设备的SDI接收到了主机发送过来的数据1,同时主机也接收到了从设备发送过来的数据0.
SPI协议心得
SPI接口时钟配置心得:
在主设备这边配置SPI接口时钟的时候一定要弄清楚从设备的时钟要求,因为主设备这边的时钟极性和相位都是以从设备为基准的。因此在时钟极性的配置上一定 要搞清楚从设备是在时钟的上升沿还是下降沿接收数据,是在时钟的下降沿还是上升沿输出数据。但要注意的是,由于主设备的SDO连接从设备的SDI,从设备 的SDO连接主设备的SDI,从设备SDI接收的数据是主设备的SDO发送过来的,主设备SDI接收的数据是从设备SDO发送过来的,所以主设备这边 SPI时钟极性的配置(即SDO的配置)跟从设备的SDI接收数据的极性是相反的,跟从设备SDO发送数据的极性是相同的。下面这段话是Sychip Wlan8100 Module Spec上说的,充分说明了时钟极性是如何配置的:
The 81xx module will always input data bits at the rising edge of the clock, and the host will always output data bits on the falling edge of the clock.
意思是:主设备在时钟的下降沿发送数据,从设备在时钟的上升沿接收数据。因此主设备这边SPI时钟极性应该配置为下降沿有效。
又如,下面这段话是摘自LCD Driver IC SSD1289:
SDI is shifted into 8-bit shift register on every rising edge of SCK in the order of data bit 7, data bit 6 …… data bit 0.
意思是:从设备SSD1289在时钟的上升沿接收数据,而且是按照从高位到地位的顺序接收数据的。因此主设备的SPI时钟极性同样应该配置为下降沿有效。
时钟极性和相位配置正确后,数据才能够被准确的发送和接收。因此应该对照从设备的SPI接口时序或者Spec文档说明来正确配置主设备的时钟。
Subscribe to:
Posts (Atom)