没有合适的资源?快使用搜索试试~ 我知道了~
首页Xtensa_programmers_guide_中文版.pdf
Xtensa_programmers_guide_中文版.pdf
需积分: 48 677 浏览量
更新于2023-05-26
评论 2
收藏 985KB PDF 举报
Xtensa programmers guide_中文版,由于xtensa架构资料中文版比较少,个人翻译分享出来,希望可以帮助大家
资源详情
资源评论
资源推荐

2.xtensa 汇编代码
(instruction 指令,机器指令或者指令助记符,有特定的硬件实现;directive 指示,编译器指示作用伪指令,没有特定的硬件实
现)
2.1 写汇编代码的原因
为 xtensa 处理器编写操作系统需要写或使用一些汇编代码。用户异常处理,内核异常处理,窗口处理,复
位处理——由于一些原因,所有这些低层函数必须用汇编写。主要原因是这些向量表的进入和退出机制与
标准 C 函数不一致,因此编译器不能够产生他们。这些向量表也要求:
▪访问某些 C 不能访问的特殊寄存器
▪编译器不能提供控制操作顺序的层次
当然也有其他编写汇编代码的原因。通常汇编程序会展示更好的表现,尤其是操作必须在中断锁住进行时。
这在某些操作系统指定情况下是关键的。
不管什么原因,为 xtensa 处理器编写汇编代码——跟为任何处理器写汇编代码一样,需要理解该处理器和
相关工具。
2.2 一个简单例子
编写汇编代码最简单的方法是使用 C 编译器。考虑如下计算 N 级 Fibbonacci 级数的 C 代码。
unsigned int fib( unsigned int val )
{
int cur;
int cur1;
int newval;
if( val == 0 )
return 0;
if( val < 3 )
return 1;
val = val - 2;
cur = cur1 = 1;
do
{
newval = cur + cur1;
cur1 = cur;
cur = newval;
val--;
} while ( val );
return cur;
}
代码很简单:检查特定边界情况,然后计算 Fibbonacci 级数。
你可以通过几种不同的方式查看编译器产生的汇编代码。一种方法是反汇编编译过的文件。该方法首先使
用如下命令编译例程。
$ xt-xcc –O2 -Os –c fib.c

然后,使用下面命令反汇编产生的目标文件(fib.o),
$ xt-objdump –dr fib.o
该过程产生与下面类似的结果:
fib.o: file format elf32-xtensa-le
Disassembly of section .text:
00000000 <fib>:
0: 004136 entry a1, 32
3: 22cc bnez.n a2, 9 <fib+0x9>
3: R_XTENSA_SLOT0_OP .text+0x9
5: 020c movi.n a2, 0
7: f01d retw.n
00000009 <fib+0x9>:
9: 0332f6 bgeui a2, 3, 10 <fib+0x10>
9: R_XTENSA_SLOT0_OP .text+0x10
c: 120c movi.n a2, 1
e: f01d retw.n
00000010 <fib+0x10>:
10: 140c movi.n a4, 1
12: 150c movi.n a5, 1
00000014 <fib+0x14>:
14: fec222 addi a2, a2, -2
17: 654a add.n a6, a5, a4
19: 220b addi.n a2, a2, -1
1b: 054d mov.n a4, a5
1d: 065d mov.n a5, a6
1f: ff4256 bnez a2, 17 <fib+0x17>
1f: R_XTENSA_SLOT0_OP .text+0x17
22: 062d mov.n a2, a6
24: f01d retw.n
编译器产生上述代码作为 fib 功能。
使用反汇编器并不产生最具可读性的反汇编,但是有用的,因为它并不要求访问源代码。汇编器有时也不
调整编译器产生的代码。例如,汇编器可能对其分支或解决分支指令范围的限制。
然而,如果有源代码,如本例,你可以用编译器产生更具可读性的汇编代码。该命令要求编译器产生汇编
文件而不是完整的目标代码。
使用如下命令产生名为 fib.s 的文件:
$ xt-xcc –O2 -Os –S fib.c
使用如下命令,滤掉编译器产生的启发式和性能分析注释,为了更还的可读性。
$ grep -v ’^#’ fib.s
# xt-xcc::7.0.0-development
#-----------------------------------------------------------
# Compiling fib.c (/tmp/cc0M#1861f7f8.oXQX5V)
#-----------------------------------------------------------

#-----------------------------------------------------------
# Options:
#-----------------------------------------------------------
# Target:xtensa, ISA:xtensa, Pointer Size:32
# -O2 (Optimization level)
# -g0 (Debug level)
# -m2 (Report advisories)
#-----------------------------------------------------------
.text
.align 1
.literal_position
# Program Unit: fib
# Optimized for space
.type fib, @function
.align 4
.global fib
fib:
.frame a1, 32
.LBB1_fib:
entry a1,32
bnez a2, .Lt_0_2
movi.n a2,0
retw.n
.Lt_0_2:
bgeui a2,3, .Lt_0_4
movi.n a2,1
retw.n
.Lt_0_4:
movi.n a4,1
movi.n a5,1
addi a2,a2,-2
.Lt_0_7:
add.n a6,a5,a4
addi.n a2,a2,-1
mov.n a4,a5
mov.n a5,a6
bnez a2, .Lt_0_7
mov.n a2,a6
retw.n
.size fib, . – fib
在该输出,大部分信息与本讨论是没关系的。但这里的汇编代码更容易读,并且包含编译中丢失的信息,
比如汇编伪指令和编译器产生的 loop labels。从细节思考该输出的反汇编部分。
查看的第一部分是函数头。该函数头包含一系列汇编伪指令。这些伪指令并不产生任何指令,但

