CDC: đồng bộ level, giữ payload và hiểu giới hạn của Gray

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

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

  • Phân biệt level ổn định với pulse có thể mất khi qua2FF
  • Dùng request/acknowledge và payload giữ ổn định cho một transfer outstanding
  • Suy luận Gray pointer/full-empty của async FIFO mà không nhầm với proof analog
  • Nhận diện reset, reconvergence và physical constraints chưa được simulation chứng minh

Đổi clock không chỉ là thêm một always

Một producer và consumer khác clock không có cùng thời điểm lấy mẫu. Digital simulation hữu ích để kiểm protocol trao đổi, nhưng không tạo mô hình analog metastability hay con số MTBF có ý nghĩa vật lý. Bài dùng hai clock khác chu kỳ/pha, và chỉ claim những quan sát chức năng đã kiểm.

Hai tầng FF cho level, không bảo đảm mọi event

Tầng đầu lấy asynchronous level; tầng sau cho thời gian giải quyết trước khi dùng trong logic đích. Không dùng output tầng đầu cho nhiều nhánh logic rồi gọi tất cả đã synchronized. Thuộc tính async_reg trong source là hint cho flow, không tự chứng nhận placement hoặc timing. Số tầng và margin phải theo library/clock/reliability target. [1]

Pulse ngắn có thể hoàn toàn không được thấy

Destination có posedge 6,16,26,... Sau reset release, pulse 38..39 nằm giữa 36 và 46 nên không được lấy mẫu; checker 60 ns thấy 0. Level bắt đầu 61 được lấy qua hai tầng ở 66/76, checker 77 thấy 1. Không có metastability trong phép thử này; pulse mất chỉ vì không có cạnh sample. Vì vậy 2 FF level không tự là event counter.

Toggle handshake giữ dữ liệu tới khi consumer xác nhận

Source chỉ nhận send khi busy 0, chốt payload rồi đổi request toggle. Destination đồng bộ request hai tầng, khi thấy request khác ack thì capture payload, phát valid một chu kỳ và cập nhật ack toggle. Ack qua hai tầng về source; chỉ khi bằng request source mới được thay held data. TB cố ý thay input và gửi thêm lúc busy để kiểm payload đã giữ không bị phá. [2]

Reset và reconvergence là phần của contract

Mỗi domain có reset-release hai tầng để deassert theo clock local; thử nghiệm chỉ kiểm coordinated startup trước truyền. Reset một domain riêng lúc có outstanding có thể làm token/ack mất đồng bộ và cần protocol recovery riêng, chưa được chứng minh ở đây. Đồng bộ từng bit của bus tùy ý không bảo đảm word coherent; hai bản sao cùng control được đồng bộ riêng rồi hội tụ cũng cần phân tích, không chỉ nhìn waveform nhị phân đẹp.

Gray chỉ có một bit đổi giữa hai giá trị kề

Với binary 3 bit, Gray là b XOR (b>>1), chuỗi vòng kết thúc 100 rồi 000; mỗi bước kề đổi một bit. Điều đó không nói destination luôn cách current source đúng±1 khi có thể bỏ qua nhiều lần tăng. Gray nên được tạo/đăng ký ở source và chịu kiểm tra skew/path constraint thích hợp; biểu thức XOR tổ hợp từ binary đang đổi nhiều bit có thể có glitch vật lý.

Async FIFO chuyển pointer, không đồng bộ từng bit payload độc lập

Kiến trúc có bộ nhớ, pointer read/write do từng domain sở hữu và Gray pointer qua synchronizer sang phía còn lại. Empty so read Gray với write Gray đã đồng bộ. Với depth 4/pointer 3 bit, full tương ứng đảo hai bit cao của read Gray đã đồng bộ rồi so write Gray; TB kiểm đủ 64 cặp binary-distance 4 cho công thức. Flag có thể bảo thủ do độ trễ pointer sync. Đây là kiểm toán học comparator, không có complete async FIFO implementation hoặc static CDC sign-off trong example. [3]

Pulse, mailbox 16 transfer và 64 cặp Gray

Kỳ vọng trước chạy: pulse ngắn mất, level giữ được thấy;16 send được nhận theo thứ tự n XOR 9,16 send khi busy bị bỏ, cuối 6. Gray 8 state/64 pair và wrap 100→000 phải đúng.

Thí nghiệm cdc

Trước chạy:dstposedge6+10 n; resetrelease 26; pulse 38..39 mất, level 61 thấy qua 2 FFt 76/check 77. Mailbox 16 nibble n^9,16 requestbusy bịbỏ, cuối 6. Gray 8 states/64 binarypairfull-distance 4, wrap 100->000 đổi 1 bit.

