新闻中心

EEPW首页 > 汽车电子 > 设计应用 > 英飞凌 AURIX TC3XX内核寄存器结构及指令集详解

英飞凌 AURIX TC3XX内核寄存器结构及指令集详解

作者:林Nova 时间:2025-04-01 来源:英飞凌汽车电子生态圈 收藏

本文主要介绍Infineon ™️2G 系列芯片内核结构及相关寄存器,并结合代码示例讲解芯片内核指令集系统。

#01前  言
1.1 内核
芯片内核芯片内核(Core)是中央处理器(CPU)中的独立处理单元,能够执行指令、处理数据和控制操作。
™️2G 系列芯片的内核架构是一种混合架构,同时结合了精简指令集计算机(RISC)和复杂指令集计算机(CISC)的特征,称为TriCore™️内核架构(以下简称TriCore™️)。它是一款专门为实时性进行了优化的32位的多核同构嵌入式系统架构。之所以命名为TriCore™️,是因为其集成了RISC架构、DSP架构和实时系统的技术于一体,形成了兼具三方优势的内核架构。
图片
TriCore™ 架构采用了RISC的高性能load/store数据处理模式,同时具有DSP的数据处理能力。TriCore™ 是32位处理器架构,因此采用32位地址空间,支持可选的虚拟地址空间。
下面是TriCore™ 架构的特性:   

本文引用地址:https://www.eepw.com.cn/article/202504/468911.htm
  • 32位架构

  • 4GB的地址空间

  • 同时支持16位和32位指令,减少代码大小

  • 大多数指令在一个周期内执行

  • 分支指令(使用分支预测)

  • 使用并行数据存储器,实现低中断延迟与快速自动上下文切换

  • 专用接口特定于应用程序的协处理器,以允许添加定制指令

  • 零开销回路功能

  • 双/单时钟周期,16x16位乘法累加单元(可选饱和)

  • 可选浮点单元(FPU)和内存管理单元(MMU)

  • 广泛的位处理能力

  • 单指令多数据(SIMD)打包数据操作(2x16位或4x 8位操作数)

  • 灵活的中断优先级方案

  • 字节和位寻址

  • 数据内存和CPU寄存器的小端字节排序

  • 内存保护

  • 调试支持


1.2 指令集系统
每款内核都有其匹配的内核指令集,内核架构也成为指令集架构,比如我们日常使用的计算机(Intel或AMD芯片),使用的就是X86指令集,内核就属于X86架构。常见的还有ARM架构、DSP架构和RISC-V架构。当我们想要掌握某款芯片,基于该芯片进行系统搭建时,就必须要了解该芯片的内核。
指令集系统是一种用于计算机或其他数字处理器的体系结构,它规定了处理器能够执行的指令集合,包括操作码、操作数和指令格式等。不同的处理器架构(如x86、ARM、MIPS等)都有自己的指令集系统,这些系统决定了处理器如何执行各种操作,从简单的加法和乘法到复杂的条件分支和内存访问。指令集系统的设计直接影响了处理器的性能、功耗和软件兼容性。   
嵌入式领域的芯片一般都是精简指令集内核,比如RISC-V、ARM等, 精简指令集(RISC,Reduced Instruction Set Computing)是一种计算机处理器架构设计理念,其核心思想是将处理器的指令集设计得相对较小和简单,以提高执行效率。相对于复杂指令集计算机(CISC)架构而言,RISC架构强调用更少、更基本的指令来完成操作,并且这些指令的执行时间相对较短。
和ARM指令集一样,TriCore™ 指令属于通用寄存器型结构中的寄存器-寄存器结构,即除了load和store以外,其余指令的操作数都来自通用寄存器组。
本文介绍的TriCore™ 内核是™️ 系列中使用的TriCoreTC1.6.2版本的内核架构。由于篇幅原因,无法讲解所有指令,但是通过本文的介绍,读者能够较好地掌握TriCore™ 指令系统的结构和使用,然后就可以自行利用内核手册进行查阅和学习。

#02内核介绍
接下来针对内核结构,首先介绍程序模型、通用寄存器和系统寄存器等。
2.1 程序模型(Programming Model)
该小节主要介绍内核的数据类型、数据格式、存储模式以及地址空间等内容,因为计算机的本质其实是对数据的处理和呈现,因此要理解其内核,首先要理解其数据的处理模型。
2.1.1 数据及内存
首先我们来看TriCore™ 的数据模型,注意,这里提到的数据模型,是指内核在处理数据时的视角,并非我们C语言中定义数据的视角。
TriCore™ 架构支持下面几种数据类型:

  • Boolean:布尔型,FALSE表示0,TRUE表示1

  • Bit String:布尔串,一段Bit段,用于移位操作

  • Byte:字节,一个字节是8Bit的数据

  • Signed Fraction:有符号的分数,TriCore™ 支持16位、32位、64位的分数

  • Address:地址表示一段32位地址数据(TriCore™ 有专门的地址寄存器,下文会介绍)   

  • Signed and Unsigned Integers:又符合和无符号整型,表示32位有、无符号数据

  • IEEE-754 Single-Precision Floating-Point Number:浮点数


