
ALU는 산술(덧셈·뺄셈 등)과 논리(AND/OR/XOR 등) 연산을 수행하는 조합논리 블록
산술: ADD, SUB, (option) MUL, INC, DEC, ABS 등
논리: AND, OR, XOR, NOT, NAND, NOR 등
비트 연산 및 쉬프트: SHL (logical shift left), SHR (logical shift right), SRA (arithmetic right shift)
비교: A==B, A<B, A>B (결과를 플래그로 또는 1/0으로 반환)
조건적 동작: 선택한 연산을 op 신호로 결정

A[W] ----┐
├─> 산술 유닛 (Adder/Subtractor) ┐
B[W] ----┘ ├─> 멀티플렉서(op) ─> Y
│
A,B ----> 논리 유닛 (AND/OR/XOR/NOT/...) ──┘
(쉬프트 유닛) -----------------------------┘

AND, OR 연산하는 회로. a랑 b랑 AND연산의 결과를 MUX의 0과 매핑을 하고 OR연산을 할 때는 1과 매핑을 한다. 그럴 시에 MUX에 0을 인가하면은 AND연산을, 1을 인가하면은 OR연산을 수행

ADDER를 추가하고 MUX 3에 매핑

이전에 배운것 처럼 빼기를 하려면 B를 2's complement + 1 하면된다 b에 inverter를 붙이고 그 결과를 새로운 MUX에 붙인다.
Carryin 과 새로운 MUX를 조정해 덧셈기를 뺄셈으로 만들 수 있다.

NOR, NAND는 드모르간 법칙을 이용
이미 b에는 인버터가 있으니, a에 인버터와 먹스를 추가하면 NOR, NAND를 구현할 수 있다.

