Verilog 사칙연산(+-*/) 합성 가이드

Verilog 사칙연산 합성 완벽 가이드: RTL이 하드웨어로 변환되는 과정

⚡ Verilog 사칙연산 합성 완벽 가이드

RTL 코드가 실리콘 하드웨어로 변환되는 과정의 모든 것

assign c = a + b; 이 한 줄이 실제 칩에서 어떤 회로가 될까요? 합성 툴은 당신의 타이밍 제약을 보고 수십 가지 아키텍처 중 최적의 것을 자동 선택합니다. 이 글에서는 각 연산자가 어떤 하드웨어로 변환되는지, 그 과정을 시각화와 함께 상세히 설명합니다.

🔄 1. 합성 과정 개요

RTL 코드의 연산자가 하드웨어로 변환되는 과정입니다.

RTL 코드
c = a + b
연산자 추론
Inference
아키텍처 선택
DW01_add
Gate Mapping
Netlist
🤔 합성 툴의 선택 기준

1. Timing Constraints: 목표 클럭 주파수가 높을수록 빠른 아키텍처 선택
2. Area Constraints: 면적 제한이 있으면 작은 아키텍처 선택
3. Operand Width: 비트 폭이 클수록 아키텍처 차이가 커짐
4. Optimization Goal: -area vs -timing

➕ 2. 덧셈기 (+) 합성

+ Addition Operator
📝 RTL 코드
assign sum = a + b;

assign {cout, sum} = a + b + cin;
🔧 합성 결과
DW01_add

아키텍처: rpl, cla, pparch 중 선택

아키텍처별 구조

📊 1. Ripple Carry Adder (RCA) - 'rpl' A[3] A[2] A[1] A[0] B[3] B[2] B[1] B[0] │ │ │ │ ▼ ▼ ▼ ▼ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │FA │◄│FA │◄│FA │◄│FA │◄─ Cin └─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘ │───────────────────► Carry 순차 전파 ▼ ▼ ▼ ▼ S[3] S[2] S[1] S[0] Delay: O(N) | Area: O(N) | 면적 최소
📊 2. Carry Lookahead Adder (CLA) - 'cla' A[3:0] B[3:0] │ │ ▼ ▼ ┌──────────────────────────┐ │ P/G Generator │ │ P[i] = A[i] ⊕ B[i] │ ← Propagate │ G[i] = A[i] · B[i] │ ← Generate └────────────┬─────────────┘ │ ▼ ┌──────────────────────────┐ │ Carry Lookahead Unit │ │ │ │ C1 = G0 + P0·C0 │ ← 미리 계산! │ C2 = G1 + P1·C1 │ │ C3 = G2 + P2·C2 │ └────────────┬─────────────┘ │ ▼ S[3:0] Delay: O(log N) | Area: O(N log N) | 밸런스
📊 3. Parallel Prefix Adder (PPA) - 'pparch' (Kogge-Stone) P3:G3 P2:G2 P1:G1 P0:G0 │ │ │ │ Stage 0 ● ● ● ● ← P,G 생성 │╲ │╲ │╲ │ │ ╲ │ ╲ │ ╲ │ Stage 1 ─── ─── ───● ● ← Prefix 1 │╲ ╲ │╲ ╲ │ │ ╲ ╲ │ ╲ ╲ │ Stage 2 ──────────────● ● ← Prefix 2 │ │ │ │ ▼ ▼ ▼ ▼ C3 C2 C1 C0 ← 모든 캐리 동시! Delay: O(log N) | Area: O(N log N) | 최고 성능

비트 폭별 합성 결과 예측

비트 폭 느슨한 제약 보통 제약 빡빡한 제약 예상 게이트
8-bit RCA (rpl) RCA (rpl) CLA (cla) ~50
16-bit RCA (rpl) CLA (cla) PPA (pparch) ~100
32-bit CLA (cla) PPA (pparch) PPA (pparch) ~200
64-bit CLA (cla) PPA (pparch) PPA (pparch) ~400

➖ 3. 뺄셈기 (-) 합성

Subtraction Operator
💡 핵심 원리

뺄셈은 2의 보수 덧셈으로 변환됩니다!
a - b = a + (~b) + 1

📝 RTL 코드
assign diff = a - b;
🔧 변환 과정
diff = a + (~b) + 1

→ Inverter + Adder (Cin=1)
📊 뺄셈기 하드웨어 구조 A[N-1:0] B[N-1:0] │ │ │ ┌─────┴─────┐ │ │ INVERTER← ~B 생성 │ │ (~B) │ │ └─────┬─────┘ │ │ ▼ ▼ ┌───────────────────────────┐ │ ADDER │ │ │ │ Sum = A + (~B) + 1← Cin = 1 │ │ └─────────────┬─────────────┘ │ ▼ Diff[N-1:0] 면적: Adder + N개 Inverter 속도: Adder와 거의 동일

✖️ 4. 곱셈기 (*) 합성

× Multiplication Operator
⚠️ 주의

곱셈기는 덧셈기보다 10~20배 큰 면적을 차지합니다!

