Menu

Verilog X와 Z 값: unknown과 high-impedance 신호

Verilog 신호는 2개가 아니라 4개의 가능한 값을 가집니다. x(unknown)와 z(high-impedance)가 시뮬레이션에서 실제로 무엇을 의미하는지, 그리고 어떻게 디버그할지 알아봅니다.

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

2-state vs 4-state logic

소프트웨어에서 비트는 0 또는 1입니다. Verilog에서 비트는 네 가지 값 중 하나일 수 있습니다.

  • 0 - wire가 low로 구동됨.
  • 1 - wire가 high로 구동됨.
  • x - wire의 값이 unknown 임. 시뮬레이터가 알 수 없음.
  • z - wire가 high-impedance 임. 아무것도 구동하지 않음.

이 4-state 모델은 하드웨어도 같은 문제를 갖기 때문에 존재합니다. 실제 wire는 low로 묶이거나, high로 묶이거나, undefined(두 소스가 다툼)이거나, floating(드라이버 없음)일 수 있습니다. 시뮬레이터가 유용하려면 네 가지를 모두 모델링해야 합니다.

x가 등장하는 방식

이걸 실행하고 출력을 보세요.

a는 선언되었으나 결코 쓰여지지 않아 x 상태에 머뭅니다. x + 5x를 만듭니다 - unknown과의 어떤 산술도 unknown을 만듭니다. 출력에는 x가 aaaa만큼 보입니다.

설계에서 x의 흔한 출처.

  • 선언되었지만 reset되지 않은 reg(synthesis 가능 Verilog의 대부분은 명시적 reset으로 클리어).
  • default가 없는 case 문에서 어떤 case와도 매칭되지 않는 입력에 부딪힘.
  • 리팩터링 후 유일한 드라이버를 잃은 wire.
  • 한 분기는 신호를 대입하는데 다른 분기는 대입하지 않는 if/else 체인(커버되지 않으면 x를 latch).
  • vector나 array의 끝을 넘어 읽기.

X 전파: 작은 x가 모든 걸 망친다

x의 잔인한 점은 전파된다는 것입니다. 피연산자의 단 한 x 비트가 전체 결과를 x로 만듭니다.

0 & x0이고(AND-with-0은 항상 0), 1 | x1(OR-with-1은 항상 1)입니다. 시뮬레이터는 비트별로 보수적이지만 항등법칙은 존중합니다. 산술과 비교는 그렇게 관대하지 않습니다.

이게 단 하나의 초기화 안 된 register가 출력 버스 전체를 xxxx로 만들 수 있는 이유입니다. 어떤 x에서든 거꾸로 추적하면 출처를 찾을 수 있습니다.

z가 등장하는 방식

z는 아무도 구동하지 않는 wire의 값입니다.

스니펫의 두 패턴.

  • floating은 선언만 되고 결코 구동되지 않습니다. 기본이 z.
  • data_out은 의도적인 tri-state입니다. enable이 low이면 출력을 명시적으로 z로 풉니다. 그게 bus 드라이버가 "놓아주는" 방식이고, 다른 드라이버가 인계받을 수 있게 합니다.

내부 로직에서 z는 거의 항상 잘못입니다. 양방향 핀이나 공유 bus에서는 z가 딱 맞습니다.

== vs === 비교

일반 등호 연산자 ==는 피연산자에 x 또는 z 비트가 있으면 x를 반환합니다.

===(과 그 짝 !==)는 xz를 포함한 엄격한 비트별 비교를 합니다. testbench에서 x/z에 대해 테스트할 때마다 사용하세요. ===는 synthesis 가능하지 않지만, testbench의 initial 블록 안에서는 상관없습니다.

$isunknown(expr) 시스템 함수는 "이 식에 xz 비트가 있나?"를 묻는 가장 깔끔한 방법입니다 - 있으면 1, 없으면 0.

의도적 don't-care로서의 x

논쟁의 여지가 있지만 정당한 패턴: 상태 머신 default case의 'x는 synthesizer에게 "이 상태는 도달 불가이니 자유롭게 최적화하라"고 알립니다.

