Verilog always 블록으로 조합 논리 구현: 반가산기부터 디코더까지
이전 글에서 assign 문으로 조합 논리를 다뤘다면, 이번엔 always 블록으로 같은 회로들을 구현합니다. always @(*) 패턴이 현대적 표준입니다.
예제 1: 기본 조합 논리 (5입력)
module combo (input a, b, c, d, e, output reg z);
always @(a or b or c or d or e) begin
z = ((a & b) | (c ^ d) & ~e); // 블로킹 할당(=) 사용
end
endmodule
중요: 조합 논리 always 블록에서는 블로킹 할당(=) 사용. 비블로킹(<=) 사용 시 시뮬레이션/합성 불일치 유발.
예제 2: 반가산기
module ha (input a, b, output reg sum, cout);
always @(a or b) begin
{cout, sum} = a + b; // 2비트 덧셈 결과 분해
end
endmodule
예제 3: 전가산기
module fa (input a, b, cin, output reg sum, cout);
always @(a or b or cin) begin
{cout, sum} = a + b + cin;
end
endmodule
예제 4: 2:1 멀티플렉서
module mux_2x1 (input a, b, sel, output reg c);
always @(a or b or sel) begin
c = sel ? a : b;
end
endmodule
예제 5: 1:4 디멀티플렉서
module demux_1x4 (input f, input [1:0] sel, output reg a, b, c, d);
always @(f or sel) begin
a = f & ~sel[1] & ~sel[0];
b = f & sel[1] & ~sel[0];
c = f & ~sel[1] & sel[0];
d = f & sel[1] & sel[0];
end
endmodule
예제 6: 4:16 디코더 (3:8 확장)
module dec_3x8 (input en, input [3:0] in, output reg [15:0] out);
always @(en or in) begin
out = en ? (1 << in) : 0;
end
endmodule
assign vs always @(*) 비교
| 측면 | assign | always @(*) |
| 문법 스타일 | 선언적 (데이터플로) | 절차적 (알고리즘적) |
| 복잡한 로직 | 한 줄에 넣기 어려움 | if/else, case, 루프 가능 |
| 디버깅 | 중간 신호 관찰 어려움 | 중간 변수, $display 활용 용이 |
| 합성 결과 | 동일 | 동일 (동등 회로 생성) |
| 할당 타입 | 연속 할당 | 블로킹(=) 필수 |
실무 권장사항
- 단순 조합 논리:
assign 간결함
- 복잡한 조건/케이스/우선순위:
always @(*) 가독성 우수
- SystemVerilog 사용 시
always_comb 블록 사용 (민감도 자동 + 블로킹 강제 + 의도 명시)
- 래치 유도 방지: 모든 경로에서 출력 할당 보장,
default 케이스 필수
래치 유도 방지 체크리스트
always @(*) begin
out = 0; // 기본값 선할당
case (sel)
2'b00: out = in0;
2'b01: out = in1;
2'b10: out = in2;
default: out = in3; // 모든 케이스 커버
endcase
end