📝 RTL 코드
assign prod = a * b;

결과 폭: N + M bits
🔧 합성 결과
DW02_mult

Booth + Wallace Tree
📊 곱셈기 구조 (Booth + Wallace Tree) Step 1: Partial Product 생성 (Booth Encoding) A[3:0] × B[3:0] │ ▼ ┌──────────────────┐ │ Booth Encoder← Radix-4: 부분곱 절반으로 감소 │ │ │ 00 → 0 │ │ 01 → +A │ │ 10 → +2A │ │ 11 → -A (다음에 +1) │ └────────┬─────────┘ │ ▼ Step 2: Partial Products PP0: ○ ○ ○ ○ PP1: ○ ○ ○ ○ PP2: ○ ○ ○ ○ PP3: ○ ○ ○ ○ ───────────────── Step 3: Wallace Tree 압축 ┌─────────────────┐ │ CSA Tree← 3:2 Compressor │ │ ← 3개 입력 → 2개 출력 │ N rows → 2 rows│ └────────┬────────┘ │ ▼ Step 4: Final Addition ┌─────────────────┐ │ CPA (Adder)← 마지막 2개 덧셈 └────────┬────────┘ │ ▼ Product[7:0]

곱셈기 구현 옵션

타입 DW 모듈 Latency 면적 용도
Combinational DW02_mult 1 cycle 최대 고속 필요시
2-Stage Pipeline DW02_mult_2_stage 2 cycle 중간 타이밍 완화
Multi-cycle DW_mult_seq N cycle 최소 면적 우선

비트 폭별 예상 게이트 수

비트 폭 예상 게이트 상대 비용
8 × 8 ~400 1x
16 × 16 ~2,000 5x
32 × 32 ~8,000 20x
64 × 64 ~30,000 75x

➗ 5. 나눗셈기 (/) 합성

÷ Division Operator
🚨 베테랑의 경고

하드웨어에서 나눗셈은 되도록 피하십시오!
assign q = a / b; 한 줄이 합성 시간 폭발과 Timing Violation의 주범이 됩니다.

나눗셈이 어려운 이유

연산 Logic Depth 32-bit 게이트 상대 비용
덧셈 (+) ~10 levels ~200 1x
곱셈 (*) ~30 levels ~8,000 40x
나눗셈 (/) ~100+ levels ~20,000+ 100x
📊 나눗셈 합성 결과의 문제 RTL: assign q = a / b; ▼ 합성 ┌──────────────────────────────────────────────┐ │ │ │ 거대한 조합 논리 회로 │ │ │ │ Logic Depth: 100+ Gates │ │ Critical Path: 수십 ns │ │ │ │ → 클럭 주기 내 완료 불가! │ │ → Setup Violation 발생! │ │ │ └──────────────────────────────────────────────┘

나눗셈 대안 전략

📄 Verilog: 나눗셈 회피 코드
// ❌ 절대 피해야 할 코드 assign result = a / b; // 합성 폭발! // ✅ 대안 1: 2의 거듭제곱 나눗셈 (Shift) assign div_by_2 = a >> 1; // a / 2 assign div_by_8 = a >> 3; // a / 8 assign div_by_16 = a >> 4; // a / 16 // ✅ 대안 2: 역수 곱셈 (고정소수점 근사) // a / 10 ≈ a * 0.1 ≈ (a * 26) >> 8 // 26/256 ≈ 0.1015625 assign div_by_10 = (a * 8'd26) >> 8; // a / 3 ≈ (a * 85) >> 8 // 85/256 ≈ 0.332 assign div_by_3 = (a * 8'd85) >> 8; // ✅ 대안 3: Sequential Divider (Multi-cycle) DW_div_seq #( .a_width(32), .b_width(32), .num_cyc(8) // 8 cycle에 걸쳐 계산 ) u_div ( .clk(clk), .rst_n(rst_n), .a(dividend), .b(divisor), .quotient(q), .remainder(r) );

🔢 6. 나머지 (%) 합성

% Modulo Operator

나머지 연산은 나눗셈과 비슷한 복잡도를 가집니다.

📄 Verilog: 나머지 연산 최적화
// ❌ 일반적인 나머지 - 비용 큼 assign rem = a % b; // ✅ 2의 거듭제곱 나머지 - AND 연산으로 변환! assign mod_2 = a[0]; // a % 2 assign mod_4 = a[1:0]; // a % 4 assign mod_8 = a[2:0]; // a % 8 assign mod_16 = a[3:0]; // a % 16 assign mod_n = a & (n - 1); // a % n (n = 2^k) // 면적: 거의 0! (단순 wire 연결)
💡 2의 거듭제곱 나머지 변환

a % 2n = a[n-1:0]

합성 툴이 자동으로 인식하여 게이트 없이 단순 wire 연결로 변환합니다!

⚖️ 7. 비교 연산자 합성

