7.0符文铜棒:PowerPC 体系结构

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 14:17:27
   PowerPC体系结构初探2008-07-02 09:03 PowerPC体系结构初探

    从超级计算机到游戏控制台,从服务器到手机,随处可见 PowerPC 处理器的身影 -- 它们的体系结构全都相同。本篇对 PowerPC 应用级编程模型的介绍概述了指令集、重要的寄存器以及其他细节,这些细节对于开发可靠的、高性能的 PowerPC 应用程序和保持代码在不同处理器的兼容性来说是必须要了解的。

对 PowerPC 体系结构家族树的所有分支来说,PowerPC 体系结构和应用级编程模型是通用的。要获得详细的资料,请参阅 IBM® PowerPC Web 站点技术库中的产品用户手册(参阅 参考资料以获取链接)。

PowerPC 体系结构是一种精减指令集计算机(Reduced Instruction Set Computer,RISC)体系结构,定义了 200 多条指令。PowerPC 之所以是 RISC,原因在于大部分指令在一个单一的周期内执行,而且通常只执行一个单一的操作(比如将内存加载到寄存器,或者将寄存器数据存储到内存)。

PowerPC 体系结构分为三个级别(或者说是“book”)。通过对体系结构以这种方式进行划分,为实现可以选择价格/性能比平衡的复杂性级别留出了空间,同时还保持了实现间的代码兼容性。

Book I. 用户指令集体系结构
    定义了通用于所有 PowerPC 实现的用户指令和寄存器的基本集合。这些是非特权指令,为大多数程序所用。
Book II. 虚拟环境体系结构
    定义了常规应用软件要求之外的附加的用户级功能,比如高速缓存管理、原子操作和用户级计时器支持。虽然这些操作也是非特权的,但是程序通常还是通过操作系统调用来访问这些函数。
Book III. 操作环境体系结构
    定义了操作系统级需要和使用的操作。其中包括用于内存管理、异常向量处理、特权寄存器访问、特权计时器访问的函数。Book III 中详细说明了对各种系统服务和功能的直接硬件支持。

从最初的 PowerPC 体系结构的开发开始,就根据特定的市场需求而发生分支。当前,PowerPC 体系结构家族树有两个活跃的分支,分别是 PowerPC AS体系结构和 PowerPC Book E体系结构。PowerPC AS 体系结构是 IBM 为了满足它的 eServer pSeries UNIX 和 Linux 服务器产品家族及它的 eServer iSeries 企业服务器产品家族的具体需要而定义的(参阅 参考资料中的链接以获得更多资料)。PowerPC Book E 体系结构,也被称为 Book E,是 IBM 和 Motorola 为满足嵌入式市场的特定需求而合作推出的。PowerPC AS 所采用的原始 PowerPC 体系结构与 Book E 所采用的扩展之间的主要区别大部分集中于 Book III 区域中。

在这些衍生的体系结构中还有一些适当的应用级扩展,这些扩展大部分与具体应用的场合相关,但是 PowerPC AS 和 PowerPC Book E 共享在 PowerPC 体系结构的 Book I 中定义的基本指令集。虽然三种体系结构主要在操作系统级别上表现出不同,但它们在很大程度上具备应用级的兼容性。

PowerPC 最初定义了同时对 32 位和 64 位实现的支持,可以让 32 位的应用程序运行于 64 位系统之上。在 IBM pSeries 和 iSeries 服务器上使用的 PowerPC AS 系统现在只提供体系结构的 64 位实现,新的 64 位应用程序和遗留的 32 位的应用程序可以运行于同一个系统之上。PowerPC Book E 体系结构同时有 32 位实现和 64 位实现,64 位实现也完全兼容 32 位 PowerPC 应用程序。这两种体系结构都具备与 PowerPC Book I 指令和寄存器的完全兼容性,同时提供了对内存管理、异常和中断、计时器支持和调试支持等各方面的系统级扩展。

  

最初的 PowerPC 体系结构仍是 PowerPC AS 和 PowerPC Book E 的主要组成部分,并仍保持了其完整性,表现出了令人信服的应用级兼容性。

PowerPC 应用程序编程模型


当用到不只一种类型的 PowerPC 处理器时,开发人员应时刻谨记处理器处理内存的方式存在一些差异。

PowerPC 存储模型


PowerPC 体系结构本身支持字节(8 位)、半字(16 位)、字(32 位) 和双字(64 位) 数据类型。PowerPC 实现还可以处理最长 128 字节的多字节字符串操作。32 位 PowerPC 实现支持 4-gigabyte 的有效地址空间,而 64 位 PowerPC 实现支持 16-exabyte 的有效地址空间。所有存储都可以字节寻址。

对于错位数据访问来说,不同的产品家族提供了不同的校准支持,有一些是以处理异常的方式,其他的是通过硬件中的一步或者多步操作来处理访问。

最高位字节在最前(Big-endian)还是最低位字节在最前(little-endian)?


PowerPC、PowerPC AS 以及早期的 IBM PowerPC 4xx 家族大部分是字节排列顺序最高位在最前的机器,这就意味着对半字、字以及双字访问来说,最重要的字节(most-significant byte,MSB)位于最低的地址。各实现对最低位在最前的字节排列顺序方式的支持不同。PowerPC 和 PowerPC AS 提供了最小限度的支持,而 4xx 家族为最低位字节在最前的存储提供了更为健壮的支持。Book E 是字节排列顺序无关的,因为 Book E 体系结构完全支持这两种访问方法。




   回页首


PowerPC 应用级寄存器


PowerPC 的应用级寄存器分为三类:通用寄存器(general-purpose register,GPR)、浮点寄存器(floating-point register [FPR] 和浮点状态和控制寄存器 [Floating-Point Status and Control Register,FPSCR])和专用寄存器(special-purpose register,SPR)。让我们来分别看一下这三类寄存器。

通用寄存器(GPR)


用户指令集体系结构(Book I)规定,所有实现都有 32 个 GPR(从GPR0 到 GPR31)。GPR 是所有整数操作的源和目的,也是所有加载/存储操作的地址操作数的源。GPR 还提供对 SPR 的访问。所有 GRP 都是可用的,只有一种情况例外:在某些指令中,GPR0 只是代表数值 0,而不会去查找 GPR0 的内容。

浮点寄存器(FPR)


Book I 规定,所有实现都有 32 个 FPR(从 FPR0 到 FPR31)。FPR 是所有浮点操作的源和目的操作数,可以存放 32 位和 64 位的有符号和无符号整数,以及单精度和双精度浮点数。FPR 还提供对 FPSCR 的访问。

注意,嵌入式微处理器实现时经常不提供对浮点指令集的直接硬件支持,或者只是提供一个附加浮点硬件的接口。很多嵌入式应用程序很少或者根本不需要浮点算法,而当需要的时候,对 PowerPC 浮点指令执行进行软件仿真就足够了。在嵌入式微处理器中,硬件中省去浮点(支持)而为实现带来的芯片面积和功率的减少是至关重要的。

浮点状态和控制寄存器(FPSCR)捕获浮点操作的状态和异常结果,FPSCR 还具有控制位,以支持特定的异常类型和对四种舍入模式之一的选择。对 FPSCR 的访问要通过 FPR。

专用寄存器(SPR)


SPR 给出处理器核心内部资源的状态并对其进行控制。不需要系统服务的支持就可以由应用程序读写的 SPR 包括计数寄存器(Count Register)、链接寄存器(Link Register)和整型异常寄存器(Integer Exception Register)。需要系统服务的支持才可以由应用程序读写的 SPR 包括时基(Time Base)和其他各种可能支持的计时器。

    * 指令地址寄存器(Instruction Address Register,IAR)
      这个寄存器就是程序员们所熟知的 程序计数器或者 指令指针。它是当前指令的地址。这实际上是一个伪寄存器,用户只能通过“branch and link”指令才能直接使用这个寄存器。IAR 主要是由调试器使用,显示将要被执行的下一条指令。

    * 链接寄存器(Link Register,LR)
      这个寄存器存放的是函数调用结束处的返回地址。某些转移指令可以自动加载 LR 到转移之后的指令。每个转移指令编码中都有一个 LK 位。如果 LK 为 1,转移指令就会将程序计数器移为 LR 中的地址。而且,条件转移指令 bclr 转移到 LR 中的值。
    * 定点异常寄存器(Fixed-Point Exception Register,XER)
      这个寄存器存放整数运算操作的进位以及溢出信息。它还存放某些整数运算操作的进位输入以及加载和存储指令( lswx 和 stswx )中传输的字节数。

    * 计数寄存器(Count Register,CTR)
      这个寄存器中存放了一个循环计数器,会随特定转移操作而递减。条件转移指令 bcctr 转移到 CTR 中的值。

    * 条件寄存器(Condition Register,CR)
      这个寄存器分为八个字段,每个字段 4 位。很多 PowerPC 指令将指令的第 31 位编码为 Rc 位,有一些指令要求 Rc 值等于 1。当 Rc 等于 1 且进行整数操作时,CR 字段 0 被设置来表示指令操作的结果:相等(Equal, EQ),大于(Greater Than, GT),小于(Less Than, LT),以及和溢出(Summary Overflow, SO)。当 Rc 等于 1 且进行浮点操作时,CR 字段 1 被设置用来表示 FPSCR 中异常状态位的状态:FX、FEX、VX 和 OX。任何一个 CR 字段都可以是整数或者浮点比较指令的目标。CR 字段 0 还被设置用来表示条件存储指令( stwcx 或者 stdcx ) 的结果。还有一组指令可以操纵特定的 CR 位、特定的 CR 字段或者整个 CR,通常为了测试而将几个条件组合到同一个位中。

    * 处理器版本寄存器(Processor Version Register,PVR)
      PVR 是一个 32 位只读寄存器,标识处理器的版本和修订级别。处理器版本由 PowerPC 体系结构过程分配。修订级别由实现定义。需要有特权才能访问 PVR,所以应用程序只能在操作系统函数的帮助下才可以确定处理器版本。





   回页首


