Verilog Tutorial

Section 4: Examples with Testbenches

4.1 Verification and Testbenches

Verification is a crucial aspect of digital circuit design in Verilog. It involves confirming that your design functions correctly under various conditions. Testbenches are used to automate the verification process by applying stimulus to the design and checking the responses. In this section, we'll explore verification and testbench creation in Verilog.

  1. Verification Process:

    Verification is the process of ensuring that your digital circuit design operates correctly according to its specifications. It involves the following steps:

    • Design Under Test (DUT): The circuit or module you want to verify.
    • Testbench: A separate Verilog module that generates stimulus for the DUT and monitors its outputs.
    • Simulation: Running the testbench and DUT through a simulation tool to observe behavior.
    • Assertions: Adding assertions in your testbench to check if expected conditions are met.
    • Coverage Analysis: Ensuring that you have tested various scenarios to achieve high test coverage.

  2. Testbench Structure:

    A typical testbench in Verilog consists of the following components:

    • Module Instantiation: Instantiate the DUT and connect it to the testbench.
    • Clock Generation: Generate clock signals if the DUT relies on a clock.
    • Input Generation: Create input stimulus for the DUT.
    • Output Monitoring: Capture and compare DUT outputs to expected values.

  3. Example Testbench:

    Below is a simple example of a testbench for a 2-to-1 multiplexer (MUX) designed to verify its functionality:

    tb_mux_2to1.v
    
    module tb_mux_2to1; 
    
    // Inputs and outputs for the testbench 
    reg [1:0] A, B; 
    reg select; 
    wire Y; 
    
    // Instantiate the MUX DUT 
    mux_2to1 DUT ( 
    .A(A), 
    .B(B), 
    .select(select), 
    .Y(Y) 
    ); 
    
    // Clock generation (if needed) 
    reg clk = 0; 
    always begin 
    #5 clk = ~clk; 
    end 
    
    // Testbench stimulus 
    initial begin 
    // Apply test cases 
    A = 2'b00; B = 2'b11; select = 1'b0; 
    #10 if (Y !== 1'b0) $display("Test 1 failed"); 
    
    A = 2'b10; B = 2'b01; select = 1'b1; 
    #10 if (Y !== 1'b1) $display("Test 2 failed"); 
    
    // Add more test cases as needed 
    
    // Terminate simulation 
    $finish; 
    end 
    
    endmodule 
                               
  4. Running Simulation:

    To run the simulation with this testbench, you can use a Verilog simulation tool like Icarus Verilog or ModelSim. Here's an example command to simulate the testbench:

    • For Icarus Verilog:

      cmd
      
      iverilog -o tb_mux_2to1 tb_mux_2to1.v mux_2to1.v
                                        
      cmd
      
      vvp tb_mux_2to1
                                        
    • For ModelSim:

      Compile
      cmd
      
      vlog mux_2to1.v tb_mux_2to1.v 
                                        
      Run the simulation
      cmd
      
      vsim -c tb_mux_2to1 -do "run -all; exit"
                                        
  5. Analyzing Results:

    The simulation tool will provide output indicating whether the testbench passed or failed for each test case. You can use this feedback to debug and refine your design.

Creating effective testbenches and thoroughly verifying your Verilog designs are essential steps in ensuring the correctness and reliability of your digital circuits.

4.2 Examples for Combinational Logic