< > == Comparison Operators
연산자 DW 모듈 구조 게이트 (32-bit)
== DW01_cmp2 XOR + NOR Tree ~50
!= DW01_cmp2 XOR + OR Tree ~50
<, > DW01_cmp2 Subtractor ~100
<=, >= DW01_cmp2 Subtractor + EQ ~120
📊 동등 비교 (==) 구조 A[3] A[2] A[1] A[0] │ │ │ │ B[3] B[2] B[1] B[0] │ │ │ │ ▼ ▼ ▼ ▼ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │XOR│ │XOR│ │XOR│ │XOR← 각 비트 비교 └─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘ │ │ │ │ └─────┴─────┴─────┘ │ ▼ ┌────────┐ │ NOR← 모두 0이면 Equal └────┬───┘ │ ▼ EQ (1 = Equal)

🔄 8. Signed vs Unsigned

데이터 타입에 따라 합성 결과가 완전히 달라집니다!

Unsigned

reg [7:0] a;

• 범위: 0 ~ 255
• 비교: 단순 magnitude
• 곱셈: 기본 multiplier

Signed

reg signed [7:0] a;

• 범위: -128 ~ 127
• 비교: MSB 고려 필요
• 곱셈: Sign extension 필요

📄 Verilog: Signed 연산 예제
// Unsigned 곱셈 wire [7:0] a, b; wire [15:0] prod_u = a * b; // DW02_mult (unsigned) // Signed 곱셈 wire signed [7:0] sa, sb; wire signed [15:0] prod_s = sa * sb; // DW02_mult (TC=1) // ⚠️ 주의: 혼합 연산 wire [15:0] prod_mix = sa * b; // ❌ 결과 예측 어려움! // ✅ 명시적 캐스팅 wire signed [15:0] prod_ok = sa * $signed(b);
⚠️ Signed/Unsigned 혼합 주의

한쪽만 signed이면 Verilog는 unsigned로 처리합니다!
반드시 $signed()로 명시적 변환하세요.

🔗 9. 연산 조합 최적화

합성 툴은 연산 조합을 인식하여 최적화된 단일 유닛으로 합성합니다.

MAC (Multiply-Accumulate)

📝 RTL 코드
c = a * b + d;
🔧 최적화 결과
DW02_mac
단일 MAC 유닛

인식되는 연산 패턴

RTL 패턴 DW 모듈 최적화 효과
a * b + c DW02_mac Adder 공유
a * b + c * d DW02_multp 부분곱 병합
acc + a * b DW02_mac 누적 최적화
a * b - c DW02_mac 감산 통합
📄 Verilog: 최적화 유도 코딩
// FIR 필터 - MAC 최적화 유도 always @(posedge clk) begin // 각 곱셈+덧셈이 DW02_mac으로 합성됨 acc <= coef0 * data0 + coef1 * data1 + coef2 * data2 + coef3 * data3; end // 합성 로그에서 확인: // Inferring 'DW02_mac' for multiply-add pattern...

🧙‍♂️ 10. 실전 최적화 팁

✅ 연산자별 최적화 체크리스트

➕ 덧셈/뺄셈:
• 타이밍 여유 있으면 그냥 쓰세요 (툴이 알아서 최적화)
• 빡빡하면 파이프라인 분할 고려

✖️ 곱셈:
• 상수 곱셈은 Shift+Add로 변환 검토
a * 3 = a + (a << 1)
a * 5 = a + (a << 2)

➗ 나눗셈:
• 2의 거듭제곱이면 Shift 사용
• 상수 나눗셈은 역수 곱셈으로 변환
• 불가피하면 Sequential Divider 사용

% 나머지:
• 2의 거듭제곱이면 AND 마스크 사용
a % 8 = a & 7

합성 로그 확인 포인트

📄 DC 합성 로그 예시
// 덧셈기 아키텍처 변화 확인 Inferring synthetic operator for 'add_123' (add). Choosing implementation 'rpl' for 'add_123' (DW01_add). // 타이밍 빡빡하게 재합성 후 Re-optimizing synthetic operator 'add_123'. Choosing implementation 'pparch' for 'add_123' (DW01_add). // 곱셈기 인식 Inferring synthetic operator for 'mult_456' (mult). Choosing implementation 'booth' for 'mult_456' (DW02_mult). // MAC 패턴 인식 Inferring multiply-accumulate pattern... Choosing implementation for MAC (DW02_mac).

📌 핵심 요약

연산 DW 모듈 상대 비용 주의사항
+ / - DW01_add 1x 기본
* DW02_mult 20~40x 상수는 Shift로
/ DW_div 100x+ 가능하면 회피!
% DW_div 100x+ 2^n은 AND로
< > == DW01_cmp2 0.5x -

황금률: 합성 툴을 믿되, 로그를 확인하라. rplpparch 변화를 관찰하면 아키텍처 선택을 이해할 수 있습니다.

댓글

이 블로그의 인기 게시물

📚 SDC 마스터 클래스 시리즈 | Chapter 1

📚 SDC 마스터 클래스 시리즈 | Chapter 2

📚 SDC 마스터 클래스 시리즈 | Chapter 3