ALU

**算術邏輯單元**（英語：Arithmetic logic unit，縮寫：**ALU**）是一種可對二進位整數執行算術運算或位元運算的組合邏輯數位電路。ALU 與浮點數運算單元（FPU）不同，後者僅對浮點數進行操作。ALU 是許多類型的計算電路的基本部件，這些計算電路包括電腦的中央處理單元（CPU）、浮點處理單元（FPU）和圖形處理單元（GPU）。單個 CPU、FPU 或 GPU 可能包含多個 ALU。

ALU 的輸入包括需要運算的資料（也稱為運算數）和表明了運算操作類型的指令碼。ALU 的輸出是其執行運算的結果。在許多的設計中，ALU 還帶有狀態輸入或輸出，可將其之前操作或當前操作的資訊在 ALU 和外部狀態暫存器間傳遞。

## 訊號

一個 ALU 具有各種輸入和輸出網路，它們用於在 ALU 和外部電路之間傳送數位訊號。當 ALU 工作時，外部電路在 ALU 的輸入端輸入訊號，而 ALU 將產生運算結果，並將訊號通過其輸出端輸出至外部電路。

### 資料

一個基本的 ALU 具有三個並列的資料匯流排，包括兩個輸入運算元（A、B）和結果輸出（Y）。每個資料匯流排都是一組傳遞了一個二進位整數的訊號。通常，A、B 和 Y 的匯流排寬度（每個匯流排包含的訊號數）是相同的，並且與外部電路（例如，封裝 CPU 或其他處理器）的原生字長相同。

### 操作碼

_操作碼_通過並列匯流排輸入，它向 ALU 傳遞選擇的操作的資訊，該操作選擇碼是對 ALU 要執行的算術或邏輯運算的列舉。操作碼的長度（其匯流排寬度）決定了 ALU 可以執行的操作的最大數量。例如，一個 4 位元操作碼可最多為 ALU 指定不同的 16 個操作（16=24）。通常，ALU 的操作碼與機器語言的操作碼不同，儘管在某些情況下，它可以直接編碼為機器語言操作碼內的位欄位。

### 狀態

#### 輸出

ALU 的狀態輸出是各類單獨的訊號，補充表示了有關當前 ALU 操作結果的資訊。通用 ALU 通常具有以下狀態訊號：

* _Carry-out_，表示由加法運算產生的進位、由減法運算產生的借位或由二進位移位元運算產生的溢位位。
* _Zero_，表示運算結果的所有位均為邏輯零。
* _Negative_，表示算術運算結果為負。
* _Overflow_，表示算術運算的結果溢位，超出了 Y 的數值範圍。
* _Parity_，表示輸出數 Y 的奇偶校驗結果，說明 Y 中的含有邏輯 1 的數量的奇偶性。

在 ALU 的操作結束後，狀態輸出訊號通常會被儲存在外部暫存器中，以使其可用於之後的 ALU 操作（例如實作多倍精度運算，或用於控制條件分支）。儲存了狀態輸出的集合的位暫存器通常被視為一整個多位暫存器，稱為「狀態暫存器」或「條件代碼暫存器」。

#### 輸入

狀態輸入使 ALU 在執行操作時可接收其他資訊。通常，這是單個「低位進位」，傳遞了來自上一級 ALU 操作的進位。

## 電路操作

ALU 是組合邏輯電路。這意味著當輸入變化時，其輸出也將隨之變化。在正常的情況下輸入到 ALU 的訊號是穩定的，並且當經過足夠的時間（稱為「傳播延遲」）訊號通過 ALU 中的電路傳播後，ALU 操作的結果就會出現在 ALU 上輸出。連接到 ALU 的外部電路負責確保 ALU 的輸入訊號在整個操作過程中是穩定的，並在對 ALU 的輸出進行採樣之前為訊號傳播預留足夠的時間。

通常，外部電路通過將訊號施加到其輸入端來控制 ALU。外部電路往往採用順序邏輯來控制 ALU 的運作，該操作由足夠低頻率的時鐘訊號調整，以確保 ALU 的輸出在最壞情況下也有足夠的時間達到穩定。

例如，在用 ALU 執行加法操作時，CPU 會將運算元（通常是從暫存器）路由到 ALU 的輸入，而控制單元會同時將一個值傳送至 ALU 的操作碼輸入，使其執行加法操作。同時，CPU 還將把 ALU 的計算結果輸出路由到用於接收該結果的目標暫存器。在 CPU 等待下一個時鐘期間，ALU 的輸入訊號需一直保持穩定，已使其通過 ALU 併到達目標暫存器。當下一個時鐘到達時，目標暫存器將儲存 ALU 結果，並且由於 ALU 操作已完成，因此此時可以為下一次 ALU 操作設定 ALU 輸入。

