[RTL] Fan-in과 Fan-out: timing issue의 숨겨진 주범과 해결책

RTL 설계를 하다 보면 기능적으로는 완벽한데, 합성(Synthesis)이나 P&R 단계에서 Timing Violation (Setup/Hold)이 발생하여 chip이 동작하지 않는 경우를 종종 마주합니다.

STA(Static Timing Analysis) report를 열어보면 “High Fan-out Net” 이라거나 “Transition Time Violation” 같은 경고가 떠 있는 것을 볼 수 있죠. 이것이 바로 오늘 다룰 Fan-inFan-out 문제입니다.

단순히 논리 게이트의 입력/출력 개수로만 알기 쉬운 이 개념들이, 실제 실리콘 웨이퍼 위에서는 어떻게 물리적인 지연(Delay)을 만들어내는지, 그리고 RTL 설계자는 이를 어떻게 코드로 해결해야 하는지 알아보겠습니다.

Fan-in, Fan-out 설명
Fan-in, Fan-out 설명

1. Fan-in (팬인): 너무 많은 목소리를 한 번에 듣는다면?

Fan-in은 논리 게이트 하나가 받아들이는 입력 신호의 수를 의미합니다.

  • 정의: 하나의 게이트 입력단에 연결된 이전 단 게이트들의 수.
  • 비유: 한 사람(Gate)에게 동시에 여러 사람(Input)이 말을 거는 상황입니다.

(1) 물리적 문제점: 직렬 연결된 트랜지스터

CMOS 공정에서 NAND, NOR 게이트를 생각해 봅시다. 입력이 늘어날수록 내부의 PMOS나 NMOS가 직렬(Series)로 쌓이게 됩니다.

  • 저항 증가: 직렬 연결된 트랜지스터는 저항(R)을 증가시킵니다.
  • 속도 저하: 저항이 커지면 전류가 흐르기 힘들어져 게이트의 스위칭 속도가 느려집니다. (RC Delay 증가)

(2) RTL 설계 관점: Logic Depth

RTL 설계자에게 High Fan-in은 보통 복잡한 조건문(Logic Depth)의 형태로 나타납니다.

// [High Fan-in 예시] 
// 수많은 조건이 AND/OR로 묶이면 거대한 복합 게이트가 만들어짐
assign valid = (cond1 & cond2 & cond3 & ... & cond16);

이렇게 입력이 많은 로직은 합성 툴이 하나의 거대한 게이트로 만들 수 없어, 여러 단계의 작은 게이트들로 쪼개서 트리(Tree) 구조로 만듭니다. 결과적으로 Logic Depth(게이트를 거치는 횟수)가 깊어져 Setup Time Violation을 유발합니다.

2. Fan-out (팬아웃): 혼자서 너무 많은 짐을 든다면?

Fan-out은 하나의 논리 게이트 출력이 구동(Drive)해야 하는 다음 단 게이트의 입력 수를 의미합니다. 실무에서는 Fan-in보다 훨씬 자주, 심각하게 다뤄지는 문제입니다.

  • 정의: 하나의 출력이 연결된 부하(Load)의 개수.
  • 비유: 한 명의 선생님(Driver)이 수백 명의 학생(Load)에게 육성으로 강의를 해야 하는 상황입니다. 목소리(신호)가 뒤쪽까지 전달되는 데 오래 걸리겠죠.

(1) 물리적 문제점: 커패시턴스(Capacitance)의 증가

모든 게이트의 입력단에는 기생 커패시턴스가 존재하고, 배선에도 커패시턴스가 존재합니다.

DelayCloadDrive_StrengthDelay \propto \frac{C_{load}}{Drive\_Strength}

Fan-out이 높다는 것은 충전해야 할 커패시터(Cload)가 엄청나게 크다는 뜻입니다. 물통(Capacitor)이 크면 수도꼭지(Gate)에서 물을 틀어도 꽉 차는 데(Voltage High) 시간이 오래 걸립니다. 이로 인해 Transition Time(Rise/Fall Time)이 늘어지고, 전체적인 chip 속도가 느려집니다.