TriCore™ 是32位架构,因此大多数指令都是基于32位数据进行处理的,与数据类型相对应,其指令支持的数据格式如下图:
图片
程序对数据的读取、存储操作是有对齐要求的,见下表:   
图片
同样外设的地址访问也是有对齐要求的:
图片
TriCore™ 的数据存储采用小端模式,低字节存储在低地址。
图片
TriCore™ 是32位内核,因此地址最多支持0xFFFF FFFF,也就是4GB空间大小。因此地址空间的定义被分为16个段(Segments)[0 - F],地址最高位的4个Bit表示段号,每个段256MBytes大小。其中F段被用于外设寄存器地址段。
2.1.2 寻址模式    
我们知道,计算机在进行数据计算过程中,需要不断进行数据的读取和存储,这中间有一个重要的流程就是寻址。不同的内核支持不同的寻址模式,TriCore™ 支持的寻址模式如下表:
图片

  • Absolute:绝对地址寻址模式,用于外设和全局变量的寻址

  • Base+Offset:基地址加偏移寻址方式,一般用于结构体成员寻址和局部变量的栈寻址

  • Pre-Increment and Pre-Decrement Addressing:预加/预减寻址,一般用于栈的操作

  • Post-Increment and Post-Decrement Addressing:后加/后减寻址,一般用于数据数据的处理指令

  • Circular:循环寻址,一般用于循环操作中

  • Bit-reverse:位翻转寻址,用于FFT算法中


2.2 通用寄存器和系统寄存器(General Purpose and System Registers)
TriCore™ 的包括通用寄存器和系统寄存器,系统寄存器包括程序状态寄存器等。其中系统寄存器的地址是16位内核地址,读写采用的是专用的指令,MTCR(Move To Core Register)和MFCR(Move From Core Register)。
2.2.1 通用寄存器
通用寄存器是计算机体系结构中的寄存器,其设计用途是存储通用数据,而不是特定的数据类型或用途。通用寄存器可以存储整数、浮点数、内存地址等各种数据类型,而不受限于特定的操作或指令。   
在许多计算机架构中,通用寄存器用于执行各种操作,如算术运算、逻辑运算、数据移动等。程序员可以使用这些寄存器来存储临时变量、中间计算结果和其他通用数据。通用寄存器的优点在于其灵活性和通用性,允许程序员更自由地使用寄存器来执行各种任务,而不受到特定数据类型或操作的限制。这有助于提高程序的灵活性和性能。
相比于ARM内核,TriCore™ 有两类通用寄存器,包括16个32位数据寄存器(D[0]~D[15])和16个32位地址寄存器(A[0]~A[15])。
图片
其按照功能有如下几类:

  • 通用寄存器:D[0]~D[14]、A[2]~A[7]、A[12]~A[14]为真正意义上的通用寄存器

  • A[15]和D[15]为隐式寄存器(一般16位指令使用,指令语法中不用明确指出,默认使用该寄存器),32位指令下与通用寄存器功能相同,16位指令下作为默认的寄存器使用

  • A[0]、A[1]、A[8]、A[9]为全局寄存器,在函数调用、中断、异常等场景都不会进行操作

  • A[10]为栈指针寄存器

  • A[11]为返回地址寄存器


数据和地址寄存器的分离促进了算术和内存操作并行执行的高效实现。同时,相邻的两个奇偶寄存器还可以成对进行访问,比如通过E[0]访问由D[0]和D[1]组成的64位数据(D[1]存放高8位),通过P[0]访问由A[0]和A[1]组成的64位地址。   
众所周知,在操作系统调度或中断程序切换过程中,系统需要保存上下文,其中主要的内容是保存寄存器。TriCore™ 的通用寄存器分为高上下文低上下文,高上下文包括A[10]~A[15]、D[8]~D[15],低上下文包括A[2]~A[7]、D[0]~D[7]以及A[11],其中仅有返回地址寄存器A[11]在两部分中都存在。A[0]、A[1]、A[8]、A[9]作为全局地址寄存器,不存储在上下文中。
2.2.2 程序状态信息寄存器(Program State Information Registers)
程序状态信息寄存器包含三个寄存器:程序计数器,也就是我们常说的PC指针;程序状态字PSW;前一个上下文信息PCXI。           图片
PC寄存器为32位程序计数器,反映了程序执行的位置,始终指向下一条即将执行的指令。
图片
图片    图片
程序状态字PSW顾名思义,是用来反映程序执行过程中的状态信息。其中:

  • USB:用户状态位(User Status Bits),用来保存指令执行过程中的状态标志位,如溢出信息等,用于后续指令作条件判断


图片

  • RES:预留

  • PRS[2](Bit15)& PRS[1:0](Bit12,Bit13):保护寄存器集切换位(Protection Register Set bit),内存保护寄存器有多套配置,PRS用来选择程序保护寄存器或数据保护寄存器的配置切换

  • S:安全任务标识符(Safety Task Identifier),标注当前任务是否为安全任务

  • IO:I/O访问权限级别控制位(Access Privilege Level Control),TriCore™ 内核对于外设寄存器的访问是由权限级别的:

  • 0:User-0 Mode,无权访问外设,在此权限下访问会产生PSE和MPP异常,也无法进行开关中断

  • 1:User-1 Mode,常规访问权限,在此权限下能够访问大部分外设,包括Port口的读写、定时器的读取和大部分I/O状态读取,能够开关中断

  • 2:Supervisor Mode,具有完全的外设访问控制权限

  • 3:Reserved Value


  • IS:中断栈控制位(Interrupt Stack Control),TriCore™️内核是有独立的中断栈寄存器ISP的,当进入中断且IS位为0的时候,硬件会先将栈寄存器置为ISP的值,以使用中断的特有栈(当然,在此之前硬件会自动保存SP指针,中断返回后会自动加载回来,不用担心被覆盖),并将IS位置1 。因为是共享栈,如果发生中断嵌套,则不作任何操作,继续往下使用中断栈   

  • GW:全局地址寄存器写权限位,决定是否可写入全局地址寄存器,1表示有权限;全局地址寄存器一般用来指向某个关键的地址区域或关键的数据结构体,比如OS的任务调度控制块等;另外通常A[0]被用于短数据的读取和写入,A[1]也一般为编译器操作预留,A[8]和A[9]未作为编译器预留,一般用于指向系统的关键数据

  • CDE:调用深度计数使能位(Call Depth Count Enable),TriCore™️内核是有调用深度检查的,每次调用计数器加1,最大6位计数器,也就是最多支持64层调用(每个Task或者中断具有独立的上下文,因此独立计数),当超过调用上限,会产生异常;将CDE置0,可暂时关闭调用计数,但是在下次Call指令执行时会自动再次打开

  • CDC:调用深度计数器(Call Depth Counter),调用深度计数器,7个Bit位宽,溢出时产生异常,也可修改CDC降低调用深度上限,比如改为1111100,则4次调度就会产生异常