Combinational logic circuits are fundamental components in digital design, performing operations based solely on the current input values. In this section, we'll explore practical Verilog examples for various combinational logic circuits to illustrate their implementation.

  1. Basic Logic Gates:

    AND Gate:

    and_gate.v
    
    module and_gate (
    input wire A,
    input wire B,
    output wire Y
    );
    assign Y = A & B;
    endmodule
                               

    Testbench for AND Gate:

    tb_and_gate.v
    
    module tb_and_gate; 
       reg a; 
       reg b; 
       wire y; 
    
       // Instantiate the AND gate module 
       and_gate and_gate_inst ( 
          .a(a), 
          .b(b), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 0; 
          b = 0; 
    
          // Test case 1: Both inputs are 0 
          #10 assert(y === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: Input a is 1, b is 0 
          a = 1; 
          #10 assert(y === 0) else $display("Test case 2 failed"); 
    
          // Test case 3: Both inputs are 1 
          b = 1; 
          #10 assert(y === 1) else $display("Test case 3 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    OR Gate:

    or_gate.v
    
    module or_gate ( 
       input wire a, 
       input wire b, 
       output wire y 
    ); 
       assign y = a | b; // OR operation 
    endmodule 
                               

    Testbench for OR Gate:

    tb_or_gate.v
    
    module tb_or_gate; 
       reg a; 
       reg b; 
       wire y; 
    
       // Instantiate the OR gate module 
       or_gate or_gate_inst ( 
          .a(a), 
          .b(b), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 0; 
          b = 0; 
    
          // Test case 1: Both inputs are 0 
          #10 assert(y === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: Input a is 1, b is 0 
          a = 1; 
          #10 assert(y === 1) else $display("Test case 2 failed"); 
    
          // Test case 3: Both inputs are 1 
          b = 1; 
          #10 assert(y === 1) else $display("Test case 3 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    NOT Gate:

    not_gate.v
    
    module not_gate ( 
       input wire a, 
       output wire y 
    ); 
       assign y = ~a; // NOT operation 
    endmodule 
                               

    Testbench for NOT Gate:

    or_gate.v
    
    module tb_not_gate; 
       reg a; 
       wire y; 
    
       // Instantiate the NOT gate module 
       not_gate not_gate_inst ( 
          .a(a), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize input 
          a = 0; 
    
          // Test case 1: Input is 0 
          #10 assert(y === 1) else $display("Test case 1 failed"); 
    
          // Test case 2: Input is 1 
          a = 1; 
          #10 assert(y === 0) else $display("Test case 2 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    XOR Gate:

    xor_gate.v
    
    module xor_gate ( 
       input wire a, 
       input wire b, 
       output wire y 
    ); 
       assign y = a ^ b; // XOR operation 
    endmodule 
                               

    Testbench for XOR Gate:

    tb_xor_gate.v
    
    module tb_xor_gate; 
       reg a; 
       reg b; 
       wire y; 
    
       // Instantiate the XOR gate module 
       xor_gate xor_gate_inst ( 
          .a(a), 
          .b(b), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 0; 
          b = 0; 
    
          // Test case 1: Both inputs are 0 
          #10 assert(y === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: Input a is 1, b is 0 
          a = 1; 
          #10 assert(y === 1) else $display("Test case 2 failed"); 
    
          // Test case 3: Both inputs are 1 
          b = 1; 
          #10 assert(y === 0) else $display("Test case 3 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               
  2. Multiplexers (MUX):

    2-to-1 MUX:

    mux_2to1.v
    
    module mux_2to1 ( 
       input wire a,     // Input A 
       input wire b,     // Input B 
       input wire sel,   // Selection signal 
       output wire y     // Output 
    ); 
       assign y = (sel) ? b : a; // MUX operation 
    endmodule 
                               

    Testbench for 2-to-1 MUX:

    tb_mux_2to1.v
    
    module tb_mux_2to1; 
       reg a; 
       reg b; 
       reg sel; 
       wire y; 
    
       // Instantiate the 2-to-1 MUX module 
       mux_2to1 mux ( 
          .a(a), 
          .b(b), 
          .sel(sel), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize signals 
          a = 0; 
          b = 1; 
          sel = 0; 
    
          // Test case 1: Select A (sel=0) 
          #10 assert(y === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: Select B (sel=1) 
          sel = 1; 
          #10 assert(y === 1) else $display("Test case 2 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    4-to-1 MUX:

    mux_4to1.v
    
    module mux_4to1 ( 
       input wire [3:0] a,     // 4-bit Input A 
       input wire [3:0] b,     // 4-bit Input B 
       input wire [1:0] sel,   // 2-bit Selection signal 
       output wire [3:0] y     // 4-bit Output 
    ); 
       assign y = (sel == 2'b00) ? a : 
                (sel == 2'b01) ? b : 
                (sel == 2'b10) ? 4'b0 : // Default output 
                (sel == 2'b11) ? 4'bz : // High-Z output 
                4'bx; // Invalid state 
    endmodule 
                               

    Testbench for 4-to-1 MUX:

    tb_mux_4to1.v
    
    module tb_mux_4to1; 
       reg [3:0] a; 
       reg [3:0] b; 
       reg [1:0] sel; 
       wire [3:0] y; 
    
       // Instantiate the 4-to-1 MUX module 
       mux_4to1 mux ( 
          .a(a), 
          .b(b), 
          .sel(sel), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize signals 
          a = 4'b1101; 
          b = 4'b0010; 
          sel = 2'b00; 
    
          // Test case 1: Select A (sel=00) 
          #10 assert(y === 4'b1101) else $display("Test case 1 failed"); 
    
          // Test case 2: Select B (sel=01) 
          sel = 2'b01; 
          #10 assert(y === 4'b0010) else $display("Test case 2 failed"); 
    
          // Test case 3: Default output (sel=10) 
          sel = 2'b10; 
          #10 assert(y === 4'b0000) else $display("Test case 3 failed"); 
    
          // Test case 4: High-Z output (sel=11) 
          sel = 2'b11; 
          #10 assert(y === 4'bz) else $display("Test case 4 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    These Verilog code examples and testbenches demonstrate the functionality of 2-to-1 and 4-to-1 multiplexers, along with various test cases to verify their operation. You can use a Verilog simulator to run these testbenches and observe the results.

  3. DeMUX :

    1-to-2 Demultiplexer (DeMUX):

    demux_1to2.v
    
    module demux_1to2 ( 
       input wire a,           // Input 
       input wire select,      // Selection control 
       output wire y0,        // Output 0 
       output wire y1         // Output 1 
    ); 
       assign y0 = (select) ? 1'b0 : a; // Output 0 when select is 1, else input a 
       assign y1 = (select) ? a : 1'b0; // Output 1 when select is 1, else 0 
    endmodule 
                               

    Testbench for 1-to-2 Demultiplexer (DeMUX):

    tb_demux_1to2.v
    
    module tb_demux_1to2; 
       reg a; 
       reg select; 
       wire y0; 
       wire y1; 
    
       // Instantiate the 1-to-2 Demultiplexer module 
       demux_1to2 demux ( 
          .a(a), 
          .select(select), 
          .y0(y0), 
          .y1(y1) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 0; 
          select = 0; 
    
          // Test case 1: Select is 0, input is 0 
          #10 assert(y0 === 0 && y1 === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: Select is 0, input is 1 
          a = 1; 
          #10 assert(y0 === 1 && y1 === 0) else $display("Test case 2 failed"); 
    
          // Test case 3: Select is 1, input is 0 
          select = 1; 
          a = 0; 
          #10 assert(y0 === 0 && y1 === 0) else $display("Test case 3 failed"); 
    
          // Test case 4: Select is 1, input is 1 
          a = 1; 
          #10 assert(y0 === 0 && y1 === 1) else $display("Test case 4 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    1-to-4 Demultiplexer (DeMUX):

    demux_1to4.v
    
    module demux_1to4 ( 
       input wire a,             // Input 
       input wire [1:0] select, // 2-bit selection control 
       output wire y0,          // Output 0 
       output wire y1,          // Output 1 
       output wire y2,          // Output 2 
       output wire y3           // Output 3 
    ); 
       assign y0 = (select == 2'b00) ? a : 1'b0; // Output 0 when select is 00, else 0 
       assign y1 = (select == 2'b01) ? a : 1'b0; // Output 1 when select is 01, else 0 
       assign y2 = (select == 2'b10) ? a : 1'b0; // Output 2 when select is 10, else 0 
       assign y3 = (select == 2'b11) ? a : 1'b0; // Output 3 when select is 11, else 0 
    endmodule 
                               

    Testbench for 1-to-4 Demultiplexer (DeMUX):

    demux_1to4.v
    
    module tb_demux_1to4; 
       reg a; 
       reg [1:0] select; 
       wire y0; 
       wire y1; 
       wire y2; 
       wire y3; 
    
       // Instantiate the 1-to-4 Demultiplexer module 
       demux_1to4 demux ( 
          .a(a), 
          .select(select), 
          .y0(y0), 
          .y1(y1), 
          .y2(y2), 
          .y3(y3) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 0; 
          select = 2'b00; 
    
          // Test case 1: Select is 00, input is 0 
          #10 assert(y0 === 0 && y1 === 0 && y2 === 0 && y3 === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: Select is 00, input is 1 
          a = 1; 
          #10 assert(y0 === 1 && y1 === 0 && y2 === 0 && y3 === 0) else $display("Test case 2 failed"); 
    
          // Test case 3: Select is 01, input is 0 
          select = 2'b01; 
          a = 0; 
          #10 assert(y0 === 0 && y1 === 0 && y2 === 0 && y3 === 0) else $display("Test case 3 failed"); 
    
          // Test case 4: Select is 01, input is 1 
          a = 1; 
          #10 assert(y0 === 0 && y1 === 1 && y2 === 0 && y3 === 0) else $display("Test case 4 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule  
                               

    These Verilog code examples and testbenches demonstrate the functionality of 1-to-2 and 1-to-4 Demultiplexers (DeMUX) along with various test cases to verify their operation. You can use a Verilog simulator to run these testbenches and observe the results.

  4. Decoders:

    3-to-8 Decoder:

    decoder_3to8_1.v
    
    module decoder_3to8_1 ( 
       input wire [2:0] a,      // 3-bit Input 
       output wire [7:0] y      // 8-bit Output 
    ); 
       assign y = (a == 3'b000) ? 8'b00000001 : 
                (a == 3'b001) ? 8'b00000010 : 
                (a == 3'b010) ? 8'b00000100 : 
                (a == 3'b011) ? 8'b00001000 : 
                (a == 3'b100) ? 8'b00010000 : 
                (a == 3'b101) ? 8'b00100000 : 
                (a == 3'b110) ? 8'b01000000 : 
                (a == 3'b111) ? 8'b10000000 : 
                8'b00000000; // Default output (invalid input) 
    endmodule 
                               

    Testbench for 3-to-8 Decoder:

    tb_decoder_3to8.v
    
    module tb_decoder_3to8; 
       reg [2:0] a; 
       wire [7:0] y; 
    
       // Instantiate the 3-to-8 Decoder module 
       decoder_3to8 decoder ( 
          .a(a), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize signals 
          a = 3'b000; 
    
          // Test case 1: Input = 000 
          #10 assert(y === 8'b00000001) else $display("Test case 1 failed"); 
    
          // Test case 2: Input = 110 
          a = 3'b110; 
          #10 assert(y === 8'b01000000) else $display("Test case 2 failed"); 
    
          // Test case 3: Default output (invalid input) 
          a = 3'b101; 
          #10 assert(y === 8'b00000000) else $display("Test case 3 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    2-to-4 Decoder:

    decoder_2to4.v
    
    module decoder_2to4 ( 
       input wire [1:0] a,      // 2-bit Input 
       output wire [3:0] y      // 4-bit Output 
    ); 
       assign y = (a == 2'b00) ? 4'b0001 : 
                (a == 2'b01) ? 4'b0010 : 
                (a == 2'b10) ? 4'b0100 : 
                (a == 2'b11) ? 4'b1000 : 
                4'b0000; // Default output (invalid input) 
    endmodule 
                               

    Testbench for 2-to-4 Decoder:

    tb_decoder_2to4.v
    
    module tb_decoder_2to4; 
       reg [1:0] a; 
       wire [3:0] y; 
    
       // Instantiate the 2-to-4 Decoder module 
       decoder_2to4 decoder ( 
          .a(a), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize signals 
          a = 2'b01; 
    
          // Test case 1: Input = 01 
          #10 assert(y === 4'b0010) else $display("Test case 1 failed"); 
    
          // Test case 2: Input = 10 
          a = 2'b10; 
          #10 assert(y === 4'b0100) else $display("Test case 2 failed"); 
    
          // Test case 3: Default output (invalid input) 
          a = 2'b11; 
          #10 assert(y === 4'b0000) else $display("Test case 3 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    These Verilog code examples and testbenches demonstrate the functionality of 3-to-8 and 2-to-4 decoders, along with various test cases to verify their operation. You can use a Verilog simulator to run these testbenches and observe the results.

  5. Encoders

    2-to-4 Priority Encoder:

    priority_encoder_2to4.v
    
    module priority_encoder_2to4 ( 
       input wire [1:0] a,     // 2-bit Input 
       output wire [3:0] y     // 4-bit Output 
    ); 
       assign y[0] = (a[0] == 1'b1) ? 1'b1 : 1'b0; 
       assign y[1] = (a[1] == 1'b1) ? 1'b1 : (a[0] == 1'b1) ? 1'b0 : 1'b0; 
       assign y[2] = (a[1] == 1'b1 && a[0] == 1'b1) ? 1'b1 : 1'b0; 
       assign y[3] = (a[1] == 1'b0 && a[0] == 1'b0) ? 1'b1 : 1'b0; 
    endmodule 
                               

    Testbench for 2-to-4 Priority Encoder:

    tb_priority_encoder_2to4.v
    
    module tb_priority_encoder_2to4; 
       reg [1:0] a; 
       wire [3:0] y; 
    
       // Instantiate the 2-to-4 Priority Encoder module 
       priority_encoder_2to4 encoder ( 
          .a(a), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 2'b00; 
    
          // Test case 1: Input is 00 
          #10 assert(y === 4'b1000) else $display("Test case 1 failed"); 
    
          // Test case 2: Input is 01 
          a = 2'b01; 
          #10 assert(y === 4'b0100) else $display("Test case 2 failed"); 
    
          // Test case 3: Input is 10 
          a = 2'b10; 
          #10 assert(y === 4'b0010) else $display("Test case 3 failed"); 
    
          // Test case 4: Input is 11 
          a = 2'b11; 
          #10 assert(y === 4'b0001) else $display("Test case 4 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    4-to-2 Binary Encoder:

    binary_encoder_4to2.v
    
    module binary_encoder_4to2 ( 
       input wire [3:0] a,  // 4-bit Input 
       output wire [1:0] y  // 2-bit Output 
    ); 
       assign y = (a != 4'b0000) ? {1'b1, 1'b0} : 
                (a != 4'b0001) ? {1'b0, 1'b1} : 
                (a != 4'b0010) ? {1'b0, 1'b1} : 
                (a != 4'b0100) ? {1'b0, 1'b1} : {1'b0, 1'b0}; 
    endmodule 
                               

    Testbench for 4-to-2 Binary Encoder:

    tb_binary_encoder_4to2.v
    
    module tb_binary_encoder_4to2; 
       reg [3:0] a; 
       wire [1:0] y; 
    
       // Instantiate the 4-to-2 Binary Encoder module 
       binary_encoder_4to2 encoder ( 
          .a(a), 
          .y(y) 
       ); 
    
       // Test scenario 
       initial begin 
          // Initialize inputs 
          a = 4'b0000; 
    
          // Test case 1: Input is 0000 
          #10 assert(y === 2'b00) else $display("Test case 1 failed"); 
    
          // Test case 2: Input is 0001 
          a = 4'b0001; 
          #10 assert(y === 2'b01) else $display("Test case 2 failed"); 
    
          // Test case 3: Input is 0010 
          a = 4'b0010; 
          #10 assert(y === 2'b10) else $display("Test case 3 failed"); 
    
          // Test case 4: Input is 0100 
          a = 4'b0100; 
          #10 assert(y === 2'b11) else $display("Test case 4 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               
  6. Arithmetic Logic Units (ALU):

    alu_4bit.v
    
    module alu_4bit ( 
       input [3:0] A, 
       input [3:0] B, 
       input [2:0] opcode, 
       output [3:0] Y 
    ); 
       always @(*) begin 
          case (opcode) 
             3'b000: Y = A + B; // Add 
             3'b001: Y = A - B; // Subtract 
             3'b010: Y = A & B; // AND 
             3'b011: Y = A | B; // OR 
             // Add more operations as needed 
             default: Y = 4'bxxxx; // Output 'x' for unsupported opcode 
          endcase 
       end 
    endmodule 
                               

These Verilog examples cover various combinational logic circuits, from basic gates to more complex components like multiplexers, decoders, and arithmetic logic units (ALUs).

4.3 Examples for Synchronous Logic

  1. Flip-Flops (FFs):

    D flip-flop activated at positive edge of clock (posedge) :

    d_flip_flop.v
    
    module d_flip_flop ( 
       input wire clk,   // Clock input 
       input wire reset, // Reset input (asynchronous) 
       input wire d,     // Data input 
       output wire q     // Output 
    );
       always @(posedge clk or posedge reset) begin 
          if (reset) 
             q <= 1'b0; // Reset the flip-flop asynchronously 
          else 
             q <= d;    // Store the data input on the rising clock edge 
       end 
    endmodule 
                               

    Testbench for D FF :

    tb_d_flip_flop.v
    
    module tb_d_flip_flop;
       reg clk; 
       reg reset; 
       reg d; 
       wire q; 
    
       // Instantiate the D Flip-Flop module 
       d_flip_flop dflop ( 
          .clk(clk), 
          .reset(reset), 
          .d(d), 
          .q(q) 
       ); 
       
       // Clock generation 
       always begin 
          #5 clk = ~clk; 
       end 
       
       // Test scenario 
       initial begin 
          // Initialize signals 
          clk = 0; 
          reset = 0; 
          d = 0; 
          
          // Apply reset 
          reset = 1; 
          #10 reset = 0; 
          
          // Test case 1: Set D to 1 
          d = 1; 
          #10 assert(q === 1) else $display("Test case 1 failed"); 
          
          // Test case 2: Toggle D while clock is high 
          d = 0; 
          #5 d = 1; 
          #5 d = 0; 
          #5 d = 1; 
          #10 assert(q === 1) else $display("Test case 2 failed"); 
          
          // Test case 3: Set D to 0 
          d = 0; 
          #10 assert(q === 0) else $display("Test case 3 failed"); 
          
          // End simulation 
          $finish; 
       end 
       endmodule 
                               

    JK flip-flop activated at positive edge of clock (posedge) :

    jk_flip_flop.v
    
    module jk_flip_flop ( 
       input wire clk,    // Clock input 
       input wire reset,  // Reset input (asynchronous) 
       input wire j,      // J input 
       input wire k,      // K input 
       output wire q,     // Output 
       output wire q_bar  // Complement of Q 
    ); 
       reg q_next; // Next state of Q 
       
       always @(posedge clk or posedge reset) begin 
          if (reset) 
             q_next <= 1'b0; // Reset the flip-flop asynchronously 
          else 
             q_next <= (j & q_bar) | (k & q); // JK flip-flop behavior 
       end 
       
       assign q = q_next; 
       assign q_bar = ~q_next; 
    endmodule 
                               

    Testbench for JK FF :

    tb_jk_flip_flop.v
    
    module tb_jk_flip_flop; 
       reg clk; 
       reg reset; 
       reg j; 
       reg k; 
       wire q; 
       wire q_bar; 
    
       // Instantiate the JK Flip-Flop module 
       jk_flip_flop jkflop ( 
          .clk(clk), 
          .reset(reset), 
          .j(j), 
          .k(k), 
          .q(q), 
          .q_bar(q_bar) 
       );
    
       // Clock generation 
       always begin 
       #5 clk = ~clk; 
       end 
    
       // Test scenario 
       initial begin 
          // Initialize signals 
          clk = 0; 
          reset = 0; 
          j = 0; 
          k = 0; 
    
          // Apply reset 
          reset = 1; 
          #10 reset = 0; 
    
          // Test case 1: J=1, K=0 (Toggle) 
          j = 1; 
          k = 0; 
          #10 assert(q === 1 && q_bar === 0) else $display("Test case 1 failed"); 
    
          // Test case 2: J=0, K=1 (Toggle) 
          j = 0; 
          k = 1; 
          #10 assert(q === 0 && q_bar === 1) else $display("Test case 2 failed"); 
    
          // Test case 3: J=1, K=1 (No change) 
          j = 1; 
          k = 1; 
          #10 assert(q === 1 && q_bar === 0) else $display("Test case 3 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    T flip-flop at positive edge of clock (posedge) :

    t_flip_flop.v
    
    module t_flip_flop ( 
       input wire clk,    // Clock input 
       input wire reset,  // Reset input (asynchronous) 
       input wire t,      // Toggle input 
       output wire q,     // Output 
       output wire q_bar  // Complement of Q 
    ); 
       reg q_next; // Next state of Q 
       
       always @(posedge clk or posedge reset) begin 
          if (reset) 
             q_next <= 1'b0; // Reset the flip-flop asynchronously 
          else if (t) 
             q_next <= ~q_next; // Toggle the flip-flop state on the rising clock edge 
       end 
       
       assign q = q_next; 
       assign q_bar = ~q_next; 
    endmodule 
                               

    Testbench for T FF :

    tb_t_flip_flop.v
    
    module tb_t_flip_flop; 
       reg clk; 
       reg reset; 
       reg t; 
       wire q; 
       wire q_bar; 
       
       // Instantiate the T Flip-Flop module 
       t_flip_flop tflop ( 
          .clk(clk), 
          .reset(reset), 
          .t(t), 
          .q(q), 
          .q_bar(q_bar) 
       ); 
       
       // Clock generation 
       always begin 
          #5 clk = ~clk;
       end 
       
       // Test scenario 
       initial begin 
          // Initialize signals 
          clk = 0; 
          reset = 0; 
          t = 0; 
          
          // Apply reset 
          reset = 1; 
          #10 reset = 0; 
          
          // Test case 1: Toggle T input 
          t = 1; 
          #10 assert(q === 1 && q_bar === 0) else $display("Test case 1 failed"); 
          
          // Test case 2: Toggle T input again 
          t = 0; 
          #5 t = 1; 
          #10 assert(q === 0 && q_bar === 1) else $display("Test case 2 failed"); 
          
          // Test case 3: No change (T=0) 
          t = 0; 
          #10 assert(q === 0 && q_bar === 1) else $display("Test case 3 failed"); 
          
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    These Verilog examples cover various synchronous logic FFs, including D FFs , JK FFs , T FFs with testbenches to simulate them.

  2. Counters

    3-bit Up Counter:

    up_counter_3bit.v
    
    module up_counter_3bit ( 
       input wire clk,      // Clock input 
       input wire reset,    // Reset input (asynchronous) 
       output wire [2:0] y  // 3-bit Output 
    ); 
       reg [2:0] count;     // Counter register 
       
       always @(posedge clk or posedge reset) begin 
          if (reset) 
             count <= 3'b000; // Reset the counter asynchronously 
          else if (count == 3'b111) 
             count <= 3'b000; // Reset when reaching maximum 
          else 
             count <= count + 1; // Increment on rising clock edge 
       end 
       
       assign y = count; 
    endmodule 
                               

    Testbench for 3-bit Up Counter:

    tb_up_counter_3bit.v
    
    module tb_up_counter_3bit; 
       reg clk; 
       reg reset; 
       wire [2:0] y; 
    
       // Instantiate the 3-bit Up Counter module 
       up_counter_3bit counter ( 
          .clk(clk), 
          .reset(reset), 
          .y(y) 
       ); 
    
       // Clock generation 
       always begin 
          #5 clk = ~clk; 
       end 
    
       // Test scenario 
       initial begin 
          // Initialize signals 
          clk = 0; 
          reset = 0; 
    
          // Apply reset 
          reset = 1; 
          #10 reset = 0; 
    
          // Test case 1: Count from 0 to 7 
          #20 assert(y === 3'b000) else $display("Test case 1 failed"); 
          #10 assert(y === 3'b001) else $display("Test case 2 failed"); 
          #10 assert(y === 3'b010) else $display("Test case 3 failed"); 
          #10 assert(y === 3'b011) else $display("Test case 4 failed"); 
          #10 assert(y === 3'b100) else $display("Test case 5 failed"); 
          #10 assert(y === 3'b101) else $display("Test case 6 failed"); 
          #10 assert(y === 3'b110) else $display("Test case 7 failed"); 
          #10 assert(y === 3'b111) else $display("Test case 8 failed"); 
    
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    4-bit Ring Counter:

    ring_counter_4bit.v
    
    module ring_counter_4bit ( 
       input wire clk,      // Clock input 
       input wire reset,    // Reset input (asynchronous) 
       output wire [3:0] y  // 4-bit Output 
    ); 
       reg [3:0] count; // Counter register 
       
       always @(posedge clk or posedge reset) begin 
          if (reset) 
             count <= 4'b0000; // Reset the counter asynchronously 
          else if (count == 4'b1111) 
             count <= 4'b0001; // Wrap around to 0001 when reaching maximum 
          else 
             count <= count + 1; // Increment on rising clock edge 
       end 
       
       assign y = count; 
    endmodule 
                               

    Testbench for 4-bit Ring Counter:

    tb_ring_counter_4bit.v
    
    module tb_ring_counter_4bit; 
       reg clk; 
       reg reset; 
       wire [3:0] y; 
       
       // Instantiate the 4-bit Ring Counter module 
       ring_counter_4bit counter ( 
          .clk(clk), 
          .reset(reset), 
          .y(y) 
       ); 
       
       // Clock generation 
       always begin 
       #5 clk = ~clk; 
       end 
       
       // Test scenario 
       initial begin 
          // Initialize signals 
          clk = 0; 
          reset = 0; 
          
          // Apply reset 
          reset = 1; 
          #10 reset = 0; 
          
          // Test case 1: Count in a ring from 0000 to 1111 
          #10 assert(y === 4'b0000) else $display("Test case 1 failed"); 
          #10 assert(y === 4'b0001) else $display("Test case 2 failed"); 
          #10 assert(y === 4'b0010) else $display("Test case 3 failed"); 
          #10 assert(y === 4'b0100) else $display("Test case 4 failed"); 
          #10 assert(y === 4'b1000) else $display("Test case 5 failed"); 
          #10 assert(y === 4'b1100) else $display("Test case 6 failed"); 
          #10 assert(y === 4'b1110) else $display("Test case 7 failed"); 
          #10 assert(y === 4'b1111) else $display("Test case 8 failed"); 
          
          // End simulation 
          $finish; 
       end 
       endmodule 
                               

    These Verilog code examples and testbenches demonstrate the functionality of a 3-bit Up Counter and a 4-bit Ring Counter, along with various test cases to verify their operation.

  3. Memories

    4x4 SRAM (Static Random Access Memory):

    sram_4x4.v
    
    module sram_4x4 ( 
       input wire [1:0] addr,    // 2-bit Address 
       input wire write_en,     // Write Enable 
       input wire [3:0] data_in, // 4-bit Data Input 
       output wire [3:0] data_out // 4-bit Data Output 
    ); 
       reg [3:0] memory [3:0]; // 4x4 memory array 
       
       always @(addr, write_en, data_in) begin 
          if (write_en) 
             memory[addr] <= data_in; // Write data to the selected address 
       end 
       
       assign data_out = memory[addr]; // Read data from the selected address 
    endmodule 
                               

    Testbench for 4x4 SRAM:

    tb_sram_4x4.v
    
    module tb_sram_4x4; 
       reg [1:0] addr; 
       reg write_en; 
       reg [3:0] data_in; 
       wire [3:0] data_out; 
       
       // Instantiate the 4x4 SRAM module 
       sram_4x4 sram ( 
          .addr(addr), 
          .write_en(write_en), 
          .data_in(data_in), 
          .data_out(data_out) 
       ); 
       
       // Test scenario 
       initial begin 
          // Initialize signals 
          addr = 2'b00; 
          write_en = 0; 
          data_in = 4'b0000; 
          
          // Write data to address 00 
          write_en = 1; 
          data_in = 4'b1100; 
          #10 write_en = 0; 
          
          // Read data from address 00 
          addr = 2'b00; 
          #10 assert(data_out === 4'b1100) else $display("Test case 1 failed"); 
          
          // Write data to address 01 
          write_en = 1; 
          data_in = 4'b0011; 
          #10 write_en = 0; 
          
          // Read data from address 01 
          addr = 2'b01; 
          #10 assert(data_out === 4'b0011) else $display("Test case 2 failed"); 
          
          // End simulation 
          $finish; 
       end 
       endmodule 
                               

    2x2 ROM (Read-Only Memory):

    rom_2x2.v
    
    module rom_2x2 ( 
       input wire [1:0] addr,  // 2-bit Address 
       output wire [3:0] data  // 4-bit Data Output 
    ); 
       // Define ROM contents 
       reg [3:0] memory [3:0]; 
    
       initial begin 
          memory[2'b00] = 4'b1100; 
          memory[2'b01] = 4'b0011; 
          memory[2'b10] = 4'b1010; 
          memory[2'b11] = 4'b0101; 
       end
    
       assign data = memory[addr]; // Read data based on the address 
    endmodule 
                               

    Testbench for 2x2 ROM:

    rom_2x2.v
    
    module tb_rom_2x2; 
       reg [1:0] addr; 
       wire [3:0] data; 
       
       // Instantiate the 2x2 ROM module 
       rom_2x2 rom ( 
          .addr(addr), 
          .data(data) 
       ); 
       
       // Test scenario 
       initial begin 
          // Initialize signals 
          addr = 2'b00; 
          
          // Read data from address 00 
          #10 assert(data === 4'b1100) else $display("Test case 1 failed"); 
          
          // Change address to 01 
          addr = 2'b01; 
          #10 assert(data === 4'b0011) else $display("Test case 2 failed"); 
          
          // Change address to 10 
          addr = 2'b10; 
          #10 assert(data === 4'b1010) else $display("Test case 3 failed"); 
          
          // Change address to 11 
          addr = 2'b11; 
          #10 assert(data === 4'b0101) else $display("Test case 4 failed"); 
          
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    These Verilog code examples and testbenches demonstrate the functionality of a 4x4 SRAM and a 2x2 ROM, along with various test cases to verify their operation. You can use a Verilog simulator to run these testbenches and observe the results.

    4x4 DRAM :

    rom_2x2.v
    
    module dram_4x4 ( 
       input wire clk,        // Clock input 
       input wire rst_n,      // Reset input (active low) 
       input wire write_en,   // Write enable 
       input wire [1:0] addr, // 2-bit Address 
       input wire [3:0] data_in, // 4-bit Data Input 
       output wire [3:0] data_out // 4-bit Data Output 
    ); 
       reg [3:0] memory [3:0]; // 4x4 memory array 
       reg row_addr;           // Row address register 
       reg col_addr;           // Column address register 
       
       always @(posedge clk or negedge rst_n) begin 
          if (!rst_n) begin 
             row_addr <= 0; 
             col_addr <= 0; 
          end else if (write_en) begin 
             // Write data to the selected address 
             memory[row_addr][col_addr] <= data_in; 
          end else begin 
             // Read data from the selected address 
             data_out <= memory[row_addr][col_addr]; 
          end 
       end 
       
       always @(posedge clk) begin 
          if (write_en) begin 
             // Update row and column addresses on a write operation 
             row_addr <= addr[1:0]; 
             col_addr <= addr[1:0]; 
          end 
       end 
    endmodule 
                               

    This simplified DRAM module includes a 4x4 memory array and basic row and column address management. It responds to read and write operations based on the clock and reset signals. However, it doesn't implement complex DRAM features like refresh cycles or page management, which are crucial in real-world DRAM designs.

    Here's a basic testbench for the simplified DRAM module:

    tb_dram_4x4.v
    
    module tb_dram_4x4; 
       reg clk; 
       reg rst_n; 
       reg write_en; 
       reg [1:0] addr; 
       reg [3:0] data_in; 
       wire [3:0] data_out; 
       
       // Instantiate the 4x4 DRAM module 
       dram_4x4 dram ( 
          .clk(clk), 
          .rst_n(rst_n), 
          .write_en(write_en), 
          .addr(addr), 
          .data_in(data_in), 
          .data_out(data_out) 
       ); 
       
       // Clock generation 
       always begin 
          #5 clk = ~clk; 
       end 
       
       // Test scenario 
       initial begin 
          // Initialize signals 
          clk = 0; 
          rst_n = 0; 
          write_en = 0; 
          addr = 2'b00; 
          data_in = 4'b0000; 
          
          // Release reset 
          rst_n = 1; 
          
          // Write data to address 00 
          write_en = 1; 
          data_in = 4'b1100; 
          addr = 2'b00; 
          #10 write_en = 0; 
          
          // Read data from address 00 
          addr = 2'b00; 
          write_en = 0; 
          #10 assert(data_out === 4'b1100) else $display("Test case 1 failed"); 
          
          // End simulation 
          $finish; 
       end 
    endmodule 
                               

    This testbench provides a basic scenario where data is written to and read from the DRAM module. In a real DRAM design, you would need to consider complex timing requirements, refresh cycles, and address multiplexing. This example serves as a starting point for understanding the basic principles of a DRAM model.

Previous Back to Start