PowerPC 应用级指令集


表 1 列出了不同的指令类别以及每类的指令类型。

表 1. 指令类别
指令类别    基本指令
Branch    branch, branch conditional, branch to LR, branch to CTR
Condition register    crand, crnor, creqv, crxor, crandc, crorc, crnand, cror, CR move
Storage access    load GPR/FPR, store GPR/FPR
Integer arithmetic    add, subtract, negate, multiply, divide
Integer comparison    compare algebraic, compare algebraic immediate, compare logical,compare logical immediate
Integer logical    and, andc, nand, or, orc, nor, xor, eqv, sign extension, countleading zeros
Integer rotate/shift    rotate, rotate and mask, shift left, shift right
Floating-point arithmetic    add, subtract, negate, multiply, divide, square root, multiply-add,multiply-subtract, negative multiply-add, negative multiply-subtract
Floating-point comparison    compare ordered, compare unordered
Floating-point conversion    round to single, convert from/to integer word/doubleword
FPSCR management    move to/from FPSCR, set/clear FPSCR bit, copy FPSCR field to CR
Cache control    touch, zero, flush, store
Processor management    system call, move to/from special purpose registers, mtcrf, mfcr

指令解析


所有指令的编码长度都是 32 位。PowerPC 的位编号方式与大部分其他定义相反:第 0 位是最重要的位,第 31 位是最不重要的位。指令首先由一个字段中较高的 6 位进行解码,这 6 位称为 主要操作码(primary opcode)。其余 26 位包含的字段分别是操作数说明、立即(immediate)操作数以及扩展的操作码(opcode),而且这些还可能是保留的位或字段。表 2 列出了 PowerPC 定义的基本指令格式。

表 2. PowerPC 指令格式
格式    0    6    11    16    21    26    30    31
D-form    opcd    tgt/src    src/tgt    immediate
X-form    opcd    tgt/src    src/tgt    src    extended opcd
A-form    opcd    tgt/src    src/tgt    src    src    extended opcd    Rc
BD-form    opcd    BO    BI    BD    AA    LK
I-form    opcd    LI    AA    LK

    * D-form
      这一指令格式提供至多两个寄存器作为源操作数,一个立即源,至多两个寄存器作为目的操作数。这一指令格式的一些变种使用部分目的和源寄存器操作数说明符作为立即字段或作为扩展的操作码。
    * X-form
      这一指令格式提供至多两个寄存器作为源操作数,至多两个目的操作数。这一指令格式的一些变种使用部分目的和源寄存器操作数说明符作为立即字段或作为扩展的操作码。
    * A-form
      这一指令格式提供至多三个寄存器作为源操作数,以及一个目的操作数。这一指令格式的一些变种使用部分目的和源寄存器操作数说明符作为立即字段或作为扩展的操作码。
    * BD-form
      条件转移指令使用的是这一指令格式。BO 指令字段指定了条件的类型;BI 指令字段指定了以哪个 CR 位作为条件;BD 字段用作转移位置。AA 位指定了转移是绝对转移还是相对转移。换名话说,转移目标地址是立即字段的值,还是立即字段的值与转移地址的和。LK 位指定了下一个顺序指令的地址是否作为子例程调用的返回地址保存在链接寄存器中。
    * I-form
      无条件转移指令使用这一指令格式。由于是无条件的,BD 格式中的 BO 和 BI 字段改变为另外的转移位置,以构成 LI 指令字段。同 BD 格式一样,这一指令格式也支持 AA 和 LK 位。

如前所述,这些指令格式各有其变种。不过,这些格式是对大部分 PowerPC 指令集编码结构的最好描述。

转移指令


PowerPC 为控制流程提供了一组指令,包括:

    * 条件和无条件转移指令。
    * “递减计数和如果是零或者非零时转移”的能力。
    * 绝对转移和相对转移。
    * 使用链接寄存器或计数寄存器来指定转移目标地址的转移指令。

所有的转移指令都具备保存后继顺序指令地址的能力,包括到链接寄存器的转移。条件寄存器 32 位中的任意一位都可以指定为条件转移的条件,并可以指定 CR 位是否必须为 0 或 1 时转移条件才成立。

条件寄存器指令


PowerPC 提供了一组用于对 CR 的特定位执行布尔操作和对 CR 字段进行拷贝的指令。它允许组合多个转移条件,这样可以减少代价高昂的条件转移的数量。表 3 列出了 PowerPC CR 逻辑指令。

表 3. PowerPC CR 逻辑指令
助记符    指令名
crand    CR logical and
crandc    CR logical and with complement
creqv    CR logical equivalent
crnand    CR logical not and
crnor    CR logical not or
cror    CR logical or
crorc    CR logical or with complement
crxor    CR logical xor

整数运算指令


很多指令用于执行运算操作,包括 add、substract、negate、compare、multiply 和 divide。很多格式用于立即值、溢出检测以及进位和借位。各实现中 multiply 和 divide 的执行是不同的,因为这些通常是多周期指令。表 4 列出了 PowerPC 整数运算指令。

表 4. PowerPC 整数运算指令
助记符    指令名
add[o][.]    add [& record OV] [& record CR0]
addc[o][.]    add carrying [& record OV] [& record CR0]
adde[o][.]    add extended [& record OV] [& record CR0]
addi    add immediate
addis    add immediate shifted
addic[.]    add immediate carrying [& record CR0]
addme[o][.]    add to minus one [& record OV] [& record CR0]
addze[o][.]    add to zero [& record OV] [& record CR0]
divd[o][.]    divide doubleword [& record OV] [& record CR0]
divdu[o][.]    divide doubleword unsigned [& record OV] [& record CR0]
divw[o][.]    divide word [& record OV] [& record CR0]
divwu[o][.]    divide word unsigned [& record OV] [& record CR0]
mulhd[.]    multiply high doubleword [& record CR0]
mulhdu[.]    multiply high doubleword unsigned [& record CR0]
mulhw[.]    multiply high word [& record CR0]
mulhwu[.]    multiply high word unsigned [& record CR0]
mulld[o][.]    multiply low doubleword [& record OV] [& record CR0]
mulli    multiply low immediate
mullw[o][.]    multiply low word [& record OV] [& record CR0]
neg[o][.]    negate [& record OV] [& record CR0]
subf[o][.]    subtract from [& record OV] [& record CR0]
subfc[o][.]    subtract from carrying [& record OV] [& record CR0]
subfe[o][.]    subtract from extended [& record OV] [& record CR0]
subfi    subtract from immediate
subfis    subtract from immediate shifted
subfic[.]    subtract from immediate carrying [& record CR0]
subfme[o][.]    subtract from to minus one [& record OV] [& record CR0]
subfze[o][.]    subtract from to zero [& record OV] [& record CR0]

逻辑、循环和移位指令


PowerPC 提供了一组完整的逻辑操作(指令),还支持对符号的扩展以及对 GPR 中前置零的统计。表 5 列出了 PowerPC 逻辑指令。

表 5. PowerPC 逻辑指令
助记符    指令名
and[.]    and [& record CR0]
andc[.]    and with complement [& record CR0]
andi.    and immediate & record CR0
andis.    and immediate shifted & record CR0
eqv[.]    equivalent [& record CR0]
nand[.]    not and [& record CR0]
nor[.]    not or [& record CR0]
or[.]    or [& record CR0]
orc[.]    or with complement [& record CR0]
oris    or immediate shifted
ori    or immediate
xor[.]    xor [& record CR0]
xoris    xor immediate shifted
xori    xor immediate
cntlzd[.]    count leading zeros doubleword [& record CR0]
cntlzw[.]    count leading zeros word [& record CR0]
extsb[.]    extend sign byte [& record CR0]
extsh[.]    extend sign halfword [& record CR0]
extsw[.]    extend sign word [& record CR0]

PowerPC 提供了一组健壮而强大的循环和移位操作(指令),如表 6 所列。

表 6. PowerPC 循环和移位指令
助记符    指令名
rldc[.]    rotate left doubleword then clear [& record CR0]
rldcl[.]    rotate left doubleword then clear left [& record CR0]
rldcr[.]    rotate left doubleword then clear right [& record CR0]
rldicl[.]    rotate left doubleword immediate then clear left [& record CR0]
rldicr[.]    rotate left doubleword immediate then clear right [& record CR0]
rldimi[.]    rotate left doubleword immediate then mask insert [& record CR0]
rlwimi[.]    rotate left word immediate then mask insert [& record CR0]
rlwinm[.]    rotate left word immediate then and with mask [& record CR0]
rlwnm[.]    rotate left word then and with mask [& record CR0]
sld[.]    shift left doubleword [& record CR0]
slw[.]    shift left word [& record CR0]
srad[.]    shift right doubleword [& record CR0]
sradi[.]    shift right doubleword immediate [& record CR0]
sraw[.]    shift right word [& record CR0]
srawi[.]    shift right word immediate [& record CR0]
srd[.]    shift right doubleword [& record CR0]
srw[.]    shift right word [& record CR0]

浮点指令


