JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

組合邏輯模組

有些邏輯只是回答一個問題——沒有記憶,不等時脈,也沒有歷史。給它輸入,它就立刻穩定到一個輸出,就像一整面牆的電燈開關,決定哪盞燈亮起。本指南帶你走過組合邏輯的主力——多工器、解碼器、加法器和 ALU——並展示同一個想法穿上三套外衣:一張真值表、一行布林代數,以及一句 Verilog 的 `assign`。讀到最後,你還會明白為什麼連「瞬時」邏輯也不是免費的——每一道閘都在時間上收取一筆小小的過路費。

輸出只取決於輸入

這就是組合邏輯的定義性規則:輸出只取決於你此刻擁有的輸入——絕不取決於片刻之前發生了什麼。沒有記憶,沒有儲存的狀態,沒有任何要記住的東西。改變一個輸入,輸出就重新穩定;讓輸入保持不動,輸出就被釘住。把它想成一台沒有投幣口的自動販賣機:同樣的按鈕永遠給出同樣的零食,每一次都如此。

從形式上說,組合邏輯計算的是輸入的一個純函數:y = f(a, b, c, …)。同樣的輸入永遠對應到同樣的輸出,沒有任何從過去偷偷夾帶進來的隱藏變數。這就是為什麼你可以用一張真值表來描述整個模組——列出每一種輸入組合,在旁邊寫下輸出,你就*完整地*刻畫了這個模組。

多工器:一個資料選擇器

多工器——簡稱「mux」——是你會遇到的最有用的組合模組。它是一個資料選擇器:許多輸入進來,一個小小的選擇訊號指向其中之一,那一路輸入就被複製到唯一的輸出上。想像一個鐵路道岔——許多軌道匯聚,一根扳手決定哪列火車駛上月台。多工器不改變資料;它只決定*哪一路*資料能通過。

一個二選一多工器接收兩路資料輸入(`a`、`b`)、一根選擇線 `s`,產生 `y`。當 `s` 為 0 時你得到 `a`;當 `s` 為 1 時你得到 `b`。用布林代數來寫,正好是 y = (~s & a) | (s & b)——讀作「若非 s,傳 a;否則傳 b」。在 Verilog 裡,同樣的想法濃縮成優雅的一行。

module mux2(input a, b, s, output y);
  assign y = s ? b : a;   // s picks the winner: 0 -> a, 1 -> b
endmodule
一個二選一多工器。`?:` 三元運算子讀作「若 s 則 b 否則 a」——是一個選擇器,而不是一次計算。

解碼器、加法器與 ALU

解碼器與多工器的「挑選」本能正好相反:它接收一個小小的二進位編碼,然後恰好點亮一條輸出(在許多條之中)。一個二-四解碼器把一個 2 位元的數(`00`、`01`、`10`、`11`)變成四條線,其中只有匹配的那一條為高。把它想成公寓信箱——一個簡短的地址選中唯一會打開的那個信箱。解碼器正是記憶體位址挑出單獨一列的方式,也是指令運算碼挑出單獨一種運算的方式。

加法器僅憑一堆閘就完成真正的算術。它的原子是全加器:接收兩個位元再加一個進位輸入,產生一個和位元和一個進位輸出。把 N 個全加器串起來——每一個的進位輸出餵給下一個的進位輸入——你就得到一個漣波進位加法器,它把兩個 N 位元的數相加。沒有迴圈,沒有時脈;只是一大片邏輯閘穩定到正確的答案。

module full_adder(input a, b, cin, output sum, cout);
  assign sum  = a ^ b ^ cin;            // XOR of all three bits
  assign cout = (a & b) | (cin & (a ^ b)); // carry if two or more are 1
endmodule
一個 1 位元全加器。把這些堆疊起來,進位就一路漣波而過,恰如手算加法時一行接一行地進位。

把這些模組堆疊起來,你就得到 ALU(算術邏輯單元)——每一顆處理器核心裡的那台計算機。一個 ALU 大體上就是一個加法器、一些邏輯閘,外加一個根據運算碼選擇該輸出哪個結果的多工器(加法?及?比較?)。它是純粹的組合邏輯:遞給它兩個數和一個運算,答案就掉出來——不需要時脈。

真值表 → 布林 → assign

這就是實踐者的循環,把一份*規格說明*變成*硬體*的那一招。先從一張真值表起步:列舉每一種輸入組合,以及你想要的輸出。這張表就是基準事實——無歧義且完整。從這裡你提煉出一個[[boolean-algebra|布林]]表達式,再把那個表達式落進一句 Verilog 的 `assign`。三套外衣,同一具身體。

來看一個完整的例子:一個 1 位元的輸出,當三個輸入(`a`、`b`、`c`)中至少有兩個為 1 時它為高——也就是「多數表決」函數。這張真值表有八列;輸出恰好在其中四列為 1(`011`、`101`、`110`、`111`)。給每一個取勝的列各寫一項,約去冗餘之後就得到 y = a&b | a&c | b&c——三個小小的及項或在一起。(這一步整理就是[[boolean-algebra|布林]]化簡:項越少意味著邏輯閘越少,模組更快也更小。)

module majority(input a, b, c, output y);
  assign y = (a & b) | (a & c) | (b & c); // high when 2+ inputs are 1
endmodule
一行就寫完的多數函數。真值表證明了它,布林代數縮小了它,`assign` 把它建造出來。

傳播延遲:邏輯是要花時間的

現在輪到那個通往時序的轉折點了。我們一直說組合邏輯「瞬時」穩定,但那是一句客氣的謊言。每一道邏輯閘在輸入改變之後,都需要一段微小但真實的時間才能讓輸出翻轉——這就是它的傳播延遲(常寫作 *t*pd)。在閘內部,它正推動著微小的電晶體,給雜散電容充電;電荷的移動需要時間,所以輸出比輸入滯後幾皮秒到幾奈秒。

延遲會沿著一條路徑累加。一個訊號若從輸入到輸出要穿過比方說八道閘,就得一道接一道地依次繳清每道閘的過路費。一個模組中這樣最慢的那條路線,就是它的[[critical-path|關鍵路徑]]——從輸入到輸出最長的那段旅程——而正是*它*決定了整個模組能跑多快。你那個多數閘很快;而一個 64 位元的漣波進位加法器,它的進位必須爬過全部 64 級,慢得出了名。

於是這個心智模型升級了:組合邏輯並不是一次性發生的魔法查表——它是一片由閘構成的地貌,訊號在其中順坡而下、需要時間才能抵達。用真值表和布林代數把函數做對;然後留意關鍵路徑,以此尊重時脈。後半段——讓時序收斂——正是下一篇指南的全部內容。