Chapter 02

RTL과 기능 검증

칩 설계는 코드로 시작한다. 하지만 소프트웨어와 달리 이 코드는 차례로 실행되는 명령이 아니라, 동시에 존재하는 회로의 설명서다. 레지스터와 그 사이를 잇는 조합 논리를 적는다는 뜻에서 이 수준을 RTL(Register-Transfer Level)이라 부른다. 이 장에서는 Verilog로 회로를 적는 법, 클럭에 맞춰 상태가 바뀌는 순차 논리의 규칙, 그리고 칩을 만들기 전에 “맞게 적었는지”를 확인하는 검증의 방법을 배운다. 설계 일정의 절반 이상이 검증에 쓰인다.

코드가 회로가 된다

Verilog(와 그 확장인 SystemVerilog)나 VHDL 같은 하드웨어 기술 언어(HDL)는 회로를 모듈로 나눠 적는다. 모듈은 입출력 포트를 가진 상자이고, 그 안의 문장은 위에서 아래로 실행되는 것이 아니라 모두 동시에 존재한다. assign 문 하나는 게이트 묶음 하나이고, always @(posedge clk) 블록 하나는 플립플롭 묶음 하나다.

module half_adder (input a, b, output s, c);
  assign s = a ^ b;     // XOR 게이트
  assign c = a & b;     // AND 게이트
endmodule

module counter4 (input clk, rst, en, output reg [3:0] q);
  always @(posedge clk)          // 클럭 상승 엣지마다
    if (rst)     q <= 4'd0;     // 동기 리셋
    else if (en) q <= q + 1;    // 레지스터 4개 + 4비트 덧셈기
endmodule

합성기(3장)는 이 코드를 읽어 첫 모듈은 XOR와 AND 게이트로, 둘째 모듈은 플립플롭 4개와 덧셈기, 그리고 rst·en에 따라 다음 값을 고르는 멀티플렉서로 바꾼다. 그러니 RTL을 쓸 때는 늘 이 코드가 어떤 회로가 될지 머릿속에 그려야 한다.

SIMULATOR

논리 게이트 놀이터

입력 (눌러서 0/1 바꾸기)
진리표
해볼 것: ① 입력을 눌러 바꾸면 신호선 색(주황 = 1, 회색 = 0)이 게이트를 지나 출력까지 전파되고, 진리표의 해당 줄이 강조된다. ② 전가산기는 반가산기 두 개와 OR 하나다. a/b에서 자리올림 출력까지의 최장 경로는 XOR→AND→OR의 3단이다. cin에서 cout으로 가는 전파는 AND→OR의 2단이며, 리플 덧셈기에서는 이 2단 전파가 다음 비트들에 이어진다(8장 임계 경로). ③ MUX는 sel에 따라 둘 중 하나를 고른다. if와 ? : 문은 보통 MUX로 합성된다.

순차 논리: 클럭과 레지스터

조합 논리의 출력은 입력만으로 정해진다. 기억이 필요한 회로, 즉 카운터·상태 기계·파이프라인은 순차 논리다. 디지털 칩의 거의 모든 순차 소자는 D 플립플롭으로, 클럭의 상승 엣지 순간 D 입력을 받아 다음 엣지까지 Q로 내보낸다. 모든 플롭이 같은 클럭 엣지에서 동시에 갱신된다는 것, 그리고 두 엣지 사이에 조합 논리가 결과를 안정시켜야 한다는 것이 동기 설계의 전부다. 이 “안정시켜야 한다”는 조건이 8장의 셋업 시간이다.

SIMULATOR

사이클 파형 시뮬레이터

입력 파형 칸을 눌러 0/1 바꾸기

      
해볼 것: ① 카운터에서 en을 중간에 0으로 바꾸면 그다음 엣지부터 q가 멈춘다. 입력이 바뀐 순간이 아니라 다음 클럭 엣지에 반영된다는 것이 순차 논리의 핵심이다. ② 시프트 레지스터는 d 값이 한 클럭에 한 칸씩 q0 → q1 → q2 → q3로 옮겨 간다. ③ 신호등 FSM은 car 입력(차량 감지)에 따라 상태가 바뀐다. 상태도와 비교해 보자. ④ 패턴 검출기는 입력 비트열에서 101이 나올 때마다 hit를 1로 만든다(겹치는 패턴 포함).

blocking과 non-blocking

Verilog의 대입에는 두 종류가 있다. =(blocking)은 그 자리에서 바로 값을 바꾸고 다음 문장은 바뀐 값을 본다. <=(non-blocking)은 각 문장을 실행할 때 오른쪽 값을 평가하고, 왼쪽 값의 갱신을 NBA(non-blocking assignment) 이벤트 영역에 예약한다. 지연 없는 순수 <= 동기식 블록에서는 갱신 전 값을 읽지만, 앞선 blocking 대입이나 다른 프로세스와의 경쟁이 있으면 엣지 직전 값만 읽는다고 일반화할 수 없다. 같은 클럭 엣지에서 모든 플롭이 동시에 바뀌는 하드웨어를 흉내 내는 것은 후자다. 그래서 규칙은 단순하다. 순차 블록(always @(posedge clk))에는 <=, 조합 블록(always @*)에는 =.

SIMULATOR

