Building complex functions from simple gates
Combinational logic means the output depends ONLY on the current inputs. No memory, no history, no state. Change the inputs, output changes instantly (within nanoseconds).
| Gate | Operator | Function | Example |
|---|---|---|---|
| AND | & | Both inputs HIGH | y = a & b; |
| OR | | | At least one HIGH | y = a | b; |
| NOT | ~ | Invert signal | y = ~a; |
| XOR | ^ | Inputs different | y = a ^ b; |
| NAND | ~(&) | NOT AND | y = ~(a & b); |
| NOR | ~(|) | NOT OR | y = ~(a | b); |
Let's build something useful: A 2-to-4 decoder. Takes 2 input bits, activates one of 4 outputs.
A B | Y0 Y1 Y2 Y3 ------+---------------- 0 0 | 1 0 0 0 0 1 | 0 1 0 0 1 0 | 0 0 1 0 1 1 | 0 0 0 1
// 2-to-4 Decoder // Only ONE output is HIGH based on input combination module decoder_2to4 ( input a, input b, output y0, output y1, output y2, output y3 ); // Each output is HIGH for ONE specific input combination assign y0 = ~a & ~b; // 00 assign y1 = ~a & b; // 01 assign y2 = a & ~b; // 10 assign y3 = a & b; // 11 endmodule
Instead of declaring many single wires, use buses - multi-bit signals:
// Same decoder, cleaner code using buses module decoder_2to4_bus ( input [1:0] sel, // 2-bit input bus output [3:0] y // 4-bit output bus ); // Use shift operator to activate the correct bit assign y = 4'b0001 << sel; endmodule
4'b0001 << sel shifts a 1 left by sel positions. When sel=2, result is 0100 (bit 2 high). One line does everything!
A multiplexer selects ONE input from many to pass to the output. It's like a railroad switch.
// 4-to-1 Multiplexer // Select which of 4 inputs appears at output module mux_4to1 ( input [3:0] data_in, // 4 data inputs input [1:0] sel, // 2-bit selector output data_out // Selected output ); // Use selector to index into the data bus assign data_out = data_in[sel]; endmodule
data_in[sel] dynamically selects a bit. If sel=2, output = data_in[2]. Hardware builds this as actual MUX gates!
// Add two 8-bit numbers module adder_8bit ( input [7:0] a, input [7:0] b, output [7:0] sum, output carry_out ); // Add with carry capture // {carry_out, sum} creates a 9-bit result assign {carry_out, sum} = a + b; endmodule
Create an Arithmetic Logic Unit that can ADD, SUB, AND, OR based on operation select:
module alu_4bit ( input [3:0] a, b, input [1:0] op, // 00=ADD, 01=SUB, 10=AND, 11=OR output reg [3:0] result ); always @(*) begin case(op) 2'b00: result = a + b; 2'b01: result = a - b; 2'b10: result = a & b; 2'b11: result = a | b; endcase end endmodule
always @(*) creates combinational logic with procedural statements. reg type allows assignment inside always blocks. case statement builds MUX-like logic.
assign for simple connections, always @(*) for complex logic