case (state)
    IDLE:    next_state = go ? RUNNING : IDLE;
    RUNNING: next_state = done ? IDLE : RUNNING;
    default: next_state = 'x;   // 도달 불가
endcase

Synthesizer는 x를 활용해 상태를 합치고 게이트 개수를 줄일 수 있습니다. 시뮬레이션에서 추론이 틀려 default가 도달 되면 next_state에서 x가 전파되어 버그가 즉시 보입니다.

이건 default가 정말로 도달 불가인지 충분히 생각했을 때만 쓰세요. 그렇지 않다면 안전한 상태로 default를 설정하세요.

흔한 디버깅 레시피

x로 가득한 파형을 보고 있다면 레시피는

  1. 가장 이른 x를 찾으세요. 파형을 시간 거꾸로 따라가세요. 처음 x가 된 신호가 출처에 가장 가깝습니다.
  2. 드라이버를 찾으세요. 소스를 여세요. 이 신호를 무엇이 대입합니까? assign? always 블록?
  3. 드라이버의 입력을 확인하세요. 드라이버의 RHS에 x가 있다면 전파가 제대로 일어난 것입니다 - 버그는 상류에 있습니다.
  4. 드라이버 입력은 깨끗한데 출력이 x라면 드라이버가 불완전합니다. default 누락된 case, else 누락된 if, reset 누락된 register.

대부분의 x 폭풍 버그는 다음 중 하나로 귀결됩니다: 누락된 reset, 누락된 default, 연결되지 않은 서브모듈.

다음에 볼 내용

이제 완전한 데이터 타입 이야기 - wire/reg, vector, parameter, number literal, 4-state logic 모델 - 를 갖췄습니다. 다음 장은 그걸 모두 써서 식을 만들기 시작합니다 - 모든 종류의 연산자, 그 안에는 소프트웨어에서 말이 안 되는 비트 레벨 연산자도 포함됩니다.

자주 묻는 질문

Verilog에서 x는 무엇을 의미하나요?

xunknown 값입니다. x인 신호는 0일 수도 1일 수도 있습니다 - 시뮬레이터가 알 수 없습니다. 신호가 구동되지 않았거나, 두 드라이버가 충돌하거나, reset 전에 register를 읽거나, undefined behavior가 조용히 전파될 만한 어디서나 등장합니다. x는 버그 신호로 다루세요 - 거의 절대 원하던 값이 아닙니다.

Verilog에서 z는 무엇을 의미하나요?

zhigh-impedance 값 - wire가 전혀 구동되지 않는 상태입니다. tri-state 출력(데이터 버스, 양방향 핀)의 정당한 상태이지만, 내부 신호에서 z는 보통 '아무것도 연결되어 있지 않다'는 실수입니다. Synthesizer는 명시적 tri-state output enable 밖의 대부분의 z 패턴을 거부합니다.

Verilog 출력이 왜 xxxx인가요?

거의 항상 신호가 무엇으로도 구동되지 않거나, 연산이 다른 신호에서 x를 전파했기 때문입니다. 거꾸로 추적하세요: 어느 신호가 x인지, 무엇이 그것을 공급하는지, 드라이버가 활성인지. 흔한 원흉은 case 문의 default 누락, reset되지 않은 register, 리팩터링 후 드라이버를 잃은 wire입니다.

Verilog에서 x나 z를 어떻게 검사하나요?

xz를 포함해 비트 단위로 정확하게 비교하는 === 연산자를 사용합니다. a === 1'bxa가 실제로 x일 때 참입니다. 일반 ==는 피연산자에 x 비트가 있으면 x를 반환하므로, a == 1'bx는 원하는 답이 결코 아닙니다. 깔끔한 boolean인 $isunknown(a)도 있습니다.

Verilog에서 x를 기본값으로 대입할 수 있나요?

네, 그리고 case 문에서 의도적인 기법입니다: default: out = 'x;는 synthesizer에게 '이 case는 결코 일어나지 않음을 약속하니 자유롭게 최적화하라'고 알립니다. 비용은 시뮬레이션에서 만약 일어난다면 x가 전파되어 버그가 드러난다는 점입니다. 기본 case를 작성하지 않으려는 회피책이 아니라, default가 도달 불가임을 이미 증명했을 때 사용하세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기