通过FLIX指令结构提高可配置处理器计算性能
扫描二维码
随时随地手机看文章
指令集的性能更多地是与有用的操作个数相关,而不是与每个执行部件的执行时间或者每个时钟的执行相关。然而,高性能并不能保证系统具有良好的灵活性。指令集的灵活性与不同应用领域的多样性相关,在这些应用中,数据运算能够在指令流中进行有效编码。一个较长的指令字一般可以允许更多数量和更多样性的操作以及操作数标志符在每个指令字中进行编码。
在RISC体系结构中,一条指令通常只对一个原始操作进行编码。在长指令字体系结构中,一条指令可以允许对多个独立的子指令进行编码,每条子指令都有自己的操作和操作数标志符。每条子指令可以是一般的类似于RISC指令的操作或者是一条比较复杂的专用操作。指令字设计的越长,那么对于任意给定的操作数个数和操作个数而言,指令编码就越简单,正交性就越好。
长指令字处理器速度并不总是比RISC处理器快。有时,RISC处理器执行单元的简单性所带来的优点将使得处理器能够以最大时钟频率运行,并且每个时钟周期可以执行几条独特的RISC指令,这将能够弥补因RISC指令集相对简单所带来的损失。尽管如此,在绝大多数要求数据密集型的任务中使用RISC指令集,但是指令集采用超标量方式实现,每个时钟周期执行多条指令,这同长指令字体系结构中那种充分利用程序代码中潜在的指令操作的并行性是一样的。
图1表示一个基本的长指令操作编码示例,图中列出一个64位的指令字,该指令字包括三个独立的子指令槽,每个指令槽说明一个操作和若干操作数。第一个子指令(子指令0)有一个操作码和四个操作数说明符(包括两个源寄存器、一个立即数域和一个目的寄存器)。第二个和第三个子指令(子指令1和2)各有一个操作码和三个操作数说明符(两个源寄存器和一个源/目的寄存器)。左边的两位格式域表示各个子指令的特定分组情况。如果处理器支持变长指令编码的话,那么两位的格式域也可以表示整个指令的长度。
显然,系统硬件开销和长指令字是相关的。指令存储器位数越宽,译码逻辑就越大,并且系统就需要更多数量的执行部件,而且寄存器文件(或者寄存器文件端口)实现就必须满足指令并行性的要求。更大的硬件逻辑模块越多,系统优化就越困难,所以,同相对简单和位数较少的RISC指令那样编码相比较,整个系统的最大时钟频率就会降低。尽管如此,追求系统性能和灵活性的优点还是最基本的,尤其是对于那些具有高的程序潜在并行性的数据密集型应用。
在有些长指令字体系结构中,每个子指令都几乎具有完整的独自资源,包括专用执行部件、专用寄存器堆和专用的数据存储器。在另外一些处理器体系结构中,所有子指令共享公用寄存器堆和数据存储器,为保证有效的数据共享系统需要大量的数据端口与公用存储器结构.
长指令字体系结构对于如下问题而言也会有很大不同:一条长指令字该多“长”?对于高端计算机系统处理器(例如英特尔的安腾处理器系列)和高端嵌入式处理器(例如TI公司的TMS320C6400 DSP系列)而言,指令字确实是非常“长”,通常几百位。对于更多的对成本和功耗敏感的嵌入式应用,指令字可能是64位。但是,一旦多个独立的子指令打包成每个指令字后,关键的处理器体系结构原理都是一样的。
代码大小和长指令字
同每条指令只对一个独立操作进行编码的体系结构相比,长指令字体系结构的一个共同问题是代码量大。这是超常指令字VLIW体系结构的一个通病,然而这对于那些片上系统(SoC)设计而言尤其重要,因为SoC系统中的指令存储器通常会占用绝大多数的芯片面积。同那些编译代码有效的体系结构相比,VLIW代码通常需要多占用代码存储容量的两倍到五倍。
VLIW体系结构中程序代码的膨胀问题部分源于指令长度的不灵活性。例如,如果编译器只能找到一个独立操作,其源操作数和执行部件都已经准备好,那么此时编译器就不得不在编码时插入空操作NOP来填满剩下的几个子指令操作域。指令存储已经占用了大部分的嵌入式片上系统SoC硅片面积,因此代码扩充就造成了更大的硬件开销和更低的指令高速缓存性能,或者二者兼而有之。
VLIW代码膨胀问题的第二个根源在于体系结构对常用操作的松散编码,这在VLIW微处理器中是常见的。
然而,长指令并非必然会导致VLIW代码膨胀问题。Tensilica的Vectra LX DSP体系结构中的一个长指令字在指令流中只需要20位就可以说明8个按照单指令流多数据流SIMD方式执行的16位乘加操作MAC,这不包括其它的加载、存储、分支和地址计算指令。
解决长指令字体系结构代码膨胀的一种有效方法是采用更加灵活的指令长度。如果处理器允许多种不同的指令长度,包括各种对单一操作进行编码的短指令,那么与传统长指令字VLIW处理器相比,编译器就可以获得更加有效的指令代码大小和更加有效的指令存储效率。对长指令字处理器而言,减少代码大小还意味着减少所要求的总线带宽,并且减少了与取指令相关的功耗。例如,Tensilica的 Xtensa LX处理器采用了灵活的指令扩展技术(FLIX)。该体系结构的指令代码寻址可以提供16位、24位和选择一种32位或者64位的指令长度。设计人员定义的指令可以使用24位、32位和64位的指令格式。
长指令允许用户更加自由地进行编码。在这种情况下,用户可以根据每个指令槽指令操作的多少来定义(虽然在通常情况下有3到6个独立的指令操作槽)大量的子指令和操作。各个指令槽的大小不需要相等。大的指令槽(20到30位)可以放置各种不同的操作码、寄存器个数相对多的寄存器文件(16 到32个寄存器)以及3个或者4个寄存器操作数标志符。系统开发人员应当考虑到建立具有大指令槽的处理器,这种大指令槽针对应用,具有适度的并行性,但是在应用领域内应当具有较强的灵活性和一般性。
小的指令槽(8~16位)让用户可以直接说明小寄存器集之间数据的移动,并且允许用户将大量的独立指令槽打包成一个长指令字。每一个指令槽提供更有限的操作范围、更少的操作标志符(或更多隐含的操作数)及使用更少寄存器的文件。开发人员应当考虑根据不同应用建立许多小的指令槽,这些应用在许多专用功能部件之间具有高度的并行性。[!--empirenews.page--]
长指令字和自动处理器生成
长指令字非常适合处理器硬件和软件的自动生成。高级指令描述可以说明适合每个指令槽的子指令集。从这些指令描述中,处理器产生器确定每个指令槽中每个编码域的编码要求、分配操作码并建立针对所有必需的指令格式的指令译码硬件。而且处理器产生器还建立与长指令字处理器相应的编译器和汇编器。对于长指令字体系结构而言,将子指令打包成长指令是一项非常复杂的任务。汇编器可以处理这种指令包,因此程序员写的汇编语言源代码程序只需要指定不同操作或者子指令,不必过分关注打包的约束。编译器在满足系统具有最大性能和最小代码容量的前提下产生适合指令槽的程序代码,因此通常需要自己将各类操作打包成长指令。
图4表示用FLIX技术中的TIE语言描述的一个简短且完整的长指令字处理器。该处理器完全建立在32位的整数操作的基础上,并没有定义新的操作。此描述建立了一个具有高度潜在指令级并行性的处理器,其应用完全用标准C整数操作和数据类型写成。三个指令槽中的第一个指令槽支持全部常用的整数操作,包括ALU操作、加载、存储、跳转和分支指令操作。第二个指令槽提供加载和存储操作,另外还包括大多数常用的ALU操作。第三个指令槽补充全部 ALU操作,但是不包括加载和存储操作。
图3中的第一行说明了一个新的64位指令长度,并指定了确定指令长度的前4位编码。第二行说明了指令长度格式为format1,它包括 3个指令槽,分别为base_slot、ldst_slot和alu_slot,并且在新指令格式中对这三个指令槽进行了命名。第四行列出了第一个指令槽 base_slot所有可能包含的TIE指令。在这种情况下,所有在Xtensa LX处理器中的指令(除了新指令)都可能包含在这个指令槽中。处理器产生器还为每个指令槽产生一个空操作NOP,因此软件工具总是可以产生完整的指令,即使找不到可以封装到长指令字中的合适操作。第四行和第五行指定可以包含在其它两个指令槽中的指令子集.