PowerPC 提供了一组健壮的浮点运算、比较和转换操作(指令)。与软件支持一道,PowerPC 浮点运算完全符合 ANSI/IEEE 标准 754-1985 规范。在所有运算和比较操作中都支持单精度和双精度浮点格式。

虽然浮点数以双精度格式存储于 FPR 中,但是,有一组单精度运算指令,可以执行运算操作并将最终结果舍入为单精度,同时检测进行单精度操作时可能会发生的异常(比如指数溢出、下溢和失去精度)。

    * 一组 Load Floating-point Single指令可以访问存储器中的字,并在将其放入目标 FPR 前把单精度值转换为双精度值。
    * 一组 Store Floating-point Single指令可以将源 FPR 中的源操作数在存储到存储器中目标字之前转换为单精度格式。

可以启用或禁用具体种类的浮点异常来支持设陷(trapping) 环境。表 7 列出了基本的和可选的 PowerPC 浮点指令集。

表 7. PowerPC 浮点指令
助记符    指令名
fmr[.]    FP move [& record CR1]
fneg[.]    FP negate [& record CR1]
fabs[.]    FP absolute value [& record CR1]
fnabs[.]    FP negative absolute value [& record CR1]
fadd[s][.]    FP add [single] [& record CR1]
fsub[s][.]    FP subtract [single] [& record CR1]
fmul[s][.]    FP multiply [single] [& record CR1]
fdiv[s][.]    FP divide [single] [& record CR1]
fsqrt[s][.]    FP square root [single] [& record CR1]
fmadd[s][.]    FP multiply-add [single] [& record CR1]
fmsub[s][.]    FP multiply-subtract [single] [& record CR1]
fnmadd[s][.]    FP negative multiply-add [single] [& record CR1]
fnmsub[s][.]    FP negative multiply-subtract [single] [& record CR1]
fcmpo    FP compare ordered
fcmpu    FP compare unordered
fsel[.]    FP select [& record CR1]
frsp[.]    FP round to single [& record CR1]
fcfid[.]    FP convert from integer doubleword [& record CR1]
fctid[z][.]    FP convert to integer doubleword [& round to zero] [& record CR1]
fctiw[z][.]    FP convert to integer word [& round to zero] [& record CR1]
fres[.]    FP reciprocal estimate single [& record CR1]
frsqrte[.]    FP reciprocal square root estimate [& record CR1]

FPSCR 处理指令


表 8 列出了基本的 PowerPC FPSCR 处理指令集。

表 8. PowerPC FPSCR 处理指令集
助记符    指令名
mcrfs    move to CR from FPSCR
mffs[.]    move from FPSCR
mtfsb0[.]    move to FPSCR bit 0
mtfsb1[.]    move to FPSCR bit 1
mtfsf[.]    move to FPSCR field
mtfsfi[.]    move to FPSCR field immediate

加载和存储指令


所有加载和存储指令的执行都使用 GPR 或者 GPR 和指令中的立即字段作为存储器访问的地址说明符。用指令生成的数据有效地址来更新基址寄存器(也就是 RA)是大部分加载和存储指令的一个可选项。

有用于以下方面的指令:

    * 字节、半字、字和双字大小。
    * 在 GPR 或 FPR 与存储器之间移动数据。
    * 在 GPR 或 FPR 与存储器之间移动数据。

特殊的存储器访问指令包括:

    * 多字加载/存储
      即 lmw 和 stmw ,可以操作最多 31 个 32 位字。

    * 字符串指令
      这些指令可以操作最长 128 字节的字符串。

    * 内存同步指令
      这些用于实现内存同步。CR 的第 2 位(EQ 位) 设置用来记录存储操作的成功完成。内存同步指令包括:
          o lwarx (加载字并预留变址)
          o ldarx (加载双字并预留变址)
          o stwcx (存储字条件变址)
          o stdcx (存储双字条件变址)
      lwarx / ldarx 执行加载并设置处理器内部的预留位,编程模型不必明确了解这些行为。如果设置了预留位,相应的存储指令 stwcx. / stdcx. 执行条件存储,并清除预留位。




PowerPC 体系结构没有关于本地存储器的栈的概念。体系结构没有定义压入或者弹出指令,也没有定义专门的栈指针寄存器。不过,有一个软件标准可用于 C/C++ 程序,这个标准叫做嵌入式应用程序二进制接口( Embedded Application Binary Interface,EABI),它定义了栈寄存器和内存的约定。EABI 将 GPR1 预留为栈指针,GPR3 到 GPR7 用于函数参数传递,GPR3 用于函数返回值。

需要为 C/C++ 提供接口的汇编语言程序必须遵循同样的标准来保持约定。

高速缓存管理指令


PowerPC 体系结构包含了面向应用级高速缓存访问的高速缓存管理指令。高速缓存指令在表 9 中列出。

表 9. 高速缓存管理指令
助记符    指令名
dcbf    Flush Data Cache Line
dcbst    Store Data Cache Line
dcbt    Touch Data Cache Line(for load)
dcbtst    Touch Data Cache Line(for store)
dcbz    Zero Data Cache Line
icbi    Invalidate Instruction Cache Line

当将高速缓存处理代码移植到不同的 PowerPC 实现时要当心。虽然高速缓存指令可能是跨不同实现而通用的,高速缓存的组织和大小可能会有变化。例如,假定高速缓存大小以对其进行刷新的代码,在用于其他大小的高速缓存时可能需要进行修改。而且,各实现的高速缓存初始化可能不同。有一些实现提供了自动清除高速缓存标签的硬件,而其他实现需要使用软件循环来使高速缓存标签无效。

自修改代码


虽然编写自修改代码不是一个工业标准,但有些情况下它是必不可少的。下面的序列介绍了执行代码修改用到的指令:

   1. 存储修改的指令。
   2. 执行 dcbst 指令,强制包含有修改过的指令的高速缓存行进行存储。
   3. 执行 sync 指令,确保 dcbst 完成。
   4. 执行 icbi 指令,使将要存放修改后指令的指令高速缓存行无效。
   5. 执行 isync 指令,清除所有指令的指令管道,那些指令在高速缓存行被设为无效之前可能早已被取走了。
   6. 现在可以运行修改后的指令了。当取这个指令时会发生指令高速缓存失败,结果就会从存储器中取得修改后的指令。





   回页首


计时器


大部分实现都提供了一个 64 位时基,可以通过两个 32 位寄存器读取,或者通过一个 64 位寄存器读取。各实现的计时器增量不同,SPR 数和访问时基的指令也不同。所以,跨实现移植计时器代码时要当心。另外的计时器可能也不同,但大多数实现都提供了至少一种递减的可编程计时器。




   回页首


保持代码的兼容性


需要在多个实现上进行编程的 PowerPC 用户通常会问及保持代码兼容性的技巧。下面的建议将有助于尽量减少移植问题:

    * 尽可能使用 C 代码。
      当今的 C 编译器在很多情况下可以生成与直接手写的汇编代码在性能上相当的代码。作为 Book I 代码,C 代码将保证代码的兼容性。

    * 尽可能避免使用处理器相关的汇编指令。
      尽量不要在 C 中嵌入处理器相关的汇编指令,因为它们将更难被发现。分离开那些已知会包含设备相关寄存器或指令的代码。这些通常是启动次序和设备驱动程序,不过也可能包括浮点代码(包括 long long 类型)。保持假定和依赖全部归档。

    * 使用处理器版本寄存器(PVR),但只在适当的时候用。
      跨差别较小的实现的通用代码还好,PVR 可以用于做出判断。但是,在需要进行较大修改的情况下(例如,PowerPC AS 相对于 Book E MMU 代码),建议使用单独的代码库(code base)。 



结束语


PowerPC AS 和 PowerPC 都支持最初的 PowerPC 体系结构中定义的应用级基础设施,同时为它们的具体目标市场提供了最佳的优化。

就其两种操作模式之一而言,PowerPC AS 实质上与最初的 PowerPC 相同,而 PowerPC Book E 在它的 Book III 级别的定义中,已经走向了不同的方向,为低价格、低功率以及体系结构灵活的嵌入式应用进行了最优化。当然,在 32 位的实现上还不能用双字整数指令,在大部分嵌入式实现中浮点指令也只是通过软件仿真提供支持。

不过,这对应用程序二进制在 PowerPC 体系结构家族树的分支中自由地迁移来说是一个非常有意义的机会。




   回页首


