从8087数学协处理器芯片中提取ROM常量

2020-05-17 01:58:17

英特尔在1980年推出8087芯片,以提高8086和8088处理器上的浮点性能,它是在最初的IBM PC上使用的。由于早期的微处理器只对整数进行运算,浮点数的算术速度很慢,诸如反正切或对数的超越运算甚至更差。将8087协处理器芯片添加到系统中,使浮点运算的速度提高了100倍。

我打开一块8087芯片,用显微镜拍照。下面的照片显示了芯片的微小硅芯。在芯片的边缘,细小的焊线将芯片与40个外部引脚相连。标签显示了基于我的逆向工程的主要功能模块。通过仔细检查芯片,可以从芯片的ROM中读出各种常量,比如芯片在计算时使用的圆周率(Pi)。

Intel8087浮点单元芯片的裸片,标有主要功能块。常量ROM用绿色标出。单击查看大图。

芯片的上半部分包含控制电路。执行浮点指令可能需要1000个步骤;8087使用微码指定这些步骤。上面的芯片照片显示了运行微码程序的引擎;它基本上是一个简单的CPU。它旁边是容纳微码的大型ROM。

芯片的下半部分包含处理浮点数的电路。浮点数由分数(也称为有效数或尾数)、指数和符号位组成。(对于以10为基数的类比,在数字6.02×10 23中,6.02是分数,23是指数。)芯片有单独的电路来并行处理分数和指数。分数处理电路支持67位值,64位小数加上三个额外的位以保证精度。和寄存器堆栈。常量ROM(以绿色突出显示)是本帖子的主题。

8087与8086处理器作为协处理器运行。当8086遇到特殊的浮点指令时,处理器会忽略它,让8087并行执行该指令。我不会详细解释8087的内部工作原理,但作为一个概述,浮点运算是使用整数加/减和移位来实现的。要加或减两个浮点数,8087会将数字移位,直到二进制点(即小数点,但是二进制)对齐,然后加或减小数。乘法、除法和平方根是通过重复移位和加减来执行的。超越运算(tan、arctan、log。

这篇文章描述了保存常量的ROM(不要与更大的四级微码ROM混淆)。2)常量ROM保存8087计算所需的常量(如pi、ln(2)和sqrt(2)),下图显示常量ROM的一部分。为了显示下面的硅,去除了金属层。粉红色的区域是掺硅的,具有不同的性质,而红色和绿色的线条是多晶硅,这是一种特殊类型的硅布线,覆盖在顶部。请注意ROM的规则栅格结构。只读存储器由两列晶体管组成,用来存放比特。为了解释只读存储器是如何工作的,我将首先解释晶体管是如何工作的。

固定ROM的一部分,去除了金属层。三列较大的晶体管用于G。

20世纪70年代的高密度集成电路通常是由一种被称为NMOS的晶体管建造的。(现代计算机是由CMOS组成的,它由NMOS晶体管和相反极性的PMOS晶体管组成。)下图显示了NMOS晶体管的结构。集成电路是由硅衬底和晶体管构成的。硅的各个区域掺入杂质,形成具有所需电性能的扩散区域。晶体管可以看作一个开关,允许电流在称为源极和漏极的两个扩散区域之间流动。晶体管由栅极控制,栅极由一种称为多晶硅的特殊类型的硅制成。在栅极上施加电压,使电流在源极和漏极之间流动,否则就会被阻塞。8087的芯片相当复杂。8087的芯片相当复杂,它由一种特殊类型的硅制成,称为多晶硅。在栅极上施加电压,可以让电流在源极和漏极之间流动,否则就会被阻塞。8087的芯片相当复杂。3个。

放大ROM显示单个晶体管。粉红色区域是掺杂的硅,形成晶体管的源极和漏极。垂直的多晶硅选择线形成晶体管的栅极。指示的硅区连接到地,将每个晶体管的一侧拉低。圆圈是硅和上面的金属线之间的连接,称为通孔。(金属线已被移除;橙色线显示了一条的位置。)。

常量只读存储器的一部分。每条选择线选择一个特定的常数。晶体管用黄色符号表示。X表示缺少晶体管,对应于0位。橙色线条表示金属线的位置。(这张照片的金属层已经溶解。)。

ROM的重要特征是一些晶体管丢失,第一个在上行,两个在下行标有X。通过改变硅掺杂图案、创建晶体管或离开绝缘区,位被编程到ROM中。每个晶体管或丢失的晶体管代表一位。当一条选择线被激活时,该列中的所有晶体管都将接通,将相应的输出线拉低。但是,如果晶体管从选定的位置丢失,则相应的输出线将保持高电平。因此,当选择线被激活时,该列中的所有晶体管都将导通,从而将相应的输出线拉低。因此,如果晶体管从选定的位置丢失,则相应的输出线将保持高。因此,当选择线被激活时,该列中的所有晶体管都将导通,将相应的输出线拉低。

