Modeling

There are three types of modeling in Verilog, each corresponding to a different level of abstraction.

  • Behavioral (highest)
  • Dataflow
  • Gate-level / Structural (lowest)

Gate-level is rarely written by hand anymore (synthesis tools do it). Dataflow (assign) is the workhorse for combinational logic. Behavioral (always) is used for both combinational and sequential logic.

Gate-level model

Dataflow model

Behavioral model

module first_module( 
    input a, 
    input b, 
    output out );
    
    and and_gate(out,a,b);


endmodule
module first_module( 
    input a, 
    input b, 
    output out );
    
    assign out = a & b;


endmodule
module first_module( 
    input a, 
    input b, 
    output out );
    
    always @(a, b)
    begin
       out = a & b;

    end


endmodule

Adder


//-------------------------------------------------------
// 1-bit full adder (dataflow)
//-------------------------------------------------------
module fadder(
input a,   // data in a
input b,   // data in b
input cin, // carry in
output sum_out, // sum output
output c_out    // carry output
);
wire c1, c2, c3;
assign sum_out = a ^ b ^ cin;       // sum = XOR of three inputs
assign c1 = a & cin;
assign c2 = b & cin;
assign c3 = a & b;
assign c_out = c1 | c2 | c3;        // majority function
endmodule

Multiplexers

//-------------------------------------------------------
// 4-to-1 MUX with 1-bit inputs (combinational)
//-------------------------------------------------------
module mux_4to1 (
    input a,
    input b,
    input c,
    input d,
    input [1:0] sel,          // 2-bit select
    output reg out
);
    always @(*) begin
        case (sel)
            2'b00: out = a;
            2'b01: out = b;
            2'b10: out = c;
            2'b11: out = d;
        endcase
    end
endmodule

//-------------------------------------------------------
// 4-to-1 MUX with 4-bit wide inputs
//-------------------------------------------------------
module mux_4to1_4bit (
    input [3:0] a,
    input [3:0] b,
    input [3:0] c,
    input [3:0] d,
    input [1:0] sel,
    output reg [3:0] out
);
    always @(*) begin
        case (sel)
            2'b00: out = a;
            2'b01: out = b;
            2'b10: out = c;
            2'b11: out = d;
        endcase
    end
endmodule

Decoders