参考资料

    * 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

    * PowerPC 体系结构分为三个级别(或“books”)以保持跨应用的兼容性。在 PowerPC Architecture Book中查找所有的书籍以及另外的资料。



    * 要了解 IBM POWER 相关芯片制造的全部历史,请阅读“ 人类的 POWER:IBM 的芯片制造历史”( developerWorks, 2004 年 3 月)。



    * 在 IBM PowerPC站点查找更多资料。



    * 在 IBM PowerPC Web 站点技术库可以得到具有详细技术资料的用户手册。您还可以在专门的 PowerPC Product Briefs和 PPC 白皮书中找到有价值的资料。



    * 就其两种操作模式之一而言, PowerPC AS实质上与最初的 PowerPC 相同,而 PowerPC Book E为嵌入式市场而做了最优化。



    * “ PowerPC 汇编”( developerWorks, 2002 年 7 月) 从 PowerPC 的角度概要介绍了汇编语言,并对比了三种体系结构的例子。



    * “ A programmer's view of performance monitoring in the PowerPC microprocessor”( IBM Systems Journal, 1997)教您如何使用 PowerPC 芯片上的性能监控器(Performance Monitor,PM)来分析不同工作负荷下的处理器、软件和系统属性。



    * PowerPC Performance Libraries Project 为 IBM PowerPC 4xx 嵌入式处理器提供了许多优化的库函数。这些库涵盖了浮点仿真、常见的 C 库字符串和内存函数。



    * PMAPI 是一个库,它有一组 API,可以访问所选的 IBM POWERPC 微处理器所包含的硬件性能计数器。



    * PowerPC 体系结构是 精减指令集计算机(RISC)体系结构。



    * 要查找 iSeries、pSeries 和其他基于 POWER 的平台的软件和文献, developerWorks 的 Linux on POWER 开发者参考资料和下载页是极好的出发点。



    * IBM 的 Linux on PowerPC 团队的目标是改进和扩展用于嵌入式、32 位和 64 位处理器的 Linux。要获得兼容性的信息、文档、代码或者更多,请访问 Linux on PowerPC 主页。



    * IBM 的 Linux Technology Center 提供了 POWER 程序员参考资料汇编。



    * Linux on POWER概要介绍了来自 IBM 的 POWER 相关的 Linux 产品。



    * 在 developerWorks Linux 专区可以找到更多为 Linux 开发人员准备的参考资料。



    * 在 Developer Bookstore 的 Linux 区可以找到很多精选的 Linux 书籍。  PowerPC 体系结构之指令集2008-07-26 14:25 1. 概述

Book E 定义的 PowerPC 指令集的指令可分为以下几类:

    分支跳转指令
    CR 指令
    整数指令
    浮点指令
    处理器控制指令
    存储管理相关指令

CR 指令主要是对 CR 内部位运算支持的一些指令,如 crand, cror, crxor 等等。


2. 常用指令

先看一个测试程序:

------------------------------------------------------------------

int test_call(int a, int b, int c)
{
    a = b + c;
    return a;
}

int test_if(int s)
{
    int i;
    if(s > 0)
        i = s;
    else if(s < 0)
        i = -s;
    else
        i = s * 8;
    return i;
}

int test_cyc1(int c)
{
    int sum = 0;
    do {
        sum += c;
        c--;
    } while(c > 0);
    return c;
}

int test_cyc2(int c)
{
    int sum = 0;
    for(; c > 0; c--)
        sum += c;

    return c;
}

int main()
{
    int a, b, c, d;
    a = test_if(5);
    b = test_cyc1(10);
    c = test_cyc2(10);
    d = test_call(1, 2, 3);
    return a + b + c + d;
}

------------------------------------------------------------------

引入的目的在于查看判断、循环和过程调用这些基本结构在 PowerPC 里怎么被支持。

-O2 参数编译后,objdump -S -d 反汇编,则:

------------------------------------------------------------------

1000040c :
int test_call(int a, int b, int c)
{
    a = b + c;
    return a;
}
1000040c:    7c 64 2a 14     add     r3,r4,r5        ----> 对应 a, b, c 三个参数,同时 r3 又置返回值
10000410:    4e 80 00 20     blr                        ----> 跳转到 LR 所存放的地址处,即函数返回

10000414 :
int test_if(int s)
{
    int i;
    if(s > 0)
10000414:    7c 60 1b 79     mr.     r0,r3           -----> r3 移到 r0,若 r0 小于、大于、等于 0,则置 CR0 的相应位。指令后多一点,则说明该指令会据执行结果,设置 CR 的相应位
10000418:    7c 03 03 78     mr      r3,r0           -----> 此条指令多余
1000041c:    4d a1 00 20     bgtlr+                   -----> 若 CR0[gt] 位为 1,则跳转到 LR 所存放的地址处,即直接函数返回了。此条指令等价于 bclr      13, 1
        i = s;
    else if(s < 0)
10000420:    38 60 00 00     li      r3,0
10000424:    4d 82 00 20     beqlr              -----> 若 CR0[eq] 位为 1,则跳转到 LR 所存放的地址处,也直接函数返回了。此条指令等价于 bclr      12, 2
        i = -s;
10000428:    7c 60 00 d0     neg     r3,r0         -----> r0 取反,入 r3
    else
        i = s * 8;
    return i;
}
1000042c:    4e 80 00 20     blr                    -----> 跳转到 LR 所存放的地址处,函数返回

10000430 :
int test_cyc1(int c)
{
10000430:    34 03 ff ff     addic. r0,r3,-1
10000434:    7c 69 03 a6     mtctr   r3
10000438:    41 80 00 10     blt-    10000448
    int sum = 0;
    do {
        sum += c;
        c--;
1000043c:    38 63 ff ff     addi    r3,r3,-1
    } while(c > 0);
10000440:    42 00 ff fc     bdnz+   1000043c
    return c;
}
10000444:    4e 80 00 20     blr
10000448:    38 00 00 01     li      r0,1
1000044c:    7c 09 03 a6     mtctr   r0
10000450:    4b ff ff ec     b       1000043c

10000454 :
int test_cyc2(int c)
{
10000454:    2c 03 00 00     cmpwi   r3,0
10000458:    39 20 00 00     li      r9,0
1000045c:    7d 23 48 1e     .long 0x7d23481e
    int sum = 0;
    for(; c > 0; c--)
        sum += c;
    return c;
}
10000460:    7d 23 4b 78     mr      r3,r9
10000464:    4e 80 00 20     blr

int main()
{
10000468:   94 21 ff e0     stwu    r1,-32(r1)
1000046c:   7c 08 02 a6     mflr    r0
    int a, b, c, d;
    a = test_if(5);
10000470:   38 60 00 05     li      r3,5
10000474:   90 01 00 24     stw     r0,36(r1)
10000478:   93 61 00 0c     stw     r27,12(r1)
1000047c:   93 81 00 10     stw     r28,16(r1)
10000480:   93 a1 00 14     stw     r29,20(r1)
10000484:   4b ff ff 91     bl      10000414
10000488:   7c 7d 1b 78     mr      r29,r3
    b = test_cyc1(10);
1000048c:   38 60 00 0a     li      r3,10
10000490:   4b ff ff a1     bl      10000430
10000494:   7c 7b 1b 78     mr      r27,r3
    c = test_cyc2(10);
10000498:   38 60 00 0a     li      r3,10
    d = test_call(1, 2, 3);
1000049c:   7f bd da 14     add     r29,r29,r27
100004a0:   4b ff ff b5     bl      10000454
100004a4:   38 80 00 02     li      r4,2
100004a8:   7c 7c 1b 78     mr      r28,r3
100004ac:   38 a0 00 03     li      r5,3
100004b0:   38 60 00 01     li      r3,1
100004b4:   4b ff ff 59     bl      1000040c
    return a + b + c + d;
}
100004b8:   80 01 00 24     lwz     r0,36(r1)
100004bc:   7f 9c 1a 14     add     r28,r28,r3
100004c0:   83 61 00 0c     lwz     r27,12(r1)
100004c4:   7c 7d e2 14     add     r3,r29,r28
100004c8:   83 81 00 10     lwz     r28,16(r1)
100004cc:   83 a1 00 14     lwz     r29,20(r1)
100004d0:   7c 08 03 a6     mtlr    r0
100004d4:   38 21 00 20     addi    r1,r1,32
100004d8:   4e 80 00 20     blr

------------------------------------------------------------------
  

3. 分类概述

3.1 分支跳转指令


这类指令算是 PowerPC 里比较有特色的,也是稍显复杂的。这类指令与 CR, LR 和 CTR 紧密相联,建构起判断、循环和过程调用这些程序的基本结构。其大致可分为四类:

    Branch
    Branch Conditional
    Branch Conditional to Count Register
    Branch Conditional to Link Register


3.1.1 Branch


这类指令与 CR 没有联系,即为非条件跳转,助记符后直接跟立即数地址。指令内为立即数地址预留 26 位,即可跳转 2^26 大小的空间,如:(CIA, Current Instruction Address)

        b         0x20            -----> 以当前指令地址为基点,往后跳转 0x20 字节,即 PC = CIA + 0x20
        ba       0x20            -----> 直接跳转到地址 0x20 处。后缀为 a,则表示使用 Absolute Address,PC = 0x20
        bl        0x20            -----> 在 b 0x20 的基础上,将 LR 更新为 CIA + 4
        bla      0x20            -----> 使用绝对地址,且更新 LR。后缀带 l,则表示更新 LR 为 CIA + 4

以上针对 32 位的情形,对 64 位则使用指令 be, bea, bel, bela 功能与上同。

  
3.1.2 Branch Conditional

此类为条件跳转指令。皆以 bc 开头,带 3 个操作数,如:

       bc    BO, BI, BD
       bca    BO, BI, BD
       bcl    BO, BI, BD
       bcla   BO, BI, BD

后缀 a, l 的含义与 branch 类指令同。BO 指定跳转的条件,5 位;BI 指定关联的 CR 位,也是 5 位;BD 为跳转的立即数地址,16 位。

其中以 BO 的编码最为复杂(BO 从左到右编号为 0 ~ 4):
      
       BO[0]: 为 1,则直接跳转
       BO[1]: 为 1,则条件为真时,跳转。否则条件为假时,跳转
       BO[2]: 为 1,则 CTR 不自动减 1
       BO[3]: 为 1 时,则 CTR == 0 时跳转;为 0 时,则 CTR != 0 时跳转
       BO[4]: 静态预测位,1 表示 unlikely,0 表示 likely

则常见的 BO 值:
        20 (0b10100) 则表示无条件跳转
        12 (0b01100) 则表示 CR 的某个位为 1 时跳转
          4 (0b00100) 则表示 CR 的某个位为 0 时跳转

