RTL과 기능 검증
칩 설계는 코드로 시작한다. 하지만 소프트웨어와 달리 이 코드는 차례로 실행되는 명령이 아니라, 동시에 존재하는 회로의 설명서다. 레지스터와 그 사이를 잇는 조합 논리를 적는다는 뜻에서 이 수준을 RTL(Register-Transfer Level)이라 부른다. 이 장에서는 Verilog로 회로를 적는 법, 클럭에 맞춰 상태가 바뀌는 순차 논리의 규칙, 그리고 칩을 만들기 전에 “맞게 적었는지”를 확인하는 검증의 방법을 배운다. 설계 일정의 절반 이상이 검증에 쓰인다.
- RTL의 의미(레지스터 + 조합 논리)와 HDL 코드가 하드웨어로 대응되는 방식을 설명한다.
- 조합 논리와 순차 논리, 클럭 엣지와 플립플롭의 동작을 파형으로 읽는다.
- blocking(=)과 non-blocking(<=) 대입의 차이와 쓰임을 구분한다.
- FSM을 상태도·코드로 설계하고 상태 인코딩의 영향을 안다.
- 테스트벤치·무작위 자극·커버리지·어서션·형식 검증의 역할과 한계를 이해한다.
코드가 회로가 된다
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을 쓸 때는 늘 이 코드가 어떤 회로가 될지 머릿속에 그려야 한다.
논리 게이트 놀이터
if와 ? : 문은 보통 MUX로 합성된다.순차 논리: 클럭과 레지스터
조합 논리의 출력은 입력만으로 정해진다. 기억이 필요한 회로, 즉 카운터·상태 기계·파이프라인은 순차 논리다. 디지털 칩의 거의 모든 순차 소자는 D 플립플롭으로, 클럭의 상승 엣지 순간 D 입력을 받아 다음 엣지까지 Q로 내보낸다. 모든 플롭이 같은 클럭 엣지에서 동시에 갱신된다는 것, 그리고 두 엣지 사이에 조합 논리가 결과를 안정시켜야 한다는 것이 동기 설계의 전부다. 이 “안정시켜야 한다”는 조건이 8장의 셋업 시간이다.
사이클 파형 시뮬레이터
blocking과 non-blocking
Verilog의 대입에는 두 종류가 있다. =(blocking)은 그 자리에서 바로 값을 바꾸고 다음 문장은 바뀐 값을 본다. <=(non-blocking)은 각 문장을 실행할 때 오른쪽 값을 평가하고, 왼쪽 값의 갱신을 NBA(non-blocking assignment) 이벤트 영역에 예약한다. 지연 없는 순수 <= 동기식 블록에서는 갱신 전 값을 읽지만, 앞선 blocking 대입이나 다른 프로세스와의 경쟁이 있으면 엣지 직전 값만 읽는다고 일반화할 수 없다. 같은 클럭 엣지에서 모든 플롭이 동시에 바뀌는 하드웨어를 흉내 내는 것은 후자다. 그래서 규칙은 단순하다. 순차 블록(always @(posedge clk))에는 <=, 조합 블록(always @*)에는 =.
같은 코드, 다른 회로
상태 기계
제어 회로의 대부분은 유한 상태 기계(FSM)다. 상태 레지스터, 현재 상태와 입력으로 다음 상태를 정하는 조합 논리, 출력 논리로 이루어진다. 출력이 상태에만 달려 있으면 무어(Moore), 입력에도 달려 있으면 밀리(Mealy) 기계다. 위 파형 시뮬레이터의 신호등이 무어 기계다.
검증: 칩을 만들기 전에 틀린 곳 찾기
마스크 한 세트가 수십억~수백억 원인 세계(12장)에서 버그는 실리콘으로 가기 전에 찾아야 한다. 기능 검증은 몇 겹으로 이루어진다.
- 테스트벤치 시뮬레이션: 설계(DUT)에 자극을 넣고 출력을 기대값(참조 모델)과 비교한다. 자극은 직접 쓴 시나리오와 제약 무작위(constrained random)를 섞는다. UVM 같은 검증 방법론이 그 틀을 제공한다.
- 커버리지: “충분히 봤는가”를 숫자로 잰다. 코드 커버리지(줄·분기·토글)와 기능 커버리지(설계자가 정의한 관심 상황의 목록)가 있다.
- 어서션(SVA): “요청 뒤 3클럭 안에 응답” 같은 성질을 코드로 적어 시뮬레이션 중 자동 검사한다.
- 형식 검증: 시뮬레이션 대신 수학적으로 모든 입력에 대해 성질을 증명한다. RTL과 합성 후 넷리스트가 같은지 보는 등가성 검사(LEC)는 모든 칩이 거친다(3장).
- 에뮬레이션·FPGA 프로토타입: 시뮬레이터보다 수천 배 빨리 돌려 운영체제 부팅 같은 긴 시나리오를 돌린다.
무작위 테스트와 기능 커버리지
#10 같은 지연, initial 블록, 파일 입출력은 ASIC 표준 셀 흐름에서는 주로 테스트벤치에서 쓴다. 일반적인 ASIC 합성에서는 허용되지 않는다. 다만 FPGA 합성 도구는 initial에 지정한 RAM 초기 값을 구성 데이터로 만들 수 있다. 실행 중의 임의 파일 입출력·시간 지연과 이런 초기화 지원을 구분한다. 또 조합 블록에서 if에 else를 빠뜨리면 “값을 유지하라”는 뜻이 되어 래치가 생긴다. 래치는 타이밍 분석을 어렵게 하고 대부분 실수다. 그래서 린트(lint) 도구가 RTL 단계에서 이런 패턴을 경고한다.
핵심 정리
- RTL은 레지스터와 그 사이 조합 논리를 적는 수준이다. HDL 문장은 차례로 실행되지 않고 동시에 존재하는 회로다.
- 동기 설계에서 모든 플롭은 같은 클럭 엣지에 갱신되고, 입력 변화는 다음 엣지에 반영된다.
- 순차 블록은 <=, 조합 블록은 =. 의도에 맞게 선택하지 않으면 파이프라인 구조가 달라지고 별도 프로세스 사이의 경쟁도 생길 수 있다.
- FSM은 상태 레지스터 + 다음 상태 논리 + 출력 논리. 인코딩(이진·원-핫)이 면적과 속도를 바꾼다.
- 검증은 테스트벤치·무작위 자극·커버리지·어서션·형식 검증·에뮬레이션을 겹쳐 쓴다. 무작위만으로는 코너 케이스가 닫히지 않는다.
확인 퀴즈
always @(posedge clk) begin b <= a; c <= b; end를 합성하면?
조합 논리 블록 always @*에서 if (sel) y = a;만 쓰고 else를 빠뜨리면?
상태가 12개인 FSM을 원-핫으로 인코딩하면 필요한 플롭 수는?
RTL과 합성 후 게이트 넷리스트가 같은 기능인지 모든 입력에 대해 증명하는 방법은?