以下の内容はhttps://msyksphinz.hatenablog.com/entry/2025/12/06/040000より取得しました。


Common CellsのCDC実装詳細解析 (2. cdc_2phaseの実装)

cdc_2phase.svは、2相ハンドシェイクプロトコルを使用してクロックドメイン間でデータを転送するモジュールである。2相ハンドシェイクでは、各トランザクションごとにreq信号とack信号がトグルする。この方式は、4相ハンドシェイクと比較して効率的であるが、リセット時の動作に注意が必要である。

パラメータ定義

module cdc_2phase #(
  parameter type T = logic)(
  input  logic src_rst_ni,
  input  logic src_clk_i,
  input  T     src_data_i,
  input  logic src_valid_i,
  output logic src_ready_o,
  input  logic dst_rst_ni,
  input  logic dst_clk_i,
  output T     dst_data_o,
  output logic dst_valid_o,
  input  logic dst_ready_i
);
  • T: 転送するデータ型
  • src_*: ソースクロックドメイン側の信号
  • dst_*: デスティネーションクロックドメイン側の信号

2相ハンドシェイクの動作原理

2相ハンドシェイクでは、req信号とack信号がトグルする。req_src_qack_qが一致している場合、新しいデータを受け入れる準備ができていることを意味する。

ソース側の実装

module cdc_2phase_src #(
  parameter type T = logic)(
  input  logic rst_ni,
  input  logic clk_i,
  input  T     data_i,
  input  logic valid_i,
  output logic ready_o,
  output logic async_req_o,
  input  logic async_ack_i,
  output T     async_data_o
);
  (* dont_touch = "true" *)
  logic req_src_q, ack_src_q, ack_q;
  (* dont_touch = "true" *)
  T data_src_q;
  
  // The req_src and data_src registers change when a new data item is accepted.  
  always_ff @(posedge clk_i or negedge rst_ni) begin
      if (!rst_ni) begin
          req_src_q  <= 1'b0;
      data_src_q <= T'('0);
    end else if (valid_i && ready_o) begin
        req_src_q  <= ~req_src_q;
      data_src_q <= data_i;
    end
  end
  
  // The ack_src and ack registers act as synchronization stages.
  always_ff @(posedge clk_i or negedge rst_ni) begin
      if (!rst_ni) begin
          ack_src_q <= 1'b0;
      ack_q     <= 1'b0;
    end else begin
        ack_src_q <= async_ack_i;
      ack_q     <= ack_src_q;
    end
  end
  
  // Output assignments.
  assign ready_o = (req_src_q == ack_q);
  assign async_req_o = req_src_q;
  assign async_data_o = data_src_q;
endmodule

ソース側の動作:

  1. データ受け入れ: valid_i && ready_oが有効な場合、req_src_qをトグルし、data_src_qにデータを格納する
  2. ACK同期化: async_ack_iを2段の同期化レジスタで同期化し、ack_qに格納する
  3. ready信号生成: req_src_q == ack_qの場合、新しいデータを受け入れる準備ができている

デスティネーション側の実装

module cdc_2phase_dst #(
  parameter type T = logic)(
  input  logic rst_ni,
  input  logic clk_i,
  output T     data_o,
  output logic valid_o,
  input  logic ready_i,
  input  logic async_req_i,
  output logic async_ack_o,
  input  T     async_data_i
);
  (* dont_touch = "true" *)
  (* async_reg = "true" *)
  logic req_dst_q, req_q0, req_q1, ack_dst_q;
  (* dont_touch = "true" *)
  T data_dst_q;
  // The ack_dst register changes when a new data item is accepted.
  always_ff @(posedge clk_i or negedge rst_ni) begin
      if (!rst_ni) begin
          ack_dst_q  <= 0;
    end else if (valid_o && ready_i) begin
        ack_dst_q  <= ~ack_dst_q;
    end
  end
  
  // The data_dst register changes when a new data item is presented. This is
  // indicated by the async_req line changing levels.
  always_ff @(posedge clk_i or negedge rst_ni) begin
      if (!rst_ni) begin
          data_dst_q <= T'('0);
    end else if (req_q0 != req_q1 && !valid_o) begin
        data_dst_q <= async_data_i;
    end
  end
  
  // The req_dst and req registers act as synchronization stages.
  always_ff @(posedge clk_i or negedge rst_ni) begin
      if (!rst_ni) begin
          req_dst_q <= 0;
      req_q0    <= 0;
      req_q1    <= 0;
    end else begin
        req_dst_q <= async_req_i;
      req_q0    <= req_dst_q;
      req_q1    <= req_q0;
    end
  end
  
  // Output assignments.
  assign valid_o = (ack_dst_q != req_q1);
  assign data_o = data_dst_q;
  assign async_ack_o = ack_dst_q;

endmodule

デスティネーション側の動作:

  1. REQ同期化: async_req_iを3段の同期化レジスタで同期化する(req_dst_q, req_q0, req_q1
  2. データサンプリング: req_q0 != req_q1の場合(エッジ検出)、新しいデータが到着したことを意味し、data_dst_qにデータを格納する
  3. ACK生成: データが受け入れられた場合、ack_dst_qをトグルする
  4. valid信号生成: ack_dst_q != req_q1の場合、有効なデータが存在する



以上の内容はhttps://msyksphinz.hatenablog.com/entry/2025/12/06/040000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14