SystemVerilog Interview Questions


What to expect from SystemVerilog in interviews?

SystemVerilog concepts which are asked in the interviews usually target the logical and implemenattion ability of the interviewee. This means that some interviewers try to judge if the candidate has any understanding of the concept and also try to see if the candidate is able to apply the so learnt concepts. The easiest way to crack this kind of proccess is to be throughly aware of the different ways in which the same concept can be applied in practice.

Theory Questions ( Click to expand )
    module top;
    
        initial
        begin
            fork
                begin
                    #0 $display( "A" );
                end
                begin
                    #0 #0 $display( "B" );
                end
            join
        end
    
    endmodule
    
    "A" will print first ( but is simulator dependent ), since once in inactive region, using multiple #0 calls cannot be used to schedule the order of execution.
    int ARR[*][]
    
    This refers to a test that is written to force the DUT into a situation which goes beyound the scope of its valid behaviour. This helps us understand that in a situation where if a stimulus arrrives which is beyond the ideal condition, then does the DUT behave in a way which will break the original functionality. For example, in a FIFO with a depth of 8, it is expected that ideally non more that 8 push will ever happen. But in a situation where unexpectedly 9 PUSH have happened, it needs to be noted that the FIFO must still mantain its First In First Out behaviour without actually pushing the new 9th data.
    for( int i = 0; i < 3; i++ )
    begin
        fork
            $display( "i : %0d", i );
        join_none
    end
    $display( "Hello World" );
    
    /*
    Output :
    
    Hello World
    3
    3
    3
    
    Reason :
    
    1. If you will look at the SystemVerilog Section on "fork-join_none", you will see that the fork_join_none statements are 
    scheduled to be executed at the end of the active region, hence "Hello World" prints first and then before moving to inactive 
    regions, the simulator runs the "i : %0d" statements;
    2. The reason why 3,3,3 is printed is because, since it is a static variable, the i value updates after every loop, but 
    since the statement itself never executed the value last value of i before moving to inactive region is used to executed 
    the fork-join_none scheduled statements.
    3. The reason why 3 is printed and not 2, is because in the "for" statement, i increments first in the previous loop and 
    then the condition for i < 3 is checked.
    4. The solution for this problem is, by utilizing an automatic variable. Solution is given below
    
    */
    
    for( int i = 0; i < 3; i++ )
    begin
        automatic k = i;
        fork
            $display( "i : %0d", k );
        join_none
    end
    $display( "Hello World" );
    
    Callback is a method using which one can override a functionality inside any block where a overridable function is being called. Ideally this is achieved using the polymorphism concept where a base class virtual function is overridden with cust functinality which is defined in the extended class. One of the best examples of this is if you wish to corrupt the CRC generating logic's functionality. The CRC functon can be overridden which will now produce a new behaviour.
    No, as per LRM the order in which the scheduled fork-join_non threads are executed is completely dependent on the simulator. Here, scheduled means the threads are yet to execute just before closing the ACTIVE region or INACTIVE region depending on which region the fork-join_none thread was scheduled from.
    /*
    
    -----------------------
    DUT : Asynchronous FIFO
    -----------------------
    
    Depth : 8
    
    DATA_IN_WIDTH : data_in_1byte
    DATA_OUT_WIDTH : data_out_1byte
    PUSH : push_en_in_1bit
    POP : pop_en_in_1bit
    FULL : out_full_1bit
    EMPTY : out_empty_1bit
    RESET : reset_n_in_1bit
    
    */
    
    /*
    --------
    Testplan
    --------
    
    1. asyn_fifo_1_element_push_test
        Description : Simple FIFO push after reset
    
    2. asyn_fifo_1_element_pop_test
        Description : Simple FIFO pop after push
    
    3. asyn_fifo_full_push_test
        Description : Push elements until FULL flag is high and see it that is happening exactly after 8 PUSH
    
    4. asyn_fifo_full_push_empty_pop_test
        Description : Push until FULL and then POP 8 times to see if EMPTY flag is begin asserted
    
    5. asyn_fifo_full_push_4_empty_pop_4_full_push_test
        Description : PUSH until FULL, then POP 4 times and PUSH 4 times to see if the internal rollover 
        logic for full condition is correctly working or not
    
    6. asyn_fifo_push_4_empty_pop_4_full_push_8_empty_pop_test
        Description : PUSH unit 4 elements are in, then POP until EMPTY is high. Then see if after 
        doing 8 PUSH and 8 POP, any data is recurring. This would mean that internally meomory is accessing 
        only data location, which is incorrect behaviour.
    
    7. asyn_fifo_full_push_2_extra_push_8_empty_pop_test
        Description : After 8 PUSH, PUSH 2 more times and then POP to see if any successfully PUSHed data has 
        been overwritten or not during the additonal PUSH.
    
    8. asyn_fifo_empty_to_full_to_empty_2_extra_pop_test
        Description : PUSH until full, then POP until empty and then POP 2 more times to see if the output is 
        changing.
    
    9. asyn_fifo_empty_to_full_4_pop_reset_empty_flag_test
        Description : PUSH 4 elements, then RESET and see if EMPTY is high.
    
    10. asyn_fifo_push_pop_same_time_priority_test
        Description : PUSH and POP at the same clock edge of the two clock domains. As per design one of 
        them will internally have a priority. This needs to be verified.
    
    */
    
    In the context of SystemVerilog and digital circuit simulation, "quasi-static" refers to a method of handling the handling of dynamic memory elements on a need to basis, ie they exisst for a pre-defined duration while actually being dynamic and not fully statis. In SV, UVM components are considered quasi-static as although they are classes, the instances of components exist throughout the simulation.
    /*
    
    Read Rate  : 6 per 20 cycles
    Write Rate : 13 per 25 cycles
    
    Considering the worst case scenario for two back to back 25 cycles ( 25 since the nunmber is higher ) :-
    
    1.   6 READS ( for the first 20 cycles ) 
       + 6 READS ( for next 20 cycles )
       + 0 READS ( for next 5 cycles. this number is in case of worst case ) 
       = 12 READS per 50 cycles ( in worst case )
    2.   13 WRITES ( first 25 cycles ) 
       + 13 WRITES ( next 25 cycles ) 
       = 26 WRITES per 50 cycles ( worst case )
    3. Difference per 50 cycles : 26 WRITES - 12 READS = 14 WRITES ( remember that this is in worst case )
    4. Hence, a buffer/FIFO of depth 14 will be required.
    
    Solution : FIFO of 14 depth is required
    
    */
    
    Locked Solution
    module ff (q,clk,t);
        input clk t;
        output reg q=0;
    
    
        always @(posedge clk)
        begin 
            if (t)
            q <= ~q;
        end 
    
    endmodule
    
    module tb;
        reg clk=0;t=1;
        wire q=0;
    
        always @#5 = ~clk;
    
        ff u1 (q,clk,t);
    
        always @(posedge clk)
            $display ($time,q);
    
    endmodule 
    
    /*
    In above code since we are providing q with 0 and expecting it to invert its value at every posedge
    Expected value on $display 
    #5 -> 1;
    #10 -> 0;
    #15 -> 1;
    #20 -> 0;
    
    But insted we will recieve
    #5 -> 0;
    #10 -> 1;
    #15 -> 0;
    #20 -> 1;
    
    This is happening because in the prponed region, value 0 has been sampled but in NBA region the inverse value will be assigned on next edge. So, if we prints value at current edge, value will be 0 for the preponed region.
    */
    
    A cycle-driven simulator is a type of simulation methodology that focuses on the evaluation of the digital circuit's behavior in cycles, typically synchronized with the clock edges. Cycle-driven simulators in Verilog provide a more streamlined and sometimes faster way to simulate digital circuits, especially when the interest is in the behavior synchronized with clock cycles rather than the intricate details of signal propagation and asynchronous events. Example Tool : Cadence IES
