一次CPU“事故调查”
近日再一次想起CS61A的第一节课里面John DeNero说的一句话:
All computer scientist have a common enemy to bring us all together and that common enemy is called Complexity.
正如这句话所说,计算机科学是一个很大的领域,Complexity这个词在计算机范畴下的不同子领域里,有着不同的内涵。比如说算法分析里面,经常会说到Time Complexity,Space Complexity,这是算法执行的时候的精简程度考量。前几天在LeetCode上面写了几道动态规划题,我发现LeetCode有一个相当方便的地方是你在提交后可以看到所有做这道题的人代码的耗时和空间占用直方图,然后更为方便的是可以看到每个梯队的代表性代码。有道正则匹配的题,我调了半个晚上才通过,一看结果,发现全网写得最好的代码的效率是我的答案的几百倍。往往这种精简的答案比较贴近问题的本质,而停留于问题表象的人只能写出类似于暴力模拟的代码。不过这可能也是算法题的魅力吧,只要细心,题都能写出来,但是这份工程设计的优劣,还得看对于问题理解的角度与深度。
复杂性在计算机底层的表现大约是“复杂系统”这件事情本身。很多BUG,在人的线性因果思维里面难以察觉。因为人脑里很难真的想象出一台机器在严丝合缝的运转,我们只能是聚焦于局部,处理好每一个齿轮与它周围零件的咬合,但是对于这种咬合是否会影响到整台机器,我们知之甚少。有的人可能会说,那应该有那种大的框架设计呀,确实如此,大的框架设计能够在一定程度上将这种大量并行的思维转换成线性的因果关系(通过接口,通过模块化设计),不过即便如此还是会有很多意想不到的错误。当这些错误游离于注意力之外的时候,光靠人的推理是很难发现的。然后,因为一块砖没铺对,半栋楼塌了。
最近写CPU实现RISC-V基本指令的时候就发生了这样的事。在软件里面仿真的结果是正确的,但是烧写进入FPGA芯片之后,70%的指令都失效了。大惑不解的我在实验室坐了一整天,但是仍然没有什么进展。因为我自己的设计问题,产生的不定状态有时候(而且和测试代码相关,这一点非常奇怪)甚至会把整个调试系统的显示模块弄崩溃。如果说显示模块可以使用的话,我还可以把硬件的相关信号接出来查看,但是如果连屏幕都不亮的话,我甚至连指令运行的结果都无法知晓,更不用说调试了。
排查问题的过程是很长的。仿真正确而上板失效,一般来说是“综合”阶段出现了错误,EDA软件会采取一个默认的修正措施(不一定对)并抛出一个严重警告(critical warning),告知你软件的默认修正措施是什么。我最初设计的时候曾经报过一个严重错误 ,当时改换了一个模块(从源代码换成了带源代码的IP核封装,事实上就是没有改动) ,就没有报错了。
确认软件没有报错之后怀疑是FPGA坏了,于是换了另一个同款的开发板,结果仍然是错误。由于是在最后一个阶段出错,每一次代码改动都需要重新“综合”,“实现”,然后生成比特流文件写入FPGA芯片进行验证。任何一个改动需要二十分钟的等待,然后测试,思考下一步修改方案,然后再重新生成文件写入芯片……所以坐了一天,测试的次数其实很少。这一点正是硬件开发里面比软件开发难受得多的地方。同样是一个1000行代码量的项目,软件开发的一次调试可能只需要几秒,但是硬件物理验证就需要半个小时。
下午的调试没有结果,只是在摸索调试方法上面有所进展(比如新写的测试代码竟然不会炸屏幕了)。很多时候硬件出现BUG,想到调试的办法(比如说获取其中的某些信号)都需要费一些功夫。
最后的测试结果显示所有的加减移位逻辑运算都只取了第一个操作数作为结果,唯独and指令会将结果置零。得到的信息很有限,我咨询助教,助教表示也很奇怪,70%的指令都挂了,应该在仿真的时候就能看出端倪。他说如果屏幕能亮的话,推荐我把能够接到屏幕上的调试信号全部接上。后来实验室关门,回到寝室王翰说,仿真对上板错,大半是报了critical warning。我回他说已经被我处理掉了,后面没有再报。晚上陈sir把我的代码全数拿去看,也和我说报critical warning,我也回复了自己的解决方案。初步推断是第二个操作数默认为0,比较符合指令失效的现象。十一点半断电熄灯,我洗澡睡觉,想着周末实验室也不开,把信号全部接上之后,就等着周一再战了。想不到早上起来一看手机,陈sir一条凌晨一点的回复(他真的,我哭死),发现问题所在。其实就是那个critical warning的事情,只不过不知道为什么,换完了寄存器堆就不报错了。但是上板之后其实这个默认的修正是存在的,所以第二个操作数被直接接地,结果自然是0。
现在是准备好了文件,就等周一上板再调一波了。
细细想来,调试并找出错误这种事情,曾经对于一个复杂系统来说是需要耗费大量人力物力的。现代的EDA软件,还有程序的调试器,其实已经大大缩短了调试的周期。而在真正的工程实现中,准备一个调试平台就是十分困难的事情。绝大多数时候,在找BUG的过程中,工程人员更像是一个小说里的侦探,依靠逻辑推理,而不是看着数以千计的监控摄像头画面。复旦微电子的ZSN和我说,仿真正确上板错误的事情对于他们来说就是常态。而他们很多时候因为VGA的连带崩溃,只能把信号接到板载LED灯上,一次最多只能看16位的信号输出。这种调试难度,比我这次屏幕尚且可用(相当于一次可以看到1000位以上的信号),大出十倍不止。听她说,上板调试经常需要整个团队通宵达旦才能完成。
细节实现的失效对于复杂系统的崩溃很多时候是难以预料的(因为很难将两件看似毫不相干的事情联系在一起,就像是“蝴蝶效益”)。1986年“挑战者”号发射73秒后爆炸是因为火箭推进器的一个O型密封环失效导致的;而2003年哥伦比亚号航天飞机空难,调查报告发现是因为发射时超出预期的大风加上燃料箱的泡沫塑料脱落导致机翼划伤,而在返航时超高温空气渗入了机体内,导致了解体。
我又想到这次的东航空难前几天出来了初步调查报告,有的人说那份初步报告写赛没写,几乎都是已知信息,对于发生原因半字不提。距离空难发生才过去几十天,或许就像是我昨天坐在实验室测了一整天那样,只不过是刚刚找到一些破碎的表象,距离还原出真相还有很长距离(如果不是陈sir半夜看出来我的代码错误,我可能还要一两天才能定位到问题)。从已经崩塌的大楼找出最开始摆错的一块砖,对于空难调查员来说,事情大概就是这样。
最后感谢昨天一直在帮我出主意的助教、ZSN和陈sir,虽然是我个人的作业,在实验室也就我一个人,但是和你们聊天有助于我理清调试思路,同时也给了我很多解决问题的信心。
昨晚走去吃饭的时候我对自己说,已经按照成熟的方法做出来的东西,碰见了仿真和验证极不符合的情况,应该就是一个关键的小细节错了吧,这样莫名其妙产生的问题,大概在解决的时候也是突然一下子就会知道问题所在的吧。现在看来果然如此。
摘抄
槽边往事:
如果对一切事物都是用看综艺,或者短视频节目的心态来对待,那么这个世界就很容易让人难以忍耐。每一期综艺都得有个结果,给下期留个大悬念。每一个短视频节目都在结尾给出了某种确定无疑的答案,让人感觉“我懂了”。但是世界上大多数的事情不是这样的,不都有规定的格式,也不都有固定的时间,它按照自己的节奏发生,而且不是每一次都有答案。
别人找你倾诉,你得顺着对方说,这应该算是一个常识。女朋友或者老婆找你倾诉,你得顺着说跟着骂,不要给建议或者指点,这应该算是第二个常识。当你要公开讲这个常识的时候,不要强调性别,最好依照第一句话这种表达方式,这应该算是第三个常识。
倾诉,经常伪装成提问或者求教的样子——因为没有人愿意当情感垃圾桶,这是第四个常识——倾诉一旦以问号结束,人就会误以为是个提问,于是不自觉地认为自己有回答的必要,这就是个法术。破解这个法术的咒语也很简单,只有七个字:那你是怎么想的?真的有问题,那么会接着谈思路。真的想倾诉,那么会接着谈感受。
别人想要倾诉的时候你给建议,别人想要建议的时候你闭嘴聆听,因为理解错位会造成很多纷争,对话以不开心始,以更不开心结束。所以人们发明了一套言辞上的规范,好在对话一开始就界定清楚。比如说男性之间常见的请求倾诉规范是:我这几天心情不大好,晚上能不能出来陪我喝一杯?
另有一段张是之的对上海出现现在情况的分析,说上海的问题不是“电车难题”,而是一个“公地悲剧”。他写了些什么“产权不明确”之类的话。法律我不太懂,这些术语我也只能是望文生义。但大概意思是,没有人为自己正在做的事情负责,为防疫之外的连带副作用负责,这一点我还算认同,因为所有人都可以说我在奉公行事。
昨天有一场大型互联网行为艺术,“四月之声”视频接力。不禁想起之前那个被翻译成各种语言的吹哨人文章。虽然我不是键政人,未来也没打算对政治发表什么看法,但是这个独立博客,之所以是建在GitHub上,而不是选国内的公众号或者知乎、微博或者其他什么平台,就是为了消除那种想写点什么但必须时刻担心的顾虑,连一些正常的用词在非政治话题上都只能用缩写打出来的顾虑。虽然说,真要封谁,技术手段上,都是可行的(想想GitHub上的国内互联网公司作息表被举报查杀)。
对这场行为艺术,我感觉应当摘抄食指《相信未来》全诗,说不定还会有谁说一句“这是一个灰色的诗人,这是一首灰色的诗,相信未来就是否定现在。”:
当蜘蛛网无情地查封了我的炉台,
当灰烬的余烟叹息着贫困的悲哀,
我依然固执地铺平失望的灰烬,
用美丽的雪花写下:相信未来。
当我的紫葡萄化为深秋的露水,
当我的鲜花依偎在别人的情怀,
我依然固执地用凝霜的枯藤,
在凄凉的大地上写下:相信未来。
我要用手指那涌向天边的排浪,
我要用手掌那托起太阳的大海,
摇曳着曙光那支温暖漂亮的笔杆,
用孩子的笔体写下:相信未来。
我之所以坚定地相信未来,
是我相信未来人们的眼睛——
她有拨开历史风尘的睫毛,
她有看透岁月篇章的瞳孔。
不管人们对于我们腐烂的皮肉,
那些迷途的惆怅,失败的苦痛,
是寄予感动的热泪,深切的同情,
还是给以轻蔑的微笑,辛辣的嘲讽。
我坚信人们对于我们的脊骨,
那无数次地探索、迷途、失败和成功,
一定会给予热情、客观、公正的评定,
是的,我焦急地等待着他们的评定。
朋友,坚定地相信未来吧,
相信不屈不挠的努力,
相信战胜死亡的年轻,
相信未来,热爱生命。