클록 도메인 크로싱, 서로 다른 클록 사이로 신호를 건네는 법

클록 도메인 크로싱, 서로 다른 클록 사이로 신호를 건네는 법

서울 본사와 부산 지사가 서로 다른 시간표로 일한다고 해 보겠습니다. 한쪽이 서류를 건네는 순간과 다른 쪽이 그것을 집어 드는 순간이 맞아떨어진다는 보장이 없어서, 서류가 반쯤 바뀐 상태로 읽히는 사고가 생길 수 있습니다.

서로 다른 클록으로 움직이는 구역 사이에서 신호를 주고받는 문제가 바로 클록 도메인 크로싱(CDC, Clock Domain Crossing)입니다. 이 글에서는 1비트 신호부터 여러 비트 데이터, 비동기 FIFO까지 신호를 안전하게 건네는 방법과 검증이 필요한 이유를 정리합니다.

사무실에서는 받는 사람이 “방금 건넨 것 맞죠?”라고 되물을 수 있지만, 회로의 플립플롭은 값이 반쯤 바뀌었는지를 스스로 알아차리지 못한다는 점이 다릅니다. 그래서 구조 자체로 안전장치를 만들어야 합니다.

클록 도메인 크로싱이 문제가 되는 이유

클록 도메인(Clock domain)은 같은 클록, 또는 위상 관계가 알려진 클록으로 움직이는 회로 영역입니다. 한 도메인 안에서는 모든 플립플롭이 같은 에지에 맞춰 움직이므로, 정적 타이밍 분석(STA)이 셋업과 홀드를 수치로 보증할 수 있습니다.

두 클록의 주파수가 다르거나 위상 관계를 알 수 없으면 사정이 달라집니다. 신호가 수신 클록 에지에 얼마나 가까운 시점에 바뀔지 미리 알 수 없어서 셋업과 홀드를 보증할 수 없고, 이런 경로는 타이밍 분석에서 일반 경로와 따로 구분해 다룹니다.

타이밍을 보증할 수 없으니 구조로 안전을 확보해야 하고, 그러지 않았을 때 생기는 문제는 크게 세 가지입니다.

도메인을 건널 때 생기는 세 가지 문제

  • 준안정성: 수신 플립플롭이 바뀌는 중인 신호를 잡아 0도 1도 아닌 어중간한 전압이 한동안 남습니다.
  • 데이터 불일치: 여러 비트가 서로 다른 시점에 샘플링되어 한 번도 존재한 적 없는 값이 만들어집니다.
  • 데이터 손실: 빠른 클록에서 나온 짧은 펄스를 느린 클록이 한 번도 샘플링하지 못해 놓칩니다.

1비트 신호를 건네는 2-플립플롭 동기화기

1비트 신호를 건네는 기본 구조는 수신 도메인 클록으로 움직이는 플립플롭 두 개를 직렬로 이은 2-플립플롭 동기화기(Two-flop synchronizer)입니다. 첫 번째 플립플롭이 준안정 상태가 되더라도 한 클록 주기만큼 정착할 시간을 벌어, 두 번째 플립플롭이 안정한 값을 잡게 하는 방식입니다.

2-플립플롭 동기화기의 구조와 지켜야 할 점송신 도메인 플립플롭 출력 → 수신 클록 플립플롭 1 → 수신 클록 플립플롭 2 → 수신 로직

  • 입력은 송신 도메인의 플립플롭 출력이어야 합니다. 조합 논리를 거친 신호를 곧바로 넣으면 글리치가 샘플링될 수 있습니다.
  • 두 플립플롭 사이에는 논리를 두지 않고 가깝게 배치해 정착 시간을 최대한 보존합니다.
  • 첫 번째 플립플롭의 출력을 다른 곳에 따로 쓰지 않습니다.

정착 여유가 확률을 얼마나 줄이는지 감을 잡아 보겠습니다. 준안정 상태가 만드는 평균 고장 간격(MTBF)은 대략 정착 시간을 시간 상수 τ로 나눈 값의 지수 함수에 비례합니다.

가상 계산 예시, 정착 시간과 MTBFτ = 50 ps로 가정합니다. 정착에 쓸 수 있는 시간이 1 ns 늘어나면 MTBF는 e^(1,000 ps ÷ 50 ps) = e^20 ≈ 4.9 × 10^8배로 늘어납니다.

