Digital Logic Design Tutorial: Learn Logic Design from Scratch (2026)
Digital logic design is the foundation upon which all digital systems are built. From the simplest AND gate to a modern microprocessor, every digital system is constructed from Boolean logic primitives. I have designed digital circuits for ASIC and FPGA projects, and I have learned that a solid grasp of logic design — combinatorial and sequential — is essential for understanding how computers work at the lowest level.
We will build circuits incrementally: starting with Boolean algebra and Karnaugh maps, moving through flip-flops and registers, and culminating in finite state machine design in Verilog.
Boolean Algebra and Logic Gates
Boolean algebra is the mathematical framework for digital logic. The fundamental operations are AND, OR, and NOT. Boolean algebra laws allow simplification of logic expressions. Every Boolean function can be expressed in sum-of-products or product-of-sums form. NAND and NOR gates are functionally complete — any function can be implemented using only NAND gates.
module majority(input A, B, C, output F);
assign F = (A&B)|(A&C)|(B&C);
endmodule
module tb;
reg A,B,C; wire F; majority uut(.A(A),.B(B),.C(C),.F(F));
initial begin
$monitor("%b %b %b = %b",A,B,C,F);
{A,B,C}=3'b000;#10;{A,B,C}=3'b001;#10;{A,B,C}=3'b010;#10;{A,B,C}=3'b011;#10;
{A,B,C}=3'b100;#10;{A,B,C}=3'b101;#10;{A,B,C}=3'b110;#10;{A,B,C}=3'b111;#10;$finish;
end
endmodule
Karnaugh Maps and Logic Minimization
Karnaugh maps provide a visual method for minimizing Boolean expressions. A K-map arranges truth table outputs in a grid where adjacent cells differ by exactly one variable. Grouping adjacent 1s in powers of two yields prime implicants. For five or more variables, the Quine-McCluskey algorithm is preferred.
from itertools import combinations
def qm(minterms, dont_cares, nv):
terms = [(t, bin(t)[2:].zfill(nv)) for t in minterms+dont_cares]
primes = set(); merged = True
while merged:
merged=False; new=[]; used=set()
for i,(_,a) in enumerate(terms):
for j,(_,b) in enumerate(terms):
if i
Latches and Flip-Flops
Latches and flip-flops are sequential elements that store a single bit. An SR latch using cross-coupled NAND gates is the simplest bistable element. D flip-flops sample the input on a clock edge — edge-triggered behavior essential for synchronous design. JK and T flip-flops are variants for specific applications.
module dff(input clk, rst, D, output reg Q);
always @(posedge clk) if (rst) Q<=0; else Q<=D;
endmodule
module shift_reg(input clk, rst, sin, output reg [3:0] q);
always @(posedge clk) begin
if (rst) q<=0; else begin q[0]<=sin; q[1]<=q[0]; q[2]<=q[1]; q[3]<=q[2]; end
end
endmodule
module jkff(input clk, J, K, output reg Q);
always @(posedge clk) case({J,K}) 2'b00:Q<=Q; 2'b01:Q<=0; 2'b10:Q<=1; 2'b11:Q<=~Q; endcase
endmodule
Counters and Registers
Counters cycle through a sequence of states. A binary counter increments on each clock edge; a decade counter counts 0-9. Synchronous counters use parallel clocking for higher speed, while ripple counters chain flip-flops where each output clocks the next. Universal shift registers support parallel load, left/right shift, and hold.
module bcd_counter(input clk, rst, en, output reg [3:0] count, output carry);
assign carry = (count==4'd9)&en;
always @(posedge clk) if(rst) count<=0; else if(en) count<=count==9?0:count+1;
endmodule
module usr(input clk, rst, [1:0] mode, in sin_l, sin_r, [3:0] pin, output reg [3:0] q);
always @(posedge clk) if(rst) q<=0; else case(mode)
0:q<=q; 1:q<={sin_r,q[3:1]}; 2:q<={q[2:0],sin_l}; 3:q<=pin; endcase
endmodule
Finite State Machine Design
FSMs model systems that transition between a finite number of states based on inputs. A Mealy machine's outputs depend on state and inputs; a Moore machine's outputs depend only on state. The design process: state diagram, transition table, state encoding, next-state logic, and hardware implementation.
// 101 sequence detector (Mealy, non-overlapping)
module seq101(input clk, rst, din, output reg dout);
reg [1:0] s, nx; parameter S0=0, S1=1, S2=2;
always @(posedge clk) if(rst) s<=S0; else s<=nx;
always @(*) begin nx=s; dout=0;
case(s) S0: if(din) nx=S1; S1: if(!din) nx=S2; else nx=S1; S2: if(din) begin nx=S1; dout=1; end else nx=S0;
endcase
end
endmodule
Programmable Logic: FPGAs and HDL Synthesis
FPGAs contain configurable logic blocks with LUTs, flip-flops, and carry chains connected by programmable routing. HDL synthesis transforms Verilog into a gate-level netlist mapped to FPGA resources. Timing analysis ensures the design meets clock frequency targets by checking setup and hold times.
module led_blinker(input clk, rst, output reg led);
reg [24:0] c;
always @(posedge clk) if(rst) c<=0; else c<=c+1;
always @(posedge clk) if(rst) led<=0; else led<=c[24];
endmodule
// create_clock -period 20.000 -name sysclk [get_ports clk]
Frequently Asked Questions
What is the difference between a latch and a flip-flop?
A latch is level-sensitive; a flip-flop is edge-triggered. Flip-flops are preferred in synchronous designs because they eliminate transparency windows that cause race conditions.
What is the difference between Mealy and Moore machines?
Moore outputs depend only on the current state (glitch-free). Mealy outputs depend on state and inputs (faster but may glitch).
What are setup time and hold time?
Setup time is the minimum time data must be stable before the clock edge; hold time is after. Violating these causes metastability.
Why are NAND gates called universal gates?
Any Boolean function can be implemented using only NAND gates. AND = NAND + NOT (inverter from NAND with tied inputs). OR from De Morgan: A OR B = (A' NAND B')'.
Originally published on Ayodhyyya. Last updated June 1, 2026.