Sync FIFO: phân biệt request, acceptance và dữ liệu trả về

Khoảng 45 phútMức 2/3Bản nháp: một số khẳng định chưa đối chiếu spec

Sau bài này bạn sẽ

  • Định nghĩa accepted read/write ở empty/full và đồng thời
  • Kiểm pointer wrap cho depth1 và depth không phải power-of-two
  • Dùng queue oracle độc lập để bắt mất dữ liệu, đảo thứ tự và sai flags

Enables là yêu cầu, không phải sự kiện đã nhận

Wr_en1 khi FIFO full có thể không lưu gì. Rd_en1 khi empty không được đọc một word chưa hợp lệ. Muốn scoreboard đúng phải tính acceptance theo contract, không tăng/giảm expected size chỉ từ hai tín hiệu request. FIFO bài này dùng một clock cho cả hai phía. [1]

Output đăng ký, không bypass từ empty

Dout được cập nhật sau accepted read ở cạnh clock và giữ giá trị trước khi không read. Không có fall-through hoặc combinational bypass. Sau reset, count 0/empty 1/full 0/dout 0; array chưa bị xóa. Vì vậy dữ liệu mới viết vào empty không tự xuất hiện ở dout ngay cùng request.

Hai phương trình acceptance làm rõ ưu tiên

Take_read là rd_en và không empty. Take_write là wr_en và (không full hoặc take_read). Tại full, một read hợp lệ giải phóng chỗ cho write trong cùng cạnh. Tại empty, read chưa hợp lệ nên both request chỉ nhận write. Các chính sách khác có thể đúng ở thiết kế khác, nhưng expected phải đổi theo yêu cầu trước khi đổi RTL.

Full replacement phải trả head cũ

Khi full và cả hai request cùng 1, read trả word cũ ở head, write đưa din vào tail, count giữ nguyên. Với depth 1, hai địa chỉ trùng nhau; NBA đọc RHS cũ trước write giúp giữ đúng word cần pop trong RTL này. Đây cũng là ca dễ lộ khác biệt primitive read-during-write, cần kiểm lại nếu đổi mapping.

Depth 3 không được wrap theo modulo 4

Pointer width đủ biểu diễn địa chỉ nhưng mã nhị phân tối đa chưa chắc là địa chỉ cuối. RTL so với DEPTH-1 để quay về 0; không dựa riêng vào overflow của vector. Depth 1 dùng width tối thiểu 1, không tạo range âm khi clog2(1)=0. Count cần biểu diễn cả capacity nên suy ra từ DEPTH+1. Không override PW/CW trái các ràng buộc ấy.

Queue dịch khác cấu trúc ring buffer DUT

Reference model lưu head ở phần tử 0 và dịch sau pop, không sao lại wr_ptr/rd_ptr. Khi nhận cả hai, model pop trước rồi append vào size mới. Nhờ vậy lỗi wrap hoặc read/write address không tự được lặp lại nguyên trong oracle. Mỗi cạnh so count, full, empty và dout bằng dữ liệu expected, kể cả khi không có read.

Dung lượng dùng được không tự là depth-1

Nguồn tham khảo có thiết kế phân biệt full/empty bằng pointer và để trống một slot. Thiết kế count-based của bài dùng đủ DEPTH slot. Không trộn hai cách định nghĩa rồi sửa expected cho hợp snapshot. Generic synth mặc định depth 4 có 27 FF/0 latch; chưa claim RAM primitive, MTBF hoặc mọi interleaving bằng formal. [2]

Ba cấu hình,1536 cycle-configuration

Trước chạy, directed prefix bảo đảm mỗi depth gặp overflow reject, underflow reject và full replacement. Sau đó các command/data biến thiên tiếp tục qua 512 cycle cho mỗi depth 4/3/1.

Thí nghiệm fifo

