NEC V20 微码解码

Hacker News Top 新闻

摘要

本文描述了利用高分辨率芯片摄影解码NEC V20 CPU微码的过程,旨在改进MartyPC中的周期精确模拟,基于先前的8088微码工作。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/09/07 18:32

# 解码NEC V20微码 来源:https://martypc.blogspot.com/2026/09/decoding-nec-v20-microcode.html reenigne在2020年对8088微码的解码(https://www.reenigne.org/blog/8086-microcode-disassembled/)开启了对8088 CPU进行极其精确仿真的大门。 尽管我之前在MartyPC中增加了对NEC V20的支持,但V20核心在时序方面并非周期精确。它本质上是一个披着V20外衣的8088——只是在我的8088核心基础上粘贴了V20指令。 这并非理想情况,但在没有微码的情况下让V20核心实现周期精确,看起来可能是一项令人气馁的试错苦差。 那么,为什么不直接获取微码呢? 我最近委托InfoSecDJ(https://siliconpr0n.org/archive/doku.php?id=infosecdj:start#commissioning_work)对一颗NEC V20 CPU进行芯片摄影(实际上是由Sharp代工的第二来源V20,但依然是V20)。他做得非常出色。 [芯片图片] *NEC V20 CPU芯片 - InfoSecDJ* 这张照片马赛克分辨率极高——准确说是5.6吉像素,达到了惊人的70478x80672分辨率——大到甚至无法用JPEG格式存储! 您可以在[此处](https://siliconprawn.org/archive/doku.php?id=infosecdj:sharp:lh70108)以完整分辨率查看全部内容。 芯片中心下方偏下的矩形区域就是主微码ROM。 [ROM区域图片] V20微码ROM模块 ROM阵列大小为258x116,包含29,928比特。它能被29整除,而我们知道这正是V20的微码字长,这是个好迹象。但这意味着它包含1032个微码字,而我们原本只预期1024个。这有点奇怪,对吧?我们稍后会弄清原因。 这是ROM阵列的特写: [微码比特放大图] 微码比特,放大视图 水平方向明亮的走线是芯片金属层的一部分。黄色的点是连接金属层与下方多晶硅层的互连点。注意金属层后面的垂直条——并注意每个互连点两侧的多晶硅上偶尔存在间隙。 这些间隙构成了晶体管——晶体管的存在表示1比特。我将突出显示1比特以便于观察。 [1比特可视化图] 看到这个让我非常兴奋——如果我们能通过视觉识别ROM中的比特,就能提取ROM内容。但有一个小问题——阵列中有29,928个比特。手动提取会有点繁琐。 幸运的是,有工具可以完成这项任务。我使用了Travis Goodspeed的MaskRomTool(https://github.com/travisgoodspeed/maskromtool)加载ROM图像。 [MaskRomTool定义比特位置图] 使用MaskRomTool定义比特位置 MaskRomTool允许我绘制定义比特位置的行和列。但不幸的是,我发现它的比特检测方法基于阈值处理,而比特与非比特之间的对比度差异不足,无法使该机制有效工作。注意比特直方图非常集中在远端轴。这样行不通。也许没有明亮的金属层时,阈值技术会更有效,但我不想请InfoSecDJ尝试移除它。需要另一种方法。 幸运的是,我们可以使用MaskRomTool将定义的比特位置导出为JSON格式。我用这个导出的JSON文件编写了一个Python脚本,提取每个比特位置为中心的方形位图,并以比特的逻辑列号和行号命名保存。 这给了我一个105MB的ZIP文件,里面是许多42x42像素的PNG文件,每个文件包含一个比特或一个非比特。这样做的想法是训练一个卷积神经网络(https://en.wikipedia.org/wiki/Convolutional_neural_network)来识别比特是0还是1。 如果这听起来超出了你的能力范围,其实在这个项目之前我没有任何训练CNN的经验,而且我在一个下午就完成了。像PyTorch(https://pytorch.org/)这样的现代Python框架让这变得如此简单。 网上有一些很好的CNN教程(https://www.datacamp.com/tutorial/pytorch-cnn-tutorial),许多都专注于手写识别这类经典问题,而我们的任务更简单。我们只需将图像分为两类:0或1。这个问题实际上就是“热狗还是非热狗(https://www.youtube.com/watch?v=ACmydtFDTGs)”。 在训练CNN之前,我们需要训练数据集。所以我编写了一个快速简单的Python/tkinter脚本,以便我可以通过目视快速将比特分类到0或1的文件夹中。 [分类工具图] 快速简单的分类工具 这里显示的是一个“1”比特。你能通过金属层后面的晶体管发现它吗?那些按钮最终是多余的——你只需要按键盘上的1或0来分类比特。理论上,你可以这样做29,928次,几个小时就能完成工作。我原本打算将其作为CNN训练失败时的备用方法——有几个朋友愿意志愿帮忙,而“比特投票器”产生的JSON日志可以合并以支持分布式协作并达成共识。幸运的是,不需要这样做。 最终,我手动分类了超过1,000个比特。一旦它们被分到两个目录中,我们就可以尝试使用分类后的图像作为输入来训练CNN模型。 我不会详细讲解如何编写CNN——如果你感兴趣,源代码会在我的GitHub上。我使用了出色的PyTorch(https://pytorch.org/)库,它使整个过程比我想象的简单得多。 这是一次训练运行的样子: ``` [Epoch 01] train: loss=0.6945 acc=0.7273 f1=0.0164 | val: loss=0.6924 acc=0.7876 f1=0.0000 val precision=0.0000 recall=0.0000 cm=[[178, 0], [48, 0]] [Epoch 02] train: loss=0.6860 acc=0.7151 f1=0.3826 | val: loss=0.6257 acc=0.7965 f1=0.0729 val precision=0.5000 recall=0.0394 cm=[[178, 0], [46, 2]] [Epoch 03] train: loss=0.3751 acc=0.8914 f1=0.7213 | val: loss=0.3900 acc=0.7655 f1=0.6327 val precision=0.4661 recall=1.0000 cm=[[125, 53], [0, 48]] [Epoch 04] train: loss=0.1159 acc=0.9523 f1=0.9139 | val: loss=0.0495 acc=0.9912 f1=0.9773 val precision=0.9773 recall=0.9773 cm=[[177, 1], [1, 47]] [Epoch 05] train: loss=0.0251 acc=0.9945 f1=0.9888 | val: loss=0.0460 acc=0.9867 f1=0.9744 val precision=0.9514 recall=1.0000 cm=[[175, 3], [0, 48]] [Epoch 06] train: loss=0.0319 acc=0.9933 f1=0.9802 | val: loss=0.0438 acc=0.9823 f1=0.9659 val precision=0.9350 recall=1.0000 cm=[[174, 4], [0, 48]] [Epoch 07] train: loss=0.0185 acc=0.9945 f1=0.9212 | val: loss=0.0274 acc=0.9956 f1=0.9891 val precision=0.9792 recall=1.0000 cm=[[177, 1], [0, 48]] [Epoch 08] train: loss=0.0141 acc=0.9956 f1=0.9913 | val: loss=0.0271 acc=0.9956 f1=0.9891 val precision=0.9792 recall=1.0000 cm=[[177, 1], [0, 48]] [Epoch 09] train: loss=0.0101 acc=0.9978 f1=0.9940 | val: loss=0.0447 acc=0.9867 f1=0.9735 val precision=0.9488 recall=1.0000 cm=[[175, 3], [0, 48]] [Epoch 10] train: loss=0.0110 acc=0.9967 f1=0.9907 | val: loss=0.0437 acc=0.9912 f1=0.9773 val precision=0.9773 recall=0.9773 cm=[[177, 1], [1, 47]] Early stopping: no val F1 improvement >= 0.0 for 3 epoch(s). Best val F1: 0.9891 ``` 如果你有支持CUDA的GPU,训练会相当快——这只花了几分钟。 我们的想法是最大化准确率——但达到1.0可能不可行,甚至可能不可取(存在过拟合的问题)。有时训练更久反而会使情况变糟,所以如果我们看不到持续改善,就会停止训练。 训练的输出是一个神经网络模型——然后我们可以使用这个模型对整个输入数据集进行[推理](https://en.wikipedia.org/wiki/Inference_engine)。推理只是应用我们训练好的模型去执行它被训练任务的流行说法——预测给定图像是包含0比特还是1比特。 在我们继续之前——简要说明一下以避免潜在的争议。从计算机科学角度看,CNN大致属于AI的更广泛范畴,但我们并未使用现代通常指大型语言模型(LLM)的那种有争议的“AI”含义。 当我们运行推理时,每个像素都会得到一个置信度分数。我们可以使用这个置信度分数标记模型不太确定的比特,在某个特定阈值下(我这里使用<99%)。以下是第一次运行的结果,模糊的比特标记为红色: [首次推理结果图] 比特分类CNN的第一次输出 我将所有模糊比特手动分类回训练文件夹,然后重新运行训练,重复此过程直到获得以下结果: [最终推理结果图] 最终的“足够好”的推理运行 这效果相当好——只有4个比特仍然模糊,直接手动验证它们比再训练一个模型更快。 太好了,我们得到了29k个微码比特,并节省了数小时的繁琐手动劳动(换取了数小时编写Python训练脚本的时间,但至少这是可复用的!) 我们仍需将这个矩形的比特块转换为29位微码字的列表。换句话说,我们需要重组位图,使其变为29x1032,而不是258x116。具体如何操作并不明显,但我们可以暂时搁置,直到解码出[匹配解码器PLA](https://en.wikipedia.org/wiki/Programmable_logic_array)。 解码或“激活”PLA位于主微码ROM块的上方,中间夹着一些中间电路。 [解码器标注图] 解码PLA 这个PLA的任务是接收来自左侧的13个逻辑输入(每个输入都有一个反相信号线,总共26条输入线),如果输入信号匹配PLA的某一列,则激活其下方微码阵列的对应列。 “匹配”是什么意思? 特写一下可能更有信息量: [PLA放大图] 放大查看的解码PLA 我们看到类似的垂直金属走线排列,被互连点隔开,基底上的间隙形成晶体管。在这种情况下,这些晶体管构成逻辑与门。与微码ROM不同,这里只有一个晶体管连接到一个互连点,面向输入对的正常或反相信号。这使得PLA可以测试0、1或完全不关心该输入信号(即没有互连点的情况)。这创建了一种可屏蔽的布尔逻辑。由于所有门都连接在一起,只有当所有门都匹配时,相应的微码列才会被激活。 这种匹配逻辑非常巧妙——13个输入线中几乎可以肯定有8条是指令本身的指令操作码字节。在PLA中设置“无关”位允许整个指令范围共享相同的微码实现,这对于效率至关重要,这样微码ROM就不会占据整个CPU芯片。 这里存储的不是一组比特,而是逻辑——简单的与逻辑可以表示为比特对,我们可以用提取微码ROM相同的方式提取这些比特——在MaskRomTool中绘制行和列,将比特导出为图像,手动分类一千个左右,然后将其输入我们的“热狗”CNN。 这就是我们的结果: [PLA提取最终结果图] 最终的PLA提取结果 手动解码解码PLA的前几列结果很有希望。我们基本上得到: 00?00???0??00 00?00???10?00 00?000??11100 其中?表示我们“不关心”该位置的比特值,允许一系列操作码匹配。 幸运的是,指令操作码的8位似乎由从第四个输入开始的8个输入表示。 第一行将匹配64种不同的操作码,从00,01,02,03,08,09,0a,0b,10,11,12,13等开始。这恰好对应8088 ISA的一般ALU操作码,它们在8088上共享相同的微码。这是个好迹象。 第二行将匹配04,05,0c,0d,14,15,1c,1d等,对应使用立即操作数的一般ALU操作码。这肯定不只是巧合。事实上,匹配逻辑在大部分情况下都以令人安心的数值顺序排列。 需要注意的是,对于1032个微码字,只有257条激活线。这意味着对于给定指令,进入微码ROM的“入口点”必须在能被4整除的地址上——8088也是如此,所以看到这点并不奇怪。 微码的每一列包含4个字。 [列包含4个字的图片] (图片说明:V20微码ROM,列包含4个字。请注意,金属层走线是水平方向的,并且有一个间隙模式,这个模式可以识别位。) *注意:您提供的文章内容在此处不完整。*

相似文章

80386 微码反汇编

Hacker News Top

一篇博客文章,详细介绍了成功反汇编和分析 Intel 80386 微码的过程,揭示了215条指令入口点以及其复杂的内部架构。

Intel 8087浮点芯片的指令解码

Ken Shirriff

对Intel 8087浮点协处理器指令解码的详细逆向工程分析,解释主CPU与协处理器之间的交互、微码ROM的使用以及总线接口单元。