(2) 대표적인 High Fan-out Nets

RTL 설계에서 다음과 같은 신호들은 필연적으로 High Fan-out을 가집니다.

  1. Clock: 수만 개의 F/F에 연결됨 (CTS로 해결)
  2. Reset: 칩 전체의 리셋 (Reset Tree로 해결)
  3. Global Enable: 전체 모듈을 껐다 켰다 하는 제어 신호

3. High Fan-out을 해결하는 RTL 설계 기법

합성 툴(Design Compiler)이나 P&R 툴이 버퍼(Buffer)를 삽입해서 어느 정도 해결해주지만, 근본적인 해결책은 RTL 코드 레벨에서 나와야 합니다.

(1) Register Duplication (레지스터 복제)

하나의 Flip-Flop이 100개의 부하를 감당해야 한다면, 똑같은 동작을 하는 Flip-Flop을 2개 만들어서 각각 50개씩 나눠서 담당하게 하는 방법입니다.

[Bad Case: High Fan-out]

reg global_en;
// 1000개의 모듈이 global_en 하나만 바라봄 (Fan-out = 1000)
always @(posedge clk) begin
    if (global_en) data_out <= data_in;
end

[Good Case: Manual Duplication]

// 1. RTL에서 직접 복제 (물리적으로 다른 레지스터 생성)
reg global_en_1, global_en_2; 

always @(posedge clk) begin
    global_en_1 <= source_logic;
    global_en_2 <= source_logic; // 논리적으로는 같음
end

// 2. 부하 분산 (Load Balancing)
always @(posedge clk) begin
    if (global_en_1) block_A_data <= ...; // 500개 드라이빙
    if (global_en_2) block_B_data <= ...; // 500개 드라이빙
end
  • 주의: RTL에서 이름만 다르게 한다고 합성 툴이 무조건 분리해주지는 않습니다. 툴이 “어? 이거 같은 로직이네?” 하고 하나로 합쳐버릴(Merge) 수 있습니다. 이를 막기 위해 (* keep = "true" *) 같은 속성(Attribute)을 사용하거나 합성 스크립트에서 set_dont_touch 설정을 해야 합니다.
`define	keep_attib			(* keep = "true" *)

`keep_attib    reg r_sram_en;

(2) Max Fanout Constraint 설정

직접 코드를 고치기 힘들다면, 합성 툴에게 제약 조건을 걸어줄 수 있습니다. “이 디자인의 모든 넷(Net)은 Fan-out이 30을 넘지 않게 해줘!” 라고 명령하면, 툴이 알아서 버퍼(Buffer)를 중간중간 삽입하거나 로직을 복제(Cloning)합니다.

  • 단점: 버퍼가 추가되면 면적(Area)이 늘어나고, 버퍼 자체의 지연 시간 때문에 레이턴시가 미세하게 증가할 수 있습니다.

(3) Tree 구조 만들기

Reset 신호처럼 타이밍이 중요하지 않지만 Fan-out이 극도로 높은 경우, 버퍼 트리(Buffer Tree)를 만들어 단계적으로 신호를 퍼뜨립니다.

4. 결론: “내 신호는 어디까지 가는가?”

RTL 엔지니어는 단순히 01의 논리만 따지는 사람이 아닙니다. 내가 작성한 assign 구문 하나, reg 선언 하나가 실제 회로에서 얼마나 많은 게이트를 구동해야 하는지 상상할 수 있어야 합니다.

  1. Fan-in이 높으면: 로직을 쪼개거나 파이프라인(Pipeline)을 추가하여 Logic Depth를 줄입니다
  2. Fan-out이 높으면: 레지스터를 복제(Duplication)하거나, 합성 툴의 제약 조건(Constraints)을 활용합니다

High Fan-out 문제를 RTL 단계에서 미리 해결한다면, 백엔드(Back-end) 엔지니어에게 “타이밍이 안 나와요”라는 전화를 받을 확률이 획기적으로 줄어들겁니다.

참고: 위키피디아

유사한 게시물