在开工之前,每个工程师都希望自己能避开 bug,但是万一中了奖,后面的故事就有意思了。 
 
网友李幼萌:
08 年的时候,我所在的公司调试三星的一款新的 arm9 CPU,型号是 S3C2416,是 S3C2450 的简配版。开发板刚入手的时候还是热乎的,因为三星的这个芯片刚刚出来,国内的代理商一共就几块开发板。各公司评估开发板都是分时使用的,只能预约几天。开发板入手的时候,三星那面连 BSP 都没有准备好,没有 test code,没有 u-boot,没有 linux-kernel,甚至连 Spec 都是错误百出。还好我公司虽然小,研发能力在本地区还算不差,没有的东西可以自己移植。       
 
公司急着要出新品,在没有完全验证处理器的情况下,已经 layout 好了 PCB,并且去打样了(当时竞争确实比较激烈,400M 主频处理器而且这么低的价格绝对非常有诱惑力,所以公司决定冒这个险了)。在没黑没白的工作两周后,硬件和软件做的都差不多稳定了。这时候经理说,功能上问题不大了,我们来调一调休眠时的功耗吧(我们的产品一直以待机时极低功耗作为产品的卖点之一)。然而这却是噩梦的开始……
 
公司的指标是待机时休眠电流 500~800uA(电源电压 4V)之间。以前所有的产品都在这个范围之内,三星方面的技术支持也明确表示,他们的解决方案达到这个指标。
 
在我们调试过程中发现,整个系统休眠时的功耗在 1800uA 左右,一直降不下来。我们重新核对了所有的 IO 和外围电路的所有连接,以及 IO 口的电平配制,都没有问题。这时,我们决定测试每一个单元的功耗,用电流表分别串联进每一个外围电路,每个单元都很正常,就是系统总体偏大 1000uA。
 
我们连 flash 和 ram 的待机电流都测过了,仍然正常。好了,通过排除法已经确定了就是 CPU 的功耗过大。但是在开发板上调试休眠的时候,CPU 功耗却是正常的。
 
我们怀疑是开发板上 CPU 批号和拿到的 CPU 样品的批号之间有区别导致的,因为三星那面也在同步修正 CPU 的 BUG,所以我们“大胆地”把开发板上的 CPU 用风枪吹下来,换到我们的 PCB 上,把我们的 CPU 贴到了开发板上进行交叉验证。结果是开发板仍然功耗正常,我们自己的板子上功耗偏大,还是大了 1000uA。
 
CPU 周边的核心电路设计出现了问题!这是我们一致的判断!但是问题出在哪里,我们反复核对开发板的原理图和我们自己板子的原理图,简直就是一模一样!因为整个核心电路这部分就是从开发板上抄过来的,实在没有什么可比对的。我们转而又去怀疑 PCB 的问题了。 
 
我是做系统移植和软件的,纯电气的问题我就无能为力了。闲着没事,我就反复检查我在 linux 中对系统休眠的 IO 引脚配置。然后挂着电流表做反复测试。电流表也对的起我,每次都是那个数。在一次系统待机的时候,我实在忍无可忍,一把抓起了板子。突然之间,电流表的读数飞快下降,降到了 300uA!我松开手电流表的读数就又爬回来了。我把我这个惊奇的发现告诉了同事——一个硬件工程师。同事说可能是哪儿摸短路了,让我试试还能不能唤醒系统。我给了一个外部中断,系统神奇的正常唤醒了!
 
“难道这就是问题?”,我想重现一下。但是再次在待机的时候抓起电路板的时候,读数并没有显著发生变化。“可能是手法不好”,我这么想着,用手在板子上继续抚摸着。果然!当我的手指按到 PCB 中的某一个位置时,电流又降了下来!反复试了几次,都是这样,就是在我手指按压的这一片,只要是用手指按着,电流就正常!
 
