모든 이야기

두 플립플롭으로도 놓치는 펄스, 단일 비트 synchronizer의 범위

완료 펄스가 파형에는 분명히 있다. 그런데 다른 클록 영역의 카운터는 증가하지 않는다. 그 사이에는 플립플롭도 두 개 넣었다. 이 상황에서 먼저 물을 것은 “준안정성이 생겼나”보다 “첫 플립플롭이 높은 값을 샘플링할 에지가 있었나”다. 단일 비트 synchronizer는 비동기 수준 값을 목적지 클록에서 사용하도록 만드는 회로이지, 모든 짧은 이벤트를 전달하는 기록 장치가 아니다. synchronizer는 동기화 회로를 뜻한다.

단일 비트라는 조건도 중요하다. 이 회로는 하나의 제어 수준을 다룬다. 여러 비트를 각각 연결한다고 하나의 다비트 거래가 원자적으로 넘어가는 것은 아니다. 여기서는 한 비트의 상태와 펄스 포착 사이에 있는 차이를 살펴보자.

두 플립플롭의 일이 무엇인지부터

always_ff @(posedge dst_clk or negedge dst_rst_n) begin
  if (!dst_rst_n) begin
    sync_first  <= 1'b0;
    sync_second <= 1'b0;
  end else begin
    sync_first  <= async_level;
    sync_second <= sync_first;
  end
end

assign dst_level = sync_second;

첫 단계는 목적지 클록과 관계없이 바뀌는 async_level을 샘플링한다. 입력이 에지 근처에서 변하면 첫 단계가 정상적인 0이나 1로 빠르게 확정되지 않을 수 있다. 두 번째 단계는 다음 에지에서 첫 단계의 출력을 받는다. 그 사이의 시간을 확보해 불확실한 상태가 다음 논리로 전달될 가능성을 낮추는 구조다.

이것은 확률적인 실패 가능성을 줄이는 접근이다. 플립플롭 두 개가 모든 조건에서 실패를 불가능하게 만드는 것은 아니다. 클록 주파수, 입력의 변경 빈도, 셀 특성, 첫 단계와 다음 단계 사이에 실제로 확보된 안정화 시간이 중요하다. Altera의 동기화 레지스터 설명은 4.1.1절에서 레지스터 사이의 여유 시간이 안정화 시간으로 쓰인다는 점을 다룬다.

PULP의 tc_sync 모델은 이런 레지스터 체인을 구현하며, 전용 동기화 셀이 있는 기술에서는 해당 구현으로 대체하도록 설명한다. 위 코드는 같은 원리를 나타내는 별도의 두 단계 예제다. 실제 동기화 회로를 구현할 때는 기술 라이브러리와 도구의 동기화 식별 조건에 맞춰야 한다.

펄스가 에지 사이에서 끝나면

목적지 상승 에지가 0ns, 10ns, 20ns에 있고, 원천 펄스는 2ns에 올라가 6ns에 내려온다고 가정하자. 실제 제품의 수치가 아닌 설명용 시간이다. 첫 단계는 0ns에도 10ns에도 입력 0을 본다. 두 번째 단계 역시 계속 0이다.

펄스는 실제로 있었지만 이산적인 샘플링에는 나타나지 않았다. 입력이 목적지 에지에 가깝지도 않았으므로 준안정성을 이유로 들 필요가 없다. 구조가 이벤트를 포착할 기회가 없었던 것이다. 세 번째 플립플롭을 추가해도, 첫 단계에 들어오지 않은 1을 만들어 낼 수는 없다.

반면 enable처럼 충분히 오래 유지되는 상태라면 목적지에서 언젠가 그 수준을 볼 수 있다. 정확히 몇 번째 에지에 보이는지는 원천 변경 시각과 목적지 위상, 첫 단계의 샘플링 결과에 영향을 받는다. “두 단계니까 원천 변경 후 정확히 두 사이클”이라는 고정 latency 계약으로 읽어서는 안 된다.

펄스를 길게 하면 끝날까