약 5억 배라는 숫자가 가리키는 것은 증가 폭의 규모이고, 실제 τ는 공정과 전압, 온도에 따라 다릅니다.

이 구조가 보증하지 않는 것도 있습니다. 값이 한 사이클 늦게 보이거나 옛 값이 한 번 더 보일 수 있으므로, 수신 회로는 “언제 바뀔지 모르는 신호”라는 전제로 설계해야 합니다.

빠른 클록에서 느린 클록으로 건널 때는 신호가 수신 클록 주기보다 짧게 유지되면 놓칠 수 있습니다. 펄스를 수신 클록 주기 이상 길게 유지하거나, 펄스마다 신호를 뒤집는 토글 방식으로 보낸 뒤 수신 쪽에서 변화를 감지해 펄스로 복원하는 방법이 쓰이고, 이때 이벤트 사이의 최소 간격이 수신 쪽이 확인할 시간보다 길어야 합니다.

여러 비트를 건널 때 생기는 데이터 불일치

클록 도메인 크로싱에서 여러 비트의 값을 건널 때는 비트마다 2-플립플롭을 다는 것만으로 안 됩니다. 각 비트의 경로 지연이 조금씩 달라서, 값이 바뀌는 순간에 샘플링하면 어떤 비트는 새 값, 어떤 비트는 옛 값으로 잡히기 때문입니다.

예시, 3비트 카운터가 3에서 4로 바뀔 때이진수로 011에서 100으로 세 비트가 모두 바뀝니다. 각 비트가 새 값과 옛 값 중 어느 쪽으로 잡힐지 독립적이므로 수신 쪽에는 8가지 조합이 모두 나타날 수 있습니다.

올바른 값은 옛 값인 011이나 새 값인 100 두 가지뿐이고, 나머지 6가지(000, 001, 010, 101, 110, 111)는 전혀 다른 숫자로 읽힙니다.

해결책은 크게 세 갈래입니다. 데이터는 안정하게 두고 “유효하다”는 제어 신호 1비트만 동기화하는 방식, 한 번에 한 비트만 바뀌는 코드를 쓰는 방식, 그리고 두 방식을 모두 구조에 녹인 비동기 FIFO입니다.

핸드셰이크로 데이터 안정 시간 확보하기

데이터를 안정시킨 채 제어 신호만 건네는 대표적인 방식이 핸드셰이크(Handshake)입니다. 요청(req)과 응답(ack)이 서로를 확인한 뒤에야 다음 단계로 넘어가는 네 단계로 이루어집니다.

4단계 핸드셰이크

  1. 송신 도메인이 데이터를 준비해 바뀌지 않게 붙들고 req를 1로 올립니다.
  2. 수신 도메인이 동기화한 req로 요청을 감지하면 데이터를 받아 두고 ack를 1로 올립니다.
  3. 송신 도메인이 동기화한 ack로 수신을 확인하면 req를 0으로 내립니다. 이제 데이터를 바꿔도 됩니다.
  4. 수신 도메인이 req가 0이 된 것을 확인하면 ack를 0으로 내리고, 다음 전송을 받을 준비를 마칩니다.

서로의 확인을 기다리므로 두 클록의 주파수 비율이나 위상과 상관없이 동작하고, 데이터 버스에는 동기화기가 필요 없습니다. 대신 한 번 전달하려면 제어 신호를 네 번 동기화해야 해서 속도가 느립니다.

가상 계산 예시, 핸드셰이크 한 번의 소요 시간송신 클록 100 MHz(10 ns), 수신 클록 50 MHz(20 ns)이고 동기화 한 번에 수신 쪽 클록 두 사이클이 걸린다고 가정합니다.

req 상승은 수신 클록 2사이클(40 ns), ack 상승은 송신 클록 2사이클(20 ns), req 하강은 40 ns, ack 하강은 20 ns이므로 합계 약 120 ns입니다. 한 워드를 120 ns마다 보내는 셈이어서 초당 약 830만 워드이고, 매 클록 한 워드를 보내는 100 MHz 흐름의 약 1/12 수준입니다.