Trước chạy:512 cycle cho từngDEPTH 4/3/1, oracle là queue dịch độc lập pointers; directedfill/fullreject/fullreplace/drain/underflow/emptyboth bảo đảm mỗi boundary flag 1. Tổng 1536 cycle-configuration.

sync_fifo.sv RTL
`timescale 1ns/1psmodule sync_fifo #(parameter integer DEPTH=4,parameter integer PW=(DEPTH>1)?$clog2(DEPTH):1,  parameter integer CW=(DEPTH>1)?$clog2(DEPTH+1):1) (input wire clk,rst_n,wr_en,rd_en,input wire[3:0] din,output reg[3:0] dout, output wire full,empty,output reg[CW-1:0] count);  reg[3:0] mem[0:DEPTH-1];  reg[PW-1:0] wr_ptr,rd_ptr;  wire take_read=rd_en&&!empty;  wire take_write=wr_en&&(!full||take_read);  assign full=count==CW'(DEPTH);  assign empty=count==0;  always @(posedge clk) begin    if(!rst_n) begin wr_ptr<=0;rd_ptr<=0;count<=0;dout<=0;end    else begin      if(take_write) begin        mem[wr_ptr]<=din;        wr_ptr<=(wr_ptr==PW'(DEPTH-1))?0:wr_ptr+1'b1;      end      if(take_read) begin        dout<=mem[rd_ptr];        rd_ptr<=(rd_ptr==PW'(DEPTH-1))?0:rd_ptr+1'b1;      end      case({take_write,take_read})        2'b10:count<=count+1'b1;        2'b01:count<=count-1'b1;        default:begin end      endcase    end  endendmodule
tb_fifo.sv testbench
`timescale 1ns/1psmodule fifo_trial #(parameter integer D=4,parameter integer CW=(D>1)?$clog2(D+1):1)(input wire clk,output reg done=0);  reg rst_n=0,wr_en=0,rd_en=0;  reg[3:0] din=0;  wire[3:0] dout;  wire full,empty;  wire[CW-1:0] count;  reg[3:0] queue[0:D-1];  reg[3:0] last_pop=0;  integer n,j,size=0,cmd;  reg take_read,take_write,overflow_seen=0,underflow_seen=0,replace_seen=0;  sync_fifo #(.DEPTH(D)) dut(.*);  initial begin    @(negedge clk);rst_n=1;    for(n=0;n<512;n=n+1) begin      @(negedge clk);      if(n<D) cmd=2;      else if(n==D) cmd=2;      else if(n==D+1) cmd=3;      else if(n<=2*D+1) cmd=1;      else if(n==2*D+2) cmd=1;      else if(n==2*D+3) cmd=3;      else if(n==2*D+4) cmd=1;      else cmd=(n*7+n/4)%4;      wr_en=cmd[1];rd_en=cmd[0];din=4'(n*5+n/7);      take_read=rd_en&&(size>0);      take_write=wr_en&&((size<D)||take_read);      if(wr_en&&size==D&&!take_read) overflow_seen=1;      if(rd_en&&size==0) underflow_seen=1;      if(size==D&&take_read&&take_write) replace_seen=1;      if(take_read) begin        last_pop=queue[0];        for(j=0;j<D-1;j=j+1) queue[j]=queue[j+1];        size=size-1;      end      if(take_write) begin queue[size]=din;size=size+1;end      @(posedge clk);#0.1;      if(count!==CW'(size)||dout!==last_pop||full!==(size==D)||empty!==(size==0))        $fatal(1,"FIFO mismatch D=%0d cycle=%0d count=%0d expected=%0d dout=%h expected_pop=%h",D,n,count,size,dout,last_pop);    end    if(!overflow_seen||!underflow_seen||!replace_seen) $fatal(1,"FIFO boundary coverage missing");    $display("FIFO CHECK depth=%0d cycles=512 overflow_seen=1 underflow_seen=1 replace_seen=1",D);    done=1;  endendmodulemodule tb_fifo;  reg clk=0;  wire d4,d3,d1;  always #1 clk=~clk;  fifo_trial #(.D(4)) a(clk,d4);  fifo_trial #(.D(3)) b(clk,d3);  fifo_trial #(.D(1)) c(clk,d1);  initial begin wait(d4&&d3&&d1);$display("FIFO ALL depths=4/3/1");$finish;end  initial begin #2000;$fatal(1,"FIFO watchdog");endendmodule