是会告诉汇编器关于将要产生的指令的信息。
.text
.align 1
.text 伪指令告诉汇编器把输出发送到.text 段,.align 伪指令告诉汇编器把下一个目标字节按单字节对其位置
放到 text 段。
现在段建立好了,代码自动寻找函数地址,包括设置对其方式,类型和名称:
.type fib, @function
.align 4
.global fib
fib:
.type 伪指令在目标文件放置信息,指示符号 fib 作为一个函数。调试器可以通过该类型的信息知道如何显
示该符号的信息。.align 伪指令告诉汇编器在四字节边界放置下一个目标字节。PC 相关的调用指令要求目
标地址四字节对其,所以,该对其伪指令让函数可被调用。.global 伪指令使符号 fib 外部可调用,因此它可
以在该目标文件外面使用。最后,fib 符号本身被声明。
现在该函数的符号已经被正确声明,还有最后一个伪指令。特别地,它指示包含帧指针寄存器和栈帧的初
始大小。(The frame pointer is the stack pointer for frames without a call to alloca() . Functions that call alloca() must move the stack pointer to
create new space on the stack. The frame pointer is the register that is used as a base for local variables and arguments passed on the stack. These do
not move when an alloca() call is made. Because the stack pointer is moving, another register must be used to hold the base pointer for the local
variables.)
.frame a1, 32
现在考虑产生的代码本身。Recall 该示例 C 代码被固定到两个不同的段。第一个段检查输入的某些
特殊情况。尤其是,如果输入值小于 3 该循环不需要执行。
每个函数都是以 entry 指令开始。窗口寄存器调用 convention 时需要该指令,如第 2.3 节讨论的
那样。
.LBB1_fib:
entry a1, 32
函数体紧跟该入口,第一条 C 代码保护信息是:
if( val == 0 )
return 0;
该语句被如下汇编代码处理:
bnez a2, .Lt_0_2
movi.n a2, 0
retw.n
.Lt_0_2:
这段代码表示给函数的 val 参数传递到 a2 寄存器中。Bnez 指令检查 a2 是否不为 0.如果 a2 不为 0,控制跳
转到给定的标号(本例中是.Lt_0_2),紧接着 return 之后。如果分支没有采纳,然后该返回值(本例为 0),
传送到 a2(本例没有必要因为 a2 应经为 0), retw.n 指令从函数返回。
注意,一些指令使用.n 下标,xtensa 处理器拥有可选代码密度功能,提供一些常用指令的 16 位版本。这些
16 位的短指令助记符都是以.n 结尾。编译器和汇编器在可能提高代码密度的地方使用短指令。汇编器也可
能将密度指令转换为非密度指令(即将 16 位指令转换为对等的 24 位指令)以实现循环指令对其要求,对
其分支目标来提高性能。

如果 a2 非 0,那么控制将到下一个代码段。C 代码如下:
if( val < 3 )
return 1;
相应汇编代码如下:
.Lt_0_2:
bgeui a2,3, .Lt_0_4
movi.n a2,1
retw.n
.Lt_0_4:
Xtensa 处理器提供一系列扩充性的分支指令;上面用了其中一种。如果该寄存器大于等于特定立即数,bgeui
指令转移到目的地。在本例中,如果 a2 大于等于 3,控制被转移到.Lt_0_4 标号。如果小于 3,movi.n 将返
回值 1 加载到 a2,然后函数返回。
所有这些保护条件已经被处理,真正的计算开始了。该段代码分两部分:设置循环和循环体。设置的 C 代
码有由两条赋值语句组成。
val = val - 2;
cur = cur1 = 1;
这些赋值语句的汇编代码相当直接。
.Lt_0_4:
movi.n a4,1
movi.n a5,1
addi a2,a2,-2
寄存器 a4 作为变量 cur1,a5 作为 cur;初始值都为 1.作为变量 val 的 a2 寄存器减去 2.(注意有很多等价操
作可以获得该结果。利用 mov 指令赋值 a4 到 a5 来初始化 a5 也会正确并同等合并。)
循环本身在 C 和汇编代码是明确的。C 代码如下:
do
{
newval = cur + cur1;
cur1 = cur;
cur = newval;
val--;
} while ( val );
汇编代码如下:
.Lt_0_7:
addi.n a2,a2,-1
add.n a6,a5,a4
mov.n a4,a5
mov.n a5,a6
bnez a2,.Lt_0_7
一旦进入循环体,C 和汇编的关系就相当明朗,尽管编译器重新排列一些操作。循环变量(val)用 addi.n
指令减 1。用 add.n 指令加上另外两个值,产生 newval。利用 mov.n 指令,将 cur 的值付给 cur1,把 newval
的值赋给 cur。最后,bnez 指令检查 val 是否非 0。
当循环结束,代码返回。要返回的值再一次放在 a2 寄存器。循环把计算结果送到 a6 寄存器,因此返回代
码如下:
mov.n a2,a6
剩余24页未读,继续阅读













lxg2511
- 粉丝: 0
- 资源: 2
上传资源 快速赚钱
我的内容管理 收起
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助

会员权益专享
安全验证
文档复制为VIP权益,开通VIP直接复制

评论0