펄스를 목적지에서 볼 수 있을 만큼 늘리는 것은 한 방법이다. 하지만 필요한 폭은 목적지의 최소 샘플링 간격과 타이밍 여유를 포함해야 한다. 단순히 한 주기와 같다는 조건도 경계의 setup·hold와 위상을 무시하면 충분하지 않을 수 있다. 클록 속도가 바뀌거나 잠시 멈추는 상황에서는 원래의 폭 가정이 깨진다.

또 펄스가 서로 가까우면 늘린 펄스가 이어져 한 번의 긴 수준으로 보일 수 있다. 목적지에서 상승 에지만 세는 회로는 이를 이벤트 한 개로 센다. 높은 수준을 확실히 보이게 하는 것과, 각 이벤트를 분리해 세는 것은 다른 요구다.

각 이벤트가 반드시 전달되어야 한다면 이벤트를 유지되는 상태 변화로 표현하거나, 목적지의 확인을 받고 다음 이벤트를 허용하는 프로토콜이 필요할 수 있다. 중요한 것은 이 글의 동기화 체인에 없는 기능을 시스템이 기대하고 있는지 확인하는 것이다. 단일 비트 수준 전달과 정확한 이벤트 전달을 같은 블록으로 취급하면 설계 조건이 숨는다.

첫 단계의 출력을 다른 곳에 쓰지 않기

불확실성이 생길 수 있는 첫 단계 출력을 일반 제어 로직에도 연결하면 두 번째 단계를 넣은 목적이 무너진다. 서로 다른 소비자가 그 값을 다르게 해석하거나, 조합 경로가 안정화 시간을 소모할 수 있다. 다음 논리는 원칙적으로 동기화 체인의 마지막 단계 값을 사용해야 한다.

두 단계를 RTL에 썼더라도 배치가 멀어져 중간 배선 지연이 길어지면 확보되는 안정화 시간이 줄어든다. 동기화 레지스터 식별, 관련 속성, 단계 사이 타이밍이 구현에 유지되는지 확인해야 한다. 선언의 개수와 실패 확률의 근거는 같은 것이 아니다.

원천에 조합 글리치가 있다면 첫 단계가 보는 입력 변화 빈도도 달라질 수 있다. 필요하면 원천 클록에서 신호를 등록하여 의도한 수준만 전달한다. 다만 원천 등록 역시 짧은 이벤트를 목적지에서 반드시 보이게 만드는 기능은 아니다. 입력을 정리하는 일과 전달을 보장하는 일을 구분해야 한다.

무엇을 검증하면 질문에 답할 수 있나

RTL 시뮬레이션은 일반적으로 아날로그적인 준안정성의 지속 시간을 모델링하지 않는다. 위상을 바꿔 테스트하여 짧은 펄스 누락은 볼 수 있어도, 통과했다고 물리적인 실패율이 정해지는 것은 아니다. 반대로 파형이 깨끗한 것은 첫 단계의 샘플링이 논리 모델에서 0 또는 1로 처리되었다는 뜻일 수 있다.

따라서 먼저 전달 요구가 수준인지 이벤트인지 정한다. 수준이라면 유지 시간과 목적지 사용 시점을 확인하고, 이벤트라면 펄스 폭과 이벤트 간격 또는 확인 프로토콜의 조건을 확인한다. 그 위에 동기화 구조와 실제 안정화 시간의 구현 검사를 더한다.

처음의 완료 펄스는 두 플립플롭의 안정화 능력이 부족해서가 아니라, 목적지 에지 사이에서 사라져 포착되지 않았을 수 있다. 단일 비트 synchronizer를 올바르게 쓰려면 불확실성을 줄이는 기능과 정보를 놓치지 않는 기능을 분리해서 요구해야 한다. 그 구분이 있으면 플립플롭을 하나 더 넣을지보다, 무엇을 얼마나 오래 유지하거나 확인해야 하는지가 먼저 보인다.

두 플립플롭으로도 놓치는 펄스, 단일 비트 synchronizer의 범위 · ariser