Computer Architecture & Digital Electronics

Design and Working of a
4-Bit Arithmetic Logic Unit (ALU)

A detailed study of the Logic Unit, Arithmetic Unit, and Multiplexer, with interactive animated circuit diagrams, real-time signal propagation, and dynamic truth tables.

⚡ Master 4-Bit ALU Interactive Lab

Test any 4-bit binary inputs, switch control lines, and inspect live data flow across the Logic and Arithmetic units in real time.

Presets:
Operand A (A3 A2 A1 A0) 1011b = 11d
Operand B (B3 B2 B1 B0) 0011b = 3d
ALU Operation Select (S2, S1, S0)
S2 UNIT SELECTOR:
OPERATION CHIPS:
ALU Output Bus F [3..0]
1000
SUBTRACT | Dec: 8 | Unit: Arithmetic Unit
STATUS FLAGS:
Cout: 1 (No Borrow / Carry)
Zero (Z): 0
Negative (N): 1
Overflow (V): 0
Figure 1. Block Diagram of 4-Bit ALU — Live Dataflow & Routing Live Signal Visualizer
Section 1

1. Introduction

An Arithmetic Logic Unit (ALU) is the part of a digital system that actually does the computing. Every time a processor adds two numbers, compares them, or performs a bitwise operation such as AND or OR, it is the ALU that carries out the work. It sits at the core of the datapath of any processor, microcontroller, or digital calculator, and its design directly affects how fast and how efficiently a system can execute instructions.

A 4-bit ALU is a simplified version of this circuit that operates on two 4-bit binary numbers, commonly labelled A (A3 A2 A1 A0) and B (B3 B2 B1 B0). Despite its small word size, a 4-bit ALU contains every structural idea found in the ALU of a full 32-bit or 64-bit processor. Understanding it in depth is therefore the natural first step before studying larger, more complex arithmetic units.

Three Cooperating Sub-Circuits

A 4-bit ALU is generally built from three cooperating sub-circuits:

  • The Logic Unit: Performs bitwise Boolean operations such as AND, OR, XOR, and NOT across parallel bit slices.
  • The Arithmetic Unit: Performs binary addition and subtraction using full adders and two's complement arithmetic.
  • The Multiplexer (MUX): Selects whichever of the two results, logical or arithmetic, the control lines have asked for, and routes it to the final output bus F.

The sections that follow examine each of these blocks individually, build them up gate by gate, and finish by combining them into the complete 4-bit ALU. Every stage includes its logic diagram and truth table so that the behaviour of the circuit can be verified bit by bit.

Section 2

2. Block-Level Overview of the 4-Bit ALU

Before looking inside each unit, it helps to see how the three blocks are wired together. Both operands, A and B, are 4 bits wide and are fed in parallel to the Logic Unit and to the Arithmetic Unit. Each unit produces its own 4-bit result. A set of select lines chooses which specific operation each unit performs internally (for example, whether the Logic Unit outputs A AND B or A XOR B), and one further select line decides, through the multiplexer, whether the final output F is the logical result or the arithmetic result.

Carry-in (Cin) and Carry-out (Cout) Status Signals

Two additional signals appear in the ALU system diagram: Cin is the carry-in bit supplied to the Arithmetic Unit, used together with the mode signal M to switch between addition and subtraction. Cout is the carry-out (or borrow-complement) bit produced by the Arithmetic Unit, and is normally made available as a status flag for the processor to check for overflow or borrow conditions.

Section 3

3. The Logic Unit

The Logic Unit performs bitwise Boolean operations on the two input words. Because these operations act independently on each bit position, the Logic Unit for a 4-bit ALU is simply four identical 1-bit slices working side by side: one for bit 0, one for bit 1, and so on. Studying a single bit slice therefore explains the behaviour of the entire 4-bit block.

3.1 A Single Bit-Slice of the Logic Unit

Within each bit slice, the two operand bits Ai and Bi are fed simultaneously into four different gates: an AND gate, an OR gate, an XOR gate, and a NOT gate (which only needs Ai). All four results are computed at the same time, and a small 4-to-1 multiplexer, controlled by two select lines S1 and S0, chooses which one of the four results is passed through as the output bit Fi.

This 'compute everything, then select' approach is what allows the operation to change instantly just by changing S1 S0, without any rewiring of the gates themselves.

Figure 2. Interactive 1-Bit Slice of the Logic Unit (Gates + 4:1 MUX) Interactive Simulation
Inputs:
S1 S0 Select:

3.2 Function-Select Table for Logic Unit

Select lines S1 and S0 control the internal 4:1 multiplexer. Click any row below to test:

S1 S0 Selected Function Output Fi
0 0 AND Ai · Bi
0 1 OR Ai + Bi
1 0 XOR Ai ⊕ Bi
1 1 NOT (complement of A) Ai′