图片
前一个上下文信息PCXI用来进行程序的上下文加载,当程序需要返回时,或中断结束调用时,都需要从这个地方向前链接,因此PCXI也称为链接字(Link Word),它和CSA的配合使得TriCore™️能够实现一套高效的硬件上下文保存机制,下面我们会介绍,这里先看PCXI的内容:

  • RES:预留

  • PCPN前一个CPU中断优先级(Previous CPU Priority Number),在之前关于中断的文章中我们提到过,中断的处理流程的一个步骤,是将ICR.CCPN,也就是当前的CPU中断优先级位,写入到PCPN,也就是本寄存器位中,用于中断环境的保存,中断执行完之后再进行恢复

  • PIE:先前中断使能位(Previous Interrupt Enable),同PCPN,在中断处理流程中将CPU中断使能状态位ICR.IE,保存到PIE中,用于中断环境保存,中断执行完之后再进行恢复   

  • UL:高、低上下文标签(Upper or Lower Context Tag),1表示高上下文,0表示低上下文

  • PCXS,PCXO:CSA是存储在内存中的,PCXS和PCXO用于指示存储地址,物理地址有32位,但是实际的内存空间没有这么大的,且CSA是64字节对齐,地址末尾6个为0,所以只需要20位就能指示上下文存储位置,具体算法是Addr=(PCXS<<12)|(PCXO<<6)


TriCore™️的上下文机制是利用CSA建立链表,PCXI指示链表的首位,以此形成函数调用栈关系。
2.2.3 栈管理寄存器
栈管理包括用户栈和中断栈,用到前文提到的栈指针寄存器A[10]、中断指针寄存器ISP(Interrupt Stack Pointer),以及前文提到的PSW中的IS位,来进行栈的管理。当运行非中断程序时,CPU正常根据栈指针寄存器使用普通程序栈,当从Task进入中断时,硬件会自动切换到中断栈。
图片
栈指针寄存器通常是程序启动时进行初始化,根据链接脚本分配的栈空间,设置栈起始地址。   图片
中断栈指针寄存器的使用,能够防止中断程序侵占Task程序的栈。在程序进入中断PSW.IS==0时,系统会从ISP中加载中断栈地址到栈指针中,以使用中断的栈;如果是中断的嵌套,PSW.IS==1,则不进行任何操作,继续使用中断栈。当中断执行完毕后,PSW和栈指针都会从上下文中恢复,因此Task程序得以继续使用原先的栈。
2.2.4 系统控制寄存器
图片
系统控制寄存器SYSCON的主要功能是对所属核进行控制(该寄存器有外设32位地址,可进行跨核访问,比如主核通过控制从核的BHALT位设置启动),主要包括:

  • RES:预留

  • BHALT:启动冻结状态控制位(Boot halt status and release),当芯片启动后,除了主核以外,该位都会置1,代表该核处于冻结状态,主核通过对该位写0来启动该核,启动后写1无效

  • U1_IO:User-1  Peripheral access as supervisor,前面我们提到用户模式包括User-0、User-1和Supervisor三种模式,该位能够允许User-1获得对外设的全部访问,不改变模式的情况下提高权限   

  • U1_IED:User-1 Instruction execution disable,禁止或使能User-1模式下开关中断

  • ESDIS:Emulator Space Disable,仿真地址禁用

  • TS:进入异常(Trap)后PSW.S的默认值

  • IS:进入中断后PSW.S的默认值

  • TPROTEN:Temporal Protection Enable,1表示使能,0表示关闭

  • PROTEN:内存保护使能,注意需要先设置好内存保护配置再使能

  • FCDSF:空闲上下文列表耗尽粘滞标志位(Free Context List Depleted Sticky Flag),该位表征是否发生过上下文列表耗尽,因为CSA的空间是RAM中设置的,如果任务或中断过多,且都调用过深会导致CSA耗尽,从而该位置位,知道清除


2.2.5 Core_ID寄存器
图片
Core_ID寄存器用来获取当前核的ID,在多核系统中,共用代码尤其是OS,获取所在核是非常重要的。
中断、内存保护、Trap相关的寄存器我们将会在模块单独介绍时候进行说明,这里就不展开介绍了。


