Mở đầu: Từ hạt cát silicon đến bộ não số
Tại sao máy tính — thiết bị siêu việt có thể mô phỏng vật lý lượng tử, kết xuất đồ họa 3D lộng lẫy và chạy các mô hình AI khổng lồ — về bản chất lại chỉ hiểu hai trạng thái duy nhất: Bật (1) và Tắt (0)? Mọi lập trình viên đều biết điều này, nhưng rất ít người thực sự hình dung được làm cách nào những dòng điện tắt-bật thô sơ đó lại có thể cộng hai số, so sánh chúng, hay đưa ra quyết định rẽ nhánh logic.
Bài học này sẽ đưa bạn khám phá tầng sâu nhất của phần cứng máy tính: từ các phép toán đại số Boolean nền tảng, cách ghép các cổng logic AND/OR/XOR thành mạch cộng bán phần (Half Adder) và toàn phần (Full Adder), cho đến cách xây dựng một ALU 4-bit hoàn chỉnh. Cuối cùng, bạn sẽ tự tay vận hành và quan sát bộ mô phỏng ALU tương tác để hiểu rõ cách CPU tính toán và thiết lập các cờ trạng thái Zero, Sign, và Overflow.
1. Đại số Boolean & Các cổng logic cơ bản
Vào giữa thế kỷ 19, nhà toán học George Boole đã phát minh ra một hệ thống đại số trong đó các biến chỉ
nhận hai giá trị: True (Đúng, đại diện bởi 1) và False (Sai, đại diện bởi 0).
Đây chính là nền tảng lý thuyết cho hệ thống máy tính nhị phân ngày nay. Khi dòng điện đi qua các bóng bán
dẫn (transistor), chúng hoạt động như các công tắc đóng mở cực nhanh để biểu diễn các phép toán logic cơ
bản này.
Có 4 cổng logic cơ bản xây dựng nên toàn bộ hệ thống tính toán:
- Cổng NOT (Đảo ngược): Output luôn ngược với input. Nếu \(A = 1\), \(Y = \neg A = 0\).
- Cổng AND (Và): Output chỉ bằng 1 khi tất cả input bằng 1. Công thức: \(Y = A \land B\).
- Cổng OR (Hoặc): Output bằng 1 khi có ít nhất một input bằng 1. Công thức: \(Y = A \lor B\).
- Cổng XOR (Hoặc loại trừ): Output bằng 1 khi hai input có giá trị khác nhau. Công thức: \(Y = A \oplus B = (A \land \neg B) \lor (\neg A \land B)\).
-
&và|là các toán tử Bitwise AND và Bitwise OR. Chúng duyệt qua từng bit của dữ liệu và thực hiện phép toán logic tương ứng. -
&&và||là các toán tử Logical AND và Logical OR. Chúng coi toàn bộ biến làtrue(khác 0) hoặcfalse(bằng 0) và áp dụng quy tắc short-circuit (dừng sớm nếu đã biết kết quả).
Đoạn mã C dưới đây minh họa việc sử dụng toán tử bitwise để thực hiện phép toán logic ở mức bit và cách sử dụng mặt nạ bit (bit mask) để đọc hoặc đảo trạng thái của một bit cụ thể:
#include <stdio.h>
int main() {
unsigned char a = 5; // Hệ nhị phân: 0000 0101
unsigned char b = 3; // Hệ nhị phân: 0000 0011
// 1. Phép toán bitwise AND
printf("a & b (bitwise AND) = %d\n", a & b); // Kết quả: 1 (0000 0001)
// 2. Phép toán logical AND
printf("a && b (logical AND) = %d\n", a && b); // Kết quả: 1 (True && True)
// 3. Sử dụng mặt nạ bit (Bit Mask) để kiểm tra bit 2 (đếm từ 0)
unsigned char mask = 0x04; // Hệ nhị phân: 0000 0100
if ((a & mask) != 0) {
printf("Bit index 2 cua A dang duoc bat (1)\n");
}
// 4. Phép toán XOR dùng để đảo bit (toggling)
a = a ^ mask; // Đảo bit index 2 của a (thành 0000 0001)
printf("A sau khi toggle bit 2: %d\n", a); // Kết quả: 1
return 0;
}
2. Mạch cộng bán phần & toàn phần (Half / Full Adder)
Khi đã có các cổng logic cơ bản, làm cách nào chúng ta có thể thực hiện phép toán số học như cộng hai số nhị phân? Câu trả lời nằm ở sự kết hợp khéo léo của các cổng logic để tạo ra các mạch cộng phần cứng.
Mạch cộng bán phần (Half Adder)
Mạch cộng bán phần nhận vào 2 bit \(A\) và \(B\) và tạo ra 2 ngõ ra: bit tổng \(Sum\) (\(S\)) và bit nhớ \(Carry-out\) (\(C\)). Nhìn vào bảng chân trị nhị phân, ta thấy: - \(Sum\) chỉ bằng 1 khi một trong hai bit bằng 1 (chính là phép toán XOR): \(S = A \oplus B\). - \(Carry-out\) chỉ bằng 1 khi cả hai bit cùng bằng 1 (chính là phép toán AND): \(C = A \land B\).
Mạch này gọi là "bán phần" vì nó không có ngõ vào cho bit nhớ từ hàng trước chuyển sang.
module half_adder (
input a,
input b,
output sum,
output carry
);
assign sum = a ^ b; // Cổng XOR
assign carry = a & b; // Cổng AND
endmodule
Mạch cộng toàn phần (Full Adder)
Để thực hiện phép cộng các số nhiều bit, mạch cộng bắt buộc phải xử lý được bit nhớ từ hàng trước truyền sang. Đó là lý do mạch cộng toàn phần (Full Adder) ra đời. Nó nhận vào 3 đầu vào: \(A\), \(B\), và bit nhớ ngõ vào \(C_{in}\). Các công thức logic được tính toán như sau: \[S = A \oplus B \oplus C_{in}\] \[C_{out} = (A \land B) \lor (C_{in} \land (A \oplus B))\]
module full_adder (
input a,
input b,
input cin,
output sum,
output cout
);
wire s1, c1, c2;
// Sử dụng hai bộ Half Adder để tạo nên một bộ Full Adder
assign s1 = a ^ b;
assign c1 = a & b;
assign sum = s1 ^ cin;
assign c2 = s1 & cin;
// Gộp hai nguồn sinh bit nhớ Carry-out
assign cout = c1 | c2;
endmodule
So sánh nhanh hai loại mạch cộng vừa xây dựng:
| Tiêu chí | Half Adder | Full Adder |
|---|---|---|
| Số ngõ vào | 2 (A, B) | 3 (A, B, Carry-in) |
| Công thức Sum | \(S = A \oplus B\) | \(S = A \oplus B \oplus C_{in}\) |
| Công thức Carry-out | \(C = A \land B\) | \(C_{out} = (A \land B) \lor (C_{in} \land (A \oplus B))\) |
| Ghép chuỗi nhiều bit | Không xử lý được carry từ hàng trước — không ghép chuỗi được | Ghép nối tiếp N bộ thành bộ cộng N-bit (Ripple Carry Adder) |
3. Thiết kế khối ALU 4-bit và các cờ trạng thái
Đơn Vị Số Học Luận Lý (ALU) là trái tim tính toán của CPU. Một bộ ALU đơn giản có thể được tạo ra bằng cách đặt song song các khối chức năng (Bộ cộng, khối AND, khối OR, khối XOR) và sử dụng bộ chọn kênh (Multiplexer - MUX) để quyết định kết quả của phép toán nào sẽ được xuất ra ngoài dựa trên mã lệnh (Opcode).
Để hỗ trợ CPU đưa ra quyết định rẽ nhánh logic (ví dụ lệnh if (a < b)), bộ ALU sẽ sinh ra
các cờ trạng thái (Flags) dựa trên kết quả tính toán hiện tại:
- Cờ Zero (Z): Bằng 1 nếu toàn bộ các bit kết quả bằng 0. Công thức toán học cho kết quả 4-bit \(R\): \(Z = \neg(R_3 \lor R_2 \lor R_1 \lor R_0)\).
- Cờ Sign (S): Biểu thị dấu của kết quả. Trong hệ bù 2, bit cao nhất (MSB) quyết định dấu. Với số 4-bit: \(S = R_3\). Nếu \(S = 1\), kết quả là số âm.
- Cờ Carry-out (C): Bằng 1 nếu phép cộng số không dấu bị tràn (vượt quá \(15\)), hoặc phép trừ số không dấu cần mượn.
- Cờ Overflow (V): Bằng 1 nếu xảy ra hiện tượng tràn số đối với phép tính có dấu (signed).
- Cờ Carry (C) chỉ có ý nghĩa đối với toán học không dấu (unsigned). Nó báo hiệu kết quả vượt quá phạm vi biểu diễn của \(N\) bit (ví dụ với 4-bit, kết quả lớn hơn 15).
- Cờ Overflow (V) chỉ có ý nghĩa đối với toán học có dấu (signed). Nó báo hiệu phép tính tạo ra kết quả sai dấu do vượt quá giới hạn biểu diễn của số bù 2 (phạm vi từ \(-8\) đến \(+7\) với 4-bit).
Công thức logic phần cứng để xác định cờ Overflow (V) cho kết quả \(R\) của phép cộng \(A + B\) hoặc trừ \(A - B\) trên biến \(N\)-bit (với bit dấu thứ \(N-1\)):
- Đối với phép cộng: \[V = (A_{N-1} \land B_{N-1} \land \neg R_{N-1}) \lor (\neg A_{N-1} \land \neg B_{N-1} \land R_{N-1})\] (Tràn xảy ra khi cộng hai số cùng dấu nhưng kết quả ra ngược dấu).
- Đối với phép trừ: \[V = (A_{N-1} \land \neg B_{N-1} \land \neg R_{N-1}) \lor (\neg A_{N-1} \land B_{N-1} \land R_{N-1})\] (Tràn xảy ra khi trừ hai số khác dấu nhưng kết quả ra ngược dấu với số bị trừ).
Dưới đây là mã C mô phỏng trọn vẹn thuật toán hoạt động bên trong một bộ ALU 4-bit phần cứng:
#include <stdio.h>
#include <stdbool.h>
typedef struct {
unsigned char result; // 4-bit result (0-15)
bool carry; // Carry-out flag
bool overflow; // Overflow flag
bool zero; // Zero flag
bool sign; // Sign flag
} ALU_Output;
ALU_Output alu_4bit(unsigned char a, unsigned char b, unsigned char opcode) {
ALU_Output out = {0};
a &= 0x0F; // Rút gọn đầu vào về đúng 4-bit
b &= 0x0F;
unsigned short temp_res = 0;
switch (opcode) {
case 0: // PHÉP CỘNG (ADD)
temp_res = (unsigned short)a + b;
out.result = temp_res & 0x0F;
out.carry = (temp_res > 0x0F);
// Kiểm tra Overflow có dấu: hai toán hạng cùng dấu, kết quả khác dấu
out.overflow = (((a >> 3) == (b >> 3)) && (((a >> 3) ^ (out.result >> 3)) & 1));
break;
case 1: // PHÉP TRỪ (SUB)
// Trừ A - B bằng cách cộng A với số bù 2 của B: A + (~B + 1)
temp_res = (unsigned short)a + ((~b + 1) & 0x0F);
out.result = temp_res & 0x0F;
out.carry = (a < b); // Carry-out đối với phép trừ đóng vai trò là cờ mượn (borrow)
// Kiểm tra Overflow có dấu cho phép trừ: số bị trừ và số trừ khác dấu, kết quả khác dấu số bị trừ
out.overflow = (((a >> 3) != (b >> 3)) && (((a >> 3) ^ (out.result >> 3)) & 1));
break;
case 2: // PHÉP AND BITWISE
out.result = a & b;
break;
case 3: // PHÉP OR BITWISE
out.result = a | b;
break;
case 4: // PHÉP XOR BITWISE
out.result = a ^ b;
break;
}
out.zero = (out.result == 0);
out.sign = (out.result >> 3) & 1; // Lấy bit cao nhất làm bit dấu
return out;
}
4. Thực hành tương tác: Bộ mô phỏng ALU 4-bit
Dưới đây là bộ mô phỏng trực quan hoạt động của ALU 4-bit. Hãy tùy chỉnh giá trị đầu vào của toán hạng \(A\), \(B\) bằng các nút gạt nhị phân, lựa chọn phép toán số học hoặc luận lý và quan sát kết quả nhị phân, thập phân cùng trạng thái kích hoạt của các cờ. Sơ đồ mạch điện phía dưới sẽ tô sáng đường đi hoạt động tương ứng với phép toán đang chọn.
5. Câu hỏi trắc nghiệm ôn tập
Câu 1
Câu 1: Cho hai số 4-bit có dấu bù 2: \(A = 0101_2\) (thập phân: \(+5\)) và \(B = 0100_2\) (thập phân: \(+4\)). Khi thực hiện phép tính cộng \(A + B\) trong ALU, kết quả nhị phân thu được và trạng thái của các cờ Zero (Z), Sign (S), Overflow (V) là gì?
Câu 2
Câu 2: Điểm khác biệt mấu chốt giữa cờ Carry-out (C) và cờ Overflow (V) trong bộ ALU là gì?
Câu 3
Câu 3: Một bộ cộng song song Ripple Carry Adder 32-bit được ghép từ 32 bộ Full Adder (FA). Biết trễ lan truyền từ ngõ vào đến ngõ ra carry của mỗi bộ FA là \(2\text{ ns}\), và trễ từ ngõ vào đến ngõ ra Sum là \(3\text{ ns}\). Trễ lan truyền cực đại (worst-case delay) của bộ cộng 32-bit này là bao nhiêu?
Tải file code thực hành minh họa bài học
File JavaScript CPUJS — thư viện kiến trúc máy tính mini dùng xuyên suốt cả 12 bài, Bài 1
vừa thêm half/full adder, bộ cộng ripple-carry và ALU 4-bit (ADD/SUB/AND/OR/XOR) cùng 4 cờ
Zero/Sign/Carry/Overflow, kèm self-test đối chiếu đúng mọi con số trong bài (chạy
node cpu-core.js, không cần cài thêm gì):
📖 Tài liệu tham khảo
- Sách giáo khoa cốt lõi: Computer Organization and Design, MIPS Edition (Patterson & Hennessy) — Chương 3: Arithmetic for Computers trình bày chi tiết sơ đồ khối ALU và biểu diễn số bù 2.
- Bài viết trực quan: All About Circuits - Binary Adders — mạch cộng bán phần và mạch cộng toàn phần minh họa sinh động.
- Đại số Boolean: Wikipedia - Boolean Algebra — khái quát toán học nền tảng cho thiết kế kỹ thuật số.