至于静态预测的策略位,默认被置为 0,则其行为为:

       b1. 目标地址小于当前指令地址,预测为跳转
       b2. 目标地址大于当前指令地址,预测为不跳转
       b3. 对于目标地址在 CTR/LR 中的条件跳转指令,一律预测为不跳转

若该位被置 1,则上述 b1, b2, b3 的静态预测行为分别为:不跳转跳转跳转

可以给分支指令加一个 +/- 的后缀,来简化。加 '+' 的指令,一律预测为跳转。加 '-' 的分支指令,一律预测为不跳转。

则对于 b1,后缀 '+' 会将 y 位置 0,'-' 则将 y 位置 1。
对于 b2,后缀 '+' 会将 y 位置 1,'-' 则将 y 位置 0。
对于 b3,后缀 '+' 会将 y 位置 1,'-' 则将 y 位置 0。


BI 与关联 CR 位的关系为:

        32 + BI

即,若 BI 为 2,则对应于 CR[34],即为 CR0[gt] 位。

以上针对 32 位的情形,对 64 位则使用指令 bce, bcea, bcel, bcela 功能与上同。


3.1.3 Branch Conditional to Count Register

        bcctr     BO, BI
        bcctrl    BO, BI

后缀 l 的含义与 branch 类指令同。
BO,BI 的编码与 Branch Conditional 类指令同。
跳转目标地址位于 CTR 中。

以上针对 32 位的情形,对 64 位则使用指令 bcctre, bcctrel 功能与上同。


3.1.4 Branch Conditional to Link Register

        bclr     BO, BI
        bclrl    BO, BI

后缀 l 的含义与 branch 类指令同。
BO,BI 的编码与 Branch Conditional 类指令同。
跳转目标地址位于 LR 中。

以上针对 32 位的情形,对 64 位则使用指令 bclre, bclrel 功能与上同。


3.2 CR 指令


这类指令包括用来支持 CR 内部位运算的指令和 CR 与 GPR 之间的数据交换指令。

3.2.1 CR 内位运算指令

这类指令的格式皆为:   crxxx    BT, BA, BB

BT, BA, BB 是 CR 内的位编号,取值范围为 0 ~ 31,如 crand    0, 8, 12,则将 CR[32+8] (CR2[lt]) 与 CR[32+12] (CR3[lt]) 作与操作后,将结果置入 CR[32+0] (CR0[lt]),即 CR[32] = CR[40] & CR[44]

crand:            CR[32+BA] & CR[32+BB]
crandc:           CR[32+BA] & (~CR[32+BB])
creqv:            ~(CR[32+BA] ^ CR[32+BB]),即位相等,则置 1;不等则置 0
crnand:       ~(CR[32+BA] & CR[32+BB])
crnor:            ~(CR[32+BA] | CR[32+BB])
cror:             CR[32+BA] | CR[32+BB]
crorc:            CR[32+BA] | (~CR[32+BB]),先取反后再或
crxor:            CR[32+BA] ^ CR[32+BB],位异或


3.2.2 CR 与 GPR 间数据交换指令

3.2.2.1
mcrf         BF, BFA   ---   Move Condition Register Fields

将 CR 之 BFA 域复制到 BF 域。如 mcrf    0, 3 则将 CR3 拷入 CR0


3.2.2.2
mtcrf      FXM, RS   ---   Move To Condition Register Fields

通用寄存器 RS 之 32 ~ 63 位以 4 位为单位,对应于 CR 的 8 个域,编号为 RS0 ~ RS7;
FXM 为域掩码,8 位,从左到右编号为 0 ~ 7,对应于 RS、CR 的 8 个域。某位为 1,则将对应的 RS 域拷贝到对应的 CR 域中。
若 FXM 为 0x80,则只将 CR0 = RS0
若 FXM 为 0xc8,则将 CR0 = RS0, CR1 = RS1, CR4 = RS4

3.2.2.3 mfcr      RT   ---   Move From Condition Register

将 CR 的内容移入通用寄存器 RT 的 32 ~ 63 位。


3.3 整数指令

这类指令大致分为如下几类:

    整数访存指令
    整数算术运算指令
    整数逻辑运算指令
    整数比较指令
    整数陷阱指令
    整数移位指令
    XER 指令

其中以整数循环移位指令最为特别。

3.3.1 整数访存指令

3.3.1.1 Load Byte/Half-word/Word and Zero

该类指令从指定地址处读取 8 位、16 位、32 位数据,置入 RT,RT 高位置 0

    lbz/lbzu            RT, D(RA)
    lbzx/lbzux          RT, RA, RB

    lhz/lhzu            RT, D(RA)
    lhzx/lhzux          RT, RA, RB

    lwz/lwzu            RT, D(RA)
    lwzx/lwzux          RT, RA, RB

RT, RA, RB 皆为 GPR,D 为有符号立即数。则有效地址的计算分别为:RA[32:63] + D,RA[32:63] + RB[32:63],有效地址高 32 位置 0。

后缀 u 表示 Update,即将有效地址更新到 RT 中
后缀 x 表示 Indexed,即使用 RA + RB 的寻址方式

以上用于 32 位,对 64 位,上述指令皆加后缀 'e'。则有效地址的 0 ~ 31 位, 不再置 0,其计算方式如下:RA + D,RA + RB。

此外,用于 64 位的还有对双字操作的支持:

lde/ldue/ldxe/lduxe


3.3.1.2 Load Half-word Algebraic

    lha/lhau            RT, D(RA)
    lhax/lhaux          RT, RA, RB

与 lhz 不同的是,该指令将所读取的半字的最高位 (RT[48]) 填充到 RT[32:47],实际上就是形成一个 16 位的补码有符号数。

有效地址计算与上同;后缀 u, x 与上同。

对 64 位情形,上述指令皆加后缀 'e',有效地址计算与上小节同。


3.3.1.3 Load Halfword/Word Byte-Reverse

从指定地址处读取 16 位/32 位数据,将字节反转后置入 RT,RT 高位置 0

    lhbrx   RT, RA, RB
    lwbrx   RT, RA, RB

如: lwbrx   r3, r2, r1,若 r2 + r1 地址处的数据为 0x55aa66bb,则 r3 的结果为 0xbb66aa55

对 64 位情形,上述指令皆加后缀 'e',有效地址计算与上小节同。


3.3.1.4 Load Multiple Word

    lmw    RT, D(RA)

该指令将 RA + D 开始处的数据,顺序置入 RT ~ R31 中,共读取 31 - T 个字

该指令无 64 位扩展。


3.3.1.5 Load String Word

    lswi    RT, RA, NB      (后缀 i,表示立即数 Immediate)
    lswx    RT, RA, RB      (后缀 x,表示 Indexed 寻址方式)

加载 n 个字节到 RT 开始的寄存器中;
当 NB == 0 时 n = 32; NB != 0 时 n = NB,NB 取值范围为 0 ~ 31

如 r3 = 0x1000 :

    lswi   r4, r3, 16  

则将 0x1000 处的 16 字节,依次写入 r4, r5, r6, r7

对于 lswx,要加载的字节数则位于 XER[57:63]

该指令无 64 位扩展。


3.3.1.6 Load Word/Doubleword and Reserve

    lwarx    RT, RA, RB

该指令与 lwzx 的差别在于,其还将处理器内部的 RESERVE 位置为 1,并将有效地址对应的物理地址放入 RESERVE_ADDR 中。

该指令的 64 位版本为: lwarxe   RT, RA, RB,差别还是在于有效地址的 0 ~ 31 不被置 0

此外还有一个 ldarxe   RT, RA, RB,与 lwarxe 的差别在于其加载 8 字节的数据。

这三条指令常分别与 stwcx./stwcxe./stdcxe. 联用,用于实现锁操作,类似 MIPS 之 ll/sc 指令。


3.3.1.7 Store

    stb/stbu            RS, D(RA)
    stbx/stbux          RS, RA, RB
    sth/sthu            RS, D(RA)
    sthx/sthux          RS, RA, RB
    stw/stwu            RS, D(RA)
    stwx/stwux          RS, RA, RB

该类指令将 RS 中的 8 位、16 位、32 位数据写入有效地址处。后缀 'u', 'x' 之含义与 3.3.1.1 同。

上述 12 条指令加后缀 'e' 则用于 64 位。有效地址计算与与 3.3.1.1 同。

另用于 64 位的还有对双字操作的支持:

stde/stdue/stdxe/stduxe


3.3.1.8 Store Halfword/Word Byte-Reverse

    sthbrx RS, RA, RB
    stwbrx RS, RA, RB

将 RS 中的 16/32 bit 的数据字节反转后置入有效地址处。该指令用于支持在大端系统上以小端序存储数据,亦或在小端系统上以大端序存储数据。

对 64 位情形,上述指令皆加后缀 'e',有效地址计算与上小节同。


3.3.1.9 Store Multiple Word

    smw   RS, D(RA)

该指令将 RS ~ R31 中的数据 (一个字),写入到 RA + D 处

该指令无 64 位扩展。


3.3.1.10 Store String Word

    stswi      RS, RA, NB    (后缀 i,表示立即数 Immediate)
    stswx      RS, RA, RB    (后缀 x,表示 Indexed 寻址方式)

与 lswi/lswx 的操作相反,其将 RS 开始的寄存器组中的 n 个字节,写入到有效地址处(位于 RA)

当 NB == 0 时 n = 32; NB != 0 时 n = NB,NB 取值范围为 0 ~ 31

如 r3 = 0x1000 :

    stswi   r4, r3, 16  

则将 r4, r5, r6, r7 中的 16 字节数据依次写入 0x1000 处