#03TriCore™️指令集结构
TriCore™是一种通用的、32位的微控制器-DSP,针对实时嵌入式系统进行了优化的多核同构架构。
TriCore™️指令集支持32位指令和16位指令,支持嵌入式常用的微控制器系列处理指令,也支持一些DSP数据处理指令。   
3.1 指令语法
和ARM等指令集系统一样,TriCore™️指令集的语法也是由指令和操作数组成,操作数可以是立即数、寄存器或内存。一条指令包括基础操作符、操作修饰符、操作数修饰符和操作数组成。如下面这条指令,是将一个立即数写入地址寄存器的高位:
图片

  • 基础操作符:定义了该指令的基础操作,比如mov就是赋值,add就是累加;

  • 操作修饰符:定义该操作更准确的细节,比如movh中的h定义操作a15的高16位;

  • 操作数修饰符:定义了操作数的数据类型,比如movh.a中的a定义了目标寄存器为地址寄存器;

  • 操作数:需要操作的寄存器、立即数或内存。


3.1.1 基础操作符
基础操作符即该指令的操作定义,操作类型较多,下文会给出部分示例,此处就不一一列出了。
3.1.2 操作修饰符
TriCore™️指令集的操作修饰符见下表:   图片
图片
3.1.3 操作数修饰符
TriCore™️操作数修饰符见下表:
图片
有些16位指令还会使用隐式寄存器,即该条指令不指定寄存器,而是固定使用约定好的寄存器,隐式寄存器包含下面几种:   

  • D[15]:通用数据寄存器D[15]可以作为16位指令的隐式数据寄存器使用

  • A[15]:通用地址寄存器A[15]可以作为16位指令的隐式地址寄存器使用

  • A[10]:A[10]为栈指针寄存器,所以对于栈相关操作指令默认操作数为A[10];

  • A[11]:A[11]为返回地址寄存器,即链接寄存器,所以跳转指令默认使用A[11]作为指令默认的操作数。


3.2 程序状态字标志位(用户状态位)
之前的介绍中提到过,程序状态字寄存器PSW中有5个用户状态位,用于指令的状态存储。
图片
这5个状态位会会影响指令的操作逻辑,同时它们也会因为指令的操作结果而发生变化。比如第一个Carry位,它表示计算进位,比如在使用加法指令ADDC(带进位的加)中结果溢出了,那C位就会置位;并且如果在执行前C位已经置位了,那ADDC的结果就会加1。
在手册中每个指令后都会描述PSW的影响情况,可在指令详细信息中查看。   图片
3.3 指令权限
TriCore™️内核分为三个权限等级,因此有些指令对权限是有限制的。下表列出的指令表示只有该权限等级下能够使用。
图片
#04TriCore™ 指令介绍
本章节将分类介绍TriCore™ 指令集中的指令,同时包括部分操作修饰符和操作数修饰符的介绍。
4.1 如何查询指令手册
TriCore™ 指令数百条本文不一一解释,这里先介绍下如何查询TriCore™ 指令手册。
作为示例,我们来看ADD.A指令的文档介绍,这里各部分用序号标出,以做解释。   
图片

  1. 指令助记符;

  2. 指令长名;

  3. 32bit格式指令的描述;

  4. 16bit格式指令的描述;

  5. 32bit格式指令使用语法,有时候操作数可以有多种类型,所以会有多种指令语法描述;

  6. 操作码,也就是指令译码最后的二进制数格式;  

  7. 指令的运行逻辑描述,这里使用了仿C语言的RTL(Register Transfer Level)语言描述,在1.3章节中有语法介绍;

  8. 16bit格式指令语法;

  9. 16bit格式指令操作码;

  10. 16bit格式指令逻辑描述;

  11. 对PSW中状态位的影响,如溢出等;

  12. 32bit格式指令示例;

  13. 16bit格式指令示例;

  14. 其他相关指令。


当我们遇到要查询某条指令的时候,先根据其结构识别出它的指令助记符(并非所有指令-修饰符组合在内核手册中都有示例),然后查到对应的指令,再根据其逻辑及修饰符的逻辑解释其指令含义。
4.2 Load/Store指令
在Load-Store结构的内核中,Load(LD.X)和Store(ST.X)指令是唯一两条能够访问内存的指令,它们是内存和寄存器间的桥梁,其余的所有指令都是基于寄存器进行操作的。
Load/Store指令支持以下这些寻址模式:
图片
其中最主要的还是绝对地址寻址和基址-偏移寻址。
TriCore™ 指令集的Load/Store除了支持字节(byte)、半字(Half Word)、字(Word)、双字(Double Word)等基础数据类型,还支持浮点型和地址型(地址寄存器的读写)。TriCore™ 中一个字Word,代表32字节。   
图片
下面对Load/Store指令结合操作修饰符和操作数修饰符进行指令说明,这里需要注意的是,TriCore™ 的通用寄存器是分为通用地址寄存器通用数据寄存器两类的,因此操作数修饰符会强调操作数类型。

Assembly language                  

LD.A    A0, [A15]       //将A15指向的内存中的4字节数据,取到A0中,A表示操作数为地址寄存器

