- Published on
Big Endian & Little Endian
- Authors
- Name
- ToanNV
Big Endian & Little Endian
Có những lúc gửi cùng một con số, nhưng bên nhận lại hiểu thành giá trị khác. Nguyên nhân thường nằm ở một chi tiết rất nhỏ mà cực kỳ quan trọng: byte ordering hay còn gọi là Endianness — thứ tự lưu byte trong bộ nhớ.
Register là gì?
Trước khi nói về Big Endian hay Little Endian, cần hiểu rõ register.
Tùy ngữ cảnh, từ "register" có thể mang nhiều nghĩa:
- Trong CPU: register là vùng nhớ rất nhỏ, rất nhanh bên trong bộ xử lý.
- Trong giao tiếp dữ liệu hoặc tài liệu thiết bị: register thường là một ô dữ liệu có kích thước cố định (thường gặp là 16-bit).
Trong phạm vi bài viết này, "register" được hiểu theo cách dùng phổ biến trong các giao thức công nghiệp như Modbus: một ô dữ liệu có kích thước 16-bit.
Cần lưu ý rằng trong kiến trúc CPU, register có thể có nhiều kích thước khác nhau như 8-bit, 16-bit, 32-bit, 64-bit hoặc lớn hơn. Tuy nhiên để giải thích Endianness một cách trực quan, bài viết sẽ sử dụng khái niệm register 16-bit.
Vì 1 byte = 8 bit, nên:
- 1 register 16-bit = 2 byte
- 1 giá trị 32-bit = 2 register
Ví dụ một register có giá trị hex 0x1234:
0x1234 = 00010010 00110100 (nhị phân)
[ high byte ][ low byte ]
- High byte (byte cao):
0x12 - Low byte (byte thấp):
0x34
Thành phần thường gặp khi đọc register
Khi tài liệu kỹ thuật ghi một địa chỉ register, chúng ta thường cần quan tâm:
- Address: địa chỉ register (ví dụ 40001, 40002...)
- Length: số register cần đọc (1, 2, 4...)
- Data type: kiểu dữ liệu (uint16, int16, uint32, int32, float32...)
- Byte/Word order: thứ tự byte hoặc thứ tự word khi ghép nhiều register
Vậy tại sao lại có high byte và low byte?
Hiểu đơn giản, khi ta có một số lớn hơn 255 (1 byte), ta cần nhiều byte để lưu trữ nó. Ví dụ, số 0x1234 cần 2 byte. Byte đầu tiên (high byte) chứa phần giá trị lớn hơn, còn byte thứ hai (low byte) chứa phần giá trị nhỏ hơn.
Vì sao high byte và low byte quan trọng?
Với dữ liệu 16-bit, bạn chỉ cần biết high byte và low byte. Nhưng với dữ liệu 32-bit (ghép từ 2 register), ngoài thứ tự byte trong từng register, bạn còn phải để ý thứ tự 2 word 16-bit.
Đó chính là lý do sinh ra các kiểu như ABCD, DCBA, BADC, CDAB mà phần dưới sẽ giải thích chi tiết.
Bắt đầu từ điều cơ bản nhất
Máy tính lưu dữ liệu theo đơn vị nhỏ nhất là byte (8 bit). Khi bạn có một con số lớn hơn — ví dụ số nguyên 32-bit — cần dùng 4 byte liên tiếp trong bộ nhớ.
Câu hỏi đặt ra: byte nào được lưu trước? Byte lớn hay byte nhỏ? Đây chính là vấn đề Endianness (thứ tự byte).
📖 Hãy nghĩ đến cách viết số: Người Việt (và hầu hết thế giới) viết số 1234 từ trái sang phải, chữ số hàng nghìn đứng trước. Nếu ai đó viết ngược lại — 4321 — bạn sẽ đọc ra con số hoàn toàn khác. Endianness trong máy tính cũng là vấn đề tương tự.
Ký hiệu quy ước
Để dễ giải thích, người ta dùng các chữ cái A, B, C, D để đại diện cho 4 byte của một số 32-bit:
Số hex: 0x12345678
A = 0x12 (byte cao nhất — Most Significant Byte)
B = 0x34
C = 0x56
D = 0x78 (byte thấp nhất — Least Significant Byte)
Thứ tự lưu trong bộ nhớ sẽ xác định loại endianness.
Các kiểu thứ tự byte
1. Big Endian — ABCD
Byte lớn nhất lưu trước (địa chỉ thấp nhất).
Địa chỉ: 0x00 0x01 0x02 0x03
Dữ liệu: 12 34 56 78
Đặc điểm: Lưu đúng thứ tự như ta đọc số — byte quan trọng nhất đứng đầu.
Dùng bởi: Giao thức mạng TCP/IP, Motorola 68k, PowerPC, SPARC.
2. Little Endian — DCBA
Byte nhỏ nhất lưu trước (địa chỉ thấp nhất).
Địa chỉ: 0x00 0x01 0x02 0x03
Dữ liệu: 78 56 34 12
Đặc điểm: Ngược với cách con người thường viết số, nhưng giúp một số kiến trúc CPU thuận tiện hơn trong việc mở rộng độ rộng dữ liệu và xử lý các phép toán nhiều byte liên tiếp.
Dùng bởi: Intel x86/x64, AMD và hầu hết các hệ thống ARM hiện đại (Android, iPhone, Raspberry Pi, Linux ARM). Vì vậy đây là kiểu Endianness phổ biến nhất trên các thiết bị ngày nay.
3. Mid Big Endian (Byte-swapped Words) — BADC
Đây là biến thể trong đó thứ tự các word 16-bit vẫn được giữ nguyên, nhưng byte bên trong mỗi word bị đảo ngược.
AB CD
↓
BA DC
Ví dụ với giá trị:
0x12345678
ABCD
↓
BADC
34 12 78 56
Từng dùng bởi: Honeywell 316.
Gặp trong thực tế: Một số thiết bị công nghiệp hoặc giao thức cũ có thể sử dụng kiểu sắp xếp này.
4. Mid Little Endian (Word-swapped) — CDAB
Đây là kiểu lưu giữ nguyên thứ tự byte bên trong mỗi word 16-bit, nhưng hoán đổi vị trí của hai word.
AB CD
↓
CD AB
Ví dụ:
0x12345678
ABCD
↓
CDAB
56 78 12 34
Từng dùng bởi: PDP-11 (DEC).
Gặp trong thực tế: Đây là một trong những định dạng phổ biến nhất khi lưu giá trị 32-bit trong các thiết bị Modbus.
Bảng tổng hợp 4 kiểu
Với số 0x12345678, lưu vào 4 ô nhớ từ địa chỉ 0x00:
| Tên | Ký hiệu | 0x00 | 0x01 | 0x02 | 0x03 | Ghi chú |
|---|---|---|---|---|---|---|
| Big Endian | ABCD | 12 | 34 | 56 | 78 | Network byte order (TCP/IP) |
| Little Endian | DCBA | 78 | 56 | 34 | 12 | PC, laptop, ARM |
| Mid Big Endian | BADC | 34 | 12 | 78 | 56 | Honeywell 316, một số hệ đặc thù |
| Mid Little Endian | CDAB | 56 | 78 | 12 | 34 | PDP-11, một số định dạng word-swapped |
Khi nào byte ordering gây ra lỗi thực tế?
Vấn đề chỉ xuất hiện khi dữ liệu đi qua ranh giới hệ thống:
- Máy A dùng Little Endian, máy B dùng Big Endian
- File nhị phân được tạo trên kiến trúc này nhưng đọc trên kiến trúc khác
- Giao tiếp mạng yêu cầu network byte order (Big Endian)
- Giao thức chỉ chuẩn hóa kích thước dữ liệu nhưng không ép chặt byte/word order cho giá trị 32-bit
Ví dụ cụ thể: Modbus
Modbus quy định mỗi register là 16-bit (2 byte). Khi ghép 2 register thành số 32-bit (float hoặc integer), cách sắp xếp byte/word có thể khác nhau tùy thiết bị.
Cách debug khi đọc sai giá trị
Nếu giá trị đọc ra là 0, một số âm bất thường hoặc các giá trị rất nhỏ như 1.73e-38, nguyên nhân thường là sai byte/word order.
ABCD và CDAB là hai định dạng phổ biến nhất trong các thiết bị Modbus.
Khi debug, hãy thử lần lượt cả bốn cách sắp xếp:
1. ABCD (Big Endian)
2. CDAB (Mid Little Endian) ← phổ biến nhất trong Modbus
3. BADC (Mid Big Endian)
4. DCBA (Little Endian)
Ví dụ code Python — đọc float 32-bit từ Modbus
import struct
# Giả sử đọc được 2 registers: reg1=0x4248, reg2=0x51EC
reg1 = 0x4248
reg2 = 0x51EC
raw_bytes = struct.pack('>HH', reg1, reg2)
# ABCD - Big Endian
val_abcd = struct.unpack('>f', raw_bytes)[0]
# DCBA - Little Endian
val_dcba = struct.unpack('<f', raw_bytes)[0]
# CDAB - Mid Little Endian (hoán đổi word)
raw_cdab = struct.pack('>HH', reg2, reg1)
val_cdab = struct.unpack('>f', raw_cdab)[0]
# BADC - Mid Big Endian (hoán đổi byte trong mỗi word)
raw_badc = struct.pack('>HH',
((reg1 & 0xFF) << 8) | (reg1 >> 8),
((reg2 & 0xFF) << 8) | (reg2 >> 8)
)
val_badc = struct.unpack('>f', raw_badc)[0]
print(f"ABCD: {val_abcd:.4f}")
print(f"DCBA: {val_dcba:.4f}")
print(f"CDAB: {val_cdab:.4f}")
print(f"BADC: {val_badc:.4f}")
Tóm tắt nhanh
| Big Endian (ABCD) | Little Endian (DCBA) | |
|---|---|---|
| Byte đầu tiên | MSB — lớn nhất | LSB — nhỏ nhất |
| Đọc như người viết số | ✅ Trực quan | ❌ Ngược |
| Phổ biến trên PC | ❌ | ✅ |
| Mạng Internet | ✅ | ❌ |
| Dùng làm chuẩn truyền mạng | ✅ | ❌ |
💡 Mẹo nhớ:
- Big Endian → byte lớn đứng đầu — như viết số bình thường.
- Little Endian → byte nhỏ đứng đầu — ngược lại.
- CDAB / BADC → dạng "mixed" — swap thêm một lần nữa theo word.