Adder 타입
Ripple-carry adder: 구조가 단순하지만 느림 (비트 수 커질수록 지연 증가)
Carry-lookahead / Carry-select / Carry-skip: 더 빠른 대안(복잡도↑)
Subtraction: 대부분 2's complement 방식으로 구현 → B를 반전(XOR)하고 Cin=1으로 덧셈 수행
Overflow 판단 (signed): overflow = carry_into_MSB ^ carry_out_of_MSB
Carry 플래그 (unsigned): 최종 MSB의 carry-out
Zero (Z): 결과가 0인가?
Carry (C): unsigned 연산에서의 최종 올림
Overflow (V): signed 연산에서 오버플로우 발생 여부
Negative / Sign (N): 결과의 MSB(Signed) by using SLT
연산 결과가 0인지 아닌지를 알려주는 신호입니다.
ALU 연산 결과가 0000...0000 (모든 비트가 0)이면 1
그 외의 경우엔 0
assign Zero = (Result == 0);
결과가 표현할 수 있는 범위를 벗어났다
오버플로우는 부호 있는(signed) 연산에서만 의미가 있음
보통 덧셈은 필요 bit수를 1bit 늘리고
곱셈은 input bit를 전부 더하면 overflow가 발생하지 않는다.
디지털 회로(특히 2’s complement 표현)에서 4비트 정수라면
표현 가능한 범위는 -8 ~ +7
2진수 10진수
0111 +7
1000 -8
즉, 4비트 signed 값으로는 8이나 -9를 표현할 수 없음
그런데 덧셈 결과가 그 범위를 넘으면
결과가 “틀린 방향으로 wrap-around” 되기 때문에
이때 Overflow flag (V) 를 1로 세우게됨
Overflow 발생 조건 (2’s complement 기준)
➤ 덧셈의 경우:
두 양수끼리 더했는데 음수가 되면 overflow
두 음수끼리 더했는데 양수가 되면 overflow
➤ 뺄셈의 경우:
양수에서 음수를 빼서 음수가 되면 overflow
음수에서 양수를 빼서 양수가 되면 overflow
즉, 입력 두 수의 부호가 같고 결과의 부호가 다를 때 overflow입니다.
Overflow = Carryin_MSB XOR Carryout_MSB
이 공식이 ALU 코드에서 assign Overflow = C[3] ^ C[2];
로 표현되어 있었죠.
C[2] : 최상위 비트 이전 자리에서 넘어온 캐리
C[3] : 최상위 비트(부호비트)에서 넘어간 캐리
이 둘이 다르면 부호비트 계산이 “틀린” 경우이기 때문
오버플로우 발생
A = 0100 (4)
B = 0101 (5)
----------------
Y = 1001 (-7) ← 부호비트가 잘못됨!
C_in_to_MSB = 0
C_out_of_MSB = 1
=> Overflow = 1
실제로는 +9가 되어야 하는데, 4비트로는 표현 불가능 → 오버플로우!
A = 1000 (-8)
B = 1001 (-7)
----------------
Y = 0001 (+1)
C_in_to_MSB = 1
C_out_of_MSB = 0
=> Overflow = 1
음수 + 음수 = 양수가 나옴 → 부호 뒤틀림
Carry: unsigned에서 자리올림, 0~15처럼 "부호 없는 수" 연산
Overflow: signed(부호 있는 수)에서 부호 오류, 2’s complement 표현에서 사용
Carry → 하드웨어적인 “자리 올림/내림”
Overflow → “수학적으로 결과 부호가 틀림”
SLT (Set on Less Than) 는 비교 연산을 수행할 때 자주 등장하는 기능
CPU 명령어(slt, sltu)나 비교기 설계에도 필수적으로 들어감
A < B이면 결과를 1로, 그렇지 않으면 0으로 설정(Set)한다”는 뜻이에요
ALU는 보통 여러 연산(ADD, SUB, AND, OR, XOR...)을 하는데,
SLT는 그중 “비교 연산(compare)” 역할이에요.
실제로 CPU(MIPS, RISC-V 등)에서는
slt 명령어가 바로 이 ALU 연산으로 구현됩니다.
A < B ?
이걸 직접 비교하는 대신, A-B의 결과(sign bit)를 사용
Diff = A - B 계산
Diff의 부호비트(MSB) 를 확인
1 → 음수 → A < B
0 → 양수 또는 0 → A ≥ B
SLT = (A - B)의 부호비트
단순히 A-B의 부호비트로만 판단하면
overflow가 생긴 경우 잘못된 결과가 나올 수 있습니다.
그래서 아래와 같이 계산
SLT = Overflow ⊕ Sign
뺄셈 결과의 부호비트(Sign)과 Overflow 플래그를 XOR 하면 정확한 비교 결과가 나옵니다.
SLT (signed) → 위에서 설명한 “overflow XOR sign” 사용
SLTU (unsigned) → carry bit 반전으로 표현 가능 (SLTU = ~CarryOut)
일반적으로 op는 2~4비트로 정의: 예)
000 = ADD
001 = SUB
010 = AND
011 = OR
100 = XOR
101~ = shift / compare 등 추가
ALU는 case(op)로 각 유닛 출력 중 하나를 Y로 선택
A,B 입력이 들어옴
산술 유닛은 동시에 A+B(또는 A-B) 연산 수행 → addsum, addcout[] 생성
논리 유닛은 A&B, A|B, A^B를 동시에 계산
멀티플렉서가 op에 따라 최종 Y 선택
플래그는 산술 유닛 결과 및 Y 값으로 계산되어 출력