Coding Questions ( Click to expand )
    module top;
    
        int A[][*];
    
        initial
        begin
            A = new[5];
            A[0]["HELLO"] = 100;
            A[1]["BYE"] = 200;
    
            $display( "%p", A[0] );
            $display( "%p", A[1] );
        end
    
    endmodule
    
    int num = ( %urandom >> 1 ) << 1;
    /*
         <OR>
    */
    class ran;
         rand int num;
    
         constraint even {
              num[0] == 0;
         }
    endclass
    
    class ran;
        rand bit [63:0] ph_num;
    
        constraint valid_nums {
            ph_num inside { 64'd0, 64'd911, [64'd1000_0000:64'd1999_9999], [64'd10_0000_0000:64'd19_9999_9999] };
        }
    endclass
    
    class ran;
        rand int base_addr[];
        rand int offset[];
    
        /*
            Example:
    
            ---------- -> 1024KB ( 2^20 -> 20bits )
            |        |
            |        |
            |        |
            |        |
            |        |
            |        | -> offset[n+1]+base_addr[n+1]
            |        | -> base_addr[n+1] -> ( SEGMENT N+1 )
            |        |
            |        |
            |        |
            |        |
            |        | -> offset[n]+base_addr[n]
            |        |
            |        |
            |        | -> base_addr[n] -> ( SEGMENT N )
            .        .
            .        .
            .        .
            |        |
            ---------- -> 0B
        */
    
        constraint segments {
            base_addr.size == 8;
            offset.size == 8;
        }
    
        constraint addr {
            foreach( base_addr[i] ) {
                if( i > 0 ) {
                    base_addr[i] >= base_addr[i-1]+offset[i-1];
                }
                base_addr[i] >= 0 && base_addr[i] < 2**20;
                offset[i] >= 0 && offset[i] < 2**20;
            }
        }
    endclass
    
    class ran;
    
        int num;
    
        function void post_randomize();
            num = ( $urandom % 91 ) + 10;
        endfunction
    
    endclass
    
    class ran;
        rand bit [31:0] data[];
        rand int m0;
        rand int m1;
    
        constraint m0_m1_con {
            m0 < 10;
            m1 < 10;
            m0 > 0;
            m1 > 0;
        }
    
        constraint pattern {
            data.size == ( m0 + m1 );
            foreach( data[i] ) {
                if( i < m0 ) {
                    data[i] == 32'hFFFF_FFFF;
                } else {
                    data[i] == 32'h0000_0000;
                }
            }
        }
    
        function void post_randomize;
            data.shuffle();
        endfunction
    endclass
    
    class ex;
    
        rand bit rand_arr [8][8]; // Horizontal, Vertical
    
        rand bit [7:0] h [8];
        rand bit [7:0] v [8];
    
        constraint board_conditions {
            foreach( h[i] ) {
                $countones(h[i]) == 1;
                $countones(v[i]) == 1;
            }
    
            foreach( v[i] ) {
                foreach( v[j] ) {
                    v[j][i] == h[i][j];
                    rand_arr[i][j] == h[i][j];
                }
            }
        }
    
        function void print_board();
            int t;
    
            t = 0;
            
            foreach( rand_arr[i] )
            begin
                foreach( rand_arr[j] )
                begin
                    if( t != i )
                    begin
                        $display( "" );
                        t = i;
                    end
                    $write( "%1d ", rand_arr[i][j] );
                end
            end
            $display( "\n" );
        endfunction
    
    endclass
    
    module top;
    
        ex R;
    
        initial
        begin
            R = new();
    
            repeat( 10 )
            begin
                R.randomize();
                R.print_board();
            end
        end
    
    endmodule
    
    module abc();
     
    class abc;
    rand int arr[];
    rand int num1,num2;
    
    constraint siz {arr.size ==10;
                    num1 >0 ;
                    num1 < arr.size;
                    num2 >0;
                    num2 <arr.size;
                    num2 > num1+1;
                    arr[num1] == arr[num2];
                  }
    
    constraint values {
                        foreach(arr[i])
                        {
                            arr[i] inside{[0:99]};
                            if (i<9)
                            {
                                arr[i] != arr[i+1];
                            }
                        }
                    }
    
        function new (int seed);
            srandom(seed);
        endfunction
    endclass
    
    initial
    begin
        abc a;
        a=new(10);
    
        void'(a.randomize);
    
        foreach (a.arr[i])
            $display (a.arr[i]);
    
        $display ("karan num1 %0d num2 =%0d " ,a.num1,a.num2);
    end
    
    endmodule
    
    class ran;
        rand int a;
    
        constraint con {
            $countones( a ) == 5;
        }
    
        /*
            There are more ways to solve this. See if you can try to think of them.
        */
    endclass
    
    class ran;
        rand int arr[];
    
        constraint size_arr {
            arr.size == 15;
        }
    
        constraint sum {
            arr.sum() <= 100;
        }
    endclass
    
    module top;
    
        bit [7:0] mask;
        bit [7:0] val_in;
    
        initial
        begin
            mask = $urandom;
            val_in = $urandom;
    
            val_in = mask ^ val_in; // XOR acts as an inverter when input is XORed with 1 and when XORed with 0, the input remains the same
    
            $display( "Output : %h", val_in );
        end
    
    endmodule
    
    Locked Solution
    /*
         Arbiter
              _____
         R0 -|     |- G0
         R1 -|     |- G1  
         R2 -|     |- G2
         R3 -|_____|- G3
        
    1. This arbiter is driven by a simple CLK. There is no reset pin.
    2. At any point of time, there can be multiple requests, but they are granted on a first come basis.
    3. Grants can last from anywhere from 1-10 clock cycles.
    4. REQ for any pin, must be high for only 1 clock cycle to be registered. More than that, and multiple requests for same source will be registered.
    
    */
    
    class seq_item;
    
        rand bit [3:0] req[];
        bit [3:0] gnt[];
    
        // This is the value which tell which all req pins are high at a certain clock.
        // The array exists to give the seq the ability to drive request values over multiple 
        // subsequent clock cycles
        // For example, if you wish to drive R0 as high for 2 clocks and R3 high for 3 clocks from the second clock cycle, 
        // then the req variable will need to have values like this
        // req[0] = { 1'b0, 1'b0, 1'b0, 1'b1 }
        // req[1] = { 1'b1, 1'b0, 1'b0, 1'b1 }
        // req[2] = { 1'b1, 1'b0, 1'b0, 1'b0 }
        // req[3] = { 1'b1, 1'b0, 1'b0, 1'b0 }
    
        // When the above example is driven by the druven, the waveform will look like this
        //           ___     ___     ___     ___     ___    
        // clock  __|   |___|   |___|   |___|   |___|   |___
        //           _______________
        // req[0] __|               |_______________________
        // 
        // req[1] __________________________________________
        //
        // req[2] __________________________________________
        //                           ______________________ 
        // req[3] __________________|                      |
    
        constraint req_size {
            soft req.size == 4; // Default value ( as an example )
        }
    
        constraint req_order {
            foreach( req[i] ) {
                soft $onehot( req[i] ); // Not exclusively neccessary
            }
        }
    
        // This function is used just to sample gnt values from the dut and send the framed packet to monitor
        function update_gnt( bit [3:0] gnt_val );
            if( gnt.size )
            begin
                gnt = new[gnt.size+1](gnt);
                gnt[gnt.size-1] = gnt_val;
            end
            else
            begin
                gnt = new[1];
                gnt[0] = gnt_val;
            end
        endfunction
    
    endclass
    
    class driver;
    
        seq_item pkt;
        virtual intf_if intf;
    
        function new();
            // Get intf ( using config_db::get )
        endfunction
    
        task run_phase();
                
                fork
                    driving_logic();
                    sampling_logic();
                join_none
    
        endtask
    
        task driving_logic();
            forever
            begin
                // 
                // get_next_item
                // 
                foreach( pkt.req[i] )
                begin
                    @( posedge intf.clk );
                    { intf.R3, intf.R2, intf.R1, intf.R0 } <= pkt.req[i];
                end
    
                // item_done
            end
        endtask
    
        task sampling_logic();
            seq_item sampled;
            bit [3:0] sampled_val;
    
            forever
            begin
                sampled_val = 0;
                sampled = new;
                sampled_val = { intf.R3, intf.R2, intf.R1, intf.R0 };
    
                begin
                    wait( { intf.G3, intf.G2, intf.G1, intf.G0 } );
                    sampled.update_gnt( { intf.G3, intf.G2, intf.G1, intf.G0 } );
                end
                
                @( posedge intf.clk );
                // send pkt to SCB/MON
            end
        endtask
    
    endclass
    
    function void check_string( string s );
        signed int nb;
        signed int cb;
        signed int sb;
    
        foreach( s[i] )
        begin
            case( s[i] )
                "[" : sb++;
                "{" : cb++;
                "(" : nb++;
                "]" : sb--;
                "}" : cb--;
                ")" : nb--;
            endcase
            if( sb < 0 || cb < 0 || nb < 0 )
            begin
                $display( "Error : String does not follow the requirements" );
                return;
            end
        end
    
        if( sb == 0 && cb == 0 && nb == 0 )
        begin
            $display( "Pass : String does follow the requirements" );
            return;
        end
        else
        begin
            $display( "Error : String does not close properly" );
            return;
        end
    
    endfunction
    
    class sampler_pkt;
        
        bit [4-1:0] pattern_detect;
        bit start_transmit;
        byte pkg;
        int count;
    
        virtual interface dut_intf;
    
        // All functions execution function
        function new();
            // Code to get the dut_intf using any method
            fork
                detect_and_transmit_func();
            join
        endfunction
    
        function detect_and_transmit_func();
            // Thread
            fork
            begin
                forever
                begin
                    @( posedge dut_intf.clk )
                    begin
                        pattern_detect = {pattern_detect[4-1-1:0],dut_intf.in};
                        if( start_transmit )
                        begin
                            pkg = {pkg[8-1-1:0],dut_intf.in};
                            count++;
                            if( count == 8 )
                            begin
                                count = 0;
                                // Send pkt to scbd CODE goes HERE
                                // pkg variable is transmisste
                            end
                        end
                        if( pattern_detect == 4'b1011 && !start_transmit )
                        begin
                            start_transmit = 1;
                            count = 0;
                        end
                    end
                end
            end
            join
        endfunction
    
    endclass
    
    /*
    
    ---
    DUT
    ---
                _____
               |     |
      D_IN  ---|     |---  D_OUT
      T_IN  ---|     |---  T_OUT
      EN    ---|     |---  VLD
               |_____|
    
    
    1. D_IN & D_OUT -> Input and Output pins respectively ( each of 8bits each )
    2. T_IN & T_OUT -> 1bit inputs and outputs respectively ( represent a TAG which is added along with the input data )
    3. EN -> 1bit enable input pin
    4. VLD -> 1bit output valid pin which tells that the current output is valid transaction
    5. Assume there is a CLK and RESET
    6. D_IN and T_IN togather form a transaction which is sampled by the DUT only when EN is high
    7. Multiple transactions with the same tag can be sampled and will be valid
    8. For the same tag, at the output side, transaction will come out in ORDER
    9. For different tags, the output can come out in any order
       Example :
       Sampled Input Transaction
       -------------------------
       A. D_IN : 10, T_IN : 0
       B. D_IN : 20, T_IN : 1
       C. D_IN : 30, T_IN : 0
       D. D_IN : 40, T_IN : 0
       E. D_IN : 50, T_IN : 1
    
    Acceptable Behaviour at output :> B - A - C - E - D
    Unacceptable Behaviour at output :> E - C - A - B - D ( here if you observer, E & B are out of order. Same for C & A. )
    
    10. Assume there are 4 monitors, two at the input and two at the output. The input ones individually work on transactions 
        depending on the tag value. Same applies for the output. In short, for example a monitor at the input will only monitor
        all transactions with T_IN as 0 while the other input monitor will monitor all the transactions with the T_IN as 1. Same
        goes for the output monitors.
    
    */
    
    Locked Solution
    class ran;
    
        rand int num;
        bit is_first = 1;
        bit even_odd; // 0 : Even, 1 : Odd
    
        constraint toggle {
            is_first == 0 -> num[0] != even_odd;
        }
    
        function void post_randomize();
            even_odd = num[0];
            is_first = 0;
        endfunction
    
    endclass
    
    `timescale 1ns / 1ps
    
    module ClockGenerator #(
        parameter N = 1,          // Output clock frequency in MHz
        parameter M = 50          // Duty cycle as a percentage
    )(
        output reg clk_out = 0   // Output clock with configurable duty cycle
    );
    
        localparam PERIOD = 1000 / N;                   // Period of the clock in ns
        localparam HIGH_TIME = PERIOD * M / 100;        // High time in ns
        localparam LOW_TIME = PERIOD - HIGH_TIME;       // Low time in ns
    
        initial begin
            forever begin
                clk_out = 1'b1;
                #HIGH_TIME;
                clk_out = 1'b0;
                #LOW_TIME;
            end
        end
    endmodule
    
    // Testbench
    module tb_ClockGenerator();
        wire clk_out;
    
        // Clock generator instance (e.g., 10 MHz with 25% duty cycle)
        ClockGenerator #(.N(10), .M(25)) generator (
            .clk_out(clk_out)
        );
    
        initial begin
            // Simulation run for 2000 ns to observe the clock
            $dumpfile("clock_generator.vcd");
            $dumpvars(0, tb_ClockGenerator);
            #2000;
            $finish;
        end
    endmodule
    
    `timescale 1ns / 1ps
    
    module PRBS_Generator(
        input clk,                // Clock input
        input rst_n,              // Active-low reset
        output reg prbs_out       // Output pseudo-random bit sequence
    );
    
        reg [8:0] lfsr;           // 9-bit linear feedback shift register
    
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                lfsr <= 9'b1;     // Reset LFSR to non-zero value
            end else begin
                // Feedback polynomial: X^9 + X^5 + X^1 + 1
                lfsr <= {lfsr[7:0], (lfsr[8] ^ lfsr[4] ^ lfsr[0])};
            end
            prbs_out <= lfsr[8];  // Output the MSB of the LFSR
        end
    endmodule
    
    // Testbench
    module tb_PRBS_Generator();
        reg clk = 0;
        reg rst_n = 0;
        wire prbs_out;
    
        PRBS_Generator generator (
            .clk(clk),
            .rst_n(rst_n),
            .prbs_out(prbs_out)
        );
    
        // Clock generation
        always #10 clk = ~clk; // Generate a clock with a period of 20 ns
    
        initial begin
            // Reset sequence
            rst_n = 0;
            #40 rst_n = 1; // Release reset after 40 ns
    
            // Monitor output
            $monitor("At time %t, PRBS Output = %b", $time, prbs_out);
    
            // Run simulation for 2000 ns to observe behavior
            #2000;
            $finish;
        end
    endmodule
    
    // Code your testbench here
    // or browse Examples
    `timescale 1ns / 1ps
    
    module SerialToParallel(
        input clk,          // Clock input
        input rst_n,        // Asynchronous reset, active low
        input serial_in,    // Serial input data
        output reg [15:0] parallel_out, // Parallel output data
        output reg data_valid           // Data valid output
    );
    
      reg [4:0] bit_count = 0; // Counter to track the number of bits received
    
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                bit_count <= 0;
                parallel_out <= 0;
                data_valid <= 0;
            end else begin
                if (bit_count < 16) begin
                    parallel_out <= (parallel_out << 1) | serial_in; // Shift in new bit
                    bit_count <= bit_count + 1;
                    data_valid <= 0; // Data is not yet valid
                end
                if (bit_count == 16) begin
                    data_valid <= 1; // Indicate that the data is now valid
                    bit_count <= 0; // Reset bit counter
                end
            end
        end
    endmodule
    
    // Testbench
    module tb_SerialToParallel();
        reg clk = 0;
        reg rst_n = 0;
        reg serial_in = 0;
        wire [15:0] parallel_out;
        wire data_valid;
    
        SerialToParallel converter (
            .clk(clk),
            .rst_n(rst_n),
            .serial_in(serial_in),
            .parallel_out(parallel_out),
            .data_valid(data_valid)
        );
    
        // Clock generation
        always #5 clk = ~clk; // Generate a clock with a period of 10 ns
    
        initial begin
            // Reset sequence
            rst_n = 0;
            #15 rst_n = 1; // Release reset after 15 ns
    
            // Sending serial data
            #10 serial_in = 1;
            #10 serial_in = 0;
            #10 serial_in = 1;
            #10 serial_in = 1;
            #10 serial_in = 0;
            #10 serial_in = 1;
            #10 serial_in = 0;
            #10 serial_in = 0;
            #10 serial_in = 1;
            #10 serial_in = 0;
            #10 serial_in = 1;
            #10 serial_in = 0;
            #10 serial_in = 1;
            #10 serial_in = 1;
            #10 serial_in = 0;
            #10 serial_in = 1;
    
            // Observe output for additional 50 ns
            #50;
    
            $finish;
        end
    
        initial begin
            $monitor("At time %t, Output = %b, Data Valid = %b", $time, parallel_out, data_valid);
        end
    endmodule
    
    Locked Solution
    `timescale 1ns / 1ps
    
    module OnesCounter(
        input [15:0] data_in,    // 16-bit input bus
        output reg [4:0] count   // 5-bit output for the count of 1's
    );
    
        // Function to manually count the number of 1s
        function [4:0] countOnes(input [15:0] data);
            integer i;
            begin
                countOnes = 0;
                for (i = 0; i < 16; i = i + 1) begin
                    countOnes = countOnes + data[i];
                end
            end
        endfunction
    
        // Count the 1s whenever data_in changes
        always @(data_in) begin
            count = countOnes(data_in);
        end
    
    endmodule
    
    // Testbench
    module tb_OnesCounter();
        reg [15:0] data_in;
        wire [4:0] count;
    
        OnesCounter counter(
            .data_in(data_in),
            .count(count)
        );
    
        initial begin
            // Monitor changes
            $monitor("At time %t, Input = %b, Number of 1s = %d", $time, data_in, count);
    
            // Test different values
            data_in = 16'hFFFF; // All ones, expect 16
            #10;
            data_in = 16'hF0F0; // 8 ones
            #10;
            data_in = 16'h1234; // 5 ones
            #10;
            data_in = 16'h0000; // No ones
            #10;
            data_in = 16'h8001; // 2 ones
            #10;
    
            // Finish the simulation
            $finish;
        end
    endmodule
    
    `timescale 1ns / 1ps
    
    class Person;
        string name;
        int age;
        int scores[];  // Dynamic array of scores
    
        // Constructor to initialize the Person object
        function new(string init_name, int init_age, int init_scores[]);
            this.name = init_name;
            this.age = init_age;
            this.scores = new[int'(init_scores.size())] (init_scores);
        endfunction
    
        // Deep copy function
        function Person deep_copy();
            Person copy = new(this.name, this.age, this.scores);
            return copy;
        endfunction
    endclass
    
    // Testbench to demonstrate the use of deep copy
    module tb_Person;
        Person original;
        Person copy;
    
        initial begin
            int scores[] = {90, 85, 93};
            original = new("John Doe", 30, scores);
            copy = original.deep_copy();
    
            // Display original and copied data
            $display("Original: %s, %d, Scores: %p", original.name, original.age, original.scores);
            $display("Copy: %s, %d, Scores: %p", copy.name, copy.age, copy.scores);
    
            // Modify the original object to demonstrate that the copy is independent
            original.name = "Jane Doe";
            original.age = 32;
            original.scores[0] = 75;
    
            // Display modified original and unchanged copy
            $display("Modified Original: %s, %d, Scores: %p", original.name, original.age, original.scores);
            $display("Unchanged Copy: %s, %d, Scores: %p", copy.name, copy.age, copy.scores);
    
            // Finish the simulation
            $finish;
        end
    endmodule
    
    Locked Solution
    class RAND;
    
        rand int DARR0[];
        rand int DARR_EVEN[];
    
    endclass
    
    class RAND;
    
        rand int DARR0[];
        rand int DARR_EVEN[];
    
        rand int ctr[];
    
        constraint darr0 {
            DARR0.size < 101;
            ctr.size == DARR0.size;
            foreach( DARR0[i] ) {
                DARR0[i] > 0;
                DARR0[i] < 1000;
            }
        }
    
        constraint darr_even {
            foreach( ctr[i] ) {
                foreach( DARR0[j] ) {
                    if( j > i && DARR[j] % 2 == 0 ) {
                        DARR_EVEN[i] == DARR[j];
                    }
                }
            }
        }
    
    endclass