같은 코드, 다른 회로


          
해볼 것: 클럭 엣지 하나 동안 세 줄의 대입을 한 줄씩 실행한다. ① non-blocking에서는 세 줄이 모두 엣지 직전 값(a=1, b=2, c=3)을 읽고, 끝에서 한꺼번에 바뀐다. 결과는 값이 한 칸씩 밀린 시프트 레지스터다. ② blocking에서는 첫 줄이 b를 바꾸자마자 둘째 줄이 바뀐 b를 읽는다. 결과는 세 레지스터에 모두 같은 값, 즉 합성하면 플롭 하나만 남는 전혀 다른 회로다. 이 예제는 시뮬레이션과 합성 모두 같은 한 단계 동작을 하며, 의도한 세 단계 파이프라인과 달라지는 실수다.

상태 기계

제어 회로의 대부분은 유한 상태 기계(FSM)다. 상태 레지스터, 현재 상태와 입력으로 다음 상태를 정하는 조합 논리, 출력 논리로 이루어진다. 출력이 상태에만 달려 있으면 무어(Moore), 입력에도 달려 있으면 밀리(Mealy) 기계다. 위 파형 시뮬레이터의 신호등이 무어 기계다.

GREEN큰길 초록 YELLOW2 클럭 RED골목 초록 car = 1 타이머 끝 3 클럭 뒤 (또는 car = 0) car = 0
그림 02-1. 신호등 FSM의 상태도. 상태 셋은 이진 인코딩으로 2비트(00, 01, 10), 원-핫 인코딩으로 3비트(001, 010, 100)가 필요하다. 원-핫은 플롭이 늘지만 다음 상태 논리가 단순하고 빨라서 FPGA와 고속 설계에서 자주 쓴다. 합성기가 상태 인코딩을 바꾸기도 한다.

검증: 칩을 만들기 전에 틀린 곳 찾기

마스크 한 세트가 수십억~수백억 원인 세계(12장)에서 버그는 실리콘으로 가기 전에 찾아야 한다. 기능 검증은 몇 겹으로 이루어진다.

SIMULATOR

무작위 테스트와 기능 커버리지

1만 개 테스트 후 커버리지—
100% 도달 테스트 수—
해볼 것: ① 무작위 자극은 처음엔 커버리지를 빠르게 올리지만, 드문 코너 케이스(예: FIFO가 가득 찬 순간 리셋)는 좀처럼 나오지 않아 곡선이 오래 평평해진다. ② 드문 항목의 확률을 10⁻⁴로 낮추면 100%에 도달하지 못한다. ③ 커버리지 구동 제약을 켜면 아직 못 본 항목 쪽으로 자극을 기울여 꼬리가 짧아진다. 그래도 남는 것은 직접 쓴 테스트나 형식 검증으로 닫는다. (각 테스트가 항목마다 독립적으로 정해진 확률로 그 항목을 친다고 가정)
합성할 수 없는 코드, 원치 않는 래치

#10 같은 지연, initial 블록, 파일 입출력은 ASIC 표준 셀 흐름에서는 주로 테스트벤치에서 쓴다. 일반적인 ASIC 합성에서는 허용되지 않는다. 다만 FPGA 합성 도구는 initial에 지정한 RAM 초기 값을 구성 데이터로 만들 수 있다. 실행 중의 임의 파일 입출력·시간 지연과 이런 초기화 지원을 구분한다. 또 조합 블록에서 if에 else를 빠뜨리면 “값을 유지하라”는 뜻이 되어 래치가 생긴다. 래치는 타이밍 분석을 어렵게 하고 대부분 실수다. 그래서 린트(lint) 도구가 RTL 단계에서 이런 패턴을 경고한다.

핵심 정리

  1. RTL은 레지스터와 그 사이 조합 논리를 적는 수준이다. HDL 문장은 차례로 실행되지 않고 동시에 존재하는 회로다.
  2. 동기 설계에서 모든 플롭은 같은 클럭 엣지에 갱신되고, 입력 변화는 다음 엣지에 반영된다.
  3. 순차 블록은 <=, 조합 블록은 =. 의도에 맞게 선택하지 않으면 파이프라인 구조가 달라지고 별도 프로세스 사이의 경쟁도 생길 수 있다.
  4. FSM은 상태 레지스터 + 다음 상태 논리 + 출력 논리. 인코딩(이진·원-핫)이 면적과 속도를 바꾼다.
  5. 검증은 테스트벤치·무작위 자극·커버리지·어서션·형식 검증·에뮬레이션을 겹쳐 쓴다. 무작위만으로는 코너 케이스가 닫히지 않는다.

확인 퀴즈

always @(posedge clk) begin b <= a; c <= b; end를 합성하면?

non-blocking은 엣지 직전 값을 모두 읽은 뒤 동시에 갱신하므로 c는 이전 b를 받는다.

조합 논리 블록 always @*에서 if (sel) y = a;만 쓰고 else를 빠뜨리면?

모든 경우에 값을 주지 않으면 기억 소자가 필요해진다. 기본값을 먼저 대입하거나 else를 반드시 쓴다.

상태가 12개인 FSM을 원-핫으로 인코딩하면 필요한 플롭 수는?

원-핫은 상태마다 플롭 하나. 이진 인코딩이면 ⌈log₂12⌉ = 4개다.

RTL과 합성 후 게이트 넷리스트가 같은 기능인지 모든 입력에 대해 증명하는 방법은?

시뮬레이션은 넣어 본 입력만 확인한다. 등가성 검사는 두 회로의 출력 함수가 같음을 수학적으로 보인다.