54 / 63 · Khái niệm
Bộ nhân unsigned nhiều chu kỳ bằng dịch và cộng
Chia phép cộng các tích riêng qua W chu kỳ, kiểm chứng thời điểm nhận và hoàn tất, lần tích lũy cuối, độ rộng và bất biến.
Bài học miễn phí. Đăng ký để lưu tiến độ học tập.
Kiến thức cần có: mạch tuần tự, phép gán nonblockingNonblocking Phép gán <= trong RTL tuần tự. Đánh giá vế phải rồi lên lịch cập nhật, cho các thanh ghi cùng cạnh tính từ trạng thái cũ. Tìm hiểu thêm, bộ đếm, phép dịch và bộ cộng.
Dùng lại một bộ cộng qua nhiều chu kỳ
Phép nhân hai số unsignedunsigned Quy tắc diễn giải chuỗi bit là số nguyên không âm. Phạm vi n bit là 0 đến 2^n−1 và có thể khác diễn giải signed của cùng chuỗi bit. Tìm hiểu thêm W bit là tổng các tích riêng được chọn bởi từng bit của số nhân B.
Cộng đồng thời mọi tích riêng cần nhiều logic cộng. Lần này ta dùng một bộ cộng W lần. Kiến trúc này đánh đổi diện tích với thông lượng; viết vòng lặp trong RTL không tự tạo ra mạch nhiều chu kỳ. Cần thanh ghiThanh ghi Mạch lưu trạng thái nhiều bit. Thanh ghi đồng bộ trong khóa học lưu đầu vào đã quy định tại cạnh clock. Tìm hiểu thêm lưu trạng thái tính toán qua các chu kỳ và điều kiện kết thúc.
Bộ tích lũy P rộng 2W bit, số bị nhân đang dịch M cũng rộng 2W bit, còn số nhân dịch phải Q rộng W bit. Khi bắt đầu, đặt P=0, M=zero-extend(A), Q=B. Ở mỗi chu kỳ xử lý, nếu Q[0]=1 thì cộng M vào P, rồi dịch M trái một vị trí và Q phải một vị trí.
Sau W bước xử lý, Q bằng 0 và P là tích đầy đủ. Độ rộng 2W bit là đủ: giá trị lớn nhất nhỏ hơn , và P trung gian khi cộng các tích riêng unsigned cũng không vượt tích cuối cùng.
Ví dụ A=13,B=11,W=4: bit thấp của Q lần lượt là 1,1,0,1. P biến đổi 0→13→39→39→143, còn các giá trị M được sử dụng là 13→26→52→104. Ở bước thứ ba, dù Q[0]=0, vẫn phải dịch và tăng bộ đếm.
Mỗi cột là trạng thái ngay sau cạnh chốt yêu cầu hoặc cạnh xử lý. Cột đầu khởi tạo trước khi tính, bốn cột tiếp theo là các bước xử lý. Ở bước thứ ba, bit thấp trước đó của Q bằng 0 nên chỉ P giữ nguyên, còn M và Q vẫn dịch.
Xem dữ liệu dạng sóng
| Tín hiệu | Dạng sóng | Giá trị bus |
|---|---|---|
| edge | 23452 | capture → work1 → work2 → work3 → work4 |
| P after | 234.5 | 0 → 13 → 39 → 143 |
| M after | 23452 | 13 → 26 → 52 → 104 → 208 |
| Q after | 23452 | 11 → 5 → 2 → 1 → 0 |
| done after | 0...1 |
Xác định trước thời điểm nhận yêu cầu và hoàn tất
Giao diện dưới đây nhận yêu cầu tại cạnh lênCạnh lên Khoảnh khắc clock chuyển 0 sang 1, khác với mức cao chỉ cả khoảng CLK=1. Tìm hiểu thêm khi idle và start=1. Cạnh t0 đó chỉ lưu đầu vào. Mạch tính W lần từ t1 đến tW; product và done có hiệu lực ngay sau tW. Từ lúc nhận đến hoàn tất là W chu kỳ, còn nếu đếm cả cạnh bắt đầu thì có W+1 cạnh.
Mạch bỏ qua start khi busy=1. Ở cạnh hoàn tất, busy ngay trước cạnh vẫn bằng 1 nên chưa nhận yêu cầu mới. Yêu cầu tiếp theo sớm nhất được nhận ở tW+1; khoảng cách nhận tối thiểu giữa hai yêu cầu liên tiếp là W+1 chu kỳ. Nếu giữ start=1, mạch sẽ bắt đầu lại tại cạnh tiếp theo sau khi về idle. Phía gọi thường phát một xung đúng thời điểm yêu cầu được nhận.
module mul_iter #(parameter int W = 8) (
input logic clk, rst, start,
input logic [W-1:0] a, b,
output logic busy, done,
output logic [2*W-1:0] product
);
localparam int CW = (W <= 1) ? 1 : $clog2(W);
localparam logic [CW-1:0] LAST = CW'(W-1);
logic [CW-1:0] step;
logic [2*W-1:0] acc, m, next_acc;
logic [W-1:0] q;
assign next_acc = acc + (q[0] ? m : {2*W{1'b0}});
always_ff @(posedge clk) begin
if (rst) begin
busy <= 1'b0;
done <= 1'b0;
product <= '0;
acc <= '0;
m <= '0;
q <= '0;
step <= '0;
end else begin
done <= 1'b0;
if (!busy) begin
if (start) begin
busy <= 1'b1;
acc <= '0;
m <= {{W{1'b0}}, a};
q <= b;
step <= '0;
end
end else begin
acc <= next_acc;
m <= m << 1;
q <= q >> 1;
if (step == LAST) begin
product <= next_acc;
busy <= 1'b0;
done <= 1'b1;
end else begin
step <= step + 1'b1;
end
end
end
end
endmoduleGiả sử W≥1; CW được giữ tối thiểu bằng 1 để W=1 không tạo bộ đếm 0 bit. reset đồng bộ, tích cực mức cao và ưu tiên hơn phép tính đang chạy. Sau khi nhận yêu cầu, thay đổi a,b bên ngoài không ảnh hưởng kết quả vì phép tính dùng m,q đã lưu. product chỉ đổi khi reset hoặc hoàn tất. done là xung một chu kỳ, không phải valid được giữ đến khi kết quả được tiêu thụ.
Phép gán cuối phải là product<=next_acc, không phải product<=acc. Vế phải của phép gán nonblocking đọc giá trị trước cập nhật; xuất acc sẽ bỏ mất tích riêng do bit cuối của số nhân tạo ra. Cũng vì vậy, dù viết phép gán dịch q trước, next_acc vẫn được tính từ Q[0] hiện tại.
Bất biến tính toán và lựa chọn thiết kế thực tế
Ngay sau k bước xử lý, quan hệ sau vẫn được duy trì.
Tách Q thành phần chẵn và bit thấp nhất sẽ thấy quan hệ được bảo toàn qua bước tiếp theo. Khi k=W, Q=0 nên P=AB. Bất biếnBất biến Điều kiện phải luôn đúng trong mọi thực thi được phép. Ví dụ số phần tử của FIFO sâu 4 luôn nằm từ 0 đến 4. Tìm hiểu thêm này làm rõ tính đúng đắn của thứ tự tích lũy và dịch hơn việc chỉ xem dạng sóng của một ví dụ nhỏ.
Đường tới hạn của một bước chứa bộ cộng 2W bit và logic chọn đầu vào. Bỏ ký hiệu nhân không bảo đảm mạch luôn nhỏ hơn hay nhanh hơn. Cần so sánh cả khối DSP chuyên dụng của FPGA, kết quả tổng hợp và thông lượngThông lượng Số tác vụ được nhận hoặc hoàn tất trong một đơn vị thời gian. Pipeline có thể tăng thông lượng mà không giảm độ trễ của một tác vụ. Tìm hiểu thêm yêu cầu. Mô-đun này không có kết thúc sớm, xử lý signed hay backpressure cho kết quả. Hợp đồng phải bảo đảm phía tiêu thụ không bỏ lỡ done.
Khi kiểm chứng, hãy thử 0,1, giá trị cực đại và B chỉ có bit cao nhất bằng 1; đồng thời kiểm tra start và thay đổi đầu vào khi busy, cũng như reset hủy phép tính. Đặc biệt, B chỉ có bit cao nhất bằng 1 sẽ phát hiện lỗi bỏ sót lần tích lũy cuối.
Tự thử
Mạch nhận phép tính W=4,A=9,B=10 tại t0. Hãy xác định acc, q và done ngay sau t1–t4, rồi giải thích cạnh nhận yêu cầu tiếp theo sớm nhất. Nếu cuối cùng viết product<=acc thì đầu ra là bao nhiêu?
Đọc giải thích
Ban đầu acc=0,m=9,q=1010. Sau t1: bit thấp bằng 0 nên acc=0,q=0101,done=0. Sau t2: cộng m=18, được acc=18,q=0010,done=0. Sau t3: bit thấp bằng 0 nên acc=18,q=0001,done=0. Sau t4: cộng m=72, được acc=90,q=0000,done=1,busy=0 và product=90. Ngay trước cạnh t4, busy=1 nên mạch không nhận start; t5 là cạnh nhận tiếp theo sớm nhất. Nếu viết product<=acc, phép gán nonblocking đọc acc=18 trước cập nhật và xuất 18, bỏ mất tích riêng cuối là 72.