비트는 안정됐는데 값이 섞인다: bundled-data CDC의 약속
원천 도메인은 숫자 3 다음에 4를 보냈다. 목적지에서는 잠깐 7을 읽었다. 각 비트마다 플립플롭 두 개를 넣었는데도 가능한 일이다. bundled-data CDC는 여러 비트를 따로 동기화하는 대신, 데이터를 안정된 묶음으로 유지하고 그 묶음을 읽을 시점을 제어 신호로 전달하는 방식이다. bundled-data는 데이터 경로를 하나의 묶음으로 취급한다는 뜻이다.
다비트 값에서 필요한 것은 각 비트가 결국 0이나 1로 안정되는 것뿐 아니라, 모든 비트가 같은 거래를 대표하는 것이다. 목적지가 읽은 7이 정상적인 이진수여도 원천에서 보낸 값이 아니라면 전달은 실패했다.
비트마다 잘 넘어가도 전체 값은 섞인다
3비트 이진 값이 011에서 100으로 바뀌는 상황을 생각하자. 세 비트가 모두 바뀐다. 비트별 동기화에서 일부는 새 값을, 일부는 이전 값을 한 목적지 사이클 더 유지할 수 있다. 예를 들어 최상위 비트는 새 1을 보았는데 하위 두 비트는 이전 11을 보았다면 목적지는 111, 즉 7을 읽는다.
이것은 원천의 정상 값 3이나 4 중 하나를 늦게 읽은 상황과 다르다. 어느 시점에도 보내지 않은 조합이다. 비트별 동기화는 전체 버스에 대해 “동일한 원천 시점”이라는 관계를 만들지 않는다. 각 체인의 안정화만으로 원자적인 거래를 보장할 수 없다.
데이터는 보관하고 요청만 동기화하기
한 거래씩 전달하는 구조에서 원천은 payload를 레지스터에 기록하고 요청 상태를 바꾼다. 목적지의 확인이 돌아올 때까지 payload를 덮어쓰지 않는다.
assign src_ready = (request_q == ack_synced);
assign src_accept = src_valid && src_ready;
always_ff @(posedge src_clk or negedge src_rst_n) begin
if (!src_rst_n) begin
request_q <= 1'b0;
payload_q <= '0;
end else if (src_accept) begin
payload_q <= src_payload;
request_q <= !request_q;
end
end
요청 상태는 목적지의 동기화 체인을 통과한다. 목적지는 새 요청을 인식한 뒤, 그동안 안정되어 있던 payload_q를 한 번에 샘플링한다. 받은 데이터를 보관하거나 소비한 뒤 확인을 돌려보내고, 그 확인이 원천에 도착해야 다음 payload를 기록할 수 있다.
이 조각은 원천의 유지 조건만 보여 준다. 완전한 수신기와 reset 제어를 구현한 예제는 아니다. 확인이 어떤 수신 사건을 의미하는지, 목적지가 언제 데이터와 valid를 저장하는지는 전체 프로토콜에서 정의해야 한다.
PULP의 2-phase CDC는 원천 데이터와 요청을 수락 시점에 기록하고, 동기화된 요청 변화에 따라 목적지 데이터를 저장하는 사례다. 확인은 목적지 수락에 연결된다. 원문의 src와 dst 구현을 함께 읽으면 데이터가 보관되는 기간과 요청·확인의 관계를 확인할 수 있다.
긴 유지 시간이 물리 제약을 대신하지 않는다
요청이 여러 목적지 에지를 거치는 동안 데이터가 도착할 여유가 있어 보인다. 하지만 데이터 경로가 유난히 길면 요청이 먼저 인식될 수 있다. 목적지가 새 요청을 보고 샘플링할 때 아직 일부 데이터 비트가 바뀌는 중이라면, 안정된 묶음이라는 전제가 깨진다.
따라서 “요청을 동기화하니까 데이터는 언젠가 도착한다”는 설명은 부족하다. 데이터가 목적지의 샘플링 시점보다 먼저 안정되도록 경로 지연을 제한해야 한다. 예를 들어 가상의 제어 경로가 요청 이후 20ns에 데이터 샘플링을 유도한다면, payload의 최악 도착이 24ns인 구현은 올바른 묶음을 보장하지 못한다. 수치는 개념 설명용이며 특정 셀이나 모듈의 timing budget이 아니다.
PULP 원문은 async_req, async_ack, async_data 경로에 대해 두 클록의 최소 주기를 기준으로 한 max-delay 제약을 요구한다. 이 제약은 해당 구현의 조건이다. 다른 handshake에 그대로 같은 값을 복사하기보다, 실제 수신 시점과 경로를 기준으로 필요한 안정 여유를 확인해야 한다.
확인이 빠르면 데이터 소유권도 너무 빨리 끝난다
목적지가 요청 수준을 보자마자 확인을 보내고, 실제 payload는 한 사이클 뒤 읽는 잘못된 수신기를 생각하자. 원천은 확인을 보고 다음 데이터를 쓸 수 있다. 목적지가 아직 이전 payload를 읽지 않았다면 두 거래가 섞인다.
확인은 단순한 “요청을 보았다”보다 강한 의미를 가져야 할 수 있다. 목적지 내부에 데이터를 안전하게 보관했다면 그때 원천을 풀어 줄 수 있다. 내부 보관이 없다면 실제 소비까지 원천 payload의 유지가 필요하다. 어느 방식이든 확인 이후 원천이 데이터를 바꾸어도 이전 거래가 손상되지 않는 경계가 있어야 한다.
같은 이유로 목적지의 데이터와 valid도 정렬되어야 한다. 새 valid를 먼저 올린 뒤 payload 저장을 다음 에지에 하면, 소비자는 새 거래 표시로 이전 데이터를 받을 수 있다. 데이터가 안정되었다는 조건과 유효하다고 선언하는 시점을 함께 검증해야 한다.
버스를 크게 만들 때 추가되는 것
묶음에는 숫자뿐 아니라 주소, 명령 종류, 오류 비트, 거래 식별자도 포함될 수 있다. 데이터를 보관했지만 주소를 현재 입력에서 그대로 가져오면 서로 다른 거래의 정보가 섞인다. 원천 수락 에지의 모든 의미 있는 필드를 같이 저장하고, 같은 확인까지 유지해야 한다.
payload가 커지면 유지 레지스터의 비용과 경로 지연도 커질 수 있다. 한 거래씩 왕복하는 구조는 버스 폭과 관계없이 확인 latency에 처리율이 제한된다. 높은 지속 처리율을 원한다면 더 많은 항목을 보관하는 구조가 필요할 수 있지만, 묶음의 안정 구간을 명확히 한다는 원칙은 유지된다.
PULP의 4-phase CDC도 데이터 경로를 따로 보관하고 제어 handshake로 전달 시점을 관리한다. 요청 신호의 형식이 바뀌어도 데이터와 제어 경로 사이의 도착·유지 계약은 사라지지 않는다.
원천과 목적지 reset이 그 계약을 중간에서 끊을 수 있다는 점도 확인해야 한다. payload를 초기화하면서 상대가 이전 요청을 아직 유효하다고 읽으면, 안정된 데이터가 잘못된 거래로 전달될 수 있다. 정상 전달의 원리와 운용 중 reset 지원은 각각 명시되어야 한다.
처음의 7은 비트마다 실패한 숫자가 아니라, 거래의 경계가 없어서 섞인 숫자였다. bundled-data CDC의 핵심은 데이터를 오래 유지하는 것, 그 데이터가 도착한 뒤 읽는 것, 읽기 책임이 끝난 뒤 확인하는 것을 하나의 계약으로 맞추는 데 있다. 이 관계가 구현 지연과 reset 조건에서도 유지될 때 여러 비트가 하나의 값으로 넘어간다.