조건에따라 다른 동작을 수행하는 구문
HW 적으로는 MUX로 합성된다.
if (조건1)
문장1;
else if (조건2)
문장2;
else if (조건3)
문장3;
else
문장4; // 기본값
if 문은 위와 같은 문법으로 코드를 짜면된다.
만약 각 if,else if, else 문 내부에 한 줄이 아니라 여러 줄의 코드를 적고 싶은 경우
begin-end 블록을 사용하면된다.
여러 문장을 묶을 때 사용한다
if (조건) begin
문장1;
문장2;
문장3;
end
else begin
문장4;
문장5;
end
위와 같이 begin~end 블록을 사용하면 if, else if, else 문 내부에 한 줄이 아니라 여러 줄의 코드를 적을 수 있다.
// 조합 논리
always @(*) begin
if (sel)
y = b;
else
y = a;
end
always @(*) begin
if (req[3])
grant = 2'd3;
else if (req[2])
grant = 2'd2;
else if (req[1])
grant = 2'd1;
else if (req[0])
grant = 2'd0;
else
grant = 2'd0;
end
// ❌ 래치 발생!
always @(*) begin
if (sel)
y = a;
// else 없음 → y가 유지됨 → 래치!
end
// ✅ 래치 방지
always @(*) begin
if (sel)
y = a;
else
y = b; // 모든 경로 커버
end
// ✅ 디폴트 값 패턴
always @(*) begin
y = b; // 디폴트 먼저
if (sel)
y = a;
end
순차 논리에서는 메모리 특성이 있는게 정상이므로 굳이 else 쓸 필요 없다.
조건에따라 다른 동작을 수행하는 구문
case 문은 if 문보다 가독성이 좋고, 병렬 선택 구조이다.
case (선택신호)
값1: 문장1;
값2: 문장2;
값3: 문장3;
default: 기본문장;
endcase
case문에서도, 각 case에 여러 문장을 적고싶다면 begin-end 블록을 활용하면된다.
always @(*) begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
2'b11: y = d;
endcase
end
module alu (
input wire [7:0] a, b,
input wire [2:0] op,
output reg [7:0] result
);
always @(*) begin
case (op)
3'b000: result = a + b; // ADD
3'b001: result = a - b; // SUB
3'b010: result = a & b; // AND
3'b011: result = a | b; // OR
3'b100: result = a ^ b; // XOR
3'b101: result = ~a; // NOT
3'b110: result = a << 1; // SHL
3'b111: result = a >> 1; // SHR
endcase
end
endmodule
앞서 if문에서와 마찬가지로, 조합 논리 회로의 경우, 의도치 않은 래치의 생성을 조심해야한다.
이를 위해 case문의 마지막에 default를 추가해주면 된다.
z(고임피던스)를don't care로 처리하는 case문
? 를 dont' care 표기로 사용한다.
always @(*) begin
casez (opcode)
4'b1???: y = a; // 상위 1비트가 1이면 (나머지 무관)
4'b01??: y = b; // 상위 2비트가 01이면
4'b001?: y = c; // 상위 3비트가 001이면
4'b0001: y = d; // 정확히 0001
default: y = 8'b0;
endcase
end
위와 같이 사용할 수 있다.
always @(*) begin
casez (instruction[15:12])
4'b0000: alu_op = ADD;
4'b0001: alu_op = SUB;
4'b001?: alu_op = AND; // 0010, 0011 둘 다 AND
4'b01??: alu_op = LOAD; // 0100~0111 모두 LOAD
4'b1???: alu_op = JUMP; // 1000~1111 모두 JUMP
default: alu_op = NOP;
endcase
end
x(불확정)와z모두don't care로 처리
always @(*) begin
casex (data)
4'b1xxx: result = 1; // 최상위 비트가 1이면
4'b01xx: result = 2;
4'b001x: result = 3;
4'b0001: result = 4;
default: result = 0;
endcase
end
위와 같이 사용할 수 있다.
한 줄로 표현하는 if-else 문
결과 = 조건 ? 참일때값 : 거짓일때값;
// if-else 버전
always @(*) begin
if (sel)
y = b;
else
y = a;
end
// 삼항 연산자 버전 (위의 if-else문과 동일하다)
assign y = sel ? b : a;
// 4:1 MUX
assign y = (sel == 2'b00) ? a :
(sel == 2'b01) ? b :
(sel == 2'b10) ? c :
d ;
중첩 구조:
sel==00? ──yes──▶ a
│
no
↓
sel==01? ──yes──▶ b
│
no
↓
sel==10? ──yes──▶ c
│
no
↓
d
이런식으로 삼항 연산자를 중첩해서 사용할수도 있다.
module simple_control (
input wire [3:0] opcode,
input wire zero_flag,
output reg reg_write,
output reg mem_read,
output reg mem_write,
output reg branch,
output reg [1:0] alu_op
);
// 명령어 정의
localparam ADD = 4'b0000;
localparam SUB = 4'b0001;
localparam AND = 4'b0010;
localparam OR = 4'b0011;
localparam LW = 4'b0100; // Load Word
localparam SW = 4'b0101; // Store Word
localparam BEQ = 4'b0110; // Branch if Equal
localparam BNE = 4'b0111; // Branch if Not Equal
always @(*) begin
// 디폴트 값 (래치 방지)
reg_write = 1'b0;
mem_read = 1'b0;
mem_write = 1'b0;
branch = 1'b0;
alu_op = 2'b00;
case (opcode)
ADD, SUB, AND, OR: begin
reg_write = 1'b1;
alu_op = opcode[1:0];
end
LW: begin
reg_write = 1'b1;
mem_read = 1'b1;
end
SW: begin
mem_write = 1'b1;
end
BEQ: begin
branch = zero_flag; // 같으면 분기
end
BNE: begin
branch = ~zero_flag; // 다르면 분기
end
default: begin
// 디폴트 값 유지
end
endcase
end
endmodule