LD.BU   D5, [A0]        //将A0指向的内存中的一字节无符号数据,取到D5中,B表示Byte,1字节,U表示无符号                  
LD.D    E0, [A0]        //将A0指向的内存中的8字节数据,取到E0中(D0+D1),D表示Double Word                  
LD.H    D1, [A0]        //将A0指向的内存中的2字节数据,取到D1的低16位中,H表示Half Word                  
LD.W    D2, [A2]0x44    //将A2+0x44指向的内存中的4字节数据,取到D2中,W表示Word,0x44表示地址偏移                  
ST.A    [A1], A0        //将A0的数据,存储到A1指向的4字节内存中,A表示操作数为地址寄存器                  
ST.B    [A1], D0        //将D0的1字节数据,存储到A1指向的1字节内存中,B表示长度为1字节                  
ST.D    [A1], E0        //将E0(D0+D1)中的8字节数据,存储到A1指向的8字节内
存中,D表示Double Word                  
ST.H    [A0], D0        //将D0中的2字节数据,存储到A0指向的2字节内存中,H表示Half Word                  
ST.W    [A1], D0        //将D0中的4字节数据,存储到A1指向的4字节内存中,W表示Word      
  


4.3 算术运算指令
算术运算指令包括MOVE、加减乘除、绝对值、逻辑运算、移位等,还包括计算非零bit位等。
TriCore™ 算术运算符有一种Saturation运算,操作修饰符为S,即饱和运算,意思是说当运算结果超出内存大小,结果是当前内存的最大值。比如uint8的最大值为255,用ADDS去计算200+200,得到的结果是255。同样的如果减法运算值小于负的最大值,结果也是负的最大值。
4.3.1 MOV指令
MOV指令是将一个立即数或者寄存器的值,赋值给另一个寄存器的指令。

Assembly language                  MOV      D0, D1    //将D1中的值赋值给D0MOV.A    A0, D0    //将D0中的值赋值给地址寄存器A0,A表示目的操作数是地址寄存器MOV.D    D3, A4    //将地址寄存器A4的值赋值给数据寄存器D3,D表示目的操作数是数据寄存器MOV.AA   A3, A4    //将地址寄存器A4的值赋值给地址寄存器A3,AA表示源操作数和目的操作数都是地址寄存器                  MOVH     D3, 180   //将立即数180赋值给D3的高16位,并将D3的低16位清零,H表示高位


4.3.2 ADD/SUB指令
加法和减法指令类似,此处只列出加法的说明。
加法指令是ADD,普通加法运算中,溢出会自动去除高位,比如uint8的255+100,结果等于99。   
而带修饰符的加法运算,是会修改PSW的用户状态位的,比如ADDC指令如果溢出会修改Carry位。

Assembly language                  
ADD     D0, D1, D2    //计算D1+D2的值,结果存到D0中,溢出位舍弃,如255+1=0(uint8)                  
ADD.A   A0, A1, A2    //计算地址寄存器A1+A2的值,结果存到A0中,A表示操作数为地址寄存器                  
ADDX    D3, D1, D2    //计算D1+D2的值,结果存到D3中,如果溢出,PSW.C=1,否则PSW.C=0                  
ADDC    D3, D1, D2    //计算D1+D2+PSW.C的值,结果存到D3中,如果溢出,PSW.C=1,否则PSW.C=0                  
ADDI    D3, D1, 858   //计算D1+858的值,结果存到D3中                  
ADDIH   D3, D1, 0x8   //立即数移位加法,计算D1+0x8<<16的值,结果存到D3中,IH表示立即数左移16位                  
ADDS     D3, D1, D2    //计算D1+D2的值,如果溢出则做饱和运算处理,结果存放到D3中                  
ADDS.U   D3, D1, D2    //计算无符号D1+D2的值,如果溢出则做饱和运算处理,结果存放到D3中


4.3.3 MUL乘法指令
乘法指令为MUL,部分乘法指令同样也会修改PSW的用户状态位。

Assembly language                  
MUL    D3, D1, D2    //计算D1*D2的值,溢出位舍弃,结果存放到D3中           
MUL    E0, D1, D2    //计算D1*D2的值,结果存放到E0(D1+D2)中       
MULS   D2, D1, D2    //计算D1*D2的值,溢出则做饱和运算处理,结果存放到D3中                  
MUL.U  E0, D1, D2    //计算无符号D1*D2的值,结果存放到E0(D1+D2)中


4.3.4 DIV除法指令
TriCore™ 指令集支持32位的有/无符号除法,硬件不支持双精度64位触发,对于双精度64位的除法,则需要依赖于编译器的软件实现。另外由于除法指令计算时间相对较长,会消耗数个指令周期,因此会降低中断的实时性响应,这种影响与商的有效位成正比。除法运算在计算时结果可以占据一个寄存器对,商保存在第一个寄存器中,余数保存在第二个寄存器中。
有符号整数除法的计算相对于加法等有较多的判断逻辑:   

C                  
DIV    E[c], D[a], D[b]        //计算D[a]/D[b],商存放到E[c][31:0],余数存放到E[c][63:32]中                  
    dividend = D[a];                  
    divisor = D[b];                  
    if (divisor == 0) then {                  
        if (dividend >= 0) then {                  
            quotient = 0x7fffffff;                  
            remainder = 0x00000000;                  
        } else {                  
            quotient = 0x80000000;                  
            remainder = 0x00000000;                  
        }                  
    } else if ((divisor == 0xffffffff) AND (dividend == 0x80000000)) then {                  
        quotient = 0x7fffffff;                  
        remainder = 0x00000000;                  
    } else {                  
        remainder = dividend % divisor                  
        quotient = (dividend - r
emainder)/divisor                  
    }                  
    E[c][31:0] = quotient;                  
    E[c][63:32] = remainder;


无符号整数的计算也类似上面的形式:

C                  
DIV.U   E[c], D[a], D[b]       //计算无符号D[a]/D[b],商存放到E[c][31:0],余数存放到E[c][63:32]中,U表示无符号                  
dividend = D[a];                  
divisor = D[b];                  
if (divisor == 0) then {                    
    quotient = 0xffffffff;                  
    remainder = 0x00000000;                  
} else {                  
    remainder = dividend % divisor                  
    quotient = (dividend - remainder)/divisor                  
}                  
E[c][31:0] = quotient;                  
E[c][63:32] = remainder; 


浮点数的除法运算逻辑整体不复杂,根据IEEE754规范计算32位浮点数除法,指令的后台执行流程相对要多一些,这里不详细列出,感兴趣的读者可以查看内核手册。

Assembly language                  

DIV.F    D3, D1, D2    //计算浮点数D1/D2的值,结果存放到D3中       


4.3.5 ABS/ABSDIF绝对值指令
TriCore™ 指令集支持直接进行有符号数的绝对值运算,或者计算两个数的差值的绝对值。

Assembly language                  
ABS       D1, D2        //计算D2的绝对值,结果存放到D1中                  
ABSDIF    D3, D1, D2    //计算D1-D2的值的绝对值,结果存放到D3中


4.3.6 MIN/MAX比较指令
TriCore™ 指令集支持直接对两个数进行比较,取其较大值或较小值,输入源可以是整形或者无符号整形。

Assembly language                  

MIN        D3, D1, D2       //比较D1和D2的大小,将较小的存放到D3中             
MIN.U      D3, D1, 8        //比较D1和8的大小,将较小的存放到D3中,U表示无符号数                  

MAX        D3, D1, D2       //比较D1和D2的大小,将较大的存放到D3中            
MAX.U      D3, D1, 8        //比较D1和8的大小,将较大的存放到D3中,U表示无符号数


4.3.7 CADD/CSUB/SEL条件指令
TriCore™ 对于整数运算提供了条件执行指令,即先对某个数进行判断,来决定是否执行加减或者选择赋值来源。

Assembly language                  

CADD        D4, D1, D2, D3    //如果D1非零,则D4=D2+D3,否则D4=D2      

CADDN       D4, D1, D2, 8     //如果D1为零,则D4=D2+8,否则D4=D2          

CSUB        D4, D1, D2, D3    //如果D1非零,则D4=D2-D3,否则D4=D2       

SEL         D4, D1, D2, D3    //如果D1非零,则D4=D2,否则D4=D3               
SELN        D4, D1, D2, 8     //如果D1为零,则D4=D2,否则D4=8


4.3.8 AND/OR/XOR逻辑指令
TriCore™ 支持AND/OR/XOR等按位的逻辑指令,同时也支持结果取反的NAND/NOR/XNOR指令,以及源操作数取反的ANDN、ORN等。后两者容易混淆,要注意辨别,比如NAND和ANDN。   

Assembly language                  
AND        D3, D1, D2        //计算D1与D2的按位与,结果存放到D3中,即D3 = D1&D2                  
OR         D3, D1, 0xCC      //计算D1与0xCC的按位或,结果存放到D3中,即D3 = D1|0xCC                  
XOR        D3, D1, D2        //计算D1与D2的按位异或,结果存放到D3中,即D3 = D1^D2                  
NAND       D3, D1, D2        //计算D1与D2的按位与,并按位取反,结果存放到D3中,即D3 = ~(D1&D2)                  
ANDN       D3, D1, D2        //计算D2的按位取反,然后与D1按位取与,结果存放到D3中,即D3 = D1&~D2                  
NOT        D1                //计算D1的按位取反,结果存放到D1中,即D1 = ~D1


4.3.9 CLO/CLZ/CLS计位指令
TriCore™ 提供了用于位计数的功能,能够计算高位连续的0位、1位或符号位。
CLO(Count Leading Ones)用于计算从最高位开始一共多少个连续的1,比如0xF000 0000计算的结果为4,0xF800 0000计算的结果为5。
CLZ(Count Leading Zeros),用于计算从最高位开始一共多少个连续的0,比如0x0FFF 0000计算的结果为4,0x07FF 0000计算的结果为5。
CLS(Count Leading Signs),用于计算从次高位开始一共多少个与最高位相同值的连续位数,比如第31位为1,则从30开始计算连续的1的个数。

Assembly language                  
CLO        D2, D1        //计算D1的CLO的值,结果存放到D2中                  
CLZ        D2, D1        //计算D1的CLZ的值,结果存放到D2中                  
CLS        D2, D1        //计算D1的CLS的值,结果存放到D2中


4.3.10 SH/SHA/SHAS移位指令
TriCore™ 的移位指令支持有符号数移位,如果是正数就左移,如果是负数就右移。

Assembly language                  
SH    D3, D1, D2    //按照D2的值对D1进行移位,填充值始终为0,结果存放到D3中                  
SHA   D3, D1, D2    //按照D2的值对D1进行移位,左移填充位为0,右移填充位同符号位,结果存放到D3中                  
SHAS  D3, D1, D2    //与SHA计算相同,区别在于结果取饱和操作,S代表SATURATION        


4.3.11 EXTR/INSERT位提取/插入指令
TriCore™ 支持对数据进行位提取和位插入功能。
位提取是指提取源操作数的某个bit段,赋值给目的操作数,操作数可以是整形或无符号整形。

Assembly language                  
EXTR        D3, D1, D2, #4      //D2[4:0]为pos,4为width,然后按照下图从D1中截取,赋值到D3中


图片

Assembly language                  
EXTR.U        D3, D1, D2, #4      //D2[4:0]为pos,4为width,然后按照下图从D1中截取,赋值到D3中,U表示无符号


