⚡ Digital Design Blog

Part 2 of 10

Combinational Circuits

Output depends only on current inputs - no memory

By Praveen Kumar Vagala

1,042 views

What is Combinational Logic?

Output = f(current inputs only)

No memory, no feedback, no clock. Change the input, output changes immediately.

Inputs Outputs │ │ A ───┤ ├─── Y B ───┤ Comb. ├─── Z C ───┤ Logic │ │ │ └─────────────┘ No clock! No storage!

Multiplexer (MUX)

Selects one of many inputs and passes it to output.

Think of it as a "data switch" or "selector".

2:1 MUX

┌───────┐ I0 ───┤ │ │ MUX ├─── Y I1 ───┤ │ └───┬───┘ │ SEL SEL=0 → Y=I0 SEL=1 → Y=I1 Y = (SEL' · I0) + (SEL · I1)
// 2:1 MUX in Verilog
module mux2to1 (
    input  i0, i1, sel,
    output y
);
    assign y = sel ? i1 : i0;
endmodule

4:1 MUX

┌───────┐ I0 ───┤ │ I1 ───┤ MUX ├─── Y I2 ───┤ │ I3 ───┤ │ └───┬───┘ │ S1 S0 S1 S0 │ Y ──────┼─── 0 0 │ I0 0 1 │ I1 1 0 │ I2 1 1 │ I3
// 4:1 MUX in Verilog
module mux4to1 (
    input  [3:0] i,
    input  [1:0] sel,
    output y
);
    assign y = i[sel];  // Elegant!
    
    // Or explicit:
    // assign y = (sel==2'b00) ? i[0] :
    //            (sel==2'b01) ? i[1] :
    //            (sel==2'b10) ? i[2] : i[3];
endmodule

Demultiplexer (DEMUX)

Opposite of MUX - routes one input to one of many outputs.

┌───────┐ │ ├─── Y0 Din ──────┤ DEMUX ├─── Y1 │ ├─── Y2 │ ├─── Y3 └───┬───┘ │ S1 S0 S1 S0 │ Y3 Y2 Y1 Y0 ──────┼───────────────── 0 0 │ 0 0 0 Din 0 1 │ 0 0 Din 0 1 0 │ 0 Din 0 0 1 1 │ Din 0 0 0

Decoder

Converts binary code to one-hot output.

Only ONE output is active at a time.

2-to-4 Decoder

┌─────────┐ A ────┤ ├─── Y0 (A'B') B ────┤ DECODER ├─── Y1 (A'B) │ ├─── Y2 (AB') │ ├─── Y3 (AB) └─────────┘ A B │ Y3 Y2 Y1 Y0 ─────┼───────────────── 0 0 │ 0 0 0 1 0 1 │ 0 0 1 0 1 0 │ 0 1 0 0 1 1 │ 1 0 0 0
// 2-to-4 Decoder in Verilog
module decoder2to4 (
    input  [1:0] in,
    output reg [3:0] out
);
    always @(*) begin
        out = 4'b0000;
        out[in] = 1'b1;  // Set only one bit
    end
endmodule

Encoder

Opposite of decoder - converts one-hot to binary.

Priority Encoder

When multiple inputs are active, highest priority wins.

// 4-to-2 Priority Encoder
module priority_enc (
    input  [3:0] in,
    output reg [1:0] out,
    output valid
);
    assign valid = |in;  // Any input active
    
    always @(*) begin
        casez (in)
            4'b1???: out = 2'd3;  // Highest priority
            4'b01??: out = 2'd2;
            4'b001?: out = 2'd1;
            4'b0001: out = 2'd0;
            default: out = 2'd0;
        endcase
    end
endmodule

Adders

Half Adder

Adds two 1-bit numbers. No carry input.

A B │ Sum Carry ─────┼──────────── 0 0 │ 0 0 0 1 │ 1 0 1 0 │ 1 0 1 1 │ 0 1 Sum = A ⊕ B (XOR) Carry = A · B (AND)

Full Adder

Adds two 1-bit numbers plus carry input.

A B Cin │ Sum Cout ──────────┼─────────── 0 0 0 │ 0 0 0 0 1 │ 1 0 0 1 0 │ 1 0 0 1 1 │ 0 1 1 0 0 │ 1 0 1 0 1 │ 0 1 1 1 0 │ 0 1 1 1 1 │ 1 1 Sum = A ⊕ B ⊕ Cin Cout = (A·B) + (Cin·(A⊕B))
// Full Adder in Verilog
module full_adder (
    input  a, b, cin,
    output sum, cout
);
    assign sum = a ^ b ^ cin;
    assign cout = (a & b) | (cin & (a ^ b));
endmodule

Ripple Carry Adder

Chain full adders to add multi-bit numbers.

A3 B3 A2 B2 A1 B1 A0 B0 │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ FA │◄─│ FA │◄─│ FA │◄─│ FA │◄─ 0 └─┬──┘ └─┬──┘ └─┬──┘ └─┬──┘ │ │ │ │ S3 S2 S1 S0 Carry "ripples" through - slow for large widths!
// 4-bit Adder (simple)
module adder4 (
    input  [3:0] a, b,
    input        cin,
    output [3:0] sum,
    output       cout
);
    assign {cout, sum} = a + b + cin;
endmodule

Comparator

Compares two numbers.

// Magnitude Comparator
module comparator (
    input  [7:0] a, b,
    output       eq, lt, gt
);
    assign eq = (a == b);
    assign lt = (a < b);
    assign gt = (a > b);
endmodule

Practical: ALU (Arithmetic Logic Unit)

Combines multiple operations, selected by opcode.

module alu (
    input  [7:0] a, b,
    input  [2:0] op,
    output reg [7:0] result,
    output zero
);
    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;     // Shift left
            3'b111: result = a >> 1;     // Shift right
        endcase
    end
    
    assign zero = (result == 8'b0);
endmodule

Summary

CircuitFunctionKey Point
MUXSelect 1 of N inputsData selector
DEMUXRoute to 1 of N outputsData distributor
DecoderBinary to one-hotAddress decoding
EncoderOne-hot to binaryPriority logic
AdderBinary additionArithmetic core
ComparatorCompare valuesDecision making