嵌入式C语言自我修养:从芯片、编译器到操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.9 指令集与微架构

图灵原型机的基本思想是:任何复杂的运算都可以分解为有限个基本指令的组合来完成。我们的CPU在设计的时候就是这么干的,只支持有限个基本的运算指令,如加、减、乘、与、或、非、移位、跳转等。这些指令通过不同的组合,可以构成不同的指令序列(程序),实现不同的逻辑功能。

不同架构的处理器支持的指令类型是不同的。ARM架构的处理器只支持ARM指令,X86架构的处理器只支持X86指令。如果你在ARM架构的处理器上运行X86指令,就无法运行,报未定义指令的错误,因为ARM架构的处理器只支持ARM指令集中定义的指令。CPU支持的有限个指令的集合,我们称之为指令集。

2.9.1 什么是指令集

指令集架构(Instruction Set Architecture,ISA)是计算机体系架构的一部分。指令集是一个很虚的东西,是一个标准规范。红灯停、绿灯行、黄灯亮了等一等,只有行人和司机都去遵守这套交通规则,我们的交通系统才能有条不紊地运行下去。指令集也一样,芯片工程师在设计CPU时,也要以指令集中规定的指令格式为标准,实现不同的译码电路来支持指令集各种指令的运行。指令集最终的实现就是微架构,就是CPU内部的各种译码和执行电路。

编译器厂商在研发编译器工具或IDE时,也要以指令集为标准,将我们编写的C语言高级程序转换为指令集中规定的各种机器指令。为什么我们编写的高级程序经过编译后,可以直接在CPU上运行呢?就是因为CPU设计者和编译器开发者遵循的是同一个指令集标准,编译器最终编译生成的指令,都是CPU硬件电路支持运行的指令。每一种不同架构的CPU一般都需要配套一个对应的编译器。

指令集作为CPU和编译器的设计规范和参考标准,主要用来定义指令的格式、操作数的类型、寄存器的分配、地址的格式等,指令集主要由以下内容组成。

● 指令的分发、预取、解码、执行、写回。

● 操作数的类型、存储、存取、旁路转移。

● Load/Store架构。

● 寄存器。

● 地址的格式、大端模式、小端模式。

● 字节对齐、边界对齐等。

指令集也不是一成不变的,也会随着应用需求的推动不断迭代更新,不断扩充新的指令。例如ARM指令集,从最初的ARM V1发展到目前的ARM V8,一直在不断地发展,不断添加新的指令。

● ARM V1:最初版本,26位寻址空间,无乘法指令,没有商业化。

● ARM V2:增加了乘法指令,支持协处理器。

● ARM V3:寻址范围从26位扩展到32位。

● ARM V4:首次增加Thumb指令集。

● ARM V5:增加了增强型DSP指令、Java指令。

● ARM V6:首次增加60多条SIMD指令。

● ARM V7:增加长乘法指令、NEON指令。

● ARM V8:首次增加64位指令集、寄存器数量增加到31个。

指令集的价值在于大家都遵守同一个标准去开发计算机系统的不同硬件和软件,这非常有利于整个计算机系统生态的构建:IC工程师在设计CPU处理器时,遵守指令集标准,设计出硬件电路,支持标准规定的各种指令的运行;编译器开发者在开发编译器时,也会遵守指令集标准,将程序员编写的高级语言翻译成CPU支持运行的指令。从CPU到编译器,从编译器到应用程序,一个完整的计算机系统生态就建立起来了。如何吸引更多的开发者基于你的处理器平台做方案,如何吸引更多的开发者基于你的编译器或IDE环境开发应用程序,这就涉及行业生态和市场推广了,在此不再一一赘述。

2.9.2 什么是微架构

微架构,对应的英文是Microarchitecture,也就是处理器架构。集成电路工程师在设计处理器时,会按照指令集规定的指令,设计具体的译码和运算电路来支持这些指令的运行;指令集在CPU处理器内部的具体硬件电路的实现,我们就称为微架构。一套相同的指令集,可以由不同形式的电路实现,可以有不同的微架构。在设计一个微架构时,一般需要考虑很多问题:处理器是否支持分支预测,单发射还是多发射,顺序执行还是乱序执行?流水线需要多少级?主频需要多高?Cache需要多大?需要几级Cache?根据不同的配置选项,我们可以基于一套指令集设计出不同的微架构。以ARM V7指令集为例,基于该套指令集,面向高性能、低功耗等不同的市场定位,ARM公司设计出了Cortex-A7、Cortex-A8、Cortex-A9、Cortex-A15、Cortex-A17等不同的微架构。基于一款相同的微架构,通过不同的配置,也可以设计出不同的处理器类型。不同的SoC厂商,获得ARM公司的Cortex-A9微架构授权后,基于该内核集成不同的IP,就可以搭建出不同的SoC芯片,并最终流向市场。如三星公司的Exynos 4412处理器、瑞芯微公司的RK3188处理器都采用了Cortex-A9内核。

