Altifigence Academy

55 / 63 · Khái niệm

Bộ nhớ đồng bộ và hợp đồng xung đột đọc/ghi

Phân biệt thời điểm phản hồi của đọc qua thanh ghi và các chế độ read-first·write-first·no-change; thiết kế trách nhiệm riêng của valid và khởi tạo địa chỉ.

Quyết định trước địa chỉ: thời điểm phản hồi

Khi kết nối bộ nhớ, phải quy định cả dung lượng, độ trễ đọc và hành vi khi xung đột. Ta xây dựng bộ nhớ đồng bộ gồm 16 địa chỉ, mỗi địa chỉ lưu 8 bit, có thể nhận một yêu cầu đọc và một yêu cầu ghi tại cùng một cạnh lên. Hai yêu cầu dùng chung đồng hồ nhưng có địa chỉ độc lập. Dung lượng là 16×8=128 bit, độ rộng địa chỉ là log⁡216=4\log_2 16=4 bit.

Nếu địa chỉ đọc và rd_en ổn định trước cạnh lên, rd_data và rd_valid qua thanh ghi xuất hiện sau cạnh đó. Đây là độ trễ một tầng đọc qua thanh ghi. Một always_ff khác đọc các đầu ra này tại cùng cạnh sẽ thấy giá trị trước cập nhật, nê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 tiêu thụ phản hồi nhận chúng tại cạnh lên tiếp theo. Cần đánh dấu riêng thời điểm yêu cầu và thời điểm quan sát phản hồi trên dạng sóng. Mạch có thể nhận yêu cầu mới mỗi chu kỳ; độ trễ một tầng không có nghĩa 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 bằng 1/2.

Đọc và ghi cùng địa chỉ

Giả sử ngay trước cạnh, M[3]=8'h25 và mạch vừa đọc địa chỉ 3 vừa ghi 8'h9A vào đó. Chế độ read-first phản hồi 25 rồi thay nội dung lưu bằng 9A. Chế độ write-first phản hồi giá trị mới 9A. Chế độ no-change giữ nguyên thanh ghi đầu ra, nên không bảo đảm đầu ra là giá trị cũ của địa chỉ 3. Cả ba đều là những hợp đồng hoạt động hợp lệ khác nhau. Tài liệu các chế độ đồng bộ RAM của AMD cũng phân biệt ba chế độ này.

Ví dụ này dùng read-first. Gọi M−M^{-} là mảng ngay trước cạnh và M+M^{+} là mảng sau cạnh; ở cạnh không reset, với địa chỉ aa bất kỳ ta có:

M+[a]={wr_data,wr_en=1∧a=wr_addrM−[a],otherwiseM^{+}[a]=\begin{cases} \mathrm{wr\_data}, & \mathrm{wr\_en}=1\land a=\mathrm{wr\_addr}\\ M^{-}[a], & \text{otherwise} \end{cases}
rd_data+={M−[rd_addr],rd_en=1rd_data−,rd_en=0rd_valid+=rd_en\begin{aligned} \mathrm{rd\_data}^{+}&=\begin{cases} M^{-}[\mathrm{rd\_addr}], & \mathrm{rd\_en}=1\\ \mathrm{rd\_data}^{-}, & \mathrm{rd\_en}=0 \end{cases}\\ \mathrm{rd\_valid}^{+}&=\mathrm{rd\_en} \end{aligned}

Ngay cả khi xung đột, phép đọc dùng M−M^{-} thay vì M+M^{+}. Nếu không có yêu cầu đọc, rd_data giữ nguyên.

SystemVerilog
module sync_mem16x8 (
  input  logic       clk, rst,
  input  logic       wr_en, rd_en,
  input  logic [3:0] wr_addr, rd_addr,
  input  logic [7:0] wr_data,
  output logic [7:0] rd_data,
  output logic       rd_valid
);
  timeunit 1ns;
  timeprecision 1ps;
  logic [7:0] mem [0:15];

  always_ff @(posedge clk) begin
    if (rst) begin
      rd_valid <= 1'b0;
    end else begin
      rd_valid <= rd_en;
      if (wr_en)
        mem[wr_addr] <= wr_data;
      if (rd_en)
        rd_data <= mem[rd_addr];
    end
  end
endmodule

Vế phải của cả hai phép gán đều dùng giá trị trước cập nhật; các 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 được áp dụng sau đó. Vì vậy, đặt câu ghi trước câu đọc không đổi hành vi thành đọc giá trị mới. Nếu cần write-first, có thể thể hiện rõ ý định bằng biểu thức đọc (wr_en && wr_addr==rd_addr) ? wr_data : mem[rd_addr]. Đường bypass này vẫn giữ thanh ghi đầu ra đọc.

valid và khởi tạo là hai cam kết khác nhau

rst là reset đồng bộ, tích cực mức cao. Nếu rst=1 tại cạnh lên, mạch bỏ qua cả hai yêu cầu và chỉ đưa rd_valid về 0. Mảng và rd_data không được điền 0. Hợp đồng này không định nghĩa nội dung của địa chỉ chưa từng được ghi sau khi cấp nguồn. Trình mô phỏng có thể hiển thị X; trên phần cứng thật cũng không thể giả định một giá trị cụ thể.

rd_valid=1 nghĩa là “chu kỳ phản hồi của yêu cầu đọc”, không phải “địa chỉ đã được khởi tạo và có giá trị đã biết”. Người dùng phải chỉ đọc địa chỉ đã ghi, hoặc duy trì bảng khởi tạo riêng cho từng địa chỉ. Đọc read-first cùng cạnh với lần ghi đầu tiên vẫn trả về nội dung cũ chưa xác định. Cũng cần điều kiện bảo vệ valid để không dùng rd_data trong tính toán khi valid=0. Điều kiện chỉ đọc địa chỉ đã khởi tạo và điều kiện valid không thay thế được nhau.

Reset không xóa bộ nhớ nên các giá trị đã ghi vẫn còn theo RTL. Tuy nhiên, nếu hệ thống quyết định bỏ nội dung cũ sau reset, phải quản lý lại cả bảng khởi tạo bên ngoài. Yêu cầu xóa thông tin nhạy cảm là một hạng mục thiết kế riêng với reset điều khiển.

Mỗi cột ghi lại quan sát tại một 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. Đầu vào là giá trị ngay trước cạnh, còn trạng thái có tên chứa after là giá trị ngay sau cập nhật. Cách căn cột thể hiện thứ tự lấy mẫu, không mô tả độ trễ lan truyềnĐộ trễ lan truyền Thời gian từ khi đầu vào đổi đến khi đầu ra ổn định ở giá trị đúng. Tương đương logic và đặc tính thời gian là hai vấn đề riêng. Tìm hiểu thêm vật lý. Mọi địa chỉ đọc và ghi đều là 3. E0 ghi 25 lần đầu; E1 vừa ghi đè 9A vừa đọc. Dấu gạch trong data là giá trị chưa xác định; bus có valid=0 không được tính là phản hồi. Dữ liệu được viết ở hệ thập lục phân.

Đọc giá trị cũ khi xung đột read-first
Xem dữ liệu dạng sóng
Dữ liệu dạng sóng: mỗi ký tự là một khoảng; dấu chấm giữ trạng thái trước; p là một chu kỳ đồng hồ.
Tín hiệuDạng sóngGiá trị bus
edge2345E0 → E1 → E2 → E3
wr_en1.0.
wr_data23..25 → 9A
rd_en01.0
M[3] after23..25 → 9A
rd_data after234.- → 25 → 9A
rd_valid after01.0

Theo dõi thủ công bốn cạnh

Ở E0, ghi 25 vào địa chỉ 3 và đặt rd_en=0. Ở E1, vừa ghi 9A vào địa chỉ 3 vừa đọc cùng địa chỉ; sau E1, valid=1 và data=25. Ở E2, đọc lại địa chỉ 3 mà không ghi thì data=9A. Nếu rd_en=0 ở E3, valid=0 và data vẫn là 9A. Nếu đếm một phản hồi mới chỉ vì bus đầu ra ở E3 mang 9A, cùng dữ liệu sẽ bị xử lý hai lần.

Khi khác địa chỉ, đọc và ghi độc lập. Ví dụ vừa đọc địa chỉ 7 vừa ghi địa chỉ 3 vẫn phản hồi giá trị cũ ở địa chỉ 7. Trên FPGA thực tế, phải kiểm tra báo cáo tổng hợp và tài liệu linh kiện để biết RTL ánh xạ tới loại RAM nào và tài nguyên đó xử lý xung đột cùng địa chỉ ra sao. Quy định ý nghĩa RTL và xác minh triển khai trên một thiết bị cụ thể là hai bước riêng.

Tự thử

M[6]=8'h10 đã được lưu. Ở E0, vừa đọc địa chỉ 6 vừa ghi 8'h20; ở E1 đọc lại mà không ghi; ở E2 đặt rd_en=0. Hãy tính data/valid sau mỗi cạnh với read-first. Nếu đổi thí nghiệm sang write-first thì điều gì khác? Xung đột read-first đầu tiên ở một địa chỉ chưa từng được ghi có trả về giá trị xác định không?

Đọc giải thích

Sau E0, data=10, valid=1 và mảng được cập nhật thành 20. Sau E1, data=20, valid=1. Sau E2, data giữ 20 nhưng valid=0 nên không tính là phản hồi. Với write-first, chỉ data ở E0 đổi thành 20. Lần đọc read-first trùng lần ghi đầu tiên trả về nội dung chưa khởi tạo trước ghi, nên không thể đặt một giá trị cụ thể làm đáp án. valid=1 cũng không bảo đảm đã khởi tạo.

Lựa chọn áp dụng cho trình duyệt này. Có thể đổi ở chân trang bất cứ lúc nào.