CPU tính 5 + 3 như thế nào?
Bạn bấm 5 + 3. Bên trong CPU, phép cộng đó diễn ra thế nào?
Ở bài trước, ta đã có các cổng AND, OR, XOR và NOT. Mỗi cổng chỉ làm một việc rất nhỏ: nhận vài bit rồi cho ra một bit khác.
Nhưng CPU còn phải làm những việc như:
5 + 3 = 8
Làm sao từ vài cổng logic đơn giản, ta ghép thành một mạch biết cộng?
CPU cũng cộng có nhớ
Trước hết, nhớ lại cách ta cộng trên giấy:
27
+ 15
----
42
Ta đi từ phải sang trái:
7 + 5 = 12: viết2, nhớ12 + 1 + nhớ 1 = 4
CPU làm gần như vậy.
Chỉ khác là thay vì các chữ số từ 0 đến 9, CPU làm việc với 0 và 1.
Số 5 và 3 trong hệ nhị phân là:
5 = 0101
3 = 0011
Đặt chúng lên nhau:
0101
+ 0011
------
1000
Hình dưới cho thấy phép cộng diễn ra từng cột. Các bit nhớ được tô màu cam. Đọc từ phải sang trái.
- Cột phải nhất: 1 + 1 = 10. Viết 0, nhớ 1.
- Cột hai: 0 + 1 + nhớ 1 = 10. Viết 0, nhớ 1.
- Cột ba: 1 + 0 + nhớ 1 = 10. Viết 0, nhớ 1.
- Cột trái nhất: 0 + 0 + nhớ 1 = 1.
| Cột | Phép tính | Viết xuống | Nhớ |
|---|---|---|---|
| 1 | 1 + 1 |
0 |
1 |
| 2 | 0 + 1 + nhớ 1 |
0 |
1 |
| 3 | 1 + 0 + nhớ 1 |
0 |
1 |
| 4 | 0 + 0 + nhớ 1 |
1 |
0 |
Kết quả là 1000, tức là 8.
Điểm quan trọng nằm ở bit nhớ.
Khi cộng trên giấy, ta có thể tự nhớ rằng cột trước đang dư 1. CPU thì không có “trong đầu” để nhớ như vậy.
Bit nhớ phải trở thành một tín hiệu thật, đi từ cột này sang cột tiếp theo.
Vì vậy, mỗi cột của phép cộng phải tạo ra hai thứ:
- chữ số viết xuống
- bit nhớ chuyển sang cột bên trái
Ta bắt đầu với trường hợp đơn giản nhất.
Trước hết, thử cộng một cột
Tạm bỏ bit nhớ từ cột trước sang một bên.
Trường hợp đáng chú ý nhất là:
1 + 1 = 10₂
Ta cần tạo ra hai bit: 0 để viết xuống và 1 để nhớ sang cột bên trái.
Thử bốn trường hợp trong mô phỏng dưới đây và xem XOR, AND tạo ra hai bit đó như thế nào.
CPU tạo hai bit này như thế nào?
BIT TỔNG · sum
XOR = 1 khi hai bit khác nhau
1 XOR 1 → 1
BIT NHỚ · carry
AND = 1 khi cả hai bit đều là 1
1 AND 1 → 1
Mạch gồm XOR và AND này gọi là mạch cộng nửa (half adder).
Nó cộng được hai bit, nhưng vẫn còn thiếu một thứ: bit nhớ từ cột bên phải truyền sang.
Và ngay ở cột tiếp theo của 5 + 3, ta đã gặp vấn đề đó.
Cột tiếp theo phải cộng ba bit
Ở cột ngoài cùng bên phải:
1 + 1 = 10₂
Ta viết 0, nhớ 1.
Sang cột tiếp theo, CPU phải tính:
0 + 1 + nhớ 1
Lần này có tới ba bit cần cộng:
- bit của số thứ nhất
- bit của số thứ hai
- bit nhớ từ cột bên phải
Mạch cộng nửa chỉ xử lý được hai bit, nên chưa đủ.
Ta cần mạch cộng đầy đủ (full adder).
Một cách xây nó là ghép hai mạch cộng nửa lại:
A ──┐
├─ mạch cộng nửa ──┐
B ──┘ ├─ mạch cộng nửa ── bit tổng
bit nhớ vào ───────────┘
các bit nhớ ── cổng logic ── bit nhớ ra
Mạch đầu tiên cộng A với B.
Mạch thứ hai cộng kết quả đó với bit nhớ đi vào.
Cuối cùng, các cổng logic xác định có cần gửi tiếp một bit nhớ sang cột bên trái hay không.
Một mạch cộng đầy đủ vì thế có:
- 3 đầu vào: A, B và bit nhớ vào
- 2 đầu ra: bit tổng và bit nhớ ra
Một mạch cộng đầy đủ xử lý một cột của phép cộng nhị phân.
Nhưng 0101 + 0011 có bốn cột.
Vậy ta chỉ cần ghép nhiều mạch như vậy lại.
Ghép nhiều cột lại với nhau
Muốn cộng hai số 4 bit, ta đặt bốn mạch cộng cạnh nhau.
Bit nhớ tạo ra ở một cột được đưa sang cột bên trái:
cột 4 ←─ nhớ ── cột 3 ←─ nhớ ── cột 2 ←─ nhớ ── cột 1
Thử mô phỏng dưới đây.
A đang là 0101 (5), B là 0011 (3). Bấm “Cộng” và theo dõi bit nhớ đi từ phải sang trái.
Tổng cần 5 bit: số nhớ cuối tràn ra ngoài 4 bit.
Với 5 + 3, các bit nhớ lần lượt truyền qua từng cột cho tới khi kết quả ổn định:
0101
+ 0011
------
1000
Kiểu bộ cộng này gọi là bộ cộng lan truyền số nhớ (ripple-carry adder), vì bit nhớ phải truyền từ cột này sang cột kế tiếp.
Trường hợp 15 + 1 cho thấy điều đó rõ hơn:
1111
+ 0001
------
10000
Bit nhớ phải chạy xuyên qua cả bốn cột.
Ta cũng thấy thêm một điều: cộng hai số 4 bit đôi khi cần tới 5 bit để chứa kết quả.
Đến đây ta đã có một mạch biết cộng.
Nhưng trong mô phỏng, 0101 và 0011 chỉ nằm trên các nút bấm.
Trong CPU thật, hai số này nằm ở đâu?
CPU giữ 5 và 3 ở đâu?
Trong lúc mạch đang tính, hai số đầu vào phải được giữ ổn định.
Nếu một trong hai thay đổi giữa chừng, kết quả cũng sẽ thay đổi.
CPU có những vùng nhớ rất nhỏ và rất nhanh để giữ các giá trị đang được sử dụng. Chúng gọi là thanh ghi (register).
Có thể hình dung một thanh ghi 4 bit như bốn ô, mỗi ô giữ một bit:
Thanh ghi A = 0 1 0 1
Thanh ghi B = 0 0 1 1
Thanh ghi A đang giữ số 5.
Thanh ghi B đang giữ số 3.
Các bit từ hai thanh ghi được đưa vào bộ cộng:
Thanh ghi A ──┐
├── bộ cộng ──► 1000
Thanh ghi B ──┘
Sau khi phép cộng hoàn tất, kết quả 1000 cũng có thể được ghi vào một thanh ghi để CPU tiếp tục sử dụng.
Bây giờ ta đã có số để tính và có mạch để cộng.
Nhưng chương trình đâu phải lúc nào cũng cần cộng.
Nó còn cần trừ, AND, OR, XOR và nhiều phép khác.
Mạch cộng chỉ là một phần của khối số học và logic (ALU)
Giả sử hai thanh ghi vẫn đang chứa:
A = 0101 → 5
B = 0011 → 3
CPU có thể cần:
A + B
A - B
A AND B
A OR B
A XOR B
Các mạch thực hiện những phép này được đặt chung trong khối số học và logic (Arithmetic Logic Unit, ALU).
Bộ cộng mà ta vừa xây chỉ là một phần bên trong khối đó.
Thử mô phỏng dưới đây. Giữ nguyên hai số 5 và 3, rồi đổi phép tính.
Kết quả: 1000 = 8
Hai số đầu vào vẫn là 0101 và 0011.
Thứ thay đổi là CPU muốn làm gì với chúng.
Khối số học và logic nhận thêm một số bit điều khiển để chọn phép tính.
Chẳng hạn, một thiết kế đơn giản có thể quy ước:
000 → cộng
001 → trừ
010 → AND
011 → OR
100 → XOR
Nếu bit điều khiển là 000, kết quả của bộ cộng được chọn.
Nếu đổi thành 010, kết quả của mạch AND được chọn.
Hai số đầu vào không đổi. Chỉ cần đổi tín hiệu điều khiển, kết quả ở đầu ra cũng đổi theo.
Từ transistor đến phép tính 5 + 3
Ta bắt đầu từ một transistor chỉ có khả năng bật hoặc tắt dòng điện.
Từ đó, từng lớp được ghép lên:
transistor
↓
cổng logic
↓
mạch cộng nửa
↓
mạch cộng đầy đủ
↓
bộ cộng nhiều bit
↓
khối số học và logic
↓
5 + 3 = 8
Không transistor nào biết số 5 là gì.
Cổng XOR cũng không biết mình đang tham gia một phép cộng.
Mỗi phần chỉ thực hiện một quy tắc rất nhỏ với các bit 0 và 1.
Nhưng khi ghép đủ nhiều quy tắc nhỏ lại với nhau, ta có một mạch có thể tính:
5 + 3 = 8
Vẫn còn một câu hỏi.
Khối số học và logic có thể cộng, trừ, AND, OR và XOR.
Nhưng tự nó không biết lúc này chương trình muốn phép nào.
Ai bảo nó lần này phải cộng, chứ không phải trừ?