[RTL] 블록 담당에서 SoC 통합으로: wrapper를 만들며 배운 것들

블록 하나를 설계하고 검증까지 마치면 꽤 큰 산을 넘었다고 생각했습니다. 시뮬레이션도 통과했고, 제 블록은 제가 정한 클록과 리셋 아래에서 잘 돌았습니다.

그런데 그 블록을 SoC에 붙이는 wrapper를 맡으면서 질문이 바뀌었습니다. “이 블록이 맞게 동작하는가”가 아니라 “이 블록이 시스템의 클록, 리셋, 버스와 만났을 때도 맞게 동작하는가“를 묻게 된 것입니다. 클록 도메인 경계, 리셋이 풀리는 순서, 시스템 쪽과의 핸드셰이크, 그리고 다른 팀과 함께 보는 CDC 리포트까지 새로 챙길 것이 생겼습니다.

지난 글에서는 처음 ECO를 맡았을 때의 이야기를 했습니다. 이번 글도 “3년 차가 프로젝트에서 배운 것들” 시리즈의 하나로, 블록 담당에서 통합 담당으로 넘어가며 확인하게 된 것들을 정리한 기록입니다.

이번 글에서는 wrapper가 하는 일과 클록·리셋 경계를 정리하는 순서, 그리고 CDC 리포트를 읽고 waive를 판단하는 기준을 살펴보겠습니다.

1. 블록 담당과 통합 담당은 보는 것이 다르다

블록을 설계할 때 제 관심은 안쪽에 있었습니다. 연산이 맞는지, 파이프라인이 막히지 않는지, 코너 케이스를 다 덮었는지가 중요했습니다. 입력 클록은 하나라고 가정했고, 리셋은 테스트벤치가 깔끔하게 풀어 줬습니다.

통합 단계에서는 시선이 경계로 옮겨 갑니다. 블록 바깥에는 다른 주파수로 도는 버스가 있고, 리셋은 정해진 순서로 풀리며, 시스템 쪽은 언제든 요청을 보내거나 멈출 수 있습니다. 블록 안쪽이 완벽해도 경계에서 신호 하나가 잘못 넘어가면 칩은 틀리게 동작합니다.

2. wrapper가 하는 일

wrapper는 블록을 감싸서 SoC가 기대하는 모양으로 맞춰 주는 층입니다. 처음에는 포트 이름만 바꿔 주는 껍데기라고 생각했는데, 실제로 맡아 보니 아래 일들이 모두 이 층에 모였습니다.

  • Clock: 블록 클록과 시스템 클록이 다르면, 둘 사이를 넘는 모든 신호의 처리가 wrapper의 몫이 됩니다.
  • Reset: 시스템 리셋을 받아 블록 도메인에 맞게 동기화하고, 풀리는 순서를 정합니다.
  • Interface adaptation: 시스템 버스의 프로토콜과 블록 내부 인터페이스(valid/ready 등) 사이를 변환합니다.
  • Registers / config: 소프트웨어가 쓰는 설정 레지스터를 두고, 그 값이 블록에 언제 반영되는지 정합니다.
  • Debug visibility: 칩이 나온 뒤 상태를 읽을 수 있도록 상태·에러 신호를 레지스터로 끌어냅니다.

아래는 설명을 위해 만든 가상의 예시입니다.

// 가상의 예시: wrapper 포트 스케치
module blk_wrapper (
  // 시스템 도메인
  input  wire        sys_clk,
  input  wire        sys_rst_n,
  input  wire        cfg_wr,      // 설정 레지스터 쓰기
  input  wire [31:0] cfg_wdata,
  input  wire        s_valid,     // 시스템 → 블록 데이터
  output wire        s_ready,
  output wire        irq,         // 완료/에러 알림
  // 블록 도메인
  input  wire        core_clk,
  input  wire        core_rst_n
);
  // 내부: 리셋 동기화기, 비동기 FIFO, 설정 동기화, 블록 인스턴스
endmodule

3. 클록 도메인이 나뉘는 경계 정리하기

CDC 이론 자체는 CDC와 Metastability 글에 정리해 두었으니, 여기서는 통합하는 입장에서의 순서만 이야기하겠습니다. 가장 도움이 된 습관은 RTL을 고치기 전에 크로싱 표를 먼저 그리는 것이었습니다.

블록, wrapper, SoC 사이의 클록 도메인 경계
블록, wrapper, SoC 사이의 클록 도메인 경계

표에는 경계를 넘는 신호를 하나씩 적고, 신호마다 방향, 폭, 바뀌는 빈도, 받는 쪽이 놓치면 안 되는지를 적었습니다. 그다음 크로싱마다 방법을 골랐습니다.

  • 1비트 레벨 신호: 2-FF 동기화기로 충분한 경우가 많습니다.
  • 연속 데이터 스트림: 비동기 FIFO를 둡니다.
  • 가끔 바뀌는 멀티비트 값(설정 등): 값을 안정시켜 두고 요청/응답 핸드셰이크로 넘깁니다.

설정 레지스터와 디버그 상태 신호도 경계를 넘는다는 점을 처음에는 자주 놓쳤습니다. 데이터 경로만 표에 적으면 이런 “조용한” 신호들이 빠집니다.