//-------------------------------------------------------
// 3-to-8 decoder (dataflow with ternary operators)
//-------------------------------------------------------
module decoder_3_to_8 (
    output [7:0] F,
    input  [2:0] ABC
);
    assign F = (ABC == 3'b000) ? 8'b0000_0001 :
               (ABC == 3'b001) ? 8'b0000_0010 :
               (ABC == 3'b010) ? 8'b0000_0100 :
               (ABC == 3'b011) ? 8'b0000_1000 :
               (ABC == 3'b100) ? 8'b0001_0000 :
               (ABC == 3'b101) ? 8'b0010_0000 :
               (ABC == 3'b110) ? 8'b0100_0000 :
               (ABC == 3'b111) ? 8'b1000_0000 :
                                 8'bxxxx_xxxx;   // don't-care
endmodule
//-------------------------------------------------------
// 7-segment decoder (decimal 0-9)
// Segment order: abcdefg (active-high)
//-------------------------------------------------------
module decoder_ssd_dec (
    output [6:0] SSD,
    input  [3:0] IN
);
    assign SSD = (IN == 4'b0000) ? 7'b1111110 :  // 0
                 (IN == 4'b0001) ? 7'b0110000 :  // 1
                 (IN == 4'b0010) ? 7'b1101101 :  // 2
                 (IN == 4'b0011) ? 7'b1111001 :  // 3
                 (IN == 4'b0100) ? 7'b0110011 :  // 4
                 (IN == 4'b0101) ? 7'b1011011 :  // 5
                 (IN == 4'b0110) ? 7'b1011111 :  // 6
                 (IN == 4'b0111) ? 7'b1110000 :  // 7
                 (IN == 4'b1000) ? 7'b1111111 :  // 8
                 (IN == 4'b1001) ? 7'b1111011 :  // 9
                                   7'bXXXXXXX;   // don't-care
endmodule
//-------------------------------------------------------
// 7-segment decoder (hexadecimal 0-F)
//-------------------------------------------------------
module decoder_ssd_hex (
    output [6:0] SSD,
    input  [3:0] IN
);
    assign SSD = (IN == 4'b0000) ? 7'b1111110 :  // 0
                 (IN == 4'b0001) ? 7'b0110000 :  // 1
                 (IN == 4'b0010) ? 7'b1101101 :  // 2
                 (IN == 4'b0011) ? 7'b1111001 :  // 3
                 (IN == 4'b0100) ? 7'b0110011 :  // 4
                 (IN == 4'b0101) ? 7'b1011011 :  // 5
                 (IN == 4'b0110) ? 7'b1011111 :  // 6
                 (IN == 4'b0111) ? 7'b1110000 :  // 7
                 (IN == 4'b1000) ? 7'b1111111 :  // 8
                 (IN == 4'b1001) ? 7'b1111011 :  // 9
                 (IN == 4'b1010) ? 7'b1110111 :  // A
                 (IN == 4'b1011) ? 7'b0011111 :  // b
                 (IN == 4'b1100) ? 7'b0001101 :  // C
                 (IN == 4'b1101) ? 7'b0111101 :  // d
                 (IN == 4'b1110) ? 7'b1001111 :  // E
                 (IN == 4'b1111) ? 7'b1000111 :  // F
                                   7'bXXXXXXX;
endmodule

Sequential Logic – Flip-Flops & Registers

Combinational logic (assign / always @(*)) has no memory – outputs change as soon as inputs change.
Sequential logic uses a clock edge and stores state. The fundamental building block is the D flip-flop.

//-------------------------------------------------------
// Simple positive-edge D flip-flop with async reset
//-------------------------------------------------------
module dff (
    input clk,
    input rst,          // active-high async reset
    input d,
    output reg q
);
    always @(posedge clk or posedge rst) begin
        if (rst)
            q <= 1'b0;
        else
            q <= d;
    end
endmodule

Shift Registers

Shift registers are among the most common sequential blocks in digital design. They convert between serial and parallel data and form the heart of many serial protocols.

//-------------------------------------------------------
// 8-bit serial-in, parallel-out (SIPO) shift register
// Shifts left on every rising clock edge
//-------------------------------------------------------
module shift_reg_8 (
    input clk,
    input rst_n,         // active-low async reset
    input serial_in,
    output reg [7:0] q
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 8'h00;
        else
            q <= {q[6:0], serial_in};   // shift left, new bit enters LSB
    end
endmodule
//-------------------------------------------------------
// 16-bit serial-in, parallel-out shift register
//-------------------------------------------------------
module shift_reg_16 (
    input clk,
    input rst_n,
    input serial_in,
    output reg [15:0] q
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 16'h0000;
        else
            q <= {q[14:0], serial_in};
    end
endmodule
//-------------------------------------------------------
// 8-bit shift register with parallel load
// Useful when you need to load a byte then shift it out
//-------------------------------------------------------
module shift_reg_8_load (
    input clk,
    input rst_n,
    input load,              // 1 = load parallel data
    input [7:0] parallel_in,
    input serial_in,
    output reg [7:0] q
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            q <= 8'h00;
        else if (load)
            q <= parallel_in;
        else
            q <= {q[6:0], serial_in};
    end
endmodule


Simple Counter

//-------------------------------------------------------
// 8-bit up-counter with enable and async reset
//-------------------------------------------------------
module counter_8 (
    input clk,
    input rst_n,
    input en,               // count enable
    output reg [7:0] count
);
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            count <= 8'h00;
        else if (en)
            count <= count + 1'b1;
    end
endmodule

Quick tips
• Prefer non-blocking assignments (<=) inside clocked always blocks.
• Prefer blocking assignments (=) inside combinational always @(*) blocks.
• Always reset your registers (async or sync – both are common).
• Use reg for anything driven inside an always block; use wire for continuous assign.