Verilog Interview Questions

What to expect from Verilog in interviews?

Just like systemverilog, verilog interview questions focus on conceptual understanding and their application. But, one key difference with Verilog is that, since it is used in RTL coding, the interviewers usually are inclied to ask synthesis and basic design level questions. Still yet, Verilog is also used for coding testebenches, so its always better to be prepared to answer any questions from that point of view.

Theory Questions ( Click to expand )
    There are various kinds of simulator, but the most popular ones are event based and cycle based. Questasim is an exmaple of event based simulator and Verilator is an example of cycle driven simulation.
    The main difference between is, that wire is continous driven datatype while reg is a procedurally assigned datatype. This also means that reg can retain information, but wire cannot. Additionally, the default value of reg is 1'bx and wire is 1'bz.
    Blocking assignment are procedural and hold the simulation until executed, while non blocking is scheduled, which means it is scheduled to be executed later. ( mostly in NBA region )
    /*
        Listed below are the primary differences between fucntions and tasks:
        1. Functions are not allowed to have any delay based keywords or blocks, while tasks can have delays.
        2. Tasks can return multiple values depending on how many <>output<> arguments have been passed, while functions can only return one value.
        3. Functions can only call functions while tasks can call both functions and tasks.
    */
    
    Void is also called null datatype. This mean the nothing is returned. This is mostly used when you wish to deassign memory of an object or when you do not wish to return anything in a function call.
    Once the simulator comes across a NBA statement in the Active Region, it evaluates it like a blocking statement, but schedules the expression on the RHS of the assignment operator in the NBA region. For eg, take a <= b. In this case, the value of be at the time of evaluating the a<=b, the simulator uses a temp variable internally, lets say a_temp. This variable is assigned the RHS part of the <= operator. And then this a_temp will be actually assigned to the varibale "a" in the NBA region. Hence, in NBA, the actualy statement being executed will look like a = a_temp. (Note: Check the Home -> Practive Questions : Verilog -> Verilog Regions page for better clarity)
    Inputs and outputs are by default wires.
        /* Naming each thread using <>named block<> concept and the making sure before a block completes execution, it kills all the other blocks. */
    
        fork
            begin : A
                #( ($urandom%100)+1 );
                $display( $time, "A finished" );
                disable B;
            end : A
            begin : B
                #( ($urandom%100)+1 );
                $display( $time, "B finished" );
                disable A;
            end : B
        join
        $display( $time, "Finished" );
    
        /* Alternatively you can use the event datatype and place waits to trigger a disable in a third thread */
    
    Yes, but only one of them is triggered, provided the variables that are being monitored are same. In any case, if only display string is different, then the last $monitor statement scheduled by the simulator will be printed. (For eg, $monitor("A") & $monitor("B"), will result in "B" alone being printed)
    These are two different types of vector slicing constructs. Packed arrays can be sliced in fixed chunks of bits using these operators. +: refers to slicing in incrementing order from LSB to MSB, while -: does it in decrementing manner.
    $bits can be used to find the number of bits in a variable
    /*
        Followings are brief descriptions of the differences between case, casex and casez:
        1. <>case -<> This is the regular case construct of verilog. The <>case(<expression/variable>)<> evaluats its argument as a 4 state value and does strict comparison.
        2. <>casez -<> Here, "z" & "?" are considered wildcard or don't cares in comparisons. Which means, they are considered as 0 and 1 both. So, for example 4'b110? will match with both 4'b1100 and 4'1101, but depending on which case item is coded first, that particular item will be executed.
        3. <>casex -<> Like casez, casex treats "x" as wildcard as well.
    */
    
    PREPONE exists to provide fixed sampled values of all the variables, while POSTPONED exists to evaluate the final values of the variables. It is possible to acces a PREPONED region values of a variable even though it might have changed in the active regions. Check the system tasks to see which task helps you accomplish this.
    Skew is the delay that is observed in a signal, particularly in clocks pins. This means that, when two blocks receive a same clock source, but one clock is slower/faster that, then we say there is a skew in the clock. This usually, could happen, of the clock is travelling through intermediate blocks or gating logic. Even things like difference in trace lengths of the two paths of the same clock source can add to skew when reaching their respective destination.
    Using for-generate construct
    The state of voltage which makes a digital logic unable to distinguish it from a logical 1 or a logical 0 is called metastability. ( for exampl, if 0.7V is logic 0 and 0.9V is logic 1, then metastable input is when the sampled pin voltage level is between 0.9 and 0.7 )
    Different types of synchronizers which designers usually use are Toggle Synchronizer, Pulse Synchronizer, 2-Flop Synchronizer, MUX Synchronizer, 3-Flop Synchronizer and others. Most of the time, a 2-flop synchronizer will get the job done.