💡 Pro Tip: 크로싱 표는 RTL과 같은 저장소에 두고, 신호를 추가할 때마다 표도 같이 고치세요. 나중에 CDC 리포트를 볼 때 이 표가 판단 근거가 됩니다.

4. 리셋 순서와 초기 구간

블록 테스트벤치에서는 리셋이 한 번에 깔끔하게 풀렸습니다. SoC에서는 시스템 도메인과 블록 도메인의 리셋이 서로 다른 시점에 풀릴 수 있고, 그 사이 짧은 구간에는 한쪽만 깨어 있는 상태가 생깁니다.

그래서 리셋 해제 직후를 따로 떼어 이런 것들을 확인하게 됐습니다.

  • 리셋은 비동기로 걸고, 해제는 각 도메인 클록에 동기화해서 푸는가?
  • 한쪽 도메인만 깨어 있을 때 다른 쪽으로 valid나 push가 넘어가지 않는가?
  • FIFO와 동기화기가 비어 있는 상태로 시작하고, 첫 데이터가 오기 전에 준비되는가?
  • 초기 구간에 나오는 출력이 의미 없는 값이라면, 받는 쪽이 그것을 무시한다는 보장이 어디에 있는가?

마지막 질문이 특히 중요했습니다. “처음 몇 사이클은 어차피 의미 없는 구간”이라는 설명은 맞을 수 있지만, 그 구간이 RTL에서 실제로 보장되는지는 따로 확인해야 합니다.

5. CDC 리포트 읽고 waive 판단하기

통합 RTL이 모이면 CDC 툴 리포트를 다른 팀과 같이 보게 됩니다. 항목이 많으면 익숙한 패턴은 “괜찮겠지” 하고 넘기고 싶어집니다. 저는 항목마다 세 가지 중 하나로 분류하는 방식으로 정리했습니다.

CDC 리포트 항목 분류 흐름
CDC 리포트 항목 분류 흐름
  • 반드시 수정: 동기화기가 빠졌거나 구조가 잘못된 경우입니다. RTL을 고치고 리포트를 다시 돌립니다.
  • 근거와 함께 waive: 의도된 구조임을 확인한 경우입니다. 왜 안전한지와 그 판단이 성립하는 조건을 같이 적습니다.
  • 확인 필요: RTL만으로는 결론이 안 나는 경우입니다. 다른 블록의 동작이나 사용 조건에 달려 있으면 담당 팀에 질문하고 보류합니다.

waive 기록에는 파일과 위치, 근거, 그리고 “이 조건이 깨지면 waive도 무효”라는 조건을 함께 남겼습니다. 같은 리포트를 몇 달 뒤 다시 보는 사람이 제가 아닐 수도 있기 때문입니다.

💡 Pro Tip: “아마 괜찮을 것”은 waive 사유가 아닙니다. 근거를 한 줄로 쓸 수 없다면 그 항목은 아직 확인 필요입니다.

6. 실수와 배운 점

  • wrapper를 포트 연결 정도로 가볍게 봤습니다. 실제로는 클록, 리셋, 레지스터가 모두 모이는 곳이라 블록만큼 설계 판단이 필요했습니다.
  • 데이터 경로만 크로싱으로 생각했습니다. 설정과 상태 신호도 경계를 넘는다는 것을 표를 그리고 나서야 알았습니다.
  • 초기 구간을 말로만 설명했습니다. “그게 RTL에서 보장되나요?”라는 질문에 바로 답하려면 근거 위치를 적어 두어야 했습니다.
  • 다른 팀과의 질문을 늦게 시작했습니다. 확인 필요 항목은 일찍 정리해서 보낼수록 일정이 덜 밀렸습니다.

7. 통합 체크리스트

  • ☐ 경계를 넘는 모든 신호(데이터, 제어, 설정, 상태)를 크로싱 표에 적었는가?
  • ☐ 크로싱마다 동기화 방법(동기화기, 비동기 FIFO, 핸드셰이크)을 정하고 이유를 적었는가?
  • ☐ 멀티비트 신호를 비트별 동기화기로 넘기지 않았는가?
  • ☐ 리셋을 도메인마다 동기화해서 해제하는가?
  • ☐ 한쪽만 깨어 있는 초기 구간에 잘못된 전송이 없는지 확인했는가?
  • ☐ 설정 레지스터 값이 블록에 반영되는 시점이 정의돼 있는가?
  • ☐ 디버그용 상태·에러 신호를 읽을 수 있는가?
  • ☐ CDC 리포트 항목을 반드시 수정 / 근거와 함께 waive / 확인 필요로 분류했는가?
  • ☐ waive마다 근거와 성립 조건을 기록했는가?

마무리: wrapper는 블록과 시스템 사이의 약속

블록을 설계할 때는 제가 정한 규칙 안에서만 생각하면 됐습니다. wrapper를 만들면서는 시스템이 정한 규칙과 제 블록의 규칙이 만나는 지점을 하나하나 맞춰야 했습니다. 그 과정에서 “동작한다”와 “안전하다고 설명할 수 있다”가 다르다는 것을 배웠습니다.

다음 글에서도 프로젝트에서 직접 부딪히며 배운 것들을 이어서 정리해 보겠습니다.

참고: 위키피디아

유사한 게시물