**// 1-bit Full Adder**
module full_adder(
input a, b, cin,
output sum, cout
);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
**// 4-bit Ripple Carry Adder/Subtractor**
module ripple_carry_adder_subtractor(
input [3:0] A, B,
input SUB, // 0=ADD, 1=SUB
output [3:0] S,
output CarryOut,
output Overflow
);
wire [3:0] Bc;
wire [3:0] C; // 내부연결을 위해 carryout을 직접쓰는것이 아니라 C를 하나 만듬
assign Bc = B ^ {4{SUB}}; // SUB=1이면 B 반전
full_adder fa0 (A[0], Bc[0], SUB, S[0], C[0]);
full_adder fa1 (A[1], Bc[1], C[0], S[1], C[1]);
full_adder fa2 (A[2], Bc[2], C[1], S[2], C[2]);
full_adder fa3 (A[3], Bc[3], C[2], S[3], C[3]);
assign CarryOut = C[3];
assign Overflow = C[3] ^ C[2]; // signed overflow
endmodule
// ==================================
// ALU with SLT
// op code table:
// 000 = ADD
// 001 = SUB
// 010 = AND
// 011 = OR
// 100 = XOR
// 101 = SLT
// ==================================
module ALU4bit (
input [3:0] A, B,
input [2:0] op,
output reg [3:0] Y,
output reg CarryOut,
output reg Overflow,
output Zero
);
wire [3:0] addsub_result;
wire addsub_carry, addsub_overflow;
ripple_carry_adder_subtractor addsub(
.A(A), .B(B), .SUB(op == 3'b001 || op == 3'b101),
.S(addsub_result), .CarryOut(addsub_carry), .Overflow(addsub_overflow)
);
// Logic results
wire [3:0] and_result = A & B;
wire [3:0] or_result = A | B;
wire [3:0] xor_result = A ^ B;
// SLT (signed less than)
wire slt_result = addsub_overflow ^ addsub_result[3];
always @(*) begin
case (op)
3'b000: begin // ADD
Y = addsub_result;
CarryOut = addsub_carry;
Overflow = addsub_overflow;
end
3'b001: begin // SUB
Y = addsub_result;
CarryOut = addsub_carry;
Overflow = addsub_overflow;
end
3'b010: begin // AND
Y = and_result;
CarryOut = 0;
Overflow = 0;
end
3'b011: begin // OR
Y = or_result;
CarryOut = 0;
Overflow = 0;
end
3'b100: begin // XOR
Y = xor_result;
CarryOut = 0;
Overflow = 0;
end
3'b101: begin // SLT
Y = {3'b000, slt_result}; // SLT 결과는 1비트만 의미
CarryOut = 0;
Overflow = addsub_overflow;
end
default: begin
Y = 4'b0000;
CarryOut = 0;
Overflow = 0;
end
endcase
end
assign Zero = (Y == 4'b0000);
endmodule
output 은 조합회로에서 사용 // assign 문으로 연결되는 wire 타입, 저장안됨
output reg는 순차회로에서 사용 // always문, <=, 저장가능
`timescale 1ns/1ps
module ALU4bit_TB;
reg [3:0] A, B;
reg [2:0] op;
wire [3:0] Y;
wire CarryOut, Overflow, Zero;
ALU4bit uut (
.A(A), .B(B), .op(op),
.Y(Y), .CarryOut(CarryOut), .Overflow(Overflow), .Zero(Zero)
);
initial begin
$monitor("op=%b | A=%b(%0d), B=%b(%0d) => Y=%b(%0d), C=%b, V=%b, Z=%b",
op, A, $signed(A), B, $signed(B), Y, $signed(Y), CarryOut, Overflow, Zero);
// ADD
op=3'b000; A=4'd3; B=4'd5; #5;
// SUB
op=3'b001; A=4'd7; B=4'd2; #5;
// AND
op=3'b010; A=4'b1100; B=4'b1010; #5;
// OR
op=3'b011; A=4'b1100; B=4'b1010; #5;
// XOR
op=3'b100; A=4'b1100; B=4'b1010; #5;
// SLT
op=3'b101; A=4'b0011; B=4'b0101; #5; // 3 < 5 → Y=0001
op=3'b101; A=4'b0111; B=4'b0010; #5; // 7 < 2 → Y=0000
op=3'b101; A=4'b1100; B=4'b0100; #5; // -4 < 4 → Y=0001
op=3'b101; A=4'b0100; B=4'b1100; #5; // 4 < -4 → Y=0000
#5 $finish;
end
initial begin
$dumpfile("alu_slt.vcd");
$dumpvars(0, ALU4bit_TB);
end
endmodule