위상 정렬에 따라 실제 값은 한두 사이클 달라질 수 있습니다. 이 방식은 설정값이나 상태 값처럼 가끔 넘기는 데이터에 맞고, 데이터가 계속 흘러야 한다면 FIFO가 맞습니다.

비동기 FIFO와 그레이 코드 포인터

연속 데이터를 건널 때는 비동기 FIFO가 표준적인 해법입니다. 쓰기 클록과 읽기 클록이 다른 이중 포트 메모리가 데이터를 담고, 쓰기 도메인의 쓰기 포인터와 읽기 도메인의 읽기 포인터가 위치를 가리킵니다.

데이터는 메모리에 머물러 있다가 읽는 쪽이 자기 클록으로 꺼내 가므로 데이터 버스를 동기화할 필요가 없습니다. 도메인을 건너야 하는 것은 상대 쪽 포인터 값뿐이고, 읽기 쪽은 쓰기 포인터를 알아야 “비었는지”를, 쓰기 쪽은 읽기 포인터를 알아야 “가득 찼는지”를 판단할 수 있습니다.

포인터는 여러 비트의 값이라 이진수로 보내면 앞에서 본 불일치가 생깁니다. 그래서 인접한 값 사이에 한 비트만 다른 그레이 코드(Gray code)로 바꾼 뒤 2-플립플롭 동기화기를 거칩니다.

3비트 이진수와 그레이 코드0: 000과 000, 1: 001과 001, 2: 010과 011, 3: 011과 010, 4: 100과 110, 5: 101과 111, 6: 110과 101, 7: 111과 100 (왼쪽이 이진수, 오른쪽이 그레이 코드)

3에서 4로 갈 때 이진수는 011에서 100으로 세 비트가 바뀌지만, 그레이 코드는 010에서 110으로 한 비트만 바뀝니다.

한 비트만 바뀌므로 샘플링 순간에 걸리더라도 옛 값이나 새 값 둘 중 하나가 잡히고, 둘 다 실제로 존재했던 포인터 값입니다. 포인터가 한 사이클 늦게 보일 뿐 엉뚱한 값은 나오지 않는 셈입니다.

포인터는 주소 비트보다 한 비트 더 갖습니다. 깊이가 8이면 포인터를 4비트로 두어, 쓰기 포인터가 읽기 포인터보다 한 바퀴 앞선 가득 찬 상태와 둘이 같은 빈 상태를 구분합니다. 그레이 코드 포인터에서는 두 포인터가 완전히 같으면 비어 있음, 상위 두 비트가 서로 반대이고 나머지가 같으면 가득 참으로 판단하며, 이 단순한 방식은 깊이가 2의 거듭제곱일 때 성립합니다.

동기화 때문에 상대 포인터가 늦게 보이므로 판단은 보수적입니다. 실제로는 데이터가 있는데 비었다고 보거나 자리가 있는데 가득 찼다고 보는 쪽으로만 틀리고, 데이터를 잃는 방향으로는 틀리지 않습니다. 처리량이 조금 손해 보는 대가로 안전을 얻는 구조입니다.

가상 계산 예시, FIFO 최소 깊이쓰기 클록 100 MHz, 읽기 클록 40 MHz로 가정합니다. 쓰기 쪽이 중간에 멈출 수 없이 80워드를 매 클록 연속으로 쓰고, 읽는 쪽은 매 클록 한 워드씩 읽을 수 있는 가장 유리한 경우로 봅니다.

버스트 시간은 80 × 10 ns = 800 ns이고 그동안 읽는 워드는 800 ns ÷ 25 ns = 32개이므로, 남는 데이터는 80 − 32 = 48워드입니다. 포인터 동기화 지연에 따른 여유를 더해 2의 거듭제곱인 64워드 정도로 잡을 수 있습니다.

CDC 검증이 따로 필요한 이유

일반적인 RTL 시뮬레이션은 값이 클록 에지에 정확히 맞춰 바뀌는 이상적인 모델이라 준안정성을 만들지 않습니다. 동기화기를 빼먹고 연결해도 시뮬레이션은 통과하는데, 실제 칩에서 드물게 오류가 나고 온도와 전압에 따라 달라져 재현조차 어렵습니다.