这回同事开始重视了,打开 PCB 图,拿着电路图和万用表,查查我摸的到底是那块电路。硬件工程师觉得不可思议,因为我摸的部分并没有连接任何的电路——焊盘是空的。他于是用万用表的表笔去检查是不是 PCB 制版的问题,测一下这些空焊盘到底哪一个有电压。但是万用表中没有读数,这块都没有电。但是当万用表的表笔落在一处空焊盘的时候,电流表的读数又降下来了!
 
这可是重大发现,我们对照了一下电路图。这处空焊盘是 CPU 中 USB-Host 模块的 D+信号。由于我们的产品不需要 USB 的主机功能,所以这一块儿没有做任何处理。多亏了画原理图和 PCB 的同事,多留了一手,把 USB Host 的引脚都在 PCB 上做了个引出。谁也没想到是这个引脚出现了问题,辛亏这个信号引出来了,要是没有引出来,一辈子也查不出问题。我们给 D+信号加了一个下拉电阻后,系统的功耗瞬间正常了。
 
事后分析,三星自己开发板上有 USB-Host 的功能,所以 USB-Host 的外围电路也是完备的,所以功耗不会有问题。但是我们自己的产品上不使用 USB-Host 功能,没有相关外围电路,所以出了问题。这是因为在 CPU 休眠的时候,D+信号内部被悬空了!一句话,是三星 CPU 自己的 BUG。我们修改了我们的 PCB,增加了一个下拉电阻,同时将问题反馈给了三星。        
 
一个月后,当我们的产品量产时,三星也及时的解决了这个问题。那个下拉电阻也不需要再贴上去了。
 
网友辛晓晨
每次想起这个 bug,虽然很多很多年了,我仍然满脸都是泪水啊!当年做 x86 BIOS,客户是长城电脑。有一回我们的新版本发布给他们后进行系统重启测试,就是安装好操作系统后反复不停的重启机器,看看重启几百上千次后情况如何。原因是客户买了电脑每天用,至少得保障人家用个俩三年没事吧。结果我们的新版本重启到一百多次的时候挂了,现象就是开机黑屏,没有任何输出,就和当年的 CIH 病毒发作一模一样,经验判断系统压根还没有 boot OS 就跑飞了,我们自己测试也是这样,而且一旦出现问题就只能重新刷 BIOS 这个 bug 非常难调,因为当时我们的版本将近 300 万行源代码,大概 2%的汇编与 98%的 C,几千个源文件,光是用来参与 build 过程的工具就有十几个。而且这些工具都是自己写的,构建项目的时候先编译这些工具,再去用这些工具加编译器来生成最后的 ROM 文件并且更加恼人的是,我们当时没有 source level 的 debug tool,甚至连汇编级别的单步调试工具也没有,压根没法对代码做 step into/over,更没法加个断点。。。
 