Đã kiểm chứng thế nào

  • Phân tích và elaborate theo LRM (pyslang 12.1.0)
  • Lint -Wall (Verilator 5.050)
  • Mô phỏng, exit 0 và output khớp kỳ vọng tính tay (Icarus Verilog 11.0, 4-state)
  • Tổng hợp, số cell khớp kỳ vọng (Yosys 0.70)

Output phải khớp: FIFO CHECK depth=4 cycles=512 overflow_seen=1 underflow_seen=1 replace_seen=1FIFO CHECK depth=3 cycles=512 overflow_seen=1 underflow_seen=1 replace_seen=1FIFO CHECK depth=1 cycles=512 overflow_seen=1 underflow_seen=1 replace_seen=1FIFO ALL depths=4/3/1

Kỳ vọng sau tổng hợp: sync_fifo: $_*DFF* = 27sync_fifo: $_*DLATCH* = 0

DEPTH>=1, không tự override PW/CW sai; synchronous registered output, không fall-through. Full+read+write thay thếoldhead; empty+both chỉwrite. Không claim mọi interleaving bằng formal hoặc vendor RAM mode.

Các kiểm tra chạy lúc xây dựng nội dung (2026-11-06), không chạy trong trình duyệt. Kỳ vọng được tính tay trước khi chạy.

TB có watchdog, kiểm đủ cờ boundary đã gặp, và queue oracle so dữ liệu từng cạnh. Depth 3 và 1 là biên thiết kế, không chỉ tăng số seed của depth 4. [3]

Tự kiểm tra

Tự kiểm tra: fifo

  1. FIFO full và cả read/write cùng request, contract bài này làm gì?
  2. FIFO empty và cả read/write cùng request, contract không bypass làm gì?
  3. Pointer depth 3 đang 2, accepted operation tiếp theo quay về đâu?
  4. Reference queue của TB khác DUT ring buffer ở điểm nào?
  5. Ở depth 1 full replacement, dout phải nhận gì?

Chốt chính sách biên trước khi kiểm throughput

Ghi rõ accepted operation, latency, output hold, capacity, reset và replacement. Một FIFO không mất dữ liệu phải đúng cả thứ tự và điều kiện nhận, không chỉ count cuối. Bài giữ draft/LRM needs-spec-check.

Nguồn và mức độ đối chiếu

  1. Synchronous FIFO — bản chụp trong resources.zip, Khái niệm sync FIFO. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. Thí nghiệm nguyên bản ex.a8.fifo, Phạm vi mô hình. Trích dẫn; nguồn đã đọc toàn văn.
  3. Thí nghiệm nguyên bản ex.a8.fifo, Bằng chứng. Trích dẫn; nguồn đã đọc toàn văn.
  4. IEEE 1800-2023 SystemVerilog LRM, fifo semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  5. Thí nghiệm nguyên bản ex.a8.fifo, Code, metadata, self-checks và verification.json. Trích dẫn; nguồn đã đọc toàn văn.
  6. IEEE 1800-2023 SystemVerilog LRM, fifo RTL/simulation semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  7. Synchronous FIFO — bản chụp trong resources.zip, What is a synchronous FIFO?; What are the main IO ports?; How does it work?. Trích dẫn; nguồn đã đọc các mục liên quan.
  8. Thí nghiệm nguyên bản ex.a8.fifo, Source, assertions, metadata và verification.json. Trích dẫn; nguồn đã đọc toàn văn.