cdc_models.sv RTL
`timescale 1ns/1psmodule reset_release(input wire clk,arst_n,output wire rst_n);  (* async_reg="true" *) reg[1:0] pipe;  always @(posedge clk or negedge arst_n)    if(!arst_n) pipe<=0; else pipe<={pipe[0],1'b1};  assign rst_n=pipe[1];endmodulemodule bit_sync(input wire clk,rst_n,d,output wire q);  (* async_reg="true" *) reg first,second;  always @(posedge clk or negedge rst_n)    if(!rst_n) begin first<=0;second<=0;end    else begin first<=d;second<=first;end  assign q=second;endmodulemodule toggle_mailbox(input wire src_clk,dst_clk,src_rst_n,dst_rst_n,send, input wire[3:0] data,output wire busy,output reg valid,output reg[3:0] received);  reg request,ack;  reg[3:0] held;  (* async_reg="true" *) reg ack_first,ack_second,req_first,req_second;  assign busy=request!=ack_second;  always @(posedge src_clk or negedge src_rst_n) begin    if(!src_rst_n) begin request<=0;held<=0;ack_first<=0;ack_second<=0;end    else begin      ack_first<=ack;ack_second<=ack_first;      if(send&&!busy) begin held<=data;request<=~request;end    end  end  always @(posedge dst_clk or negedge dst_rst_n) begin    if(!dst_rst_n) begin req_first<=0;req_second<=0;ack<=0;valid<=0;received<=0;end    else begin      req_first<=request;req_second<=req_first;      valid<=0;      if(req_second!=ack) begin        received<=held;valid<=1;ack<=req_second;      end    end  endendmodule
tb_cdc.sv testbench
`timescale 1ns/1psmodule tb_cdc;  reg src_clk=0,dst_clk=0,arst_n=0,send=0,pulse=0;  reg[3:0] data=0;  wire src_rst_n,dst_rst_n,busy,valid,synced;  wire[3:0] received;  reg[3:0] expected[0:15];  reg last_valid=0;  integer accepted=0,consumed=0,ignored=0,n,w,r,pairs=0,diff;  reg[2:0] gw,gr,full_target;  reset_release sr(src_clk,arst_n,src_rst_n);  reset_release dr(dst_clk,arst_n,dst_rst_n);  bit_sync level_sync(dst_clk,dst_rst_n,pulse,synced);  toggle_mailbox mailbox(src_clk,dst_clk,src_rst_n,dst_rst_n,send,data,busy,valid,received);  always #3 src_clk=~src_clk;  initial begin #1;forever #5 dst_clk=~dst_clk;end // Destination posedges6,16,26,...; source3,9,15,...  initial begin #14;arst_n=1;end  function automatic reg[2:0] gray(input reg[2:0] binary);    gray=binary^(binary>>1);  endfunction  initial begin    for(w=0;w<8;w=w+1) begin      gw=gray(3'(w));diff=int'(gw^gray(3'((w+1)%8)));      if(diff==0||(diff&(diff-1))!=0) $fatal(1,"gray adjacency mismatch");      for(r=0;r<8;r=r+1) begin        gr=gray(3'(r));full_target={~gr[2:1],gr[0]};        if((gw==full_target)!==(((w-r+8)%8)==4)) $fatal(1,"gray full-comparison mismatch");        pairs=pairs+1;      end    end    if(pairs!=64||gray(7)!==3'b100||gray(0)!==0) $fatal(1,"gray coverage mismatch");    $display("GRAY CHECK states=8 pairs=64 wrap_last=100 next=000");  end  // Short pulse lies strictly between destination edges36 and46 after reset release.  initial begin    #38;pulse=1;#1;pulse=0;#21;    if(synced!==0) $fatal(1,"short pulse unexpectedly captured");    $display("CDC PULSE short_lost=1 at=60");    #1;pulse=1;#6;    if(synced!==0) $fatal(1,"level crossed before second stage");    #10;    if(synced!==1) $fatal(1,"held level not synchronized");    $display("CDC LEVEL held_seen=1 at=77");    #23;pulse=0;  end  always @(posedge src_clk) if(src_rst_n) begin    if(send&&!busy) begin      if(accepted>=16) $fatal(1,"unexpected source acceptance");      expected[accepted]=data;accepted=accepted+1;    end else if(send&&busy) ignored=ignored+1;  end  always @(posedge dst_clk) begin    #0.1;    if(dst_rst_n&&valid) begin      if(last_valid||consumed>=accepted||received!==expected[consumed]) $fatal(1,"mailbox duplicate/order/data mismatch");      consumed=consumed+1;    end    last_valid=valid;  end  initial begin    #22;    for(n=0;n<16;n=n+1) begin      wait(src_rst_n&&dst_rst_n&&!busy);      @(negedge src_clk);send=1;data=4'(n^9);      @(posedge src_clk);#0.1;      if(!busy) $fatal(1,"request not made busy");      @(negedge src_clk);data=~data;send=1; // Deliberately rejected while busy; held payload must stay unchanged.      @(negedge src_clk);send=0;      wait(!busy);    end    #1;    if(accepted!=16||consumed!=16||ignored!=16||received!==6||busy!==0) $fatal(1,"mailbox coverage mismatch");    $display("CDC MAILBOX accepted=%0d received=%0d ignored_busy=%0d last=%h",accepted,consumed,ignored,received);    $finish;  end  initial begin #5000;$fatal(1,"CDC 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: GRAY CHECK states=8 pairs=64 wrap_last=100 next=000CDC PULSE short_lost=1 at=60CDC LEVEL held_seen=1 at=77CDC MAILBOX accepted=16 received=16 ignored_busy=16 last=6

