前言

这里将收录一些不成体系的,不值得用一整个文章篇幅去探讨的,或者暂时只关注上层定义的关于计算机体系结构的杂项。每个小节之间的关联性不强,仅做快速查阅之用。

CISC与RISC

定义

复杂指令集(Complex Instruction Set Computing,CISC),代表就是Intel的x86架构

精简指令集(Reduced Instruction Set Computing,RISC),代表就是ARM架构

1

两种架构的优化思路不同:

程序的CPU执行时间=指令数×CPI×Clock Cycle Time程序的CPU执行时间 = 指令数 × CPI × Clock \space Cycle \space Time

CISC 架构是通过优化指令数,来减少 CPU 的执行时间。而 RISC 架构是在优化 CPI,因为指令比较简单,需要的时钟周期就比较少。

Intel的反击

CISC的设计是有受限于硬件条件不足的历史因素的影响的,但随着硬件的发展,内存和寄存器变得越来越多,像CISC这种为了更高效(充分)利用内存和寄存器的设计思路显然是过时的,且为了支持更多的复杂指令,硬件电路设计就要更复杂更困难,在散热和功耗上,也是更大的挑战。而RISC的设计思路则是遵循二八定律,仅在硬件上实现20%的最常用(程序执行时间的80%)的简单指令,而复杂操作则通过(编译器实现)简单指令的组合完成。这大大降低了CPU硬件电路设计的难度,同时腾出来的晶体管可以用来增加更多的通用寄存器或者其他用来提升CPU实际执行效率的优化。无论怎么看,RISC既是现在,又是未来,那么Intel是怎么反击的呢?

Intel不是不想完全摒弃x86的历史包袱(安腾处理器的IA-64架构就是在Intel迈入64位时代的一次尝试,它因为不兼容x86指令集,最终以失败告终。这此失败也给了AMD机会,AMD在此期间推出了能够兼容32位x86指令集的64位架构,即AMD64。没错,你有时候会看到x86-64指令集被称为AMD64,就是AMD“偷袭”了老师傅),但向前兼容的问题牵制住了Intel的步伐。推出新的完全基于RISC的CPU架构,意味着基于x86架构设计的操作系统和应用软件都将不能再使用,这对用户来说必然是不可接受的。既然步子不能迈太大,那就一步步同化喽。

于是,从 Pentium Pro 时代开始,Intel 就开始在处理器里引入了微指令(Micro-Instructions/Micro-Ops)架构。而微指令架构的引入,也让 CISC 和 RISC 的分界变得模糊起来。

在微指令架构的 CPU 里面,编译器编译出来的机器码和汇编代码并没有发生什么变化。但在指令译码的阶段,译码器会把一条机器码,“翻译”成好几条“微指令”,而不是一条CPU指令。这里的一条条微指令,就变成了固定长度的 RISC 风格的了。这些 RISC 风格的微指令,会被放到一个微指令缓冲区里面,然后再从缓冲区里面,分发给到后面的应用了超标量乱序执行的流水线架构里面。不过这个流水线架构里面接受的,就不是复杂的指令,而是精简的指令了。在这个架构里,我们的指令译码器相当于变成了设计模式里的一个适配器(Adaptor)。这个适配器,填平了 CISC 和 RISC 之间的指令差异。示意图如下所示:

2

但实现这样一个能够把 CISC 的指令译码成 RISC 指令的指令译码器,比原来的指令译码器要更复杂,这也就意味着更复杂的电路和更长的译码时间。为了缩短译码时间,Intel在CPU里增加一层L0 Cache用来存放已经译码好的好的微指令,基于程序代码的局部性假设,CPU在大部分情况下,都可以直接从Cache中拿到译码后的结果,从而不必执行重复的译码操作,一方面减少了译码时间,另一方面还减少了译码器的功耗。

尽管Intel处理器通过微指令架构融合了大量RISC处理器的设计,不断想RISC架构方向靠拢。但x86的CPU在功耗上还是远远超过了ARM的CPU(这篇论文详细论证了x86和ARM之间的功耗差异与指令集是CISC还是RISC并无直接关系),这也是智能手机时代成为ARM的时代的根本原因(另外还有只授权不生产的商业模式导致的良性竞争,进一步导致单片价格低,出货量大)。

当然ARM发展到现在,也同样借鉴(技术上的事儿能叫抄吗?)了CISC的诸多优点,如乱序执行和多发射。这进一步使得CISC和RISC之间的界限更加模糊。

不过,ARM并不开源,想用ARM架构来生产CPU就得交授权费,想要自己改动ARM架构的设计,不好意思,得加钱。不想交钱,倒是有RISC-V这样的开源架构,不够目前还不成气候,难以撼动ARM的垄断地位。

FPGA

FPGA即现场可编程门阵列(Field-Programmable Gate Array),它可以让我们像对软件一样对硬件(门电路阵列)进行编程,可以反复烧录,可以组合实现复杂的芯片功能。从而在芯片设计阶段提前验证芯片技术方案,而不需要真的生产一块芯片。那要怎么去“编程”硬件呢?FPGA的实现逻辑可以从以下三个层次去看:

  • 第一,用存储换功能实现组合逻辑。在 FPGA 里,基本的电路逻辑,不是采用布线连接的方式进行的,而是预先根据我们在软件里面设计的逻辑电路,算出对应的真值表,直接存到一个叫作 LUT(Look-Up Table,查找表)的电路里。而LUT其实就是一块存储空间,里面存储了“特定的输入信号下,对应输出是0还是1”。
3
  • 第二,对于需要实现的时序逻辑电路, 我们可以FPGA 里面直接放上 D 触发器,作为寄存器。把很多个 LUT 的电路和寄存器组合在一起,变成一个逻辑簇(Logic Cluster),在 FPGA 里也被叫做 CLB(Configurable Logic Block,可配置逻辑块)。CLB在最基础的门电路上做了组合,能够实现更复杂一点的功能,如全加器。

  • 第三,通过可编程逻辑布线,来连接各个不同的 CLB,最终实现我们想要实现的芯片功能。可编程逻辑布线可类比为铁路网,整个铁路网已经铺设好了,但设计了很多个道岔,我们可以通过控制道岔,来确定不同的列车线路。在可编程逻辑布线里面,“编程”在做的,就是拨动像道岔一样的各个电路开关,最终实现不同 CLB 之间的连接,完成我们想要的芯片功能。

4