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_qとack_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
ソース側の動作:
- データ受け入れ:
valid_i && ready_oが有効な場合、req_src_qをトグルし、data_src_qにデータを格納する - ACK同期化:
async_ack_iを2段の同期化レジスタで同期化し、ack_qに格納する - 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
デスティネーション側の動作:
- REQ同期化:
async_req_iを3段の同期化レジスタで同期化する(req_dst_q,req_q0,req_q1) - データサンプリング:
req_q0 != req_q1の場合(エッジ検出)、新しいデータが到着したことを意味し、data_dst_qにデータを格納する - ACK生成: データが受け入れられた場合、
ack_dst_qをトグルする - valid信号生成:
ack_dst_q != req_q1の場合、有効なデータが存在する