Kỳ vọng sau tổng hợp: reset_release: $_*DFF* = 2reset_release: $_*DLATCH* = 0bit_sync: $_*DFF* = 2bit_sync: $_*DLATCH* = 0toggle_mailbox: $_*DFF* = 15toggle_mailbox: $_*DLATCH* = 0

Chỉ functional digital model, không mô phỏng analog metastability/MTBF hoặc chứng nhận static CDC/physical constraints.

Mailbox một transfer outstanding và coordinated startup reset; không chứng minh reset riêng giữa transfer hoặc mọi clock ratio/phase. Async FIFO chỉ kiểm công thức Gray/comparator, không có complete dual-clock FIFO implementation trong example này.

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.

Clock test được chọn để các cạnh producer/consumer không trùng, oracle ghi nhận acceptance rồi so mọi valid/payload và bắt duplicate. Generic synthesis kiểm 2 FF cho reset release,2 FF level và 15 FF mailbox, không latch. Không suy ra metastability probability, bundled-data timing, arbitrary reset hoặc mọi clock ratio đều đã safe. [4]

Tự kiểm tra

Tự kiểm tra: cdc

  1. Một pulse nằm hoàn toàn giữa hai cạnh destination qua 2 FF có kết quả nào?
  2. TB kiểm công thức full-distance 4 trên bao nhiêu cặp pointer 3 bit?
  3. Mailbox source được thay held payload khi nào theo contract?
  4. Digital tests và cell checks có chứng minh MTBF hoặc analog metastability không?
  5. Example CDC đã có implementation complete async FIFO được sign-off chưa?

Chọn cơ chế theo thông tin cần chuyển

Level, pulse, payload coherent và dòng dữ liệu cần các contract khác nhau. Dùng 2 FF, handshake hoặc async FIFO theo yêu cầu; sau digital tests vẫn cần kiểm CDC topology, constraints, reset và physical implementation. Bài giữ draft/LRM needs-spec-check, không dùng một số MTBF từ tutorial làm bảo đảm cho chip.

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

  1. Clock Domain Crossing — bản chụp trong resources.zip, 2FF pattern. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. Clock Domain Crossing — bản chụp trong resources.zip, Handshake data stability. Trích dẫn; nguồn đã đọc các mục liên quan.
  3. Clock Domain Crossing — bản chụp trong resources.zip, Async FIFO architecture. Trích dẫn; nguồn đã đọc các mục liên quan.
  4. Thí nghiệm nguyên bản ex.a8.cdc, Bằng chứng. Trích dẫn; nguồn đã đọc toàn văn.
  5. IEEE 1800-2023 SystemVerilog LRM, cdc semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  6. Thí nghiệm nguyên bản ex.a8.cdc, Code, metadata, self-checks và verification.json. Trích dẫn; nguồn đã đọc toàn văn.
  7. IEEE 1800-2023 SystemVerilog LRM, cdc RTL/simulation semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  8. Clock Domain Crossing — bản chụp trong resources.zip, Two-Flip-Flop Synchronizer; Gray Code for Counters; Handshake Protocols; Dual-Clock FIFO Architecture; Key FIFO CDC Concepts. Trích dẫn; nguồn đã đọc các mục liên quan.
  9. Thí nghiệm nguyên bản ex.a8.cdc, Source, assertions, metadata và verification.json. Trích dẫn; nguồn đã đọc toàn văn.