그래서 구조를 정적으로 검사합니다. 클록 도메인 크로싱이 일어나는 경로를 설계 전체에서 모두 찾아, 각 경로가 허용된 동기화 구조인지 확인하는 방식입니다.

CDC 검사가 걸러 내는 대표적인 패턴

  • 동기화기 없이 도메인을 건너는 경로
  • 조합 논리를 거친 뒤 동기화기에 들어가는 경로
  • 여러 비트 버스가 비트마다 따로 동기화되는 경로
  • 서로 다른 동기화기를 지난 신호가 수신 도메인에서 다시 합쳐지는 재수렴(Reconvergence), 각 신호가 다른 사이클에 정착하면 서로 어긋난 값이 만들어집니다.

구조가 맞아도 물리 설계에서 확인할 것이 남습니다. 그레이 코드 포인터의 각 비트가 한 클록 주기보다 크게 어긋나지 않도록 경로 지연 차이에 제약을 걸어, 배선 때문에 한 비트만 늦게 도착하는 사고를 막습니다.

동기화기의 지연을 임의로 흔들어 보는 시뮬레이션도 보조 수단으로 쓰입니다. 클록 신호뿐 아니라 리셋 신호도 도메인을 넘나드는 신호라서 이에 맞는 구조가 따로 필요합니다.

CDC 기법 비교 표

구분2-플립플롭 동기화기핸드셰이크비동기 FIFO
대상1비트 제어 신호, 레벨 신호다중 비트 값, 가끔 보내는 데이터연속적인 데이터 스트림
안전 확보 방식정착 시간을 한 주기 더 확보데이터를 안정시키고 제어 신호만 동기화그레이 코드 포인터와 메모리
지연과 처리량수신 클록 2~3사이클 지연동기화 네 번, 처리량 낮음포인터 동기화 지연, 처리량 높음
비용플립플롭 두 개제어용 상태 기계와 신호메모리와 포인터 로직, 면적 큼
주의할 점짧은 펄스 손실, 다중 비트에 사용 금지한 번에 한 워드, 느림깊이 계산, 그레이 코드 성립 조건

핵심 정리

클록 도메인 크로싱의 핵심은 타이밍을 보증할 수 없는 경계에서 구조로 안전을 만드는 것입니다. 1비트 신호는 2-플립플롭 동기화기로, 가끔 넘기는 다중 비트 값은 핸드셰이크로, 연속 데이터는 그레이 코드 포인터를 쓰는 비동기 FIFO로 건넵니다.

본사와 지사의 서류 비유로 돌아가면, 동기화기는 서류를 받는 접수 창구이고 비동기 FIFO는 시간표가 다른 두 부서가 각자 때에 맞춰 넣고 꺼내는 무인 보관함에 가깝습니다. 비유와 달리 칩에서는 “서류가 반쯤 바뀌었는지”를 창구에서 확인할 수 없으므로 구조와 정적 검사가 안전을 지키는 거의 유일한 방법입니다.

면접 대비 질문

면접 대비 질문서로 비동기인 두 클록 도메인 사이에서 8비트 데이터를 전달하는 방법을 두 가지 이상 들고 장단점을 비교한 뒤, 비동기 FIFO가 포인터를 그레이 코드로 보내는 이유를 설명해 보세요.

[답변 포인트] 비트마다 2-플립플롭을 달면 경로 지연 차이로 일부는 새 값, 일부는 옛 값이 샘플링되어 불일치가 생긴다는 점을 먼저 짚습니다. 해법으로 데이터를 안정시킨 채 제어 신호만 동기화하는 핸드셰이크(안전하고 단순하지만 처리량이 낮음)와 메모리와 포인터로 연속 전송을 소화하는 비동기 FIFO(처리량이 높지만 면적과 깊이 설계 부담)를 비교합니다. 그레이 코드는 인접한 값이 한 비트만 달라서 샘플링 순간에 걸려도 옛 값이나 새 값 중 하나만 읽히고, 그 판단이 보수적일 뿐 데이터를 잃는 방향으로는 틀리지 않는다고 설명하면 좋습니다. CDC 정적 검사와 포인터 비트 간 지연 제약으로 마무리합니다.