Menu

Verilog Case 문: 다중 분기 디코딩 제대로 하기

깔끔한 다중 분기 디코딩을 위한 case의 동작 방식, 절대 빠뜨리면 안 되는 default, 그리고 case, casex, casez의 차이.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

다중 분기

case는 Verilog의 평탄한 dispatch 구문입니다. 식을 주면 매칭 분기를 고릅니다.

case (expression)
    pattern_1: statement_1;
    pattern_2: statement_2;
    pattern_3: begin
        statement_3a;
        statement_3b;
    end
    default: default_statement;
endcase

구조적으로는 C의 switch와 비슷하지만,

  • break가 없습니다 - 각 분기는 다음 분기로 암시적으로 종료됩니다.
  • 패턴이 정수만이 아니라 vector도 됩니다.
  • synthesizer가 전체를 평탄한 mux(또는 패턴이 가능하게 하면 one-hot 디코더)로 변환합니다.

동작 예시: 4-to-1 Mux

case 본문에는 네 개의 명시적 패턴에 더해 default가 있습니다. synthesizer는 2비트 입력이 네 값 중 하나로 매핑되는 것을 보고 4-to-1 멀티플렉서를 내보냅니다. 깔끔하고, 평탄하고, 빠릅니다.

default가 필요한가

조합 case에서 default를 생략하는 건 else 없는 if와 같은 함정입니다: 매칭되지 않는 입력 값이 out을 미할당으로 두고, synthesizer는 latch를 inferred합니다.

2비트 sel이라면 위 패턴이 가능한 네 값을 모두 커버하므로 이론상 default는 불필요합니다. 실전에서는

  1. synthesizer가 항상 case가 exhaustive함을 증명하지는 못합니다.
  2. 시뮬레이션에서 selector가 xz일 수 있고, 이는 어떤 명시적 case와도 매칭되지 않습니다.
  3. 나중에 새 case를 추가하면 기본 동작이 명시되지 않은 채 남을 수 있습니다.

항상 default를 쓰세요. default가 도달 불가임을 아는 상태 머신과 mux 로직에서는 'x 대입:

default: out = 8'bx;

…이 synthesizer에게 "이건 don't-care이니 자유롭게 최적화하라"고 알리고, 만약 도달 불가 case가 실제로 도달되면 시뮬레이션에서 밝은 빨간 x로 드러납니다. 양쪽의 좋은 점을 다 갖는 셈입니다.

case로 상태 머신

case의 고전적 용도는 유한 상태 머신의 상태 전이 로직입니다.

case (state) 블록이 상태 전이 로직입니다. 각 분기가 다음 상태와 그 상태에 머물 시간을 결정합니다. 여기서 default는 도달 불가이지만(2비트 공간에서 RED/GREEN/YELLOW가 exhaustive) 안전망으로 둡니다 - state가 어쩌다 2'd3이 되면 FSM이 latch하지 않고 RED로 깔끔하게 reset됩니다.

Finite State Machines에서 이 패턴을 더 깊이 다룹니다.

한 분기에 여러 패턴

쉼표로 구분해 여러 패턴이 한 문장을 공유하게 할 수 있습니다.

case (opcode)
    4'h0, 4'h1, 4'h2: result = a + b;
    4'h3, 4'h4:       result = a - b;
    4'h8:             result = a & b;
    default:          result = 8'd0;
endcase

"빼기"인 opcode 둘, "더하기"인 opcode 셋. synthesizer는 비교기를 위해 패턴들을 OR로 묶습니다.

casezcasex: don't-care 매칭

일부 비트가 명시되지 않은 패턴 - "010으로 시작하는 어떤 opcode든" - 을 매칭하고 싶을 때가 있습니다.

casez (opcode)
    8'b010?_????: instruction = ALU_OP;
    8'b110?_????: instruction = LOAD_OP;
    8'b1110_????: instruction = JUMP_OP;
    default:      instruction = UNKNOWN;
endcasez

casez는 패턴의 ?(와 z)를 don't-care로 다룹니다. 각 ?는 0이나 1과 매칭됩니다. 일부 비트 위치가 특정 opcode 클래스에서 미사용인 명령어 포맷 디코딩에 유용합니다.