## 功能

ALU 通常支援許多基本算術和按位元邏輯函式。基本的通用 ALU 通常支援以下操作：

### 算術運算

* _加法_：將運算元 A、B 相加，並在 Y 處得到二者的和
* _帶進位加法_：將運算元 A、B、進位相加，並在 Y 處得到三者的和
* _減法_：將運算元 A、B 相減，並在 Y 處得到二者的差。對於此功能，結轉實際上是「借入」指示器。此操作也可以用來比較 A 和 B 的大小；在這種情況下，處理器可能會忽略 Y 輸出，該處理器僅對操作產生的狀態位（尤其是零和負）感興趣。
* _帶借位減法_：借位（進位）從 A 中減去 B（反之亦然），差值出現在 Y 處並結轉（借入）。
* _二補數（取相反數）_ ：得到 A 或 B 的相反數（將 0 與 A 相減或將 0 與 B 相減），並在 Y 處得到計算結果
* _加 1_：將 A（或 B）增加 1，並在 Y 處得到計算結果
* _減 1_：將 A（或 B）減小 1，並在 Y 處得到計算結果
* _直通（Pass through）_ ：保持 A（或 B）的所有位不變，並在 Y 處得到原輸入數；該操作常用於對運算元進行奇偶校驗，判斷是否為 0，判斷是否為負數，或者為了將運算元直接載入到暫存器中。

### 按位元邏輯運算

* _AND_：將 A 和 B 按位元進行「與」運算，並在 Y 處得到計算結果
* _OR_：將 A 和 B 按位元進行「或」運算，並在 Y 處得到計算結果
* _XOR_：將 A 和 B 按位元進行「互斥或」運算，並在 Y 處得到計算結果
* _二補數_：將 A（或 B）的每一位都反轉，並在 Y 處得到計算結果

### 移位元運算

在不同的操作碼下，ALU 的移位元運算可將運算元 A（或 B）向左或向右移位，移位的結果將出現在 Y 處。簡單的 ALU 通常只能將運算元移位一位，而更複雜的 ALU 使用桶形移位器，在一次操作中可移位任意的位數。在一位的移位元運算中，從運算元移出的位會被轉移到_進位_輸出中。而被移位到運算元中的數位取決於移位的類型，比如：

* _算術移位_：運算元被視為二補數整數，其高有效位是符號位，在移位時會被保留。
* _邏輯移位_：移位時用邏輯 0 補充運算元，這適合於無符號整數。
* _迴圈移位_：此時運算元被視為一個迴圈緩衝區，因此在移位時，其最低和最高位就像是相鄰的。
* _迴圈移位（帶進位_）：進位輸入（C）和運算元被視為整個運算元的迴圈移位。

## 應用

### 多倍精度運算

在整數算術計算中，多倍精度運算（Multiple-precision arithmetic）是對字長大於 ALU 字長的整數進行運算的操作。為此，該演算法將每個運算元視為多個 ALU 能夠處理的運算元片段的集合（並從最高位（MSB）到最低位（LSB）排列，或相反）。例如，在使用 8 位 ALU 的情況下，將 24 位整數 `0x123456` 視為如下三個 8 位元片段的集合：`0x12`（高位）、`0x34`、`0x56`（低位）。由於片段的大小與該 ALU 字長完全匹配，因此可以直接對這幾個數進行操作。

該演算法使用 ALU 直接對特定的運算元片段進行運算，從而生成多精度結果的相應片段。在運算元的每個片段被計算後，結果都將被儲存到對應的位置。對所有運算元片段重複此過程，將這些結果進行拼合，即可得到完整的運算結果。

對於算術運算（如加法和減法），該演算法首先對運算元的最低位片段進行運算，從而生成最低位對應的結果和進位。隨後將該結果儲存至某個指定的位置，而處理器通常會儲存 ALU 狀態輸出中的進位。然後，該演算法繼續計算每個運算元片段集合的下一個片段，並對這些片段呼叫 ALU 操作，並將先前 ALU 操作的進位一起輸入，從而產生另一個更高位的片段和進位。重複此過程，直到處理完所有運算元片段為止，從而在儲存位置中得到完整的運算結果。

對於移位元運算，運算元片段處理的順序取決於移位方向。在左移操作中，首先對最低位片段進行處理，因為必須用低位片段左移的溢位位來得到該運算的最低位。類似地，在右移操作中應首先對運算元的最高位進行處理。