当时可以用来调试 BIOS 的工具有两个,一个是 Intel 自己内部用的 ITP,这个是人家公司自己的,一般不给外面人用,当时我们公司与 I 公司的关系尚处蜜月期,给了我们两个,但是当时被 Chipset team 霸占着做 porting 用;另一个工具就是 American Arium(这家鸟公司不知道现在还活着不),这个东西说白了就是商品化的 ITP,因为目标客户少,所以价格巨贵巨贵!一套系统价格几万美金,而且每一代 CPU 都要换一个插座上的适配器,这个适配器又是一万美金好像,还不太稳定,用着用着就挂了。。。我们公司当时有俩,但是因为没有买新一代处理器的适配器,于是只能吃灰了于是我们唯一的调试手段就是 serial debug,就是系统启动的时候会通过 port 80 把一些重要信息打出来,然后我们根据这些信息判断执行到哪里了,系统的情况如何。这类似原始的 printf 打印。如果要看一个变量的值或者验证一下我们的判断,就得重新写代码,在需要的地方加入调试语句,然后花上半个小时 rebuild bios,再重新烧录,再上电运行看看打出来的到底是啥。如果有疑问,或者发现这里没有问题,又或者有了新的思路,重复上述过程。记忆中整整一个礼拜,我们都在不停的看 debug info,反复烧录 bios 哭啊!简直不是人过的日子!最后发现系统可以成功的跑过 PEI,到了 DXE 阶段的某个环节,突然就像心脏骤停一样,跑飞了!去看疑似跑飞的 DXE Driver,是个很普通的平台硬件初始化程序,没什么疑点,压根没有头绪。那段时间,几乎每时每刻都在想着这个 bug,实在是茶饭不思,根本没心情做任何事!就这样差不多过了俩礼拜,经过了无数次的重启与烧录 bios,以及猜测,验证,被否定,再猜测,再验证,再否定。。。。。的过程后,我们终于发现了问题的原因:大家可能还记得电脑主板上有个 CMOS,传统上用来存 bios 设置,但是现代的系统已经逐渐弃用这个东西。我们现在的 bios 芯片都是可擦写的,也就是用程序可编程。bios 大小是 8MB,里面会规划好,哪里是 code,哪里放设置等等,然后代码里有专门写 flash 的函数,让大家可以保存一些东西,比如你想用硬盘还是光驱启动等等。同时系统每次启动也都会自己写一点没什么鸟用的信息进来。问题就出在这个写 flash 的函数上,我们后来发现,这哥们算错了存储区域的地址,导致写很多次后终于越界,误写到了人家代码区,把人家好端端的代码给写的乱七八糟,就如同当年 CIH 破坏系统的方法一模一样,照这样哪个机器能点亮才怪呢!又因为每次系统写的信息不一样,比如启动时间就不太一样,所以越界需要的次数不是恒定,更加重了我们排错的难度,泪啊!
 
网友林铠鹏
差不多 10 年前,我们做了一个 ARM 核的芯片,据说还是国内第一批用 ARM7 做的,还挺高端,带有很多安全功能,当然安全就意味着难以调试,整个系统全部打散,不能分块。俺负责前端设计,系统,硬件软件驱动等杂七杂八一堆工作。    然后芯片流出来了,封装回来,几天几夜的调试,功能都正常了,那个高兴呀,第一个芯片就成功,奖金有了!
 
不过做稳定性测试时候有一个问题一直困扰着,这系统总是莫名其妙的有时候启动不起来,概率有个百分之几左右。上电就是不 LOAD。而一旦起来之后,就很容易了。
 
反正功能设计,硬件,驱动都是俺的,那就调呗,软件,硬件,电路,仿真,研究了好几天,抓狂,无解。又整个系统不能分块,我都开始怀疑是不是 ARM 核的问题。
 
又做了一个不断重启的测试系统,不断啪啪响上电断电,针对上电的情况作了统计。得出结论就是,上午不启动的概率高,下午不启动的概率低,晚上不启动的概率高,深夜不启动的概率低。。。。。和饥饿程度快挂钩了。。。
 
那时候那个抓狂啊,怀疑是什么干扰的,连屏蔽房,隔离电源啥都整出来了。就是没头绪,而公司给客户演示的时间快到了,要是现场挂掉就丢脸了,心里那个急啊。那段时间,每个深夜,公司里就是我座位上啪啪啪的声音 ------ 继电器的啪啪声。      接下来一个周日测试,公司空调坏了,汗流浃背,脾气极坏,几乎就要摔板子了。不过发现这天运气非常好,成功概率很高。没头绪,直接抽上烟,看着板子发呆,不知那根神经搭错,直接把烟头对着芯片戳上去!咱第一个亲生芯片!!如果不行了就掐死它!!!结果发现怪了,戳了烟头,启动哗哗的,每次都 OK。遂怀疑是尼古丁过敏或者是温度原因。拿着烙铁烫着它,每次必成。于是送进高低温箱,做温度曲线测试,发现环境温度 40 度以上,成功概率极高,刚好碰见今天加班没空调,平均温度高,所以表现良好。而启动起来因为系统一发热,所以后面启动就容易了,温度一凉下来表现惨不忍睹,敢情这芯片是非洲来的。
 
