Skip to content
YMK Must be Kidding
Go back

Tang Console 板上的 UART 壞了,只壞一個方向,底下還疊著另一個 bug

這篇是叫 Claude 從我的筆記整理出來,事情真的發生過,字是他打的,連語氣也是叫他學我的,我只負責瞄一下的喔。

板子不講話的時候,先確定你聽得到

先講這是什麼東西

Tang Console,Sipeed 出的一塊 FPGA 板子,上面是一顆 138K 的 Gowin 晶片,算蠻大的。前幾篇講的那台跑 Linux 的 terminal 就是在它上面。這篇是更前面的事,九月初剛拿到板子,第一步是在上面拼一顆小小的 RISC-V,叫 picorv32,讓它從 UART 印一行字出來。就這樣而已耶。

板子上有一顆 debugger 晶片,接 USB 之後電腦這邊就看到 JTAG 跟一個 serial port,燒 bitstream 跟看 UART 都走它。很方便,照理說。

症狀:DONE 亮了,就是不講話

bitstream 燒進去,DONE 燈亮,表示 FPGA 有收到設定。然後 serial port 開著,什麼都沒有。一個字都沒有。

這樣卡了好幾個禮拜。改設計、換 core、改 clock、改 UART 的 baud,每一個版本都一樣安靜。安靜的問題最討厭的地方是它沒有訊息:你分不出來是「設計壞了所以沒在講」還是「設計好好的在講,只是我們聽不到」。這兩個可能性每次都一起出現,每次都沒辦法排除其中一個。

後來才知道,是兩個 bug 疊在一起。一個是聽不到,一個是沒在講。所以不管修哪一邊,看起來都沒有動。

一顆一顆驗

既然猜不出來,那就每一樣東西都量一次:

然後 debugger 的韌體重刷過,hash 對過。USB 線換過,port 換過,電源換過,連電腦都換過一台。還是零。

一個 bitstream,兩根腳

最後是這樣抓到的。寫一個設計,同一個 register 的輸出同時接到兩根腳:一根是 U15,板上 debugger 那條;另一根是 pmod 排針上隨便一根,外面接一個 USB 轉 serial 的小板子。然後讓它一直送 0x55。

腳收到
U15,板上 debugger0 bytes
pmod 那根,外接 serial36046 bytes,7199 b/s,預期是 7200

同一個設計、同一個 clock、同一瞬間,一邊完美,一邊零。這就不是程式的問題了,除了那條線本身是斷的,好像想不到別的解釋。

debugger 的韌體查了一下,Sipeed 只發過一版,所以也沒有東西可以升級。那就是硬體了。板上的 UART 從此放棄,改用 pmod 那根加外接的 serial 小板,一路用到現在。

有了會講話的腳,SoC 還是不講話

這下聽得到了,picorv32 還是一個字都不印。不過這次不一樣了,不講話終於是有意義的:它真的沒在講。

Claude 去讀 program counter,發現 0x40 這個位址有抓進來,0x44 從來沒有,可是這兩個是連在一起的指令耶。picorv32 會先多抓一條,branch 跳走的時候再丟掉,所以 0x44 沒出現,代表它在 0x40 就跳走了,而它不應該跳。

原因是這樣。picorv32 讀 register file 是用組合邏輯的,意思是這個 cycle 讀,這個 cycle 就要拿到。可是 Gowin 的工具看到一個 32 個 32-bit 的陣列,就很貼心的幫你放進 block RAM。block RAM 讀出來要等下一個 cycle。所以每一次讀 register,拿到的都是上一拍的值。

所以它 fetch 是對的,store 是對的,從 bus 上讀到的資料也是對的,然後拿舊的 register 去算。表面上的症狀是 uart_putc 卡在一個 tx_busy 的 bit 上一直等,可是同一個 bus 上同一個 cycle 另外接一個 probe 去讀,那個 bit 明明是 0。它看到的是上一拍的 1。

修法是一個 attribute:

(* syn_ramstyle = "registers" *) reg [31:0] cpuregs [0:regfile_size-1];

叫工具不要用 block RAM,用 flip-flop。多了 1024 個 flip-flop,在 138K 的晶片上好像不算什麼。燒進去,selftest 過,讀 clock 剛好 50,000,000 Hz。第一行字終於出來了。

這段最有感的是,工具從頭到尾都說它是健康的喔。0 個 setup violation、0 個 hold violation,fmax 84 MHz 對 50 MHz 的要求綽綽有餘。timing 對不代表邏輯對,它們是兩件事,工具只管前面那件。

後來 U15 又活了一下

這個有點好笑。幾天後把板子上的 SoM 拆下來看了看、再裝回去,U15 居然送出 2582 個 bytes,有史以來第一次。然後再試 20 次,每次 2 秒,全零。同一個 register 接到 pmod 的那根,每次都是完美的 16384。

所以它不是斷掉的線,也不是少焊了什麼,是一個接觸不良的點,剛好碰到了一下。這也解釋了更早的一件事:第一次玩這塊板子的時候,有看過一顆 LED 跟著這條線閃 3 秒亮 3 秒暗,之後就再也沒看過。兩次都是真的,只是那個接點心情不一樣。

Sipeed 的 wiki 上有一條「板上 debugger 的 UART 不能用」,症狀是 baud 變四倍或是亂碼,跟我們這個一個字都沒有的不太一樣。總之板上的那條就讓它去了,pmod 很好用。

路上踩到的

下次怎麼看

  1. 板子不講話,先把「沒在講」跟「聽不到」分開。最快的方法是同一個 register 推兩根腳,一根走原本的路,一根走別的路。
  2. 每一個版本都安靜,那是板子的證據,不是設計的。
  3. 工具說 0 個 violation,只代表 timing 過了,邏輯對不對它不管。
  4. core 用組合邏輯讀的陣列,小心被工具推成 block RAM,讀出來會慢一拍。
  5. 一條線曾經通過一次又死掉,那是接觸不良,不是設計的問題,可以不用再查設計了。

一根腳有字一根腳沒有,吵了好幾個禮拜的架,原來是兩個 bug 在輪流擋喔


分享這篇:

Next Post
自己架的 Gitea 七月底被挖礦,Linode 的 CPU 警告比漏洞新聞還早了五天