Coding Questions ( Click to expand )
    module FA_RANDOM_WIDTH # (
        parameter WIDTH = rand_width()
    )(
        input [WIDTH-1:0] A, B,
        output [WIDTH:0] OUT
    );
    
        function integer rand_width();
            return ( ( $urandom % 8 ) + 1 );
        endfunction
    
        assign OUT = A + B;
    
    endmodule
    
    Yes, verilog allows this kind of function calling in the parameter declaration.
    module top;
        int A;
    
        initial
        begin
            A = $urandom & ( {~( 31'd0 ),1'b0} );
    
            $display( "Number : %0d", A );
        end
    endmodule
    
    module DFF #(
        parameter WIDTH = 1
    )(
        input clk,
        input rst_n,
    
        input wire [WIDTH-0:0] d_in,
        output reg [WIDTH-1:0] d_out
    );
    
        always@( posedge clk or negedge rst_n )
        begin
            if( !rst_n )
            begin
                d_out <= 0;
            end
            else
            begin
                d_out <= d_in;
            end
        end
    
    endmodule
    
    module FA #(
        parameter D_WIDTH = 4
    )(
        input wire [D_WIDTH-1:0] INA, INB,
        output wire [D_WIDTH-1:0] SUM_OUT,
        output wire [0:0] CARRY_OUT
    );
    
        assign {CARRY_OUT,SUM_OUT} = INA + INB;
    
    endmodule
    
    module detect#(
        parameter DETECT_WIDTH = 5,
        parameter reg [DETECT_WIDTH-1:0] PATTERN = 5'b10110
    )(
        input clk_i,
        input rst_n_i,
    
        input d_in,
        output detected_o
    );
    
        reg [DETECT_WIDTH-1:0] SISO_DATA;
        reg [DETECT_WIDTH-1:0] SISO_FLAG;
    
        always@( posedge clk_i or negedge rst_n_i )
        begin
            if( !rst_n_i )
            begin
                SISO_DATA <= 0;
                SISO_FLAG <= 0;
            end
            else
            begin
                SISO_DATA <= { d_in, SISO_DATA[DETECT_WIDTH-1:1] };
                SISO_FLAG <= { ( { d_in, SISO_DATA[DETECT_WIDTH-1:1] } == PATTERN && |SISO_FLAG == 0 ) ? 1'b1, 1'b0, SISO_FLAG[DETECT_WIDTH-1:1] };
            end
        end
    
        assign detected_o = SISO_FLAG[DETECT_WIDTH-1];
    
    endmodule
    
    module assert_verilog(
        input a,
        input clk
    );
    
        // It is not possible to write assertions in Verilog the way it is
        // possible in SystemVerilog. So, instead a assertion like behavioural
        // block needs to be coded to get the job done.
    
        reg fail_flag = 0;
    
        always@( posedge clk )
        begin
            if( a == 0 ) // Trigger
            begin : ASSERT_CHECK_A
                for( integer i = 1; i <= 1 && a == 0; i = i+1 )
                begin
                    @( posedge clk );
                    fail_flag = ( a != 0 ) 1 : 0;
                end
                for( integer i = 1; i <= 3 && a == 1; i = i+1 )
                begin
                    @( posedge clk );
                    fail_flag = ( a != 1 ) 1 : 0;
                end
            end : ASSERT_CHECK_A
        end
    
        always@(fail_flag)
        begin
            if( fail_flag == 1 )
            begin
                disable ASSERT_CHECK_A;
                fail_flag = 0;
            end
        end
    
    endmodule
    
    module DFF_ASYN_RST #(
        parameter WIDTH = 1
    )(
        input clk,
        input rst_n,
    
        input wire [WIDTH-0:0] d_in,
        output reg [WIDTH-1:0] d_out
    );
    
        always@( posedge clk )
        begin
            if( !rst_n ) // Since there is no "negedge rst_n" in the sensitivity list, now the reset is synchronous
            begin
                d_out <= 0;
            end
            else
            begin
                d_out <= d_in;
            end
        end
    endmodule
    
    module DFF_ASYN_RST_DATA_EN #(
        parameter WIDTH = 1
    )(
        input clk,
        input rst_n,
        
        input en_in,
        input wire [WIDTH-0:0] d_in,
        output reg [WIDTH-1:0] d_out
    );
    
        always@( posedge clk )
        begin
            d_in <= d_in;
    
            if( !rst_n ) // Since there is no "negedge rst_n" in the sensitivity list, now the reset is synchronous
            begin
                d_out <= 0;
            end
            else
            begin
                if( en_in )
                begin
                    d_out <= d_in;
                end
            end
        end
    endmodule
    
    module Nx1_MUX #(
        parameter WIDTH = 1,
        parameter N_INPUT = 4
    )(
        input [N_INPUT-1:0][WIDTH-1:0] data_i,
        input [$clog2(N_INPUT)-1:0] sel_i,
        output [WIDTH-1:0] data_o
    );
    
        // If the number of inputs is 10 and the sel_i is driven any value > 9, then by default
        // 0 will be driven to the output
        assign data_o = ( sel_i > ( N_INPUT - 1 ) ) ? data_i[sel_i] : 0;
    
    endmodule
    
    module dut(
        input [7:0] IN,
        output [7:0] OUT
    );
    
        assign OUT = {IN[4:0],3'b011}; // Can you guess why this logic?
    
    endmodule
    
    module BinaryToGray (
        input [3:0] binary,  // 4-bit binary input
        output [3:0] gray    // 4-bit gray code output
    );
    
        // Gray code computation
        assign gray[3] = binary[3];  // MSB is the same
        assign gray[2] = binary[3] ^ binary[2];
        assign gray[1] = binary[2] ^ binary[1];
        assign gray[0] = binary[1] ^ binary[0];
    
    endmodule
    
    `timescale 1ns / 1ps
    
    module tb_BinaryToGray();
    
        reg [3:0] binary;
        wire [3:0] gray;
    
        // Instantiate the BinaryToGray module
        BinaryToGray converter (
            .binary(binary),
            .gray(gray)
        );
    
        initial begin
            // Monitor changes
            $monitor("At time %t, Binary = %b, Gray = %b", $time, binary, gray);
    
            // Test various binary inputs
            binary = 4'b0000; #10;
            binary = 4'b0001; #10;
            binary = 4'b0010; #10;
            binary = 4'b0011; #10;
            binary = 4'b0100; #10;
            binary = 4'b0101; #10;
            binary = 4'b0110; #10;
            binary = 4'b0111; #10;
            binary = 4'b1000; #10;
            binary = 4'b1001; #10;
            binary = 4'b1010; #10;
            binary = 4'b1011; #10;
            binary = 4'b1100; #10;
            binary = 4'b1101; #10;
            binary = 4'b1110; #10;
            binary = 4'b1111; #10;
    
            // End of test
            $finish;
        end
    
    endmodule
    
    `timescale 1ns / 1ps
    
    module GrayToBinary(
        input [3:0] gray,  // 4-bit gray code input
        output reg [3:0] binary  // 4-bit binary output
    );
    
        always @(gray) begin
            binary[3] = gray[3];  // MSB is the same
            binary[2] = binary[3] ^ gray[2];
            binary[1] = binary[2] ^ gray[1];
            binary[0] = binary[1] ^ gray[0];
        end
    endmodule
    
    // Testbench
    module tb_GrayToBinary();
        reg [3:0] gray;
        wire [3:0] binary;
    
        // Instantiate the GrayToBinary module
        GrayToBinary converter(
            .gray(gray),
            .binary(binary)
        );
    
        initial begin
            // Monitor changes
            $monitor("At time %t, Gray = %b, Binary = %b", $time, gray, binary);
    
            // Test various Gray code inputs
            gray = 4'b0000; #10;
            gray = 4'b0001; #10;
            gray = 4'b0011; #10;
            gray = 4'b0010; #10;
            gray = 4'b0110; #10;
            gray = 4'b0111; #10;
            gray = 4'b0101; #10;
            gray = 4'b0100; #10;
            gray = 4'b1100; #10;
            gray = 4'b1101; #10;
            gray = 4'b1111; #10;
            gray = 4'b1110; #10;
            gray = 4'b1010; #10;
            gray = 4'b1011; #10;
            gray = 4'b1001; #10;
            gray = 4'b1000; #10;
    
            // End of test
            $finish;
        end
    endmodule
    
    module mux_2x1#( parameter WIDTH = 1 )(     input [WIDTH-1:0] IN0,
                                                input [WIDTH-1:0] IN1,
                                                input SEL,
                                                output [WIDTH-1:0] OUT0 );
    
        wire selNOT_w;
        wire aANDNOTSEL_w;
        wire bANDSEL_w;
    
        not selNOT(selNOT_w, SEL); // !SEL
    
        and aANDNOTSEL(aANDNOTSEL_w,IN0,selNOT_w); // IN0 & !SEL
        and bANDSEL(bANDSEL_w,IN1,SEL); // IN1 & SEL
    
        or finalOUT(OUT0,aANDNOTSEL,bANDSEL); // ( IN0 & !SEL ) | ( IN1 & SEL )
    
    endmodule
    
    /*
                 _    _    _    _    _    _    _    _
    clk_i      _| |__| |__| |__| |__| |__| |__| |__| |_
                           _
    req_in    ____________| |__________________________ 
                                            ____
    grant_out _____________________________|    |______
    */
    
    module verilog_monitor(
        input clk_i,
        input req_in,
        input grant_out
    );
    
        forever
        begin
            @( posedge req_in );
            fork
                begin : NEW_REQ_IN_FOUND
                    @( posedge req_in );
                    $display( "[ ERROR ] New req_i received when previous one has not yet received the grant" );
                    disable GRANT_CHECK;
                    break;
                end : NEW_REQ_IN_FOUND
                begin : GRANT_CHECK
                    integer cnt;
                    cnt = 0;
    
                    forever
                    begin
                        @( posedge clk_i );
                        cnt += 1;
                        if( cnt > 10 )
                        begin
                            $display( "[ ERROR ] Grant not received within 1-10 clocks" );
                            disable NEW_REQ_IN_FOUND;
                            break;
                        end
                        else
                        begin
                            if( grant_out == 1 )
                            begin
                                $display( "[ SUCCESS ] Grant received after %0d clocks", cnt );
                                disable NEW_REQ_IN_FOUND;
                                break;
                            end
                        end
                    end
                end : GRANT_CHECK
            join
        end
    
    endmodule