几年前,我研究 Altera USB Blaster 仿制品的问题时有点过头了。写那篇文章时,我在 Linux 上遇到了一些令人非常沮丧、时有时无的 jtagd 问题,一直没能弄清楚原因:
我还在一台电脑上测试时,遇到了一个与此无关、非常奇怪的问题:Linux 上的 Quartus Programmer 18.1 出现了异常。我在 Wireshark 抓包中看到了损坏的 USB 发出流量——甚至还没涉及 FT245 或 CPLD。这个问题影响了我所有的 blaster,而不仅仅是 CPLD+FT245 型号。
我有时仍会注意到,在 Linux 上插入任何一款基于 CPLD 的 USB Blaster 后,第一次尝试执行 JTAG 操作会延迟很久(约 30 秒)。这并非每次都会发生,但发生时,jtagd 似乎会卡在等待接收一个永远不会到来的字节,于是超时后再试一次。之后就能正常工作了。
当时,我已经被其他 USB Blaster 问题折腾得筋疲力尽,完全失去了继续深挖这些恼人问题的兴趣,于是就把它们搁置了。直到现在。AI 让人们有机会解决其中一些小谜团,而不必花大量时间调查。相信我,调试可以是一项令人兴奋的挑战,但诊断时有时无的问题实在一点也不好玩。
我把这两个问题都交给了 Claude Opus 5.5。它很乐意着手逆向分析 jtagd,并轻松找出了这两个问题。在调查和测试第一个错误时,它一度碰到了网络安全护栏,但我注意到,与过去的版本相比,Opus 5.5 更擅长在触发护栏时尝试稍有不同的方法,也会给我机会改写提示,而不是立刻把我降级到 Opus 4.8(然后 Opus 4.8 可能又会触发类似的保护措施,导致会话结束)。这是一个非常受欢迎的改进。
总之,我想简单分享一下最终发现,并附上一个工具链接;如果这些错误一直让你烦不胜烦,可以用它来修补 jtagd。首先,这是修补工具的链接:https://github.com/dougg3/altera-jtagd-linux-fixes
导致 USB 流量损坏的第一个错误,最终被证实是JTAG_SERVER_USB_BLASTER构造函数中的一个变量未初始化。构造函数把附近的许多变量都设为了零,但这个变量显然被不小心漏掉了。如果它的值恰好大于 0,就会把一些仅供较新的 Altera USB-Blaster II 使用的数据额外插入发出的 USB 数据中,从而让旧款 USB Blaster 感到困惑。这完全解释了我在之前文章中提到的数据损坏。和 LLM 不同,我脑子里没有足够的背景信息,没能把“损坏的数据”与较新的 USB Blaster 协议联系起来。我见过的所有仿制设备都使用原版 USB Blaster 协议,因此无一幸免。它们最终会卡住 30 秒,随后出现Unable to read device chain - JTAG chain broken错误。
这似乎只会在 jtagd 仍在运行时拔下再重新插入 blaster 的情况下发生,因为第一次分配的内存往往总是被清零的。每次热插拔都会创建一个新的JTAG_SERVER_USB_BLASTER对象,并导致堆中的部分内存被重新使用。我 2024 年做的许多早期工作都涉及 Windows 虚拟机,因此我当时很可能频繁热插拔设备。这可能不是很多人都会遇到的问题,但它确实烦人得要命。
第二个导致 30 秒延迟的错误要隐蔽一些。它似乎取决于 blaster 使用哪种 FTDI FT245 系列芯片,甚至可能还与它连接到电脑的方式有关。问题的关键在于,jtagd 首次打开芯片时,会按以下顺序向 FT245 发送这些命令:
-
- Reset
-
- Purge TX
-
- Purge RX
-
- Set Latency Timer
这组特定的命令似乎会导致某些仿制设备在约 250 毫秒内忽略收到的数据。这通常不是什么大问题,但 jtagd 会立刻与它通信,以确认连接的 JTAG 链,随后便卡住并等待响应,最长达 30 秒。我有几款不同的 blaster 仿制品,它们对这组命令的反应各不相同(有些会受影响,有些不会),但这些都只是 FT245 命令。也许不同仿制品使用了不同修订版本的芯片?我只知道,把这组命令的顺序改成先执行 Set Latency Timer,就能彻底消除这个问题。
这两个问题在 jtagd 中至少从 Quartus 18.1 起就存在了,到 Quartus 25.1 仍然没有修复。Altera,如果你们看到了这篇文章,请考虑至少修复第一个错误!老实说,我不确定第二个问题到底算不算“错误”,也可能只是某些仿制设备的芯片行为与官方设备不完全一致,但在 Set Latency Timer 之后延迟 300 毫秒,或许就能解决问题。
修复这两个问题后,我能接触到的所有 USB Blaster 仿制品现在都运行良好(当然,也要先应用我之前对固件/CPLD 所做的修复)。我甚至使用了一个兼容uhubctl的 USB 集线器,来自动切换所有仿制设备的电源。这样我就能建立一个反馈循环,进行大量压力测试,认真验证修复确实有效。
回想起来,第一个错误我完全可以(也应该)自己找出来。valgrind 似乎会直接把问题摆在我面前。我有点失望,自己当时竟没想到试试它。当时我正用rr深入调查并尝试复现这个错误,但它与我的主力开发机不兼容。后来我在一台确实兼容 rr 的旧电脑上安装了 Quartus,却再也无法复现这个问题。那时我已经厌烦了所有这些折腾,于是放弃了。话说回来,这个错误在 jtagd 中至少存在了 8 年,而且基本上一直没人注意到,除了Altera 论坛上的这篇小帖子,有人意识到情况有些古怪,但基本上没人理会。
我的博客不会彻底变成一个“嘿,看看 AI 帮我做了什么”的展示。我想做的是与社区分享一个有用的变通方法,并希望引起 Altera 对这个长期存在的错误的关注,让他们修复它。另外,也许只是我比较奇怪,但用 AI 修复闭源软件里恼人的错误还挺有趣的,尤其是解决过去留下的、我原以为根本不可能收尾的问题。有鉴于此,这篇文章之后确实还会有一篇后续文章,我会在那里解决 USB Blaster 调查中遗留的最后一个未解问题。