3.3 Bitwise Truth Tables

Each gate inside the bit slice behaves according to its own truth table (Click any row to test inputs Ai, Bi):

Ai Bi AND (Ai · Bi) OR (Ai + Bi) XOR (Ai ⊕ Bi)
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

NOT: Ai′ = 1 when Ai = 0, and Ai′ = 0 when Ai = 1 (it does not depend on Bi).

Section 4

4. The Arithmetic Unit

The Arithmetic Unit is responsible for binary addition and subtraction. It is built up in three stages: first the half adder, which combines two single bits; then the full adder, which extends this to include a carry-in so that adders can be chained together; and finally the adder-subtractor, which reuses the same full adder hardware for both addition and subtraction by manipulating the B input with a set of XOR gates.

4.1 The Half Adder

A half adder adds two single bits, A and B, and produces two outputs: the Sum bit and the Carry bit. The Sum is simply the XOR of the two inputs, since the result is 1 only when exactly one of the inputs is 1. The Carry is the AND of the two inputs, since a carry into the next bit position is only generated when both inputs are 1 (1 + 1 = 10 in binary).

Figure 3. Logic diagram of a half adder: SUM = A ⊕ B, CARRY = A · B

Half Adder Truth Table

Click any row to test inputs A and B:

A B SUM (A ⊕ B) CARRY (A · B)
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1

Limitation of the Half Adder

The half adder has no input to accept a carry coming from a previous, less significant bit position. Therefore, half adders cannot be chained alone to add multi-bit numbers—which is the exact challenge solved by the Full Adder.

4.2 The Full Adder

A full adder extends the half adder by accepting a third input, the carry-in (Cin), in addition to A and B. It produces a Sum output and a carry-out (Cout). Internally, a full adder can be built from two half adders and an OR gate:

  • The first half adder combines A and B.
  • The second half adder combines that partial sum with Cin to produce the final SUM = A ⊕ B ⊕ Cin.
  • The OR gate combines both half adders' carry outputs to produce Cout = (A · B) + (Cin · (A ⊕ B)).
Figure 4. Logic diagram of a full adder built from two half adders and an OR gate Probes Active

Full Adder Truth Table (Click any combination to test)

A B Cin SUM (A ⊕ B ⊕ Cin) Cout ((A·B) + Cin·(A⊕B))
000 00
001 10
010 10
011 01
100 10
101 01
110 01
111 11

4.3 Binary Subtraction Using Two's Complement