对于 stswx,要加载的字节数则位于 XER[57:63]

该指令无 64 位扩展。


3.3.1.11 Store Word/Doubleword Conditional

    stwcx.         RS, RA, RB

如果 RESERVE 位为 1,且 RA + RB 对应的物理地址与 RESERVE_ADDR 一致,则将 RS[32:63] 的内容写入到 RA + RB 处,将 CR0[eq] 位置为 1 后,再将 RESERVE 位复位为 0。

若条件不满足,则仅将 CR0[eq] 位置为 0

可用该指令与 lwarx 联用,实现 spin_lock:

    li   r0, 1
loop:
    lwarx   r4, 0, r3
    cmpwi   r4, 0          # r4[32:63] 与 0 比较
    bne loop
    stwcw.   r0, 0, r3
    bne   loop
    isync

解锁则为:

    msync
    li   r0, 0
    stw   r0, 0(r3)


下面的指令用于 64 位:

    stwcxe.       RS, RA, RB
    stdcxe.       RS, RA, RB

差别在于有效地址的高 32 位不再被置为 0。


3.3.2 整数算术运算指令



3.3.3 整数逻辑运算指令



3.3.4 整数比较指令


    cmp       BF, L, RA, RB      
    cmpi       BF, L, RA, SI          # RA 与有符号立即数 SI 比较
    cmpl       BF, L, RA, RB          # RA, RB 逻辑比较。l ---> logical
    cmpli      BF, L, RA, UI          # RA 与无符号立即数 UI 逻辑比较

BF 取值 0 ~ 7,用于指定使用的 CR 域
L 为 0,则为 32 位比较;为 1,则为 64 位比较

如:

    cmpi      0, 0, r3, 5

若 r3 > 5,则 CR0[gt] = 1;若 r3 < 5,则 CR0[lt] = 1


3.3.5 整数陷阱指令

    tw      TO, RA, RB
    twi   TO, RA, SI

TO 为立即数,5 位,从左到右标号为 0 ~ 4,依次表示小于、大于、等于、无符号小于、无符号大于。用于指定 Trap 的条件。
SI: Signed Immediate

如:
      
    twi   0x10, r3, 5

则如果 r3 < 5,则陷入异常。

以上用于单字 (32 bit) 比较,双字比较则用:

    td      TO, RA, RB
    tdi   TO, RA, SI


3.3.6 整数移位指令


3.3.6.1 Rotate

    rlwimi        RA, RS, SH, MB, ME            # Rotate Left Word Immediate then Mask Insert
    rlwinm        RA, RS, SH, MB, ME            # Rotate Left Word Immediate then AND with Mask
    rlwnm         RA, RS, RB, MB, ME            # Rotate Left Word then AND with Mask

以下是该指令的一些例子:

all r11 is: 0x55aa67bb

r0 = 0x00000000
rlwimi    r0, r11, 8, 0, 31
r0 is: 0xaa67bb55

r0 = 0x12345678
rlwimi    r0, r11, 8, 0, 14
r0 is: 0xaa665678

dst = 0x12345678
rlwimi    r0, r11, 8, 28, 3
r0 is: 0xa2345675

r0 = 0x00000000
rlwinm    r0, r11, 8, 0, 31
r0 is: 0xaa67bb55

r0 = 0x12345678
rlwinm    r0, r11, 8, 0, 14
r0 is: 0xaa660000

    rldcl
    rldcr
    rldic
    rldicl
    rldicr
    rldimi

3.3.6.2 Shift

    slw
    srw
    sraw
    srawi

    slw./srw./sraw./srawi.

    sld
    srd
    srad
    sradi


3.3.7 XER 指令

    mcrxr          BF
    mcrxr64        BF


3.4 浮点指令

这类指令包括:
      
    浮点数据加载与存储指令
    浮点运算指令
    浮点数近似和转化指令
    浮点比较指令
    浮点状态和控制寄存器指令

有关该指令的详情留待以后以《PowerPC 浮点结构》的形式讨论吧。

3.5 处理器控制指令

这类指令包括用于支持异常中断处理的:

    sc            System Call
    rfci          Return From Critical Interrupt
    rfi         Return From Interrup

用于处理器控制寄存器维护的:

    mfspr      RT, SPRN         Move From SPRN (Special Purpose Register) to RT
    mtspr      SPRN, RS         Move RS To SPRN

    mfdcr      RT, DCRN         Move From DCRN (Device Control Register) to RT
    mtdcr      DCRN, RS         Move RS To DCRN

    mfmsr      RT            Move From MSR to RT
    mtmsr      RS            Move RS To MSR

    wrtee        RA    Write MSR External Enable, MSR[EE] = RA[48]
    wrteei    E   Write MSR External Enable Immediate, MSR[EE] = E

以及用于指令同步的:

    isync

其保证在其之前的所有指令皆提交,在其自己被提交前,其之后的指令不会进入流水线


3.6 存储管理相关指令

这类指令包括用于 TLB 管理的:

    tlbivax
    tlbre
    tlbwe
    tlbsx
    tlbsync

    tlbivaxe
    tlbsxe

用于 Cache 管理的:

    dcba/dcbf/dcbi/dcbst/dcbt/dcbtst/dctz/icbi/icbt
    dcbae/dcbfe/dcbie/dcbste/dcbte/dcbtste/dctze/icbie/icbte

用于存储同步的:

    msync
    mbar
 PowerPC 体系结构之 ULR 和 ABI2008-07-20 17:54 A. 以下取 Book E 之 ULR 精要

ULR (User Level Register) 即用户态程序能用的寄存器,包括只读的和可读写的。

最常用的 ULR 包括以下寄存器:

1. GRP (General Purpose Register) 通用寄存器,即用于定点运算的寄存器,共 32 个

2. CR (Condition Register) 指令状态寄存器,其格式如下所示:


注意:Book E 对寄存器的位编号与通常不同,因其支持 PowerPC 64,故其将 64 位寄存器从最高位 (MSB) 到最低位 (LSB) 编号为 0 ~ 63,32 位寄存器则编号为 32 ~ 63。

CR 以4位为单位分成 8 个域,每个域能描述 4 种状态。

2.1 CR0 用于存放 RC 位为1 的整数指令执行后的状态。CR[32:35] 依次表示小于、大于、等于和溢出。当指令执行的结果小于、大于或等于 0 时则置相应的CR[32:34] 的位;CR[35] 的值则直接复制自XER 的 SO (Summary Overflow)位。

2.2 CR1用于存放 RC 位为 1 的所有浮点指令执行后的浮点异常状态。其内容直接复制于浮点状态与控制寄存器 (FPSCR),CR[36:39] 依次对应于浮点异常 (FX)、浮点使能异常 (FEX)、浮点非法操作异常 (VX) 和浮点溢出异常 (OX)。

RC 位为 1 的指令,其助记符的最后有一个 '.' ,如 addic./divw./mullw./neg./and./or./xor./nand./nor.

2.3 对于比较指令(整数和浮点),可以通过操作数 BF 指定比较结果所存储的 CR 域:

       cmp   3, 0, r3, r4        (cmp    BF, L, RA, RB)

操作数 L = 0,表示此为 32 位比较,即只比较 r3, r4 的 32 ~ 63 位。若 r3 > r4,则将 CR3[gt] (CR[45]) 置位。浮点比较类似,但域内第 4 位含义与整数不同,整数依然表示溢出 (SO),且其值拷贝自 XER[SO];而对浮点比较,若该位被置,则表示某一浮点操作数不是数值。

2.4 此外 RC 位为 1 的条件存储指令 (stwcx./stwcxe./stdcxe.),在存储操作成功时置 CR0[2] (EQ 位) 为 1。

3. LR (Link Register) 链接寄存器,用于存放返回地址
   Branch and Link 类指令(如 bl,指令内 LK 位为 1)会将下条指令的地址自动写入 LR。bl 用于调用子过程,则置入 LR 的即为返回地址
   Branch Conditional to Link Register 类指令(如 bclr)则使用 LR 作为跳转的目标地址,该类指令通常用于子过程返回

4. CTR (Count Register) 计数寄存器,常用于存放跳转目标地址,与 Branch Conditional to Count Register 类指令配合使用;亦可存放循环变量与条件分支指令 (Branch Conditional) 配合,实现循环

CR,LR 和 CTR 是与分支指令紧密联系的,在实现基本程序结构之判断、循环以及过程调用方面作用巨大。

5. XER (Integer eXception Register) 整数异常寄存器,用于置整数运算的溢出 (Overflow)、进位 (Carry Out) 以及批量加载和存储指令要操作的字节数

6. E500 ULR

下图为 e500 之 ULR 概览:


可以看到 e500 没有浮点寄存器,其没有浮点部件。


7. E600 ULR

下图为 e600 之 ULR 概览:


e600 则含32个浮点寄存器,且含 32 个向量寄存器 (VR0 ~ VR31) 用于支持向量计算。


B. ABI


ABI 即 Application Binary Interface。其主要规定通用寄存器的使用约定,以及过程调用的栈的组织。一般最常用的就是通用寄存器的使用约定。Linux 下常用SYS V ABI。

以下是比较诸多 PowerPC ABI 版本后的一个交集:

GPR1: Stack Pointer
GPR3 ~ GPR4: 参数1 ~ 2,同时复用之,置 2 个返回值
GPR5 ~ GPR10: 参数 3 ~ 8

e500 ABI 继承于 System V ABI PowerPC Processor Supplement

以较为常用的 System V ABI 为准,其他寄存器的约定如下:

GPR0: volatile, may be used by function linkage
GPR2: reserved for system
GPR11 ~ 12: volatile, may be used by function linkage
GPR13: small data area pointer
GPR14 ~ 31: saved


C. Reference

[1] Book E: Enhanced PowerPC Architecture, v1.0, 2002.7
[2] PowerPC e500 Core Family Reference Manual, Rev. 1, 2005.4
[3] e600 PowerPC Core Reference Manual, Rev. 0, 2006.3
[4] SYSTEM V APPLICATION BINARY INTERFACE PowerPC Processor Supplement (SYS V ABI), Rev. A, 1995.9
[5] PowerPC Embedded Application Binary Interface (EABI), v1.0, 1995.1
[6] PowerPC e500 Application Binary Interface User’s Guide, Rev. 1.0, 2003.3   PowerPC体系结构学习笔记之ISA篇(之四)2008-05-16 14:25 XFX-Form类指令:
该类指令主要包括mtspr,mfspr,mtcr,mfcr指令。主要用于访问SPR寄存器和CR寄存器。XFX-Form类指令格式如下:
0-5    6-10    11-20    21-30    31
OPCD    RT    spr/tbr    XO       -

XO-Form类指令:
该类指令主要用来支持带进位的算术运算和乘除指令,指令格式如下:
0-5    6-10    11-15    16-20    21    22-30    31
OPCD    RT      RA        RB OE XO    Rc
主要指令有:
addo.,subfo.,addco.,subfco.,除了影响CA,SO,OV位和CR0字段外,还可以将CA位参与加减运算
addeo.,subfeo.,addzeo.,subfzeo., 除了影响CA,SO,OV位和CR0字段外,还可以将CA位参与加减运算
mullw,divw,用作乘除运算

M-Form类指令:
该指令主要作用是对选定的字段循环左移并做一些相应的掩码操作。指令格式如下:
0-5    6-10    11-15    16-20    21-25    26-30    31
OPCD    RS      RA       SH       MB       ME      Rc
最常用的指令是rlwimi RA,RS,RH,MB,ME,功能描述如下:
首先将存放在RS中的值循环左移SH位,将结果赋予r。
然后,用MASK计算掩码m,MASK(MB+32,ME+32)表示将一个数据中的第MB+32——ME+32字段置1,其他字段置0,然后赋予m。
如果ME小于MB,则将ME+32和MB+32之间(不含ME+32,MB+32)字段置0,其他置1。
最后将r&m|(RA)&(!m)的结果赋予RA。
还有一条重要指令是rlwinm,区别在于最后将RA寄存器赋值为r&m。该指令的主要作用就是从RS寄存器中提取一个字段,然后放到RA寄存器中。

到这里,关于PowerPC指令集的部分就介绍完了。加上前面的文章,基本上涵盖了PowerPC的寄存器,ABI和指令集,都是处理器中比较核心得到部分。后面的文章主要介绍中断处理,内存管理等内容。
   PowerPC体系结构学习笔记之中断处理(1)——中断系统简介2008-07-10 17:32 在E500内核中,中断处理器正常执行流的事件有两种——异常和中断。异常是由E500内核内部产生的,比如TLB miss之类的事件;而中断是通过处理器的外部引脚产生的。其实,中断本身就是异常的一种。

E500的外部中断可以通过3个信号产生#int,#cint和#mcp,分别代表中断,critical中断和Machine Check中断。
    #int信号有效时,表示外部中断事件发生。这些中断来自于外部中断控制器(PIC)。当MSR寄存器的EE位为1且#int信号有效时,E500内核将处理这些外部中断。
    #cint信号有效时,表示critical中断发生。与#int的中断源一样,都是来自于外部中断控制器。当MSR寄存器的CE位为1且#cint信号有效时,E500内核将处理critical中断。
    #mcp出现上升沿时,表示Machine Check事件发生。当MSR寄存器的ME位为1且#mcp为上升沿时,E500将处理Machine Check事件。

E500内核异常的分类及优先级如下:
    1. HRESET。E500内核中没有为复位异常安排专门的中断向量。但是,E500的复位起始地址是0xFFFFFFFC,也可以算是复位异常的向量地址。这个处理有点像MIPS的复位,MIPS复位起始地址是在0xBFC00000。
    2. Machine Check。比如Cache的奇偶校验引发的事件就算此类异常。
    3. Critical Interrupt。前面已经提过了。
    4. Debug Interrupt。与critical中断共享异常向量,用于系统调试。当MSR寄存器的DE位为1时有效。
    5. External Interrupt。前面已介绍。
    6. ITLB MISS。当处理器取指时,如果访问指令的地址没有在TLB中命中时产生此异常。
    7. ISI。在用户模式访问内核模式下才能访问的内存空间,以及因为指令预取部件访问的字节序与定义的字节序不一致时引发此异常。
    8. Program。执行非法指令,在用户模式下执行内核模式才能执行的指令或者tw指令条件满足时,产生此异常。
    9. DTLB MISS。读取数据时,如果访问的数据地址没有在TLB中命中时产生此异常。
    10. DSI。用户模式下访问内核模式才能访问的内存空间,或者数据访问的字节序与定义的字节序不一致引发的异常。
    11. System Call。执行sc指令产生此异常。
    12. Decrementer。DEC寄存器产生的定时器异常,Linux PowerPC利用此异常实现时钟中断。  PowerPC体系结构学习笔记之中断处理(2)——异常向量2008-07-24 16:20 E500内核使用IVPR和IVORn寄存器保存中断向量。每个中断向量的入口地址是IVPR+IVORn。IVPR提供中断向量的基址,IVORn提供中断向量的偏移。IVORn与异常的对应关系如下:
IVOR0           Critical Interrupt
IVOR1           Machine Check
IVOR2           DSI
IVOR3           ISI
IVOR4           External Interrupt
IVOR5           Alignment
IVOR6           Program
IVOR7           Unsupported
IVOR8           System call
IVOR9           Unsupported
IVOR10         Decrementer
IVOR11         Fixed-interval timer interrupt
IVOR12         Watchdog timer interrupt
IVOR13         Data TLB error
IVOR14         Instruction TLB error
IVOR15         Debug
IVOR16 - IVOR31   Reserved
IVOR32            SPE APU unavailable
IVOR33            Embedded floating-point data exception
IVOR34            Embedded floating-point round exception
IVOR35            Performance monitor
IVOR36 - IVOR63   Reserved

E500这种处理异常的方式与MIPS大不一样,MIPS是安排了几个固定的入口,比如TLB refill入口就是0x80000000,而其他异常入口是0x80000180。然后通过软件查看Cause(ExeCode)位判断是什么种类的异常,然后再跳转过去。不过现在MIPS支持了VI和EIC模式中断,外部中断处理可以不通过0x80000180的固定入口了。
E500的这种处理方式也便于在多处理器上实现AMP应用,比如同时运行两个操作系统,这两个操作系统不可能共享一个异常处理程序,则可以通过IVPR区别开来。MIPS也有类似的考虑,引入了EBase寄存器来做为异常入口的基址寄存器,也是便于多处理器上的AMP应用。

在Linux内核中,通过宏SET_IVOR来设置IVORn寄存器,比如,对于外部中断处理程序,则调用SET_IVOR(4, ExternalInput)。其中,“4”表示使用IVOR4寄存器,“ExternalInput”表示外部中断处理程序的地址(低16位)。对于IVPR的初始化,则通过对IVPR直接赋值,即将interrupt_base的高地址赋给IVPR。

在Linux内核初始化的时候,会把所有需要初始化的IVOR赋上恰当的值。当有对应异常发生时,直接跳转到IVPR+IVORn所指向的地址即可。 PowerPC体系结构学习笔记之中断处理(3)——外部中断处理机制2008-07-31 16:16 在E500内核中,一个外部中断的处理机制大致如下:
1. 捕捉到外部中断信号#cint,#int
2. 保存被中断程序的返回地址到SRR0寄存器中,这个寄存器类似于MIPS的EPC寄存器
3. 保存MSR寄存器到SRR1中,如果是地址异常还会保存错误地址到DEAR寄存器中,类似于MIPS中的BadVaddr寄存器
4. 保留MSR寄存器中的CE,DE,ME位,其余全部清除。因此,E500在处理外部中断时,还可以被Critical中断,调试中断和Machine Check中断重入
5. 清除PR,IS,DS位。此时,处理器运行在内核模式
6. 根据IVPR和IVOR4寄存器获得中断向量,进行中断程序执行
7. 中断程序执行结束后,使用rfi指令返回。rfi指令从SRR1寄存器恢复MSR寄存器,从SRR0寄存器中获得中断的返回地址,注意这两步其实是一个原子操作      一个磁盘性能分析的案例2008-11-05 19:08 最近做了一个磁盘性能分析的工作,环境是在一个4核的x86机器上,有一块IDE的硬盘,用两个版本不同的kernel。现象是,老版本kernel的性能比新版本的要高很多。测试的程序就是简单的dd命令。拿到之后,开始分析,简单分析了一下磁盘访问数据zaikernel中所经的路径:

page cache->block layer->IDE driver

在这段期间从page cache到disk的数据传输,可能出现中断和DMA。

首先第一步是排除page cache的影响,先看了一下/proc/sys/vm目录下的一些关于page cache的设置,两个系统上是一样的。然后用dd命令的iflag=direct参数,这样dd命令可以绕过page cache,直接写磁盘,就是kernel中的direct I/O。结果还是老版本的性能高很多。这样,基本可以确定不是page cache引起的问题。

