Verilog로 조합 회로 기술하기: 조건부 연산자와 Always 블록
Verilog로 조합 논리를 모델링하는 방법 설명: 진리표 구현을 위한 조건부 연산자와 간결한 설계를 위한 강력한 'always' 블록 다루기
이 가이드에서는 Verilog로 조합 회로를 기술하는 핵심 기법을 다룹니다. 먼저 assign 키워드를 사용한 연속 할당 문법을 복습하고, 멀티플렉서 논리를 간결하게 표현하는 조건부 연산자(?:)를 소개합니다. 마지막으로 always 블록이 어떻게 더 명확한 절차적 설명을 제공하는지 보여줍니다.
앞서 assign 키워드를 사용한 연속 할당을 다뤘습니다. 이 문장은 활성 상태를 유지하며 회로의 게이트 레벨 설명을 얻도록 합니다. 예를 들어 다음 코드는 AND 게이트를 모델링합니다:
assign out1 = a & b;
Verilog의 조건부 연산자는 단일 할당이 조건에 따라 값을 변경하도록 합니다. 문법은:
assign [signal_name] = [conditional_expression] ? [value_if_true] : [value_if_false];
conditional_expression이 먼저 평가됩니다. 참이면 value_if_true가 signal_name에 할당되고, 그렇지 않으면 value_if_false가 사용됩니다. 예:
assign out1 = (sel) ? (a & b) : (a | b);
sel이 참이면 a와 b의 AND가 out1을 구동하고, 그렇지 않으면 a와 b의 OR이 선택됩니다. 이 한 줄로 2대1 멀티플렉서를 구현합니다.
조건부 연산자는 전통적 프로그래밍의 if 문처럼 동작하여 조합 논리의 더 추상적인 표현을 가능하게 합니다. 연산자를 중첩하면 복잡한 구조를 만들 수 있으며 예제 1에 나와 있습니다.
예제 1: 중첩 조건부 연산자
아래는 중첩 조건부 연산자로 구현된 4대2 우선순위 인코더입니다. 진리표는 그림 1에 나와 있습니다.

Verilog 코드는 다음과 같습니다:
module Prio_4_to_2 ( input wire [3:0] x, output wire [1:0] y, output wire v ); assign y = x[3] ? 2'b11 : x[2] ? 2'b10 : x[1] ? 2'b01 : 2'b00; assign v = ( x[3] | x[2] | x[1] | x[0] ) ? 1'b1 : 1'b0; endmodule
7-10라인은 중첩 조건부 연산자를 보여줍니다. 각 조건이 순차적으로 평가되어 앞선 항목에 더 높은 우선순위를 부여합니다—우선순위 논리에 완벽합니다. v에 대한 마지막 assign은 비트 OR을 사용하여 입력 중 어느 것이 높은지 나타냅니다.
Xilinx ISE 시뮬레이션 결과가 동작을 확인합니다(그림 2).

그림 2. 우선순위 인코더의 Xilinx ISE 시뮬레이션.
조건 표현식이 참 조건이 발견될 때까지 순서대로 평가되며, 해당 값이 할당된다는 점이 필수적입니다. 따라서 이 구조는 균형 잡힌 멀티플렉서(그림 4)보다는 우선순위 네트워크(그림 3)에 가장 적합합니다.

그림 3. 우선순위 네트워크.

그림 4. 입력 우선순위 없는 n대1 멀티플렉서.
이전 기사에서 VHDL 동시 할당을 논의했으며, 개념은 Verilog에도 유사하게 적용됩니다.
Verilog 절차적 문장
assign과 조건부 연산자가 많은 설계에 충분하지만, always 블록은 더 표현력 있고 절차적인 접근법을 제공합니다. always 블록 내부에서 문장은 순차적으로 실행되며 if와 case 같은 고수준 구문을 포함할 수 있어 인간 추론의 자연스러운 흐름을 반영합니다.
예제 2는 기본 always 블록으로 간단한 AND 게이트를 구현하는 것을 보여줍니다.
예제 2: "Always" 블록 문장
always 블록의 간소화된 문법은:
always @(sensitivity_list) begin sequential_statements; end
sensitivity_list는 블록이 재평가되어야 할 때를 지정합니다. 순수 조합 회로의 경우 모든 입력 신호가 이 목록에 있어야 합니다.
a나 b가 토글될 때마다 출력이 변경되는 그림 5의 회로를 고려하세요.

그림 5. Circuit_1
다음 always 블록은 블로킹 할당을 사용하여 AND 연산을 구현합니다:
always @(a, b) begin out1 = a & b; end
블로킹 할당(=)은 좌변을 즉시 업데이트하는 반면, 논블로킹 할당(<=)은 블록 끝까지 업데이트를 지연시킵니다. 조합 논리에서는 의도하지 않은 경합 조건을 피하기 위해 블로킹 할당을 사용하는 것이 가장 안전합니다.
절차적 블록은 wire를 구동할 수 없으므로 출력은 reg로 선언되어야 하며, 이는 저장 요소를 추론할 수도 있고 아닐 수도 있습니다. 전체 모듈은 다음과 같습니다:
module Circuit_1 ( input wire a, input wire b, output reg out1 ); always @(a, b) begin out1 = a & b; end endmodule
요약하면, 이 기사는 간결한 진리표 논리를 위한 조건부 연산자, 우선순위 인코딩을 위한 중첩 연산자, 더 명확한 조합 설명을 위한 절차적 always 블록을 다뤘습니다. 향후 글에서는 always를 순차 논리에 사용하는 방법을 탐구할 예정입니다.