https://github.com/pulp-platform/common_cells/blob/master/src/cdc_4phase.sv
基本設計方針
cdc_4phase.svは、4相ハンドシェイクプロトコルを使用してクロックドメイン間でデータを転送するモジュールである。
4相ハンドシェイクでは、req信号とack信号がアサート/デアサートのサイクルを繰り返す。
インタフェース定義
module cdc_4phase #( parameter type T = logic, parameter bit DECOUPLED = 1'b1, parameter bit SEND_RESET_MSG = 1'b0, parameter T RESET_MSG = T'('0) )( // ソース側の信号 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 );
パラメータ定義
DECOUPLED: デカップルモード(有効な場合、ハンドシェイク完了前に次のデータを受け入れる)SEND_RESET_MSG: リセット時にメッセージを送信するかどうかRESET_MSG: リセット時に送信するメッセージ
4相ハンドシェイクの動作原理
4相ハンドシェイクでは、以下の4つのフェーズを繰り返す:
- IDLE:
req = 0,ack = 0の状態。 - WAIT_ACK_ASSERT:
req = 1の状態。ack = 0を待つ。 - WAIT_ACK_DEASSERT:
req = 0ack = 1を待つ。 - IDLEに戻る:
req = 0,ack = 0
ソース側の実装
module cdc_4phase_src #( parameter type T = logic, parameter int unsigned SYNC_STAGES = 2, parameter bit DECOUPLED = 1'b1, parameter bit SEND_RESET_MSG = 1'b0, parameter T RESET_MSG = T'('0) )( 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_d, req_src_q; (* dont_touch = "true" *) T data_src_d, data_src_q; (* dont_touch = "true" *) logic ack_synced; typedef enum logic[1:0] {IDLE, WAIT_ACK_ASSERT, WAIT_ACK_DEASSERT} state_e; state_e state_d, state_q; // Synchronize the async ACK sync #( .STAGES(SYNC_STAGES) ) i_sync( .clk_i, .rst_ni, .serial_i( async_ack_i ), .serial_o( ack_synced ) ); // FSM for the 4-phase handshake always_comb begin state_d = state_q; req_src_d = 1'b0; data_src_d = data_src_q; ready_o = 1'b0; case (state_q) IDLE: begin // If decoupling is disabled, defer assertion of ready until the // handshake with the dst is completed if (DECOUPLED) begin ready_o = 1'b1; end else begin ready_o = 1'b0; end // Sample a new item when the valid signal is asserted. if (valid_i) begin data_src_d = data_i; req_src_d = 1'b1; state_d = WAIT_ACK_ASSERT; end end WAIT_ACK_ASSERT: begin req_src_d = 1'b1; if (ack_synced == 1'b1) begin req_src_d = 1'b0; state_d = WAIT_ACK_DEASSERT; end end WAIT_ACK_DEASSERT: begin if (ack_synced == 1'b0) begin state_d = IDLE; if (!DECOUPLED) begin ready_o = 1'b1; end end end default: begin state_d = IDLE; end endcase end
ソース側のFSMは、3つの状態を持つ:
- IDLE: 新しいデータを待つ
- WAIT_ACK_ASSERT:
ackがアサートされるのを待つ - WAIT_ACK_DEASSERT:
ackがデアサートされるのを待つ
DECOUPLEDモードが有効な場合、ハンドシェイクが完了する前に次のデータを受け入れることができる。
デスティネーション側の実装
module cdc_4phase_dst #( parameter type T = logic, parameter int unsigned SYNC_STAGES = 2, parameter bit DECOUPLED = 1)( 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" *) logic ack_dst_d, ack_dst_q; (* dont_touch = "true" *) logic req_synced; logic data_valid; logic output_ready; typedef enum logic[1:0] {IDLE, WAIT_DOWNSTREAM_ACK, WAIT_REQ_DEASSERT} state_e; state_e state_d, state_q; //Synchronize the request sync #( .STAGES(SYNC_STAGES) ) i_sync( .clk_i, .rst_ni, .serial_i( async_req_i ), .serial_o( req_synced ) ); // FSM for the 4-phase handshake always_comb begin state_d = state_q; data_valid = 1'b0; ack_dst_d = 1'b0; case (state_q) IDLE: begin // Sample the data upon a new request and transition to the next state if (req_synced == 1'b1) begin data_valid = 1'b1; if (output_ready == 1'b1) begin state_d = WAIT_REQ_DEASSERT; end else begin state_d = WAIT_DOWNSTREAM_ACK; end end end WAIT_DOWNSTREAM_ACK: begin data_valid = 1'b1; if (output_ready == 1'b1) begin state_d = WAIT_REQ_DEASSERT; ack_dst_d = 1'b1; end end WAIT_REQ_DEASSERT: begin ack_dst_d = 1'b1; if (req_synced == 1'b0) begin ack_dst_d = 1'b0; state_d = IDLE; end end default: begin state_d = IDLE; end endcase end
デスティネーション側のFSMは、3つの状態を持つ:
- IDLE: 新しいリクエストを待つ
- WAIT_DOWNSTREAM_ACK: 下流回路がデータを受け取るのを待つ
- WAIT_REQ_DEASSERT:
reqがデアサートされるのを待つ
DECOUPLEDモードが有効な場合、spill_registerを使用して出力をデカップルする。