其次,就是查看block layer和IDE driver的问题。大概看了一下,没有多大改变,性能上不能有很大的区别。所以,block layer和IDE driver也不是问题所在。

最后,就看一下中断和DMA的情况了。查看了一下/proc/interrupts文件,发现老版本上中断只有几千个,而新版本上中断却又几十万个,差的也太多了。看来性能问题就是太多的中断造成的。既然page cache,block layer和IDE driver都已经排除了,那么影响中断数量的只有DMA一项了。于是,看了一下kernel的配置。果然,新版本中忘记配上IDEDMA了。打开该选项后,两个版本上性能基本差不多了。   PowerPC体系结构学习笔记之内存管理(1)——E500MMU简介2008-09-16 17:21 E500采用了两级MMU机制,L1-MMU和L2-MMU,以及一些辅助寄存器和指令来实现虚实地址的转换。E500与其他PowerPC处理器相比的一个最大不同就是,E500不能禁止虚实地址转换,并且支持两个地址空间,分别是地址空间0与地址空间1。其实这点有些类似于MIPS,除了直接通过虚拟地址来区分以外,MIPS处理器也没有机制禁止/启用地址翻译。

L1-MMU包含I-L1VSP,D-L1VSP,I-L1TLB4K和D-L1TLB4K。I-L1VSP和D-L1VSP含有4个条目,采用全相连结构。I-L1TLB4K和D-L1TLB4K含有64个条目,采用4路组相连结构。L2-MMU包含TLB0和TLB1。其中TLB0含有256个条目,采用2路组相连结构。TLB1含有16个条目,采用全相连结构。TLB0用来进行页式映射,TLB1用来处理段式映射。

在E500内核中将MMU分为两级有一下两个作用:
    L1-MMU的访问速度较快。这个原理类似于Cache的原理。
    L1-MMU采用哈佛结构,也就是程序地址空间与数据地址空间分离,可以提高地址转换的效率。但是L2-MMU没有采用这种结构。    PowerPC体系结构学习笔记之内存管理(2)——E500虚拟地址与MMU结构2008-11-19 16:10 最近比较忙,一直没有更新PowerPC的学习笔记,今天补上一篇。

E500是一个32位的处理器,支持4GB的地址空间。在内部使用41位的临时虚拟地址,组成如下:
40    39 32 31            0
AS     PID        EA

AS:来自于MSR寄存器的IS或DS位。所以,E500可以根据AS位将地址空间分为两种:地址空间0和地址空间1。但是,Linux并没有使用这个功能。kernel和用户空间的代码,数据都在地址空间0中,即AS位一直是0。
PID:E500支持3个8位的PI的寄存器来保存进程的PID。这点于MIPS的ASID作用一样。但是,Linux并没有使用PID,在TLB中简单的忽略PID的检查,全部设为0。
EA:32位的有效地址。

下面介绍一下E500 MMU的结构。在E500中,MMU的entry包含下列字段:
V:用来表示当前entry是否有效。为0时表示无效,为1时表示有效。在系统reset之后,所有entry的V位都设为0。
TS:就是上面地址空间中的AS。为0表示地址空间0,为1表示地址空间1。
TID[0:7]:就是上面地址空间中的PID。TID为0时,忽略PID的比较。在Linux中,全为0。
EPN[0:19]:虚拟页桢号。根据页大小不同,有效的位数页不同。
RPN[0:19]:物理页桢号。
SIZE[0:3]:表示当前entry的页大小。
PERMIS[0:5]:用于描述当前entry的访问控制位。分别是UR,SR,UW,SW,UX,SX。分别表示用户态和内核态的读/写/执行权限。
WIMGE[0:4]:W表示当前entry是write through还是write back。I表示当前entry是否忽略Cache。M表示对此数据区的访问需要进行存储一致性处理。G表示对相应存储区域进运行保护。E表示当前entry是小端还是大端。
X0,X1:描述一些额外属性。
U[0:3]:自定义用途。
IPROT:当IPROT为1时,表示当前entry被保护。不能使用tlbivax指令无效该entry。只能用tlbwe指令清除。在E500中只有TLB1支持该位,在TLB0中,该位一直为0。  PowerPC体系结构学习笔记之内存管理(3)——与MMU有关的寄存器2008-11-20 16:41 E500有一系列寄存器用于管理MMU。

PID0-2寄存器:用于保存当前进程的PID,该寄存器只有54-63位有效。
MMUCSR0寄存器:用于使无效TLB0和TLB1的所有entry。该寄存器由两位组成:L2TLB0_FI使无效TLB0的所有entry;L2TLB1_FI使无效TLB1的所有entry。
MMUCFG寄存器:用来保存当前MMU的配置信息,包括PID寄存器数量,大小,TLB的数量等。
TLB0CFG和TLB1CFG寄存器:用于描述TLB0和TLB1的配置信息,包括entry数量,页表尺寸等信息。

除上述寄存器外,E500还包含一些MAS寄存器,主要作用就是维护MMU中TLB的entry,作用类似于MIPS中的EntryHi和EntryLo寄存器。其中MAS1-3中存放的各个字段与TLB entry中的各个字段一一对应,MAS0寄存器用于决定写哪个TLB的哪个entry,作用类似于MIPS中的random和index寄存器。

MAS0寄存器:TLBSEL字段用于选择要操作的TLB,0表示TLB0,1表示TLB1。ESEL字段用于选择TLB的entry。当使用TLB1时,ESEL字段中低4位有效,用于选择TLB1中的entry。当使用TLB0时,ESEL中只有最低位有效,因为TLB0是两路组相连。此时,E500还需要MAS2寄存器的EPN的第45-51位决定组内不同的entry。NV位用于确定如何替换TLB0中的entry。
MAS1寄存器:
32 33      40      47   51 52   55
V IPROT        TID      TS   SIZE

MAS2寄存器:
32                       51   57 58 59 60 61 62 63
            EPN               X0 X1 W   I   M   G   E

MAS3寄存器:
32                       51 54   57 58 59 60 61 62 63
            RPN              U0-U3   UX SX UW SW UR SR

MAS4寄存器:用于存放当TLB miss异常发生时,对MAS0-3寄存器自动加载而使用的默认值,也就是说MAS4寄存器的内容是硬件写入的。E500为了提高TLB miss异常的效率,在异常出现时,E500会用MAS4寄存器中的值,自动填写MAS0-2寄存器中的一部分内容:
    MAS0[TLBSEL] <- MAS4[TLBSELD]
    MAS1[TID] <- MAS4[TIDSELD]
    MAS1[TSIZE] <- MAS4[TSIZED]
    MAS2[X0,X1] <- MAS4[X0D,X1D]
    MAS2[WIMGE] <- MAS4[WD,ID,MD,GD,ED]
MAS6寄存器:用于对TLB进行检索,MAS6寄存器提供SPID0和SAS,也就是PID和AS字段。    风河将在UltraSPARC T2上支持CGL Linux2008-04-17 09:31 风河将移植CGL Linux和WorkBench开发环境,支持Sun UltraSPARC T2多核处理器。首先获得支持的板子是Sun Netra CP3260 ATCA。该板子基于8核,64线程的UltraSPARC T2处理器。在2006年11月,风河已经完成了一个demo版的CGL Linux在UltraSPARC T1上。
UltraSPARC T2有8个核,核心频率可以达到900MHz到1.4GHz,但是功耗低于95瓦,平均下来每个硬件线程低于2瓦。同时T2还将带有10G以太网,PCI-E等扩展I/O接口。还有浮点和加密处理单元等。   内核中的原子上下文2008-04-09 17:35 内核的一个基本原则就是:在中断或者说原子上下文中,内核不能访问用户空间,而且内核是不能睡眠的。也就是说在这种情况下,内核是不能调用有可能引起睡眠的任何函数。一般来讲原子上下文指的是在中断或软中断中,以及在持有自旋锁的时候。内核提供了四个宏来判断是否处于这几种情况里:
#define in_irq()     (hardirq_count()) //在处理硬中断中
#define in_softirq() (softirq_count()) //在处理软中断中
#define in_interrupt() (irq_count()) //在处理硬中断或软中断中
#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0) //包含以上所有情况
这四个宏所访问的count都是thread_info->preempt_count。这个变量其实是一个位掩码。最低8位表示抢占计数,通常由spin_lock/spin_unlock修改,或程序员强制修改,同时表明内核容许的最大抢占深度是256。
8-15位表示软中断计数,通常由local_bh_disable/local_bh_enable修改,同时表明内核容许的最大软中断深度是256。
位16-27是硬中断计数,通常由enter_irq/exit_irq修改,同时表明内核容许的最大硬中断深度是4096。
第28位是PREEMPT_ACTIVE标志。用代码表示就是:

PREEMPT_MASK: 0x000000ff
SOFTIRQ_MASK: 0x0000ff00
HARDIRQ_MASK: 0x0fff0000

凡是上面4个宏返回1得到地方都是原子上下文,是不容许内核访问用户空间,不容许内核睡眠的,不容许调用任何可能引起睡眠的函数。而且代表thread_info->preempt_count不是0,这就告诉内核,在这里面抢占被禁用。

但是,对于in_atomic()来说,在启用抢占的情况下,它工作的很好,可以告诉内核目前是否持有自旋锁,是否禁用抢占等。但是,在没有启用抢占的情况下,spin_lock根本不修改preempt_count,所以即使内核调用了spin_lock,持有了自旋锁,in_atomic()仍然会返回0,错误的告诉内核目前在非原子上下文中。所以凡是依赖in_atomic()来判断是否在原子上下文的代码,在禁抢占的情况下都是有问题的。