---
title: "4-phase handshake가 굳이 0으로 돌아가는 이유"
author: "브레이브브라우저쓰자"
published_at: "2026-10-09T07:30:04.915Z"
updated_at: "2026-10-05T23:49:01.928Z"
tags: ["4-phase handshake"]
canonical_url: "https://ariser.io/@writer_a9817d5a8de0/4-phase-handshake%EA%B0%80-%EA%B5%B3%EC%9D%B4-0%EC%9C%BC%EB%A1%9C-%EB%8F%8C%EC%95%84%EA%B0%80%EB%8A%94-%EC%9D%B4%EC%9C%A0"
ai_assisted: true
---

# 4-phase handshake가 굳이 0으로 돌아가는 이유

이 글은 AI가 활용됐습니다.

[원문](https://ariser.io/@writer_a9817d5a8de0/4-phase-handshake%EA%B0%80-%EA%B5%B3%EC%9D%B4-0%EC%9C%BC%EB%A1%9C-%EB%8F%8C%EC%95%84%EA%B0%80%EB%8A%94-%EC%9D%B4%EC%9C%A0)

요청을 올렸고 상대도 확인했다. 이미 일이 끝났는데 왜 요청과 확인을 다시 내려야 할까. **4-phase handshake는 요청 제시, 확인, 요청 해제, 확인 해제의 네 상태 변화를 거쳐 다음 거래를 준비하는 방식**이다. 네 번의 변화는 낭비처럼 보일 수 있지만, 매 거래가 같은 초기 상태로 돌아온다는 명확한 경계를 만든다.

여기서는 서로 다른 클록 도메인 사이에서 데이터를 한 항목씩 전달한다고 하자. 요청 req와 확인 ack는 각 방향의 동기화 회로를 거친다. 원천은 현재 항목을 보관하고, 목적지는 그 항목의 책임을 안전하게 받은 뒤 ack를 올린다. 정확한 ack 시점은 내부 버퍼가 있는지에 따라 달라질 수 있다.

## 00에서 다시 00으로 돌아오는 순서

상태를 원천의 req와 목적지의 ack 수준으로 적어 보자. 각 도메인에서 보이는 동기화 값은 이보다 늦을 수 있다.

| 단계 | req | ack | 의미 |
| --- | --- | --- | --- |
| 준비 | 0 | 0 | 새 거래를 시작할 수 있음 |
| 요청 | 1 | 0 | 원천이 항목을 제시함 |
| 확인 | 1 | 1 | 목적지가 항목의 책임을 받음 |
| 요청 해제 | 0 | 1 | 원천이 확인을 보고 요청을 내림 |
| 확인 해제 | 0 | 0 | 목적지가 요청 해제를 보고 확인을 내림 |

원천은 ack=1을 보았다고 바로 다음 req를 올리지 않는다. 먼저 req를 0으로 만들고, ack도 0으로 돌아온 것을 확인해야 다음 요청을 시작한다. 그렇지 않으면 목적지가 여전히 이전 요청을 처리하는 상태에서 새 항목이 들어와 두 거래의 경계가 사라질 수 있다.

목적지도 req=1인 동안 같은 항목을 매 클록 다시 소비하지 않는다. 한 번 데이터를 받았다면 요청이 내려갈 때까지 확인 상태를 유지한다. 에지 하나의 검출에만 기대기보다 현재 handshake의 어느 단계인지를 상태기계로 기억한다.

[PULP의 4-phase CDC 구현](https://github.com/pulp-platform/common_cells/blob/master/src/cc_cdc_4phase.sv)의 원천은 확인의 상승과 하강을 기다리는 상태를 구분하고, 목적지는 입력 요청과 하류 수락, 요청 해제를 구분한다. 네 변화가 실제로 어떤 상태에 대응되는지 볼 수 있는 사례다.

## 왕복 두 번이 처리율에 남기는 것

동기화 체인을 통과할 때마다 수신 클록의 여러 에지가 필요하다. req의 상승이 목적지로 가고 ack의 상승이 돌아오는 것만으로도 왕복 시간이 든다. 4-phase에서는 req의 하강과 ack의 하강도 다시 전달되어야 한다.

가상의 단순 시간 모델에서 각 방향의 전달·관측에 20ns가 걸리고 다른 대기는 없다고 하자. 네 변화에 순서대로 그 시간이 필요하다면 00으로 돌아오는 데 약 80ns가 걸린다. 이 경우 새 거래를 시작하는 최대 빈도는 대략 1/80ns, 1250만 번/초다. 실제 구현의 사이클 수나 성능을 나타내는 수치가 아니다. 상태 갱신 지연과 클록 위상에 따라 추가 시간이 생길 수 있다.

목적지 소비자가 100ns 기다리면 확인 상승도 늦어진다. 동기화가 잘 되어 있다는 사실만으로 처리 시간 상한이 생기지는 않는다. 반대로 원천과 목적지 주파수가 다르면 느린 도메인의 관측 단계가 왕복 시간을 지배할 수 있다. 클록 비율과 소비자의 진행 조건을 함께 보아야 한다.

요청과 확인을 반전시키는 방식은 매 거래마다 0으로 복귀하는 단계를 생략할 수 있다. 그러나 4-phase의 주된 설명은 그 비교보다, **다음 거래를 시작하기 전에 같은 빈 상태를 서로 확인한다**는 계약이다. 필요한 처리율과 reset 조건을 기준으로 선택해야 한다.

## 입력 수락과 상대 확인은 같지 않을 수 있다

원천에 내부 저장이 있으면, 상류 항목을 먼저 수락하여 보관한 뒤 handshake를 진행할 수 있다. 상류는 빨리 다음 준비를 할 수 있지만 현재 항목의 전달은 여전히 왕복을 기다린다. 내부 저장이 꽉 차면 새 수락이 멈춘다.

상류 수락을 상대의 전체 handshake 완료까지 늦추는 방식도 있다. PULP 원문의 Decoupled 옵션은 이 경계를 구분한다. 비분리 모드에서는 원천 항목의 수락을 handshake의 끝에 연결한다. 분리 모드의 조기 수락과 실제 상대 완료를 같은 사건으로 해석하면 자원 회계가 틀어질 수 있다.

출력 쪽 버퍼도 ack의 의미를 바꾼다. 내부 버퍼가 payload를 안전하게 보관했으면 외부 소비자가 아직 기다려도 원천에 확인할 수 있다. 버퍼가 없으면 외부의 실제 수락까지 payload를 유지해야 한다. “ack=1이면 소비자가 사용했다”는 해석은 구현을 읽지 않고 할 수 없는 결론이다.

## 초기 상태가 명확하다고 reset이 공짜가 되지는 않는다

4-phase는 정상 거래가 항상 req=ack=0으로 돌아온다. 거래마다 Idle 수준이 번갈아 바뀌는 방식과 비교하면 초기 빈 상태의 의미가 분명하다. 원문의 설명도 이 성질이 한쪽 reset 문제를 줄이는 데 도움이 된다고 밝힌다.

그러나 진행 중인 req=1을 reset으로 내리는 일이 정상적인 요청 해제와 언제 같은 의미인지, 목적지가 이미 보관한 항목은 어떻게 처리되는지 여전히 정해야 한다. one-sided reset에 유리하다는 설명을 모든 거래의 정확히 한 번 전달 보장으로 확대해서는 안 된다. 원천과 목적지 상태기계, 버퍼 및 주변 회로의 reset 계약이 필요하다.

또 클록이 멈추면 동기화와 상태 전이가 진행되지 않을 수 있다. req가 높은 채로 남아 있다고 틀린 데이터가 되는 것은 아니지만, 제한 시간 안에 전달이 끝난다고 보장할 수도 없다. 특별한 reset 메시지 모드를 쓰는 구현은 그 조건을 따로 읽어야 한다.

데이터 경로의 지연도 잊지 말아야 한다. 요청을 몇 단계 동기화한다고 payload가 자동으로 먼저 도착하는 것은 아니다. PULP 원문은 비동기 데이터와 제어 경로에 max-delay 조건을 명시한다. 상태 전이가 논리적으로 맞는 것과 구현에서 데이터를 안전한 시점에 읽는 것은 함께 확인할 항목이다.

확인 뒤의 두 번의 해제는 이미 끝난 일을 다시 수행하는 것이 아니다. 다음 요청이 이전 요청과 섞이지 않도록 양쪽의 준비 상태를 다시 맞추는 과정이다. **4-phase handshake의 장점과 비용은 모두 이 복귀 경계에서 나온다.** 그 경계를 수락·보관·reset의 의미와 맞추면, 처리율의 대가를 지불하는 이유도 다음 거래가 안전하게 시작되는 조건도 분명해진다.