图片

Assembly language                  
DEXTR        D3, D1, D2, 5    //将{D1,D2}左移5位,然后截取[63:32]位,赋值给D3


图片

Assembly language                  
INSERT D3, D1, D2, D4, #8    //D4[4:0]为Pos,8为width,将D2按照下图位置左移,然后覆盖到D1该段,结果赋值给D3


图片
4.4 Packed打包计算
TriCore™ 的算术运算符还有一种打包运算方法,对于一个32位数据,可以按照字节或者半字分别计算,结果分别存放到指定位置。
图片图片

Assembly language                  
ADD.B    D3, D1, D2    //将D1与D2按照字节依次相加,结果分字节存放到D3中,任意一个字节计算溢出,V和SV都会置位                  
ADD.H    D3, D1, D2    //将D1与D2按照半字依次相加,结果分半字存放到D3中,任意一个半字计算溢出,V和SV都会置位


4.5 EQ/NE/LE/GE比较指令
TriCore™ 中比较指令的使用也是比较频繁的,主要用于条件指令的操作修饰符,比如分支跳转等。

  • EQ (Equal)

  • NE (Not Equal)

  • LT (Less Than)

  • GE (Greater than or Equal to)


另外对于大于和小于等于,TriCore™ 是没有指令的,需要依赖上述指令隐式实现。
图片

Assembly language                  

LT    D3, D1, D2    //D3 = (D1 <D2)                  

GE    D3, D1, D2    //D3 = (D1>=D2)               


4.6 地址指令    TriCore™ 中对于地址的运算和操作和数据还是具有一定差异的,同时硬件上采用了通用地址寄存器等设计来实现加速。一般使用赋值加载以及LEA指令进行地址计算。
LEA(Load Effective Address)指令用于计算绝对地址,并保存到指定的地址寄存器中。

Assembly language                  
MOVH.A    A5, #0x7001        //将0x7001赋值给A5高16位,低16位置0             
LEA       A5, [A5]0x4864     //将A5中的值加上0x4864,然后赋值给A5,组成0x7001 4864地址值


4.7 跳转指令
TriCore™ 中的分支指令大致分为以下三种:

  • Jump:跳转指令,该指令不做任何其他操作,仅修改PC指针到指定地址运行,一般用于常规指令运算或分支跳转;

  • Jump And Link:链接跳转指令,该指令除了跳转以外,还会将其下一条指令的地址存到返回地址寄存器中;

  • Call And Return:调用和返回指令,该指令必须成对使用,调用指令会保存高上下文,返回指令则会恢复高上下文。一般用于函数调用。

Assembly language                  
J    foobar            //跳转到foobar                  
JI   A5                //跳转到A5指向地址                  

JL   foobar         //将下一条指令地址写入到返回地址寄存器中,并跳转到foobar   CALL foobar       //保存高上下文,将下一条指令地址写入到返回地址寄存器中,跳转到foobar                  
JEQ  D1, D2, foobar    //如果D1==D2,跳转到foobar                  
JGE  D1, D2, foobar    //如果D1>=D2,跳转到foobar


4.8 上下文相关指令
TriCore™ 的上下文除了中断、Trap以及CALL、RET指令能够操作以外,还提供了专有的上下文操作指令。主要分为以下两类:

  • SVLCX/RSLCX:低上下文保存和恢复指令,使用的内存是CSA区域,并同时会更新PCX以及FCX寄存器;  

  • STUCX/STLCX/LDUCX/LDLCX:上下文加载和存储指令,使用的区域需要指令指定,且不会更新PCXFCX


这里要注意区分,第二种使用方法虽然也能够保存和加载通用寄存器,但是它是不使用CSA区域的,同时也不会更改PCX和FCX寄存器。
4.9 系统相关指令
TriCore™ 提供了内核系统指令,主要包括System Call、指令和程序屏障、开关中断等功能。
4.9.1 SYSCAL系统调用指令
TriCore™ 的系统调用是通过Trap6来实现的,一般用来给OS做内核模式切换使用的。

Assembly language                  
SYSCALL    4    //执行System Call,4为系统调用的入参


4.9.2 DYSNC/ISYNC数据/指令屏障
这两条指令的作用是作为数据/指令屏障而使用的。
DYSNC指令
在计算机科学中,数据屏障(Memory Barrier)是一种用于控制处理器和内存之间数据同步的机制。它确保在多核或多线程系统中的内存访问顺序得到正确的维护,避免了由于并发访问导致的数据不一致性和竞态条件。DYSNC作为一个屏障,将其前后的指令进行分隔,保证后面的指令一定在前面的所有内存访问完成后执行,包括Cache,防止因编译器优化或指令流水、多线程等导致的数据访问次序错误。
ISYNC指令
指令屏障(Instruction Barrier)是另一种用于控制处理器执行指令顺序的机制。它确保在处理器执行指令时,按照程序员的预期顺序执行,避免指令重排或者乱序执行导致的问题。另外如果指令修改了系统的状态,比如内存保护设置,则该指令能够保证其后执行的所有程序都是按照新的内存保护设置执行的。

Assembly language                  
DYSNC    //数据屏障                  
ISYNC     //指令屏障
        