Rather than building an entirely separate subtractor circuit, digital systems almost always perform subtraction by adding the two's complement of the number being subtracted. The two's complement of B is obtained by inverting every bit of B (giving B′, the one's complement) and then adding 1. Since A − B = A + (−B), and −B in two's complement form is B′ + 1, subtraction becomes:

A − B = A + B′ + 1

This is extremely convenient because the same full adder hardware already built for addition can also perform subtraction, provided two things are arranged: the B input must be selectively inverted, and a 1 must be injected into the carry-in of the least significant bit. Both of these are controlled by a single mode signal, M.

4.4 The 1-Bit Adder-Subtractor

The trick that merges addition and subtraction into one circuit is to place an XOR gate in front of the B input of each full adder, with the mode signal M as the XOR's second input:

  • When M = 0: B ⊕ 0 = B, so the adder receives B unchanged. With Cin = M = 0, it computes A + B.
  • When M = 1: B ⊕ 1 = B′ (the bitwise complement). With Cin = M = 1, it computes A + B′ + 1, which is exactly A − B!
Figure 5. Interactive Single-Bit Adder-Subtractor (XOR Conditional Inverter + Full Adder)
M B fed to adder (B ⊕ M) Cin Operation performed Result
0 B (unchanged) 0 Addition F = A + B
1 B′ (complemented) 1 Subtraction F = A − B

4.5 The 4-Bit Ripple-Carry Adder-Subtractor

To handle full 4-bit operands, four of the 1-bit adder-subtractor stages shown above are connected in a chain, one for each bit position. Every stage receives the corresponding bits Ai and Bi, and every stage's B input passes through its own XOR gate controlled by the same mode signal M.

The carry-out of each stage feeds directly into the carry-in of the next more significant stage, and the mode signal M itself supplies the carry-in of the very first (least significant) stage. Because the carry has to 'ripple' from the least significant bit through to the most significant bit before the final result is valid, this structure is called a ripple-carry adder-subtractor.

Figure 6. Complete 4-Bit Ripple-Carry Adder-Subtractor Pipeline (FA0 to FA3) Ripple Animator
Ripple Operand A
Ripple Operand B
Mode M:

4.6 Worked Example Walkthrough

As a concrete check, consider A = 1011 (decimal 11) and B = 0011 (decimal 3), with M = 1 so the circuit is asked to compute A − B.

Step 0: Initial Setup & Two's Complement Inversion
Operands: A = 1011₂ (11) and B = 0011₂ (3). Mode M = 1 (Subtraction). Every bit of B is inverted through XOR gates: B′ = B ⊕ 1 = 1100₂. Initial carry Cin0 = M = 1 is injected into Bit 0.
Bit position Ai Bi′ = Bi ⊕ M Carry in (Cin) Sum (Si) Carry out (Cout)
Bit 0 (LSB) 1 0 (1 ⊕ 1) 1 (M = 1) 0 1
Bit 1 1 0 (1 ⊕ 1) 1 (from Bit 0) 0 1
Bit 2 0 1 (0 ⊕ 1) 1 (from Bit 1) 0 1
Bit 3 (MSB) 1 1 (0 ⊕ 1) 1 (from Bit 2) 1 1 (Final Cout)

Reading the Sum column from Bit 3 down to Bit 0 gives 1000, which is decimal 8, exactly matching 11 − 3 = 8. The final carry-out is 1, confirming that no borrow occurred, which is consistent with A being larger than B.

Section 5

5. The Multiplexer (MUX)

At this point, the Logic Unit and the Arithmetic Unit are both computing continuously and independently. For any given pair of inputs A and B, the Logic Unit is producing its selected Boolean result at the same moment the Arithmetic Unit is producing its sum or difference. Both 4-bit results are presented to a 2-to-1 multiplexer, which uses a single select line to decide which of the two results actually reaches the ALU's output, F.

When the select line chooses the Logic Unit, F becomes whichever bitwise operation S1 S0 requested. When it chooses the Arithmetic Unit, F becomes the 4-bit sum or difference, and the Cout flag from the Arithmetic Unit becomes meaningful as a carry or borrow indicator. Because the multiplexer operates on all 4 bits of each result at once (in practice, four parallel 2:1 multiplexers, one per bit), it introduces no ripple delay of its own; it only ever waits for whichever of the two source units is slower to finish computing.

This selection mechanism is what allows a single set of output pins to serve as the result for many different operations, and it is exactly the same principle used, in expanded form, in real processor ALUs such as the classic 74181 4-bit ALU chip, which combines its logic and arithmetic sections behind a similar selection network.

5.1 Complete Function-Select Table

Combining the Logic Unit's select lines (S1, S0), the Arithmetic Unit's mode line (M), and the final MUX select line (S2) gives the complete list of operations the 4-bit ALU can perform (Click any row to configure the Master ALU):

S2 S1 S0 Unit Selected Operation Output F
0 0 0 Logic Unit AND A · B
0 0 1 Logic Unit OR A + B
0 1 0 Logic Unit XOR A ⊕ B
0 1 1 Logic Unit NOT (complement of A) A′
1 – 0 Arithmetic Unit ADD (M = 0) A + B
1 – 1 Arithmetic Unit SUBTRACT (M = 1) A − B

Note: Here, S0 doubles as the mode signal M once the Arithmetic Unit is selected, and a dash (–) marks a don't-care input that has no effect on the result. This compact encoding is typical of real ALU designs, where control lines are reused across sections to keep the select logic small.

Section 6

6. Verilog HDL Programming & EPWave Simulation Lab

Complete IEEE 1364-2001 Verilog implementation of the 4-bit ALU, paired with an exhaustive self-checking testbench and an interactive simulation of the EPWave Logic Analyzer from EDA Playground.

// ============================================================================
// Module Name:  alu_4bit.v
// Project:      Design and Working of a 4-Bit Arithmetic Logic Unit (ALU)
// Target Tools: EDA Playground (Icarus Verilog 0.10.0 / Aldec Riviera Pro / Synopsys VCS)
// Standard:     IEEE 1364-2001 Verilog HDL
// Description:  Hierarchical structural and behavioral implementation of a 
//               4-bit Arithmetic Logic Unit (ALU) featuring:
//               - 1-Bit Logic Unit Slices (AND, OR, XOR, NOT) with 4:1 Multiplexer
//               - 1-Bit Full Adders built from cascaded Half Adders + OR gate
//               - 4-Bit Ripple-Carry Adder/Subtractor with two's complement inversion
//               - 4-Bit 2:1 Output Multiplexer (S2 unit selector)
//               - Processor Status Flags: Carry-Out (Cout), Zero (Z), Negative (N), Overflow (V)
// ============================================================================

`timescale 1ns / 1ps

// ----------------------------------------------------------------------------
// Submodule 1: 1-Bit Half Adder (HA)
// Computes: SUM = A ^ B, CARRY = A & B
// ----------------------------------------------------------------------------
module half_adder (
    input  wire a,
    input  wire b,
    output wire sum,
    output wire carry
);
    // Gate-level implementation matching Figure 3
    xor u_xor (sum, a, b);
    and u_and (carry, a, b);
endmodule

// ----------------------------------------------------------------------------
// Submodule 2: 1-Bit Full Adder (FA)
// Built from two Half Adders and an OR gate, matching Figure 4
// Computes: SUM = A ^ B ^ Cin
//           Cout = (A & B) | (Cin & (A ^ B))
// ----------------------------------------------------------------------------
module full_adder (
    input  wire a,
    input  wire b,
    input  wire cin,
    output wire sum,
    output wire cout
);
    wire s1;  // Intermediate sum from HA1
    wire c1;  // Intermediate carry from HA1
    wire c2;  // Intermediate carry from HA2

    // First Half Adder combines A and B
    half_adder ha1 (
        .a(a),
        .b(b),
        .sum(s1),
        .carry(c1)
    );

    // Second Half Adder combines partial sum s1 with carry-in cin
    half_adder ha2 (
        .a(s1),
        .b(cin),
        .sum(sum),
        .carry(c2)
    );

    // OR gate combines both carries
    or u_or (cout, c1, c2);
endmodule

// ----------------------------------------------------------------------------
// Submodule 3: 1-Bit Logic Unit Slice
// Computes AND, OR, XOR, NOT simultaneously, then uses 4:1 MUX to select,
// matching Figure 2
// S1 S0:
//   0  0 -> AND  (A & B)
//   0  1 -> OR   (A | B)
//   1  0 -> XOR  (A ^ B)
//   1  1 -> NOT  (~A)
// ----------------------------------------------------------------------------
module logic_slice_1bit (
    input  wire a,
    input  wire b,
    input  wire s1,
    input  wire s0,
    output reg  f
);
    wire out_and = a & b;
    wire out_or  = a | b;
    wire out_xor = a ^ b;
    wire out_not = ~a;

    // 4:1 Multiplexer selection
    always @(*) begin
        case ({s1, s0})
            2'b00: f = out_and;
            2'b01: f = out_or;
            2'b10: f = out_xor;
            2'b11: f = out_not;
            default: f = 1'b0;
        endcase
    end
endmodule

// ----------------------------------------------------------------------------
// Submodule 4: 4-Bit Ripple-Carry Adder-Subtractor Pipeline
// Cascades 4 Full Adders (FA0 to FA3) with conditional XOR inversion on B,
// matching Figure 5 and Figure 6
// Mode M:
//   M = 0: Addition       (Cin0 = 0, B ^ 0 = B)
//   M = 1: Subtraction    (Cin0 = 1, B ^ 1 = ~B -> two's complement A + ~B + 1)
// ----------------------------------------------------------------------------
module ripple_adder_subtractor_4bit (
    input  wire [3:0] a,
    input  wire [3:0] b,
    input  wire       m,
    output wire [3:0] sum,
    output wire       cout,
    output wire       overflow
);
    wire [3:0] b_eff;   // Conditionally inverted B bits
    wire [3:0] carry;   // Internal ripple carry lines: carry[0]=C1, carry[1]=C2, carry[2]=C3, carry[3]=Cout

    // Conditional XOR gates for each bit of B
    assign b_eff[0] = b[0] ^ m;
    assign b_eff[1] = b[1] ^ m;
    assign b_eff[2] = b[2] ^ m;
    assign b_eff[3] = b[3] ^ m;

    // Stage 0: LSB (Bit 0) receives initial carry Cin0 = M
    full_adder fa0 (
        .a(a[0]),
        .b(b_eff[0]),
        .cin(m),
        .sum(sum[0]),
        .cout(carry[0])
    );

    // Stage 1: Bit 1
    full_adder fa1 (
        .a(a[1]),
        .b(b_eff[1]),
        .cin(carry[0]),
        .sum(sum[1]),
        .cout(carry[1])
    );

    // Stage 2: Bit 2
    full_adder fa2 (
        .a(a[2]),
        .b(b_eff[2]),
        .cin(carry[1]),
        .sum(sum[2]),
        .cout(carry[2])
    );

    // Stage 3: MSB (Bit 3)
    full_adder fa3 (
        .a(a[3]),
        .b(b_eff[3]),
        .cin(carry[2]),
        .sum(sum[3]),
        .cout(carry[3])
    );

    // Final carry-out flag
    assign cout = carry[3];

    // Signed overflow occurs when carry-in into MSB != carry-out of MSB
    // V = carry[2] ^ carry[3]
    assign overflow = carry[2] ^ carry[3];
endmodule

// ----------------------------------------------------------------------------
// Top-Level Module: alu_4bit
// Interconnects 4-bit Logic Unit, 4-bit Arithmetic Unit, 4-bit 2:1 MUX,
// and status register flag generation, matching Figure 1
// ----------------------------------------------------------------------------
module alu_4bit (
    input  wire [3:0] A,        // 4-Bit Operand A
    input  wire [3:0] B,        // 4-Bit Operand B
    input  wire       S2,       // Master Unit Select (0 = Logic Unit, 1 = Arithmetic Unit)
    input  wire       S1,       // Logic Op Select Bit 1 (Don't care when S2=1)
    input  wire       S0,       // Logic Op Select Bit 0 / Mode M for Arithmetic (0=ADD, 1=SUB)
    output wire [3:0] F,        // 4-Bit ALU Final Result Bus
    output wire       Cout,     // Carry-Out / Borrow status flag (active for arithmetic)
    output wire       Z,        // Zero flag (1 if F == 0000)
    output wire       N,        // Negative flag (MSB F[3] in signed two's complement)
    output wire       V         // Signed Overflow flag
);

    // Internal 4-bit output buses from each unit
    wire [3:0] logic_out;
    wire [3:0] arith_out;
    wire       arith_cout;
    wire       arith_v;

    // ------------------------------------------------------------------------
    // 1. Instantiation of 4-Bit Logic Unit (4 parallel 1-bit logic slices)
    // ------------------------------------------------------------------------
    logic_slice_1bit ls0 (.a(A[0]), .b(B[0]), .s1(S1), .s0(S0), .f(logic_out[0]));
    logic_slice_1bit ls1 (.a(A[1]), .b(B[1]), .s1(S1), .s0(S0), .f(logic_out[1]));
    logic_slice_1bit ls2 (.a(A[2]), .b(B[2]), .s1(S1), .s0(S0), .f(logic_out[2]));
    logic_slice_1bit ls3 (.a(A[3]), .b(B[3]), .s1(S1), .s0(S0), .f(logic_out[3]));

    // ------------------------------------------------------------------------
    // 2. Instantiation of 4-Bit Arithmetic Unit
    // S0 acts as the Mode signal M (0 for ADD, 1 for SUB)
    // ------------------------------------------------------------------------
    ripple_adder_subtractor_4bit arith_inst (
        .a(A),
        .b(B),
        .m(S0),
        .sum(arith_out),
        .cout(arith_cout),
        .overflow(arith_v)
    );

    // ------------------------------------------------------------------------
    // 3. 4-Bit 2-to-1 Multiplexer (Controlled by S2)
    // S2 = 0: Route Logic Unit output
    // S2 = 1: Route Arithmetic Unit output
    // ------------------------------------------------------------------------
    assign F = (S2 == 1'b0) ? logic_out : arith_out;

    // ------------------------------------------------------------------------
    // 4. Status Flag Generation
    // ------------------------------------------------------------------------
    // Carry-out flag: Only meaningful when arithmetic unit is selected
    assign Cout = (S2 == 1'b1) ? arith_cout : 1'b0;

    // Zero Flag (Z): High if all 4 output bits of F are 0
    assign Z = (F == 4'b0000) ? 1'b1 : 1'b0;

    // Negative Flag (N): Equals the sign bit (MSB, bit 3) of output F
    assign N = F[3];

    // Overflow Flag (V): High if signed two's complement arithmetic overflow occurred
    assign V = (S2 == 1'b1) ? arith_v : 1'b0;

endmodule
// ============================================================================
// Module Name:  testbench.v (alu_4bit_tb)
// Project:      Design and Working of a 4-Bit Arithmetic Logic Unit (ALU)
// Target Tools: EDA Playground (Icarus Verilog 0.10.0 / Synopsys VCS / Cadence)
// Description:  Self-checking, comprehensive testbench with EPWave VCD dump,
//               tabular formatted console outputs, and validation of all
//               arithmetic, logic, and status flag conditions.
// ============================================================================

`timescale 1ns / 1ps

module testbench;

    // ------------------------------------------------------------------------
    // Testbench Stimulus Signals (Registers driving DUT inputs)
    // ------------------------------------------------------------------------
    reg  [3:0] A;
    reg  [3:0] B;
    reg        S2;
    reg        S1;
    reg        S0;

    // ------------------------------------------------------------------------
    // DUT Output Wires (Observed outputs)
    // ------------------------------------------------------------------------
    wire [3:0] F;
    wire       Cout;
    wire       Z;
    wire       N;
    wire       V;

    // Test tracking variables
    integer test_count;
    integer pass_count;
    integer fail_count;

    // ------------------------------------------------------------------------
    // Instantiate Device Under Test (DUT)
    // ------------------------------------------------------------------------
    alu_4bit dut (
        .A(A),
        .B(B),
        .S2(S2),
        .S1(S1),
        .S0(S0),
        .F(F),
        .Cout(Cout),
        .Z(Z),
        .N(N),
        .V(V)
    );

    // ------------------------------------------------------------------------
    // Waveform Generation for EDA Playground (EPWave)
    // ------------------------------------------------------------------------
    initial begin
        $dumpfile("dump.vcd");    // Required filename for EDA Playground EPWave
        $dumpvars(0, testbench);  // Dump all hierarchical variables in testbench
    end

    // ------------------------------------------------------------------------
    // Task: Check and Display Result
    // ------------------------------------------------------------------------
    task check_result;
        input [31:0] op_name;
        input [3:0]  exp_f;
        input        exp_cout;
        input        exp_z;
        input        exp_n;
        input        exp_v;
        reg          is_pass;
        begin
            #1; // Wait 1ns for settling
            test_count = test_count + 1;
            is_pass = (F === exp_f) && (Cout === exp_cout) && (Z === exp_z) && (N === exp_n) && (V === exp_v);

            if (is_pass) begin
                pass_count = pass_count + 1;
                $display("%4t ns | %b  %b  %b | %-10s | %4b (%2d) | %4b (%2d) | %4b (%2d) |  %b  %b  %b  %b | PASS",
                         $time, S2, S1, S0, op_name, A, A, B, B, F, F, Cout, Z, N, V);
            end else begin
                fail_count = fail_count + 1;
                $display("%4t ns | %b  %b  %b | %-10s | %4b (%2d) | %4b (%2d) | %4b (%2d) |  %b  %b  %b  %b | FAIL! (Exp F=%b C=%b Z=%b N=%b V=%b)",
                         $time, S2, S1, S0, op_name, A, A, B, B, F, F, Cout, Z, N, V, exp_f, exp_cout, exp_z, exp_n, exp_v);
            end
        end
    endtask

    // ------------------------------------------------------------------------
    // Main Stimulus Process
    // ------------------------------------------------------------------------
    initial begin
        test_count = 0;
        pass_count = 0;
        fail_count = 0;

        $display("==========================================================================================");
        $display("                      4-BIT ARITHMETIC LOGIC UNIT (ALU) VERIFICATION                      ");
        $display("==========================================================================================");
        $display("Time    | S2 S1 S0 | Operation  | A (bin/dec) | B (bin/dec) | F (bin/dec) | Flags: C Z N V | Status");
        $display("------------------------------------------------------------------------------------------");

        // --------------------------------------------------------------------
        // TEST CASE 1: Logic Unit - AND Operation (S2=0, S1=0, S0=0)
        // A = 1011 (11), B = 0011 (3) -> Expected: 0011 (3)
        // --------------------------------------------------------------------
        A = 4'b1011; B = 4'b0011; S2 = 1'b0; S1 = 1'b0; S0 = 1'b0;
        #9; check_result("AND", 4'b0011, 1'b0, 1'b0, 1'b0, 1'b0);

        // --------------------------------------------------------------------
        // TEST CASE 2: Logic Unit - OR Operation (S2=0, S1=0, S0=1)
        // A = 1011 (11), B = 0011 (3) -> Expected: 1011 (11)
        // --------------------------------------------------------------------
        A = 4'b1011; B = 4'b0011; S2 = 1'b0; S1 = 1'b0; S0 = 1'b1;
        #9; check_result("OR", 4'b1011, 1'b0, 1'b0, 1'b1, 1'b0);

        // --------------------------------------------------------------------
        // TEST CASE 3: Logic Unit - XOR Operation (S2=0, S1=1, S0=0)
        // A = 1011 (11), B = 0011 (3) -> Expected: 1000 (8)
        // --------------------------------------------------------------------
        A = 4'b1011; B = 4'b0011; S2 = 1'b0; S1 = 1'b1; S0 = 1'b0;
        #9; check_result("XOR", 4'b1000, 1'b0, 1'b0, 1'b1, 1'b0);

        // --------------------------------------------------------------------
        // TEST CASE 4: Logic Unit - NOT Operation (S2=0, S1=1, S0=1)
        // A = 1011 (11), B = don't-care -> Expected: ~A = 0100 (4)
        // --------------------------------------------------------------------
        A = 4'b1011; B = 4'b0000; S2 = 1'b0; S1 = 1'b1; S0 = 1'b1;
        #9; check_result("NOT", 4'b0100, 1'b0, 1'b0, 1'b0, 1'b0);

        // --------------------------------------------------------------------
        // TEST CASE 5: Arithmetic Unit - Addition (S2=1, S1=0, S0=0)
        // 7 + 5 = 12: A = 0111 (7), B = 0101 (5), M = 0 -> Expected: 1100 (12)
        // Signed interpretation: (+7) + (+5) = +12 (> +7, overflows 4-bit signed range [-8..+7])
        // Flags: Cout=0, Z=0, N=1 (MSB=1), V=1 (Signed Overflow)
        // --------------------------------------------------------------------
        A = 4'b0111; B = 4'b0101; S2 = 1'b1; S1 = 1'b0; S0 = 1'b0;
        #9; check_result("ADD (7+5)", 4'b1100, 1'b0, 1'b0, 1'b1, 1'b1);

        // --------------------------------------------------------------------
        // TEST CASE 6: Arithmetic Unit - Subtraction (S2=1, S1=0, S0=1)
        // WORKED EXAMPLE: 11 - 3 = 8
        // A = 1011 (11), B = 0011 (3), M = 1 (SUB)
        // Bitwise pipeline: B' = 1100, Cin0 = 1.
        // A + B' + 1 = 1011 + 1100 + 1 = 11000 (Sum = 1000 = 8, Cout = 1)
        // Flags: Cout=1 (No borrow), Z=0, N=1 (MSB=1), V=0
        // --------------------------------------------------------------------
        A = 4'b1011; B = 4'b0011; S2 = 1'b1; S1 = 1'b0; S0 = 1'b1;
        #9; check_result("SUB (11-3)", 4'b1000, 1'b1, 1'b0, 1'b1, 1'b0);

        // --------------------------------------------------------------------
        // TEST CASE 7: Arithmetic Unit - Zero Flag Verification (7 - 7 = 0)
        // A = 0111 (7), B = 0111 (7), M = 1 (SUB)
        // Result: F = 0000 (0), Flags: Cout=1 (No borrow), Z=1 (Zero Flag Active)
        // --------------------------------------------------------------------
        A = 4'b0111; B = 4'b0111; S2 = 1'b1; S1 = 1'b0; S0 = 1'b1;
        #9; check_result("SUB (7-7)", 4'b0000, 1'b1, 1'b1, 1'b0, 1'b0);

        // --------------------------------------------------------------------
        // TEST CASE 8: Arithmetic Unit - Unsigned Overflow (12 + 9 = 21 -> 5 mod 16)
        // A = 1100 (12), B = 1001 (9), M = 0 (ADD)
        // 12 + 9 = 21 = 10101b -> Sum = 0101 (5), Cout = 1
        // --------------------------------------------------------------------
        A = 4'b1100; B = 4'b1001; S2 = 1'b1; S1 = 1'b0; S0 = 1'b0;
        #9; check_result("ADD (12+9)", 4'b0101, 1'b1, 1'b0, 1'b0, 1'b1);

        // --------------------------------------------------------------------
        // TEST CASE 9: Arithmetic Unit - Negative Result Subtraction (3 - 7 = -4)
        // A = 0011 (3), B = 0111 (7), M = 1 (SUB)
        // In two's complement: -4 is represented as 1100.
        // A + B' + 1 = 0011 + 1000 + 1 = 1100 (Cout=0 indicates borrow occurred!)
        // --------------------------------------------------------------------
        A = 4'b0011; B = 4'b0111; S2 = 1'b1; S1 = 1'b0; S0 = 1'b1;
        #9; check_result("SUB (3-7)", 4'b1100, 1'b0, 1'b0, 1'b1, 1'b0);

        // --------------------------------------------------------------------
        // Final Summary
        // --------------------------------------------------------------------
        #10;
        $display("------------------------------------------------------------------------------------------");
        $display("SIMULATION COMPLETED: %0d Tests Executed | %0d Passed | %0d Failed", 
                 test_count, pass_count, fail_count);
        $display("==========================================================================================");
        
        if (fail_count == 0) begin
            $display(">>> ALL VERIFICATION TESTS PASSED SUCCESSFULLY! Ready for EDA Playground inspection.");
        end else begin
            $display(">>> WARNING: %0d TESTS FAILED! Check signals above.", fail_count);
        end

        $finish;
    end

endmodule

Structural Hierarchy in alu_4bit.v

The Verilog design mirrors the physical gate-level schematics 1-to-1:

  • half_adder: Primitive XOR sum and AND carry gates (Figure 3).
  • full_adder: Instantiates two half adders and an OR gate (Figure 4).
  • logic_slice_1bit: Parallel AND, OR, XOR, NOT gates with 4:1 multiplexer (Figure 2).
  • ripple_adder_subtractor_4bit: Chained full adders (FA0 to FA3) with XOR conditional inversion bus B ^ M (Figures 5 & 6).
  • alu_4bit: Top-level interconnecting logic slices, arithmetic unit, 2:1 output MUX, and status flags (Figure 1).

Testbench Execution Highlights in testbench.v

Constructed with rigorous verification standards:

  • $dumpfile("dump.vcd");: Generates standard VCD waveform trace for EPWave.
  • $dumpvars(0, testbench);: Recursively logs all signals across the entire hierarchy.
  • Self-Checking Task check_result: Compares simulated outputs against expected golden values.
  • Covers all 6 ALU functions, signed overflow (7+5=12), zero flag (7-7=0), and the primary worked subtraction (11-3=8).
EPWave v1.0

Interactive Waveform Logic Analyzer

Click any test step below or scrub the waveform timeline to inspect active signal values and flags.

RADIX:
TEST STEPS:
Active Time 60 ns
Operation Arith: SUB (Worked Ex)
Operand A [3..0] 1011b (11d / 'hB)
Operand B [3..0] 0011b (3d / 'h3)
ALU Output F [3..0] 1000b (8d / 'h8)
Carry-Out (Cout) 1 (No Borrow)
Zero Flag (Z) 0 (Non-Zero)
Negative Flag (N) 1 (MSB=1)
Overflow Flag (V) 0 (Valid)
Analysis at t = 60ns: Primary Worked Example: 11 - 3 = 8. B inverted (1100) + Cin(1). Cout=1 (No Borrow).

In-Depth Explanation of EPWave Waveform Parameters

When executing Verilog digital designs on EDA Playground, EPWave (the web-based GTKWave/waveform viewer) renders the Value Change Dump (VCD) signals produced by the simulation kernel. Understanding its operational parameters is essential for verifying multi-bit digital hardware:

⏱️

Timescale & Time Precision

`timescale 1ns / 1ps

The compiler directive `timescale <time_unit> / <time_precision> establishes the simulation timebase. The time unit (1ns) defines the magnitude of delay steps (e.g. #9 advances simulation by 9 nanoseconds). The time precision (1ps) defines the rounding accuracy of internal gate delays and edge transitions.

💾

VCD Dumpfile & Scope Hierarchy

$dumpfile & $dumpvars

EDA Playground looks specifically for dump.vcd in the working directory. The task $dumpvars(0, testbench); commands the simulator kernel to record all signal value transitions. The first argument 0 specifies recursive logging across all hierarchical child modules (e.g., dut.arith_inst.fa0).

🔢

Signal Radix Formats

Hex, Dec, Bin, 2's Complement

Multi-bit buses (A, B, F) can be displayed in Binary, Unsigned Decimal, Hexadecimal, or Signed Two's Complement. For instance, at t = 90ns (3 - 7), output 1100b appears as 'hC in Hex, 12 in Unsigned Decimal, and -4 in Signed Two's Complement.

📊

Bus Envelopes vs. Scalar Wires

Diamond Crossing Transits

In EPWave, 1-bit scalar signals (S2, S1, S0, Cout, Z) appear as clean square waves toggling between logic 0 (low rail) and logic 1 (high rail). Multi-bit buses (A[3:0], B[3:0], F[3:0]) appear as horizontal filled polygon envelopes with crossing diamond edges whenever any bit in the bus changes value.

🎯

Time Cursor & Delta-T (Δt)

Interactive Time Markers

Clicking anywhere along the timeline drops a vertical inspection cursor. The cursor displays the exact instantaneous state of every signal at that nanosecond. By dropping a primary and secondary marker, designers measure propagation delay (Δt) from input stimulus to output settling.

🚩

Status Flags Dynamic Decoding

Cout, Z, N, V Interpretation

The waveform trace allows instant verification of condition codes:
• Cout=1 at 60ns: Confirms A ≥ B (no borrow in two's complement).
• Z=1 at 70ns: Verifies equality detection (7 - 7 = 0).
• V=1 at 50ns: Flags signed overflow (+7 + 5 = +12 > +7).

Quickstart: Running on EDA Playground in 3 Steps

  1. Visit www.edaplayground.com. Paste alu_4bit.v into the left window (design.sv) and testbench.v into the right window (testbench.sv).
  2. In the left sidebar: Select Tools & Simulators → Icarus Verilog 0.10.0, and check "Open EPWave after run".
  3. Click the green Run button (or press Ctrl+Enter). Inspect the verification table in the console and view the interactive EPWave waveforms!
Section 7

7. Conclusion

The 4-bit ALU studied here demonstrates, in miniature, every idea that underlies the arithmetic and logic hardware of real processors. The Logic Unit shows how several Boolean functions can be computed in parallel and cleanly selected with a multiplexer. The Arithmetic Unit shows how the humble half adder grows into a full adder, and how a single clever trick—XOR-ing the B input with a mode signal and reusing that same signal as the initial carry-in—allows one chain of full adders to perform both addition and subtraction. Finally, the output multiplexer shows how two independently computed results can be merged into a single output bus under the control of just one more select line.

Scaling this design up to 8, 16, 32 or 64 bits mostly means repeating the same bit-slice structure more times and adding a few more Boolean functions and select lines; the fundamental building blocks—the AND/OR/XOR/NOT gates, the full adder, and the multiplexer—remain exactly the same.