2026. 4. 20. 14:10ㆍ기초지식
CDC(Clock Domain Crossing)란?
CDC는 서로 다른 클럭 도메인 사이에서 신호가 오가는 상황을 가리킨다. 현대 SoC는 CPU 코어, 메모리 컨트롤러, 페리페럴 버스, DSP 블록 등이 각자 다른 주파수·위상의 클럭으로 동작한다. 이때 도메인 A에서 생성된 신호를 도메인 B의 클럭으로 샘플링하면, 두 클럭이 동기화되지 않아 예측 불가한 동작이 발생한다. 이것이 CDC 문제다.
CDC는 시뮬레이션에서 거의 보이지 않는다는 점이 핵심 위험 요소다. 타이밍 위반은 게이트 레벨 시뮬레이션이나 실리콘 테스트에서야 드러나기 때문에, RTL 설계 단계에서 미리 검증 구조를 갖추지 않으면 테이프아웃 이후에 치명적인 버그로 돌아온다.
Metastability - CDC 문제의 근본 원인
플립플롭은 클럭 엣지 직전·직후의 일정 시간(setup time, hold time) 동안 입력이 안정적이어야 올바르게 동작한다. CDC 경로에서는 송신 도메인의 데이터가 수신 도메인의 setup/hold window를 침범할 수 있다. 이때 플립플롭이 논리적으로 0도 1도 아닌 중간 전압 레벨에 빠지는 상태를 Metastability라 한다.
메타스테이블 상태에 빠진 플립플롭은 일정 시간이 지나면 0 또는 1로 확정된다(resolve). 문제는 어느 쪽으로 확정될지 예측할 수 없고, 확정까지 걸리는 시간이 클럭 주기를 초과하면 다음 단의 로직까지 잘못된 값이 전파된다는 점이다. 이 현상을 MTBF(Mean Time Between Failures) 관점에서 수치화할 수 있으며, 동기화 체인을 추가할수록 MTBF가 기하급수적으로 늘어난다.
CDC 동기화 기법
1. 2-Flop Synchronizer (단일 비트)
가장 기본적인 CDC 처리 방법이다. 수신 도메인의 클럭으로 구동되는 플립플롭 2개를 직렬로 연결해, 메타스테이블 상태가 두 번째 플립플롭에 도달하기 전에 resolve되도록 시간 여유를 준다. 단일 비트 제어 신호(enable, valid, flag)에 적용한다.
// 2-Flop Synchronizer
module sync_2ff (
input logic clk_dst,
input logic rst_n,
input logic data_in,
output logic data_out
);
logic ff1, ff2;
always_ff @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
ff1 <= 1'b0;
ff2 <= 1'b0;
end else begin
ff1 <= data_in;
ff2 <= ff1;
end
end
assign data_out = ff2;
endmodule
합성 도구와 CDC 정적 분석 툴이 이 패턴을 인식할 수 있도록 두 플립플롭을 같은 셀에 배치하고, 물리 구현(P&R)에서 두 FF 사이의 배선 지연을 최소화해야 한다. Synopsys VC SpyGlass CDC, Mentor Questa CDC 등의 툴은 이 패턴이 제대로 삽입되었는지를 자동으로 체크한다.
2. Pulse Synchronizer
송신 도메인에서 짧은 폭의 펄스 신호(single-cycle pulse)를 수신 도메인으로 전달할 때 사용한다. 단순 2-FF 동기화기로는 펄스 자체가 수신 도메인에서 소실될 수 있기 때문에 Toggle 기반 구조를 사용한다.
// Pulse Synchronizer (Toggle-based)
module pulse_sync (
input logic clk_src, clk_dst, rst_n,
input logic pulse_in,
output logic pulse_out
);
logic toggle_src;
logic sync_ff1, sync_ff2, sync_ff2_d;
always_ff @(posedge clk_src or negedge rst_n) begin
if (!rst_n) toggle_src <= 1'b0;
else if (pulse_in) toggle_src <= ~toggle_src;
end
always_ff @(posedge clk_dst or negedge rst_n) begin
if (!rst_n) begin
sync_ff1 <= 1'b0; sync_ff2 <= 1'b0; sync_ff2_d <= 1'b0;
end else begin
sync_ff1 <= toggle_src;
sync_ff2 <= sync_ff1;
sync_ff2_d <= sync_ff2;
end
end
assign pulse_out = sync_ff2 ^ sync_ff2_d;
endmodule
3. Handshake Synchronizer (다중 비트 제어)
여러 비트의 제어 신호를 안전하게 넘길 때 사용한다. 송신 도메인이 req를 보내고, 수신 도메인이 ack를 돌려보내는 4-phase handshake 방식이다. req와 ack 각각에 2-FF 동기화기를 붙이고, 데이터는 req가 안정적으로 동기화된 이후에만 수신 도메인에서 래치한다.
// 4-Phase Handshake (단순화된 구조)
// 송신 도메인
always_ff @(posedge clk_src) begin
if (!busy_src) begin
data_latch <= data_out;
req_src <= 1'b1;
end
if (ack_sync) req_src <= 1'b0;
end
// 수신 도메인
always_ff @(posedge clk_dst) begin
if (req_sync && !ack_dst) begin
data_in_latch <= data_latch;
ack_dst <= 1'b1;
end
if (!req_sync) ack_dst <= 1'b0;
end
Handshake는 안전하지만 throughput이 낮다. req→sync→ack→sync 사이클이 완료되어야 다음 전송이 가능하기 때문에, 대역폭이 중요한 경로에는 적합하지 않다.
4. Async FIFO (다중 비트 데이터)
CDC를 거치는 데이터 버스에 가장 많이 쓰이는 구조다. 쓰기(write)와 읽기(read)가 각자의 클럭 도메인에서 독립적으로 동작하고, FIFO full/empty 판단을 Gray Code 포인터 + 2-FF 동기화기로 처리한다.
Gray Code를 쓰는 이유: Binary 포인터는 MSB~LSB가 동시에 바뀌는 경우가 있어(예: 0111 → 1000), 수신 도메인에서 중간 값을 샘플링하면 잘못된 포인터 값을 읽는다. Gray Code는 한 번에 1비트만 바뀌므로, 메타스테이블 상태가 발생해도 포인터 값의 오류가 최대 ±1로 제한된다.
// Binary to Gray Code 변환
function automatic logic [PTR_W-1:0] bin2gray;
input logic [PTR_W-1:0] bin;
bin2gray = bin ^ (bin >> 1);
endfunction
// Gray to Binary 변환
function automatic logic [PTR_W-1:0] gray2bin;
input logic [PTR_W-1:0] gray;
integer i;
gray2bin[PTR_W-1] = gray[PTR_W-1];
for (i = PTR_W-2; i >= 0; i--)
gray2bin[i] = gray2bin[i+1] ^ gray[i];
endfunction
Async FIFO 구현 시 full/empty 플래그가 보수적으로(conservative) 동작하는지, 동기화된 포인터로 FIFO 깊이를 계산할 때 최악의 케이스를 고려했는지 반드시 체크해야 한다.
CDC 위반 패턴 - 설계 시 피해야 할 구조
Multi-bit Signal without Synchronization
여러 비트를 동기화 없이 그냥 넘기는 건 가장 흔한 CDC 실수다. 각 비트가 서로 다른 클럭 엣지에서 샘플링되어 일관성(coherency)이 깨진다.
// 잘못된 예시 - 절대 금지
assign data_dst = data_src; // 다중 비트를 그대로 교차
// 올바른 방법: Handshake 또는 Async FIFO 사용
Combinational Logic on Synchronized Signal
2-FF 동기화기를 통과한 신호를 조합 로직에 넣고 다시 다른 도메인으로 보내면 동기화 효과가 사라진다. 동기화기 출력은 반드시 수신 도메인의 레지스터에서만 사용해야 한다.
Reconvergent Paths
같은 신호가 서로 다른 경로로 동기화되어 수신 도메인에서 다시 합쳐지는 구조다. 각 경로의 동기화 지연 차이로 인해 서로 다른 값이 동시에 참조되는 문제가 생긴다. CDC 정적 분석 툴에서 이 패턴을 명시적으로 체크해야 한다.
CDC 정적 분석 - 툴 활용
CDC 문제는 기능 시뮬레이션으로 잡기 어렵다. 클럭 주파수, 타이밍, 공정 변동에 따라 재현이 안 되는 경우가 대부분이다. RTL 단계에서 정적 CDC 분석을 반드시 수행한다.
| 툴 | 제조사 | 주요 기능 |
|---|---|---|
| VC SpyGlass CDC | Synopsys | 클럭 도메인 추출, 동기화기 누락 검출, 재수렴 경로 분석 |
| Questa CDC | Mentor/Siemens | Formal + Structural 분석, 동기화기 패턴 인식 |
| JasperGold CDC | Cadence | Formal 기반 CDC 검증, 커버리지 수집 |
툴 사용 흐름: SDC 또는 CDC constraint 파일로 클럭 소스·리셋을 정의 → 툴이 RTL을 분석해 클럭 도메인 자동 추출 → 동기화기 누락(unsynchronized CDC), 구조적 위반, 재수렴 경로 리포트 → false path 항목과 실제 수정 항목을 구분해 waiver 파일로 관리.
DV 관점에서의 CDC 검증 전략
Design Verification 엔지니어 입장에서 CDC는 단순히 RTL 설계 이슈가 아니다. TB에서 CDC 상황을 의도적으로 자극해야 하고, 기능 커버리지에 CDC 경계 통과 시나리오를 포함시켜야 한다.
Gate-level simulation with timing: RTL 레벨에서는 보이지 않는 CDC 오류가 GLS에서 X-propagation이나 타이밍 위반으로 드러난다. SDF(Standard Delay Format) 파일을 적용하고 X 전파 체크를 켜면 CDC 관련 버그를 일부 잡을 수 있다.
CDC-aware UVM 시나리오: 두 클럭 도메인이 서로 다른 주파수 비율(예: 1:3, 2:5)로 동작하는 환경을 TB에서 명시적으로 설정하고, 클럭 경계 직전에 데이터가 변화하는 코너 케이스를 랜덤으로 자극한다. UVM sequence에서 클럭 phase offset을 변화시키는 방식으로 커버리지를 높인다.
Formal CDC verification: JasperGold나 Questa Formal의 CDC 앱을 사용하면 RTL 레벨에서 수학적으로 CDC 경로가 올바르게 동기화되었는지 증명할 수 있다. 모든 클럭 관계와 동기화 구조를 모델링하고, property를 통해 데이터 일관성을 검증하는 방식이다.
정리
CDC는 멀티-클럭 SoC 설계에서 피할 수 없는 구조적 문제다. Metastability의 원리를 이해하고, 신호 종류에 맞는 동기화 기법(2-FF, Pulse Sync, Handshake, Async FIFO)을 선택하는 것이 핵심이다. 설계 단계에서 정적 CDC 분석을 수행하고, DV 단계에서 GLS와 Formal을 조합해 검증하는 체계를 갖춰야 테이프아웃 이후의 실리콘 버그를 막을 수 있다.