常量ROM有134行21列。5在显微镜下,ROM的位图案是可见的,可以提取。4如何解释原始比特并不明显,第一个问题是晶体管(相对于间隙)是0还是1。(原来晶体管表示1位。)下一个问题是如何将134×21位网格映射为值。6个。

芯片的数据路径由67个水平行组成,因此ROM中的134行似乎相当清楚地对应于两组67位的常量。我提取了一组奇数行的常量和一组偶数行的常量,但这些值没有任何意义。经过进一步考虑,我确定这两行不是交替的,而是以重复的模式排列的。7使用此模式产生了大量可识别的常量,包括pi和1。这些常量的位如下图所示。(在此照片中,1位显示为绿色条纹,而0位显示为红色条纹。)在二进制中,pi为11.001001.。这个值在上面标有标签的位中可见。最下面的值是常数1.8。

常量ROM中标记的位值。最高的位是π的第一部分,而较低的位是常数1,与其他图相比,这个图已经旋转了90度。未标记的位形成其他常量。

解释的下一个困难是这个ROM只保存数字的小数部分,而不是指数。(我还没有找到单独的指数ROM。)我尝试了各种指数,直到我得到了合理的数值。有些很简单:例如,使用指数2-2时,常量1.204120产生log10(2)。还有一些比较难,9,比如1.734723。最后,我算出了1.734723×259时是10 18.10

完整的常量表在脚注中。11从物理上讲,常量分为三组。第一组是用户可以加载的值(1、π、log210、log2e、log102和ln2)12以及内部使用的值(1018、ln(2)/3、3*log2(E)、log2(E)和sqrt(2))。第二组是16个arctan常量,第三组是14个log2常量。最后两组常量用于使用CORDIC算法计算超越函数,下面我将讨论这一点。

ROM中的常量揭示了8087使用的算法的一些细节。ROM包含16个反正切值,即2-n的弧线。它还包含14个对数值,即以(1+2-n)为底的对数。这些看起来可能不寻常的值,但它们被用于1958年发明的一种名为CORDIC的高效算法中。