有了方向就好说,先解决 DEMO,给领导好看。遂做了一个电热丝发热电路,贴在芯片上,用单稳开关控制,一上电就加热,然后不断自动啪啪啪对芯片重启,一旦芯片重启成功了就断开发热电路和重启电路。进入正常运行情况。系统搭起来一测,效果杠杠的!!!基本都能保证几秒钟内就能启动,公司上下一片赞誉。 于是,领导拿着这套带着电炉丝的系统去做报告,销售拿着这个电炉丝 Demo 去给客户演示,取得极好成功,老板都在准备后续的销售计划了。俺心里急啊,总不能出货产品也带着电炉丝吧。。。
 
静下心好好分析,和温度有关,又是随机故障,应该很可能是哪个地方悬空,存在不定态的问题,外面的电路是不可能了,前端模拟加入随机量也不能重现,那很大可能是后端的人搞的鬼,遂拉来后端人员(暂且称为 C 公司),检查扫描链和测试电路,果然发现有一个寄存器没有初始化复位。于是后面的情况就简单了,往扫描链中灌入一串数据,把未知量洗出来。成功!!!
 
所以我们第一代的产品,主芯片旁有一个奇怪的芯片。据线人报告,有竞争对手和盗版者都认为这是安全反盗版电路,因为拆掉这一块,系统工作就时不时的异常,抓不到规律,可能包含短时间正版验证,长时间正版验证,随机正版验证等高精尖反盗版措施。反正无法破解。。。。俺笑而不语。图样图森破。:)
 
至于说为什么寄存器没有初始化复位没检查出来,我也不知道,这是人家 C 公司做的后端,他们的软件自己加进去的电路。而据说这 C 公司虽然牛,但那时候后端服务还是新的,软件也是新的,刚进国内,给我们一个特惠价做白老鼠。。。
 
网友 Xiang Liu
之前用 xilinx 一块比较高端的开发板验证一个高速信号的功能,发现有一路输出幅度是其他的 1/10,感觉很诧异,于是和师弟翻了一天手册文档,难不成这货还有配置幅度的功能。最后无解,用万用表在 BGA 焊盘和走线上一点一点地量,过了一个小时,发现 ....
 
(Bug 微距图)
 
尼玛一万多的板子 表贴 SMA 接头漏焊!中间大概有 0.5mm 的距离,高速信号空间耦合过去 10%。
 
于是默默用热风枪吹上,一切正常了。
 
这个 bug 的恐怖之处在于,高速信号可以从断点发射出去,然后让人误以为卧槽这有输出啊,不会想到根本就是个直流的断路,直到你用了万用表。
 
网友包治百病的板蓝根
读大学的时候,第一次接触嵌入式编程,当时目标是从一个传感器中读取温度变化,当温度达到某个阈值后发送短信到指定机器。由于芯片厂商给的官方编辑器很难用,当时我们是在 VC 上编写好,再用官方工具烧录到芯片里。
 
VC 里环境和芯片专用的环境各种不兼容的问题前前后后折腾了不少时间,当终于把程序烧录进去并成功运行后,却总不能收到短信。嵌入式平台无法做单步调试这种事情,于是我们只能无奈用最基本的方式排除问题。先是一众人 review 代码,但显然并不能检查出什么问题,在模拟环境中确实是可以跑通的。接下来软硬兼施,隔离各个方法,烧录进去运行并查看芯片的引脚电压变化作判断。大作业经费有限。由于芯片很贵,我们整个专业也没多少套这个平台。芯片烧录次数也是有限制的,我们并不能无限次的烧进去测试。整个过程大家都很小心谨慎,痛苦得想死。但最后都检查不出个所以然。直到作业结束,我们仍然没能把发短信这步完成。
 
不过老师检查过我们代码后仍然给出了个很高的分数,算是对我们这波努力的肯定。然后高潮来了,助教,就是老师带的研究生,清理我们上交的机器,把机器里的电话卡回收,于是我们知道了一直发不出去短信的原因。欠!费!