對於按位元邏輯運算（如邏輯與、邏輯或操作）中，可以按任何任意順序處理運算元片段，因為計算結果的每個片段僅取決於相應的運算元片段（來自先前 ALU 操作的進位將被忽略）。

### 複雜運算

儘管可以將 ALU 設計於執行複雜的功能，但在許多情況下，由此導致的更高的電路複雜性，成本，功耗和更大的尺寸使其不切實際。因此，ALU 通常限於可以以非常高的速度（傳播延遲更短）執行的簡單功能，而為了執行複雜的運算，外部處理器電路則需要將一系列簡單的 ALU 操作進行編排。

例如，在 ALU 的複雜程度不同的情況下，可以通過多種方式來計算數字的平方根：

* _單周期計算_：如果一個 ALU 設計得足夠複雜，那它可能可在一次操作中得到計算結果。
* _計算管線_：可以讓一組結構不那麼複雜的 ALU 逐步地完成平方根計算，計算的中間結果儲存在各級 ALU 上，就像工廠的生產線一樣。該電路可以在完成之前的運算元前接受新的運算元，並得到與複雜的 ALU 一樣快的結果，儘管結果過程會因各級 ALU 的延遲而存在延遲。有關更多資訊，請參見指令管線。
* _迭代計算_：可讓 ALU 在控制單元的控制下經由多步計算平方根，這使其結構更加簡單。

上面的實作從最快和最昂貴的到最慢和最便宜的過渡，在這幾種情況下都可完成平方根的計算，但是結構更加簡單的 ALU 的計算時間往往更長，因為需要執行多步操作。

## 實作

ALU 通常是一個獨立的積體電路（如 74181 晶片），也可能是一個更加複雜的 IC 的一部分。在後一種情況下，ALU 一般由 VHDL、Verilog 或其他硬體描述語言編寫，並通過綜合該代碼來實例化其電路。例如，以下 VHDL 代碼描述了一個功能非常簡單的 8 位元 ALU：

## 歷史

馮・紐曼在 1945 年的一份關於新型電腦 EDVAC 的基礎的報告中提出了 ALU 的概念。

在整個資訊時代的初期，電子電路的成本，尺寸和功耗都相對較高。因此，儘管所有序列計算機和許多早期電腦（例如 PDP-8）都具有一個簡單的 ALU，但它一次只能處理一個資料位，而程式設計師們使用更長的字長。最早的具有多個分立單位 ALU 電路的電腦是 1948 年的 Whirlwind I，它使用了 16 個這樣的「數學單元」，以使其能夠對 16 位元資料進行操作。

1967 年，仙童半導體推出了第一款用積體電路實作的 ALU，名為 Fairchild 3800，由一個帶累加器的八位 ALU 構成。隨後，其他積體電路 ALU 也相繼出現，包括 4 位元 ALU，如 Am2901 和 74181。這些裝置通常具有「位片」功能，這意味著它們可以輸出「超前進位」的訊號，從而有助於使用多個 ALU 晶片相互連接以構成字長更寬的 ALU。這些裝置迅速流行起來，並廣泛用於位片微型電腦。

1970 年代初，微處理器開始出現。雖然電晶體變得越來越小，但裸晶的尺寸並不足以放下全字寬的 ALU。因此，一些早期的微處理器採用了位數更少的 ALU，每條指令因而需要多個周期來執行。比如當時流行的 Zilog Z80，它使用 4 位元 ALU 執行 8 位元加法運算。後來，正如摩爾定律預測的那樣，電晶體的尺寸進一步縮小，在微處理器上構建更寬的 ALU 從而也變得可行。

現代的積體電路中電晶體的尺寸比早期微處理器的小幾個數量級，從而可以配備高度複雜的 ALU。如今，許多現代 ALU 的字寬更寬，體系結構的提升（如使用了桶形移位器和二進位乘法器）能使它們能夠在單個時鐘周期內執行需要早期 ALU 進行多次運算才能完成的操作。

## 參見

* 加法器
* 乘法器
* 位址生成單元
* 載入儲存單元
* 執行單元

## 參考資料

## 延伸閱讀

* Hwang, Enoch. Digital Logic and Microprocessor Design with VHDL. Thomson. 2006 [2020-08-30]. ISBN0-534-46593-5. （原始內容存檔於 2008-10-12）.
* Stallings, William. Computer Organization & Architecture: Designing for Performance 7th. Pearson Prentice Hall. 2006 [2020-08-30]. ISBN0-13-185644-8. （原始內容存檔於 2007-10-24）.