CORDIC的基本思想是通过将一个角度分解成较小的角度,然后按这些角度旋转矢量来计算正切和反正切。诀窍在于,通过仔细选择较小的角度,可以用有效的移位和加法而不是三角函数来计算每次旋转。具体地说,假设我们想要求tan(Z),我们可以将z分解成更小角度的和:Z≈{atan(2-1)or 0}+{atan(2-2)或0}+{atan(2-2。旋转一个向量,比方说atan(2-2),可以通过乘以2-2和加法来完成,关键是乘以2-2只是一个快速的位移位,把所有这些放在一起,计算tan(Z)可以通过将z与atan常量进行比较,然后进行16个周期的加法和移位来完成,这在硬件上执行起来很快。13为了使算法工作,需要预先计算ATAN常量并将其存储在常量ROM中。14.。

计算以2为底的对数和以2为底的指数也使用CORDIC算法,并带有相关的对数常数,关键是通过移位和加法可以快速地乘以(1+2-n),通过将方程的一侧乘以值序列,并将相应的对数常数加到另一侧,就可以计算对数或指数。15个。

8087对超越函数的支持比你想象的要有限得多。它只支持正切和反正切函数,而不支持正弦或余弦函数;用户必须应用三角恒等式来计算正弦或余弦。对数和指数只支持基数2;对于基数10或基数e,用户必须应用适当的刻度因数。当时,8087突破了芯片容量的极限,因此指令集仅限于基本指令集。

8087是一个复杂的芯片,乍看起来就像是一个毫无希望的电路迷宫。但仔细研究一下就能理解它的大部分内容。它包含42个常量,这些常量的值可以在显微镜下提取。有些常量(如pi)是预期的,而另一些(如log2(3))则更令人费解。许多常量被用来计算正切、反正切、正切、对数和幂函数,使用快速余弦法计算正切、反正切、对数和幂函数。8087是一个复杂的芯片,起初它看起来像一个无望的电路迷宫。但只要仔细研究,就能理解它的大部分内容。它包含42个常数,这些常数的值可以在显微镜下提取。

去除金属层的8087模具照片。单击查看大图。

尽管英特尔的8087浮点单元芯片是在40年前推出的,但它在今天仍然具有很大的影响力。它催生了用于大多数现代浮点算术的IEEE754浮点标准,8087指令仍然是大多数计算机中使用的x86处理器的一部分。

有关8087的更多信息,请参阅我的其他文章:每晶体管2位ROM和衬底偏压发生器。我在Twitter上发布了我最新的博客文章,请关注me@kensheriff以获得未来的文章。我还有一个RSS订阅源。

8086处理器和8087浮点单元之间的交互有点棘手;我将讨论一些亮点。简单的观点是,8087监视8086的指令流,并执行任何属于8087指令的指令。复杂的是,8086有指令预取缓冲器,所以正在获取的指令不是正在执行的指令。因此,8087复制了8086的预取缓冲器(或8088。较小的预取缓冲区),因此它知道8086正在执行此操作。(一个Twitter线程详细讨论了这一点。)另一个复杂的问题是8086使用复杂的寻址模式,它使用8086内部的寄存器。8087不能执行这些寻址模式,因为它不能访问8086寄存器。取而代之的是,当8086看到8087指令时,它会从寻址的位置进行内存提取,并忽略结果。同时,8087从TheBus上获取地址,以便在需要时可以使用该地址。如果没有8087,您可能会认为会出现陷阱,但实际情况并非如此。相反,对于没有8087的系统,链接器会重写8087指令,用对仿真库的子例程调用替换它们。↩。

8087的微码只读存储器是用一种不同寻常的技术构建的,每个晶体管存储两个比特。通过在每个位置使用三个不同大小的晶体管或不使用晶体管来实现这一点。每个位置的四种可能性代表两个比特。为了将大容量只读存储器安装到8087芯片上,这种复杂的技术是必要的。我写了一篇博客文章,详细介绍了这一点。相比之下,常量只读存储器是使用标准技术构建的。-↩。

数据来源为8087中的晶体管数量提供了不一致的值:英特尔声称有40,000个晶体管,而维基百科声称有45,000个。这种差异可能是由于计算晶体管的方法不同。尤其是,因为ROM、PLA或类似结构中的晶体管数量取决于存储在其中的数据,所以数据来源通常计算潜在晶体管的数量,而不是物理晶体管的数量。其他差异可能是由于是否计算上拉晶体管以及是否计算大电流驱动器造成的。

我没有手动复制ROM中的位,而是编写了一个简单的JavaScript程序来帮助我读取ROM。我点击只读存储器图像来指示每个晶体管,程序产生了相应的0和1的图案。↩。

ROM有134行21位,除了左上角缺少6×6块外,所以物理大小是恒定的ROM为2946位。

由于只读存储器的布局,这一缺失的部分意味着前12个常量是64位长,而不是67位。这些是非CORDIC常量,显然不需要额外的位来保证精度。-↩。

有两种方法可以确定比特的编码。第一种是找出从只读存储器中读取数据的电路,并检查数据是如何使用的。第二种是在原始数据中寻找模式,并确定什么对编码有意义。由于8087非常复杂,我想避免完全的反向工程来理解常量,所以我使用了第二种方法。-↩

行的组织遵循ABBAABBAABBA.模式,其中行保存一组常量的位,行保存第二组常量的位。这种布局可能用来代替交替的行(#34;ABAB),因为一个连接可以驱动两个相邻的选择晶体管。也就是说,可以用一根焊线选择每个";aa&34;或";bb&34;组。“↩。

我确定了三个关键因素。首先,位从ROM的底部开始向上。第二,晶体管指示1,而不是0。第三,常量的开头没有隐含的1位。(换句话说,常量格式与8087使用的外部数据格式不匹配。)-↩。

一些指数很难确定。我对他们中的一些人使用了暴力,看是否有指数能给出某个数字的对数或幂。最难计算的数字之一是ln(2)/3;我不确定为什么这个值很重要。“↩。

为什么8087包含常量10 18?可能是因为8087支持可容纳18位的压缩bcd数据类型,因此最多可容纳10 18。/↩。

下表总结了常量ROM的内容。<#34;>一栏的意思是<#34;>一栏是我对这个数字的解释。

从CORDIC常量可以清楚地看出,只读存储器中的值没有按物理顺序存储,即连续的行没有按顺序寻址。我不确定为什么10 18出现两次;可能一个指数是不同的。二进制指数不在我检查的只读存储器中,所以我不得不估计它们。-↩。

↩提供了7条直接加载常量的指令。指令FDLZ、FLD1、FLDPI、FLD2T、FLD2E、FLDLG2和FLDLN2分别将常量0、1、p1、log210、log2e、log102和ln2加载到堆栈上。除了0之外,这些常量还可以在ROM中找到。

8087年的CORDIC算法在数值处理器的超越函数的实现中有描述,我在这里写了基于该描述的正切代码示例。在8087s的完整的TRAN算法中还有几个乘法和除法。它对剩余的角度使用了一个简单的有理正切近似,使其比直接的CORDIC更精确。-↩

计算角度的反正切差所使用的算法与切线算法类似,但与之相反:在执行旋转时,会将角度(从常量ROM中)相加,以得出最终的角度。-↩。

我找不到关于8087的对数和指数算法的文档。我认为这些算法与本页上的算法非常相似,只是8087使用的是基数2而不是基数E。我有点不明白为什么8087不需要该算法使用的常量log2(1+2-1)。-↩