casex는 이를 x까지 don't-care로 확장합니다. casex는 위험합니다 - 초기화되지 않은 신호(시뮬레이션에서 x)가 모든 case와 매칭되어 놀라운 동작을 만들어 냅니다. 대부분의 현대 스타일 가이드가 casez를 권하고 casex를 금합니다.

SystemVerilog에는 가장 깔끔한 버전인 case inside도 있습니다 - 범위와 목록을 받아들임 - 일반 Verilog은 casez까지입니다.

case vs if/else if 체인

두 구조 모두 다중 분기 결정을 표현할 수 있지만, 다른 하드웨어로 synthesize됩니다.

  • case 는 평탄한 dispatch. synthesizer가 one-hot 디코더, 균형 잡힌 mux 트리 등 평탄 구조를 만들 수 있습니다. 평가 시간 상수.
  • if/else if 는 priority 체인. synthesizer가 단계마다 지연이 추가되는 cascade를 만듭니다. 로그 단위로 느립니다.

기능적으로는 겹칩니다. 스타일적으로는: 조건이 단일 식의 값에 관한 것이면 항상 case를 쓰세요. 진짜 priority가 있거나 조건이 서로 다른 신호를 다룬다면 if/else if를 쓰세요.

// case로 더 나음:
if      (sel == 2'd0) out = a;
else if (sel == 2'd1) out = b;
else if (sel == 2'd2) out = c;
else                  out = d;

// if/else if로 더 나음:
if      (urgent_event)  next_state = HANDLE_URGENT;
else if (timer_expired) next_state = TIMEOUT;
else if (data_ready)    next_state = PROCESS;
else                    next_state = state;

첫 셋은 모두 "sel이 무엇인가?"입니다 - case가 더 자연스럽게 읽히고 더 평탄하게 synthesize됩니다. 둘째 셋은 명백한 priority가 있는 독립 이벤트들입니다 - if/else if가 더 맞습니다.

다음에 볼 내용

이 장의 마지막 문서 - For Loops - 는 Verilog의 for와 synthesizable 코드에서 그것을 쓸 때 일어나는 놀라운 일을 다룹니다. 그다음 본격적으로 순차 논리와 FSM으로 넘어갑니다.

자주 묻는 질문

Verilog의 case 문이란?

case (expr) ... endcase는 Verilog의 다중 분기 구문입니다. 식을 한 번 평가해 매칭되는 분기로 dispatch합니다. 상태 머신, opcode 디코더, mux 선택기 등 상호 배타적인 여러 옵션 중에서 고르는 모든 경우에 정석 선택입니다.

case, casex, casez의 차이는?

casexz 값을 포함해 비트 정확히 매칭합니다. casez는 case item의 z(와 ?)를 don't-care로 다룹니다. casexxz 모두 don't-care로 다룹니다. don't-care 매칭은 일부 비트가 무관한 opcode 패턴에 유용하지만, casex는 초기화되지 않은 신호(x)가 우연히 모든 case와 매칭될 수 있어 시뮬레이션에서 위험합니다.

Verilog case 문에서 왜 default가 필요한가요?

default 없이는 어떤 case도 매칭되지 않을 가능성을 synthesis 도구가 보고, 출력 신호가 이전 값을 유지해야 한다고 판단해 원치 않는 latch를 inferred합니다. default 분기가 매칭되지 않은 모든 값을 처리합니다 - 보통 출력을 안전한 값으로 설정하거나 x 대입으로 case가 도달 불가임을 표시합니다. 항상 포함하세요.

case와 if-else 중 무엇을 써야 하나요?

조건이 상호 배타적이고 단일 식의 값에 따라 dispatch한다면 case를 쓰세요 - 상태 머신, opcode 디코더, mux 선택기. 진짜 priority 순서가 있거나 조건이 서로 다른 신호를 다룬다면 if/else를 쓰세요. case는 긴 else if 체인보다 더 평탄하고 빠른 하드웨어로 synthesize됩니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기