4.9.3 MFCR/MTCR核特殊功能寄存器指令
TriCore™ 的核特殊功能寄存器CSFR是每个核特有的,而且对于多核其地址是不进行区分的,因此必须通过特殊的指令进行操作,哪个核执行该指令就作用于哪个核。
MFCR(Move From Core Special Function Register)读取指令
MFCR指令用于从CSFR中读取内容,比如我们最常用的读取当前核的ID。
MTCR(Move To Core Special Function Register)操作指令
MTCR指令用于修改CSFR中的内容,后面必须跟一条ISYNC用于指令屏障,保证操作完成。

Assembly language                  
MFCR    D15, #0xFE1C    //读取CoreID,结果保存到D15中                  
MTCR    #0xFE04, D15    //修改PSW寄存器,将其值修改为D15


4.9.4 ENABLE/DISABLE开关中断
TriCore™ 中每个核的中断使能是相互独立的,只需要使用ENABLE/DISABLE指令即可。

Assembly language                  
ENABLE    //开中断                  
DISABLE   //关中断


4.9.5 TRAP指令
TriCore™ 提供了用户软件触发Trap的方法,可以使用TRAPV或TRAPSV,当OverFlow或StickyOverflow位置位时触发Trap。 

Assembly language                  
TRAPV     //Trap on overflow                  
TRAPSV   //Trap on sticky overflow


4.10 16Bit指令
TriCore™ 的大部分指令在某些情况下是支持16位格式的,甚至有些指令只有16位格式,具体可以参考内核手册,上面有每条指令的格式说明。   


#05使用示例
下面我们通过一段示例代码,通过对汇编进行说明,来帮助读者了解TriCore™ 的指令集。下面的代码通过辗转相除法求两个数的最大公约数,数据源和结果都用了全局变量,以使用Load/Store指令。
5.1 示例代码

C                  
//变量声明                  
volatile int MyResult;                  

volatile int num1=15, num2=20;    //volatile防止编译器优化                 
                    
// 函数声明                  
int TestInstruction(void);                  
int gcd(int a, int b);                  
                    
void echoInit(void){                  
    Test
Instruction();                  
}                  
                    
int TestInstruction(void) {                  
    // 计算并输出最大公约数                  
    MyResult = gcd(num1, num2);                  
    printf("GCD of %d and %d is %dn", num1, num2, MyResult);                  
    return 0;                  
}                  
                    
// 计算最大公约数的函数                  
int gcd(int a, int b) {                  
    int temp;                  
    // 辗转相除法                  
    while (b != 0) {                  
        temp = b;                  
 
       b = a % b;                  
        a = temp;                  
    }                  
    return a;                  
}


5.2 TestInstruction调用   
我们首先看TestInstruction的调用这里,使用的是Jump指令,而不是JL或者Call。
图片
这样做的好处是,当该条代码是调用方的最后一条指令,在其执行完毕之后已经没有必要返回调用方了,而是直接返回调用方的上一层调用方,这样的优化能够节省系统时间,提高性能。
图片
但是缺点是调用栈中就会少一层,图中echoInit就没有显示,不便于调试。所以我们在调试过程中经常会遇到调用栈的信息缺失,或者使用Return调试操作的时候会直接返回两层的情况,都是这个原因。
5.3 TestInstruction函数
TestInstruction函数的C语言一共只有三行,我们一一展开描述。
图片
第一行是以num1和num2为入参,计算最大公约数:

  • 首先是两条LD.W指令,因为是全局变量,所以要从内存中取出来;  

  • 然后是CALL指令,调用gcd函数,TriCore™ 的入参是依次使用D4~D7数据寄存器,或A4~A7地址寄存器

  • 然后返回值在D2寄存器中,使用ST.W将其存入MyResult全局变量中;


第二行是调用printf函数将其显示出来:

  • 首先是三条LD.W指令将全局变量读出来(这里因为我使用了volatile,所以没有编译器优化,每次都重新读写);

  • 然后是三条ST.W指令将加载的三个变量压入栈中,作为printf函数的第2、3、4个入参;

  • 然后是MOVH.ALEA指令将地址0x8001 421C写入到A4寄存器中作为第一个入参,这里printf的第一个入参是字符串,传入的是字符串的首地址,我们打开内存使用ASCII格式可以看到这段字符;


图片

  • 然后进行printf函数调用。


最后一行是return 0:

  • 使用MOV指令,将返回值写入到D2寄存器中;

  • 使用RET指令返回,前面提到,TestInstruction是通过Jump调用的,所以这里会直接返回到调用echoInit的那层;


5.4 gcd函数
gcd函数这里是while循环,通过条件跳转,来进行循环计算。   
图片

  • 首先是一条JG指令(目前观察其功能同J,但无论是Infineon手册还是Tasking手册都没有找到JG的相关描述),作为入口跳转到0x80004078;

  • 0x80004078处通过条件指令JNE,当D5(形参b)不等于0时跳转0x80004070进入循环;

  • 然后在0x800040700开始,执行MOV、DIV、MOV进行算法的数学计算,随后进入下一轮的循环判断;

  • 当循环结束后执行到0x8000407C,这里将D4(形参a)的值写入到D2中,并调用RET函数返回;


至此全部的函数调用就介绍完了,不同的编译器或者编译优化等级、编译选项等可能有不同的汇编结果,但是整体的内容是差不多的,希望读者能够通过这个示例,了解TriCore™ 指令集的框架结构。


#06小  结
本文介绍了的AURIX™️ TC3XX内核TriCore™ 的内核架构,并对其指令集系统进行了讲解,并通过代码示例进行讲解,通过本文读者能够对TriCore™ 内核及指令集有一定的了解和掌握。   




评论


相关推荐

技术专区

关闭