Chuyển tiếp kết quả trước khi ghi vào tập thanh ghi
Theo bốn phép cộng phụ thuộc, chọn kết quả mới nhất khi hai đích trùng nhau và kiểm tra x0, cho phép ghi cùng dữ liệu chưa sẵn sàng.

Forwarding chuyển một toán hạng từ kết quả đang xử lý của lệnh trước, trước khi kết quả ấy đến tập thanh ghi. Nếu bạn đọc mã do trình biên dịch sinh, cơ chế này giải thích vì sao hai phép cộng phụ thuộc liền nhau có thể chạy mà không chờ ghi kết quả. Forwarding đúng phải chọn bên tạo kết quả mới nhất có liên quan, kiểm tra nó thật sự ghi thanh ghi đó và chờ khi giá trị chưa sẵn sàng.
Giả sử pipeline giả định thực thi theo thứ tự, chỉ bắt đầu tối đa một lệnh mỗi chu kỳ, gồm IF, ID, EX, MEM và WB. Phép cộng số nguyên tính trong EX, chốt kết quả vào thanh ghi EX/MEM ở biên chu kỳ rồi mang tiếp tới WB. Trong chu kỳ EX của mình, lệnh dùng kết quả có thể chọn toán hạng từ EX/MEM hoặc MEM/WB. Các đường chuyển và quy tắc thời gian là đặc điểm được đặt ra cho ví dụ, không phải yêu cầu ISA RISC-V.
Theo một phép cộng phụ thuộc qua thời gian
Giả sử x1, x2, x3 và x4 chứa 3, 4, 5 và 6. Giá trị x5 cũ trong tập thanh ghi là 99. Xét các lệnh RV32 minh họa:
add x5, x1, x2
add x5, x5, x3
add x6, x5, x4
add x7, x6, x5
Kết quả tuần tự là x5 bằng 7 sau I1, x5 bằng 12 sau I2, x6 bằng 18 sau I3 và x7 bằng 30 sau I4. Lần ghi thứ hai vào x5 là chủ ý: nó tạo trường hợp hai nguồn chuyển tiếp có cùng số thanh ghi đích nhưng khác tuổi lệnh và giá trị.
Khi không dừng, I1 thực hiện EX ở chu kỳ 3, còn I2 thực hiện EX ở chu kỳ 4. Đầu chu kỳ 4, EX/MEM giữ tổng đã hoàn tất của I1 là 7. I2 có thể đã chốt giá trị cũ 99 trong ID, nhưng bộ chọn toán hạng lấy 7 từ đường bypass. Nó cộng với x3 bằng 5 rồi tạo 12 ở cuối chu kỳ đó.
Điều này không đòi hai phép cộng truyền qua một giai đoạn EX trong cùng chu kỳ. Kết quả I1 đã được chốt ở biên trước. Đường bypass chọn giá trị trong thanh ghi đã có sẵn, rồi ALU của I2 tính từ nó trong khoảng tiếp theo. Ngân sách thời gian vẫn phải gồm logic chọn và dây dẫn ấy.
Bài giảng Computation Structures về thực thi pipeline giải thích bypass và ưu tiên theo tuổi lệnh tạo kết quả. Ví dụ Beta của bài giảng chuyển vào giai đoạn đọc thanh ghi, gồm đường tổ hợp từ đầu ra ALU. Bài này chủ ý dùng đường khác, đã qua thanh ghi, vào EX. Quy tắc phụ thuộc chung giống nhau; biên xung và số lần có thể phải dừng cần suy ra theo datapath được chọn.
Chọn lệnh ghi khớp gần nhất trong thứ tự chương trình
Ở chu kỳ 5, I3 đến EX. EX/MEM chứa kết quả 12 của I2 cho x5, còn MEM/WB chứa kết quả cũ hơn 7 của I1 cho x5. Cả hai số thanh ghi đích đều khớp nguồn của I3. Chỉ chọn một bên khớp bất kỳ là chưa đủ: thực thi tuần tự cần I2, tức lệnh trước gần I3 hơn, vì nó ghi đè giá trị I1 trước khi I3 đọc x5.
Trong ví dụ thực thi nghiêm ngặt theo thứ tự này, ưu tiên là EX/MEM trước MEM/WB. Nếu chọn MEM/WB trước, I3 tính 7 cộng 6, cho 13 thay vì 18. Lỗi ưu tiên có thể không lộ trong dòng lệnh chưa từng có hai lần ghi đang xử lý vào cùng đích. Hãy chủ ý thêm một lần ghi đè khi kiểm tra bộ chọn.
Với I4 ở chu kỳ 6, hai toán hạng đi theo hai đường khác nhau. x6 bằng 18 đến từ I3 trong EX/MEM, còn x5 bằng 12 đến từ I2 trong MEM/WB. Mỗi nguồn thực sự được dùng cần quyết định chọn riêng. Một cờ chung “dùng toán hạng chuyển tiếp” không mô tả được cả hai nguồn nếu phần cứng không mang thêm những lựa chọn độc lập cần thiết.
Kiểm tra điều kiện khớp trước khi dùng giá trị
Trường đích chỉ có ý nghĩa khi lệnh thật sự ghi thanh ghi ấy. Khe pipeline không hoạt động hoặc lệnh bị tắt ghi kết quả không được thay toán hạng đã đọc chỉ vì vài bit đang giữ tình cờ bằng số thanh ghi đó. Ví dụ biểu diễn điều kiện này bằng cờ cho phép ghi.
RISC-V x0 luôn đọc ra 0. Kết quả có đích danh nghĩa x0 không được biến thành toán hạng chuyển tiếp khác 0. Kiểm tra rõ quy tắc thanh ghi 0 thay vì giả định mọi lần khớp đích đều đại diện trạng thái kiến trúc. Định nghĩa thanh ghi và lệnh số nguyên RV32 cung cấp yêu cầu ngữ nghĩa này; cách mạch bypass thực hiện thuộc triển khai.
Khả năng sẵn sàng là điều kiện khác. Lệnh tạo kết quả mới hơn có thể đã khớp trước khi đáp số cuối sẵn sàng, chẳng hạn lệnh load mà kết quả EX mới chỉ là địa chỉ. Bộ chọn không được chuyển địa chỉ đó như dữ liệu đã nạp, cũng không được âm thầm dùng giá trị cũ hơn của cùng thanh ghi. Lệnh dùng dữ liệu cần kết quả của lần ghi mới hơn, nên phải chờ hoặc dùng cơ chế đúng khác được quy định.
from dataclasses import dataclass
MASK32 = (1 << 32) - 1
@dataclass(frozen=True)
class Producer:
rd: int
value: int | None
writes: bool = True
ready: bool = True
def operand(rs, decoded_value, ex_mem=None, mem_wb=None):
if rs == 0:
return 0, "zero"
for name, producer in (("EX/MEM", ex_mem), ("MEM/WB", mem_wb)):
if producer is None or not producer.writes or producer.rd != rs:
continue
if not producer.ready:
raise RuntimeError("newest matching result is not available")
if producer.value is None:
raise ValueError("ready producer must contain its result")
return producer.value & MASK32, name
return decoded_value & MASK32, "decoded"
# I1 EX in cycle 3; its result is registered for cycle 4.
i1 = Producer(5, 3 + 4)
a, route = operand(5, 99, ex_mem=i1)
assert (a, route) == (7, "EX/MEM")
i2 = Producer(5, (a + 5) & MASK32)
# I3 EX in cycle 5: both producers match x5; I2 is newer.
a, route = operand(5, 99, ex_mem=i2, mem_wb=i1)
assert (a, route) == (12, "EX/MEM")
i3 = Producer(6, (a + 6) & MASK32)
assert i3.value == 18 and (i1.value + 6) == 13
# I4 EX in cycle 6: its operands use different bypass registers.
a, route_a = operand(6, 0, ex_mem=i3, mem_wb=i2)
b, route_b = operand(5, 99, ex_mem=i3, mem_wb=i2)
assert (a + b, route_a, route_b) == (30, "EX/MEM", "MEM/WB")
assert operand(0, 99, Producer(0, 123)) == (0, "zero")
assert operand(5, 99, Producer(5, 123, writes=False)) == (99, "decoded")
assert operand(4, 6, i2, i1) == (6, "decoded")
assert operand(5, 0, Producer(5, 1 << 32)) == (0, "EX/MEM")
try:
operand(5, 99, Producer(5, None, ready=False), i1)
except RuntimeError:
pass
else:
raise AssertionError("older x5 incorrectly replaced an unavailable newer result")
print("four dependent additions, source priority, x0 and unavailable-result cases checked")
Mô hình Python chọn từ trạng thái tại một thời điểm của hai thanh ghi pipeline và giá trị từng được chốt trong ID. Nó không phải simulator pipeline đầy đủ và không tuyên bố theo dõi thời gian tập thanh ghi, phục hồi nhánh hay phản hồi bộ nhớ. Đầu ra hữu ích là giá trị cùng đường được chọn, khiến lỗi ưu tiên hiện rõ thay vì bị giấu trong assertion tổng số học cuối.
Tính cả chi phí bypass
Forwarding chỉ loại một lần chờ khi giá trị phía tạo kết quả có đủ sớm và đường tới phía dùng đáp ứng thời gian. Bộ so sánh tìm số thanh ghi khớp, bộ chọn chọn toán hạng và dây chuyển giá trị giữa các giai đoạn. Các thành phần đó tốn độ trễ và diện tích. Thiết kế có thể giảm chu kỳ dừng nhưng kéo dài chu kỳ xung, nên chỉ số lệnh hoặc số chu kỳ chưa giải quyết được đánh đổi hiệu năng.
Triển khai có thể chọn ít đường bypass hơn và dừng ở trường hợp không bao phủ. Điều đó có thể hợp lý về chi phí cho lõi nhỏ, nhưng trường hợp không hỗ trợ cần hành vi điều khiển có chủ ý. Tính đáp số từ trạng thái thanh ghi cũ không phải cách rẻ hơn để thực hiện cùng chương trình.
Forwarding cũng không loại chuỗi phụ thuộc logic. I2 vẫn cần kết quả I1, I3 vẫn cần kết quả I2. Thêm tài nguyên thực thi độc lập không tính được kết quả phụ thuộc thật trước khi có đầu vào. Sắp lịch lệnh có thể xen việc không liên quan, còn thuật toán khác đôi khi rút ngắn chuỗi phụ thuộc. Không thay đổi nào tự xuất hiện chỉ vì thêm bộ chọn bypass.
Kiểm tra ranh giới chọn thay vì chỉ tổng cuối
Bài tập: đổi đích của lệnh tạo kết quả mới nhất từ x5 sang x8. Nguồn x5 khi đó phải chọn kết quả cũ hơn trong MEM/WB. Tiếp theo tắt cho phép ghi ở cả hai bên tạo kết quả và kiểm tra bộ chọn dùng toán hạng đã chốt. Cuối cùng khôi phục bên khớp x5 mới hơn nhưng đánh dấu kết quả chưa sẵn sàng: giá trị x5 cũ hơn không còn được chấp nhận để thay thế.
Ba trường hợp phân biệt không khớp, khớp nhưng không ghi và kết quả bắt buộc chưa sẵn sàng. Đó là những lý do khác nhau khiến bộ chọn phải hành xử khác. Khi rà soát RTL hoặc simulator, ghi nguồn của lệnh dùng dữ liệu, đích của lệnh tạo kết quả, tuổi lệnh, cho phép ghi, trạng thái sẵn sàng và đường được chọn tại biên liên quan. Bằng chứng ấy giúp chẩn đoán tốt hơn quan sát chung rằng chương trình đôi khi tính sai.