一位軟體大牛(關係很好的哥們兒)問我:做硬體為什麼不能像做軟體那麼快呢?這真是一個好問題,也是一個很難回答的問題(不管是technically還是politically correctly^_^)。但這確實是一個需要好好回答的問題。因此,我決定試著以做晶元為例,跟做軟體的同學介紹一下做硬體大概是個什麼樣子。因為做硬體和軟體都涉及到非常廣泛的知識、技能、和經驗,在下所學有限,文章裡面有很多不準確之處,大家不必太較真,能以一個輕鬆活潑的方式增進軟、硬體同學們的交流即可。

先從數字電路說起吧,因為數字電路的前端設計,也就是邏輯設計,跟軟體設計有點像。你從邏輯設計攻城獅們的工位經過,瞟一眼他們的屏幕,會發現竟然也是滿屏的代碼,竟然也是以分號結束一行,以//加註釋。但是,你要是仔細看,他們寫的是看起來像C但是實際更像彙編的代碼。這樣的代碼叫RTL(Register-Transfer Level)。數字電路的基本單元是寄存器和邏輯門,一個寄存器的輸出,經過若干邏輯門之後到達另一個寄存器的輸入,在下一個時鐘周期開始的時候,這個輸入就被寄存器存起來。RTL描述的就是,每當一個新的時鐘周期開始的時候,各個寄存器上的信號如何變化(從0變成1,還是從1變成0)。所以RTL是一個非常底層的語言。不準確地估計一下,你用C能一行搞定的邏輯,邏輯設計攻城獅要寫十行甚至更多。即使他們的敲字速度都已經達到了刷leetcode的要求,他們一天能寫的邏輯也僅相當於碼工的十分之一甚至更少(還要假定碼工不能用python之類的新式武器)。

RTL天生就是多線程的。晶元上所有的寄存器和邏輯門在物理世界裡是並存的關係。因此,RTL要交代清楚每一個寄存器在每一個時鐘周期的狀態變化。一個寄存器以及與它相關的邏輯可以看成一個線程,n個這樣的線程構成一個電路模塊。然後每個模塊又可以看成一個更大的線程,這些大的線程並行處理著晶元上的各種任務。跟軟體不太一樣的是,這些線程永遠不會return,也就是說,每一個線程都是在跑while(1),沒有break,看不到pthread_join。另一方面,這些線程的顆粒度很細,因為一個寄存器上只有一個bit的信息。從時間維度上看,它們的顆粒度也非常細,需要描述每一個時鐘周期信號如何變化,也就是說,那些while(1)在每個時鐘周期走一次循環。所以邏輯設計有點像領導工作,是在下一盤很大的棋,需要通盤考慮大量棋子在每一步的變化,以及每一顆棋子每一步的變化會如何影響其它棋子在下一步的變化。

晶元上的寄存器和邏輯門不僅並存,而且一旦晶元做好,它們就永遠存在,既不能因malloc/new而生,也不會因free/delete而滅。因此,設計晶元就像做父母,一旦生了寶寶,就要對TA負責到底。做父母最頭疼的問題之一是如何讓孩子在學校表現好,做晶元最頭疼的問題之一是如何讓晶元跑起來之後性能好。以用於計算的晶元為例,好的性能通常要求我們能充分利用每一個計算單元,最好能讓它們在每一個時鐘周期都在做計算。為了讓計算單元在每一個時鐘周期都有事情可做,就需要保證整個流水線--從數據讀取到計算再到數據寫回--暢通無阻,這是邏輯設計的主要任務。因此,邏輯設計攻城獅常常要考慮晶元的方方面面,對流水線的各個環節做非常細緻的分析和設計。一個神經網路的卷積部分,寫成簡單的C程序就是幾個嵌套的for循環。在GPU上面,為了提高性能,就要用CUDA、OpenCL、甚至更底層的語言來細緻地安排各項任務,如何把計算分配到各個計算單元,如何一級一級地緩存數據,如何讓數據讀寫和計算能最大限度地並行起來。加速深度學習的各種XXU也要解決這些問題,但是全都用更底層的、顆粒度更細的RTL來描述。

(未完待續)

作者聲明:

  1. 題圖引用於網路,版權歸原作者所有。
  2. 本文為作者個人興趣之作,僅代表作者個人觀點,與任何公司或機構無關。
  3. 歡迎討論。如需轉載,請務必取得作者同意,並註明出處。

推薦閱讀:

相关文章