在X86处理器领域,目前能获得X86指令集授权,并基于该指令集设计微架构和处理器的厂商有三家:Intel、AMD和上海的兆芯。这三家厂商一般会根据新版本的X86指令集设计出各自的微架构,然后基于各自的微架构设计出不同的CPU。指令集、微架构与处理器三者之间的关系如图2-65所示。

图2-65 指令集、微架构与处理器的关系

Intel的酷睿处理器,无论是i3、i5还是i7,都基于相同的微架构,面向市场的不同定位和需求,在处理器主频、核数、Cache大小等方面进行差异性配置,设计出不同市场定位的处理器。AMD系列的处理器也是如此,基于Zen3微架构,通过不同配置,可以设计出锐龙3、锐龙5、锐龙7等面向不同市场定位的处理器。国产的兆芯处理器,也有自己设计的ZhangJiang、WuDaoKou等微架构,然后基于这些微架构设计出不同系列的处理器。

X86指令集因为专利垄断和授权限制,除了Intel、AMD、兆芯(VIA合并后的公司)这三家公司,其他公司一般无法获得授权去设计和生产自己的X86处理器。而ARM则不同,通过开放ARM指令集授权,其他公司可以基于授权的指令集去设计自己的微架构和SoC芯片,或者基于ARM官方的微架构直接去设计自己的SoC处理器。

在嵌入式处理器中,微架构不等于SoC,大家不要把概念混淆了,微架构一般也称为CPU内核。在一个ARM SoC芯片上,我们把CPU内核和各种外设IP通过AMBA总线连接起来,构成一个片上系统,即System On Chip,简称SoC。

在嵌入式芯片厂商中,并不是所有的芯片厂商都有能力和精力去设计微架构。除了ARM公司和几个技术积累比较深厚的芯片巨头,其他小芯片厂商、创业公司更倾向于直接使用ARM公司设计的微架构来快速搭建自己的SoC芯片,这种设计模式可以大大减少芯片的开发难度和成本。这种商业模式得益于ARM公司灵活的IP授权方式:ARM公司自己不生产芯片,也不卖芯片,主要靠IP授权盈利。面对不同的芯片厂商和市场需求,ARM公司有多种灵活的授权方式,目前主要有以下三种。

● 指令集/架构授权。

● 内核授权。

● 使用授权。

一个芯片厂商购买了指令集授权,可以基于该指令集实现自己的微架构,甚至可以对该指令集进行扩展或缩减。从目前来看,能获得ARM公司的指令集授权,并有能力设计微架构的公司不多,基本上也就是几个芯片巨头,如苹果公司的Swift微架构、高通公司的Krait微架构、三星公司的猫鼬微架构(三星公司目前已放弃自研)。除此之外,获得指令集授权的还有华为和龙芯,龙芯购买了MIPS指令集永久授权,自己又添加了很多条指令,根据该指令集设计出了GS464E微架构,然后基于该微架构设计出了龙芯3B2000、龙芯3A1000等处理器。

内核授权,又称为微架构授权。ARM公司根据自家的指令集标准设计出不同的微架构,其他芯片公司购买这个微架构,即CPU内核,然后使用AMBA总线和各种IP模块连接,就可以快速搭建出一个片上系统,即SoC芯片,封装测试通过后就可以快速推向市场销售了。ARM的微架构授权客户有很多,国外的公司有三星、飞思卡尔、ST、德州仪器,国内的公司有海思、瑞芯微、全志、联发科等。微架构授权的特点是客户不能对ARM的CPU内核(微架构)进行修改。为了满足不同客户的不同需求,基于一套相同的指令集,ARM公司会设计出不同的微架构,甚至会开放微架构中的一些可配置选项(如Cache大小),以方便客户搭建出差异化的处理器产品。ARM指令集与微架构如表2-1所示。

表2-1 ARM指令集与微架构

如果一个公司刚刚建立,处理器的设计和研发能力不是很强,但是又发掘到了不错的市场需求,想快速设计出一款SoC芯片产品来打开市场,此时就可以考虑使用授权。客户可以直接使用已经封装好的ARM处理器,不仅CPU内核的硬件电路已经设计好,连工艺制程、芯片生产厂家也帮你选好了。这种授权模式大大减轻了客户的设计负担,客户只需要关心自己的业务设计,快速做出产品推向市场,赢得市场先机。

当前的主流处理器市场基本上被X86和ARM瓜分。X86指令集不授权,不开放内核,靠X86专利垄断制造行业壁垒,抬高其他处理器厂商的准入门槛,所以你能看到市面上的X86 CPU厂家只有那几个巨头。ARM公司自己不生产CPU,靠IP授权盈利,众多SoC芯片厂商购买了ARM公司的IP授权后就可以自己设计和制造CPU,所以ARM处理器市场就比较热闹,各种芯片厂商、创业公司、处理器层出不穷,ARM因此也构建了一个庞大的ARM系统生态,垄断移动市场。以ARM V8指令集为例,如图2-66所示,我们可以看到基于该指令集,市场上出现的不同微架构,以及各种处理器和芯片厂商。他们和ARM公司一起构建了整个ARM开发生态。

图2-66 ARM V8指令集的开发生态

目前市面上还有一些免费开源的指令集架构,如RISC-V指令集。RISC-V指令集和ARM一样,同属于RISC指令集,两者都可以看作RISC指令集的一个分支。RISC-V属于RISC的第五个版本,因此叫作RISC-V。RISC-V指令集除了免费开源的诱人利好,还有架构精简、模块化设计灵活、指令可扩展定制等后发优势,目前已经有公司基于该指令集开发出自己的处理器,如平头哥半导体有限公司发布的玄铁910。RISC-V会不会对ARM系统生态构成冲击,还需时间验证。

2.9.3 指令助记符:汇编语言

前面已经提到,编译器开发商在针对某种架构的CPU开发编译器工具时,指令集是一个非常重要的参考。指令集是一个标准,工程师在设计CPU时会参考指令集,设计出对应的指令译码和执行电路,支持指令集中定义的各种指令在CPU上运行。编译器开发商在设计编译器时,也会参考指令集,将我们编写的高级语言程序翻译成CPU支持运行的二进制指令。

一个指令通常由操作码和操作数组成。指令格式是二进制的,就是一串数字,非常不好记,可读性差。如3+4-5运算,在ARM平台下对应的二进制机器指令如下。

为了方便编程,我们给这些二进制指令定义了各种助记符,这种助记符其实就是汇编指令。一段汇编程序经过汇编器的翻译,才能变成CPU真正能识别、译码和运行的二进制指令。上面的二进制机器指令,使用ARM汇编指令表示如下。

作为嵌入式底层、驱动开发者,笔者觉得掌握一门汇编语言是很有必要的。以ARM汇编语言为例,一方面,我们可以以汇编语言为媒介,深入学习ARM体系架构和CPU内部的工作原理;另一方面,我们也可以以汇编语言为工具,通过反汇编,深入理解C高级语言。任何编译型的高级语言,最终都会被编译器翻译成对应的汇编指令(二进制指令),通过汇编语言来分析C语言的底层实现,可以加深我们对C语言的理解,如函数调用、参数传递、中断处理、堆栈管理等。我们将可执行文件通过反编译生成汇编代码进行分析,就可以很直观地看到高级语言的这些过程在底层到底是怎么实现的。

在一些嵌入式软件优化、启动代码、Linux内核OOPS调试等场合,也需要你对汇编语言有一定的掌握。不同的编译器除了支持指令集规定的标准汇编指令,还会自己定义各种伪汇编指令,以方便程序的编写。掌握这些伪指令,对于我们分析计算机底层的工作原理和机制也很有帮助。

本书的写作初衷,是为从事嵌入式学习和开发的人员服务,而ARM又是目前嵌入式开发的主流平台,所以接下来的一章,我们将会以ARM微架构为例,重点讲解ARM体系结构、ARM指令和ARM汇编语言设计的一些知识,为后续的C语言进阶学习打下基础。