57 / 63 · Khái niệm
Con trỏ và chính sách biên của FIFO một đồng hồ
Hiện thực con trỏ và occupancy của FIFO sâu 4; quy định rõ điều kiện chấp nhận push/pop đồng thời khi empty và full.
Bài học miễn phí. Đăng ký để lưu tiến độ học tập.
Giữ đúng thứ tự mà không di chuyển dữ liệu
FIFOFIFO Hàng đợi lấy ra dữ liệu được đưa vào trước. Quy tắc đầy, rỗng và cho phép đọc/ghi đồng thời là một phần của hợp đồng hoạt động. lấy ra dữ liệu đã được đưa vào trước. Với một đồng hồ, cả đọc và ghi được quyết định tại cùng cạnh lên. Bài này cố định DEPTH=4 và độ rộng dữ liệu 8 bit. rptr trỏ đến vị trí sẽ lấy tiếp theo, wptr trỏ đến vị trí sẽ ghi tiếp theo. Mỗi con trỏ rộng 2 bit nên tự quay từ 3 về 0. occupancy phải biểu diễn số phần tử 0–4 nên cần 3 bit.
rptr=wptr có thể xảy ra cả khi empty lẫn full. Vì vậy, nếu chỉ dùng sự bằng nhau của hai địa chỉ để xác định empty, mạch sẽ làm mất dữ liệu lúc đầy. Ở đây ta duy trì riêng số phần tử , phân biệt bằng và . Với độ sâu , độ rộng địa chỉ và độ rộng bộ đếm là:
Chính sách biên phân biệt yêu cầu và chấp nhận
push và pop là yêu cầu. Gọi p và q lần lượt là push và pop thực sự được chấp nhận; từ trạng thái ngay trước cạnh không reset, tính như sau:
Ở empty, dù có cả hai yêu cầu, vẫn không có dữ liệu cũ để pop nên chỉ chấp nhận push. Dữ liệu mới không được xuất ngay tại cạnh đó. Ở full, hai yêu cầu đều được chấp nhận vì lấy head cũ ra sẽ tạo chỗ trống. full là trạng thái “hiện có 4 phần tử”; trong chính sách này, nó không phải tín hiệu cấm push vô điều kiện.
| Trạng thái ngay trước cạnh | Chỉ push | Chỉ pop | Cả push và pop |
|---|---|---|---|
| C=0 | Lưu 1 phần tử | Từ chối | Chỉ nhận push, C=1 |
| 0<C<4 | C+1 | C−1 | Nhận cả hai, giữ C |
| C=4 | Từ chối | C=3 | Nhận cả hai, C=4 |
Tiến đồng hồ để kiểm tra điều kiện biên
Đầu tiên, hãy yêu cầu push và pop cùng lúc. Vì FIFO rỗng, chỉ push được chấp nhận. Tiếp theo, tắt pop, lấp đầy 4 ô rồi bật lại cả hai yêu cầu cùng một giá trị đầu vào mới. Số phần tử phải giữ ở 4, đầu ra là head cũ và ô lưu chứa tail mới.
FIFO bốn ô. Ban đầu rỗng, con trỏ đọc và ghi đều bằng 0. Nhận pop khi không rỗng. Nhận push khi còn chỗ hoặc pop được nhận tại cùng cạnh.
Hình là mô hình học tập thể hiện trạng thái ngay sau từng 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 pop được chấp nhận, output valid bằng 1; nếu cạnh tiếp theo không chấp nhận pop, giá trị 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 đầu ra giữ nguyên nhưng valid về 0. Các bit cũ trong ô trống được ẩn. “Về đầu” khởi tạo lại toàn bộ bài thực hành, không thể hiện thao tác resetReset Điều khiển đưa trạng thái về giá trị đầu đã định. Phải quy định đồng bộ hay bất đồng bộ và ưu tiên so với các điều khiển khác. Tìm hiểu thêm bộ nhớ của RTL.
RTL bao gồm cả đầu ra qua thanh ghi
module fifo4 (
input logic clk, rst, push, pop,
input logic [7:0] din,
output logic [7:0] dout,
output logic dout_valid, full, empty,
output logic [2:0] occupancy
);
timeunit 1ns;
timeprecision 1ps;
logic [7:0] mem [0:3];
logic [1:0] rptr, wptr;
logic take_push, take_pop;
assign empty = (occupancy == 3'd0);
assign full = (occupancy == 3'd4);
assign take_pop = !rst && pop && !empty;
assign take_push = !rst && push && (!full || take_pop);
always_ff @(posedge clk) begin
if (rst) begin
rptr <= 2'd0;
wptr <= 2'd0;
occupancy <= 3'd0;
dout_valid <= 1'b0;
end else begin
dout_valid <= take_pop;
if (take_push) begin
mem[wptr] <= din;
wptr <= wptr + 2'd1;
end
if (take_pop) begin
dout <= mem[rptr];
rptr <= rptr + 2'd1;
end
case ({take_push, take_pop})
2'b10: occupancy <= occupancy + 3'd1;
2'b01: occupancy <= occupancy - 3'd1;
default: occupancy <= occupancy;
endcase
end
end
endmoduleoccupancy không được tăng và giảm bằng hai if độc lập. Nếu cùng được chấp nhận mà 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 hai lần cho một thanh ghi, chỉ phép gán cuối có hiệu lực. Hai con trỏ tiến theo từng thao tác được chấp nhận, còn số phần tử được quyết định một lần cho cả bốn trường hợp. Điều kiện chấp nhận cũng ngăn giảm khi bằng 0 hoặc tăng khi bằng 4.
Đọc và ghi cùng địa chỉ lúc full
Đưa lần lượt A, B, C, D vào sẽ có rptr=wptr=0 và occupancy=4. Nếu tiếp theo pop=push=1, din=E thì sau cạnh, dout=A, dout_valid=1 và nội dung logic là [B,C,D,E]. Hai con trỏ cùng thành 1 và số phần tử giữ ở 4. mem[0] đổi thành E, nhưng vế phải phép gán nonblocking cho dout đọc A cũ. Hành vi xung đột read-first này là điều kiện thiết yếu của chính sách thay thế lúc full.
Nếu tiếp theo push=0, pop=1 thì B được xuất và số phần tử còn 3. Ngược lại, khi empty, đưa din=Z cùng hai yêu cầu chỉ làm số phần tử thành 1, còn dout_valid=0. Phải yêu cầu pop ở cạnh tiếp theo mới xuất Z. Cần nhớ truyền thẳng khi empty và thay thế khi full là hai lựa chọn thiết kế riêng.
rst đồng bộ, tích cực mức cao, khởi tạo con trỏ, số phần tử và dout_valid. Nó không xóa bộ nhớ hay giá trị dout. Dù còn bit cũ sau reset, empty chặn pop và chỉ những vị trí được ghi bằng push sau reset mới được đọc về sau, nên hoạt động bình thường không cần reset toàn bộ mảng. Không tiêu thụ giá trị khi dout_valid=0. Đầu ra valid=1 là phản hồi của pop đã được chấp nhận; phía đầu ra không có ready riêng nên bên tiêu thụ phải nhận phản hồi đó.
Khi DEPTH không phải lũy thừa của 2, không thể dùng lại cách tràn tự nhiên của 2 bit. Ví dụ độ sâu 3 cần quay rõ ràng từ địa chỉ 2 về 0. Nếu kết quả tổng hợp của ví dụ này dùng RAM, phải kiểm tra RAM hỗ trợ trả dữ liệu cũ khi trùng địa chỉ hay không. Mã này dành cho một đồng hồ, không áp dụng nguyên trạng cho đồng bộ con trỏ FIFO có hai đồng hồ khác nhau.
Tự thử
Ngay sau reset, đưa các yêu cầu sau, mỗi bước một cạnh: (1) đồng thời push A và pop, (2) chỉ push B, (3) chỉ push C, (4) chỉ push D, (5) đồng thời push E và pop, (6) chỉ pop. Hãy ghi nội dung queue, occupancy, dout_valid và giá trị đầu ra hợp lệ sau từng bước. Giải thích vì sao bước thứ năm cần read-first.
Đọc giải thích
(1) [A],1,valid=0. (2) [A,B],2,valid=0. (3) [A,B,C],3,valid=0. (4) [A,B,C,D],4,valid=0. (5) [B,C,D,E],4,valid=1,dout=A. (6) [C,D,E],3,valid=1,dout=B. Ở bước 1, không có phần tử cũ nên pop bị từ chối. Ở bước 5, rptr=wptr=0 và đọc/ghi trùng một ô, nên phải đọc head cũ A. Nếu write-first trả về E, cả thứ tự và nội dung FIFO đều sai. Giá trị đầu ra khi valid=0 không được dùng để chấm kết quả.