System Verilog Interview Questions

  1. What is the difference between a reg, wire and logic in SystemVerilog?

reg and wire are two data types that existed from Verilog, while logic is a new data type that was introduced in SystemVerilog.

  1. A wire is a data type that can model physical wires to connect two elements. Wires can only be driven by continuous assignment statement and cannot hold onto value if not driven. Wires can hence only be used to model combinational logic.
  2. A reg is a data type that can model a storage element or a state. They need to be driven by an always block and cannot be driven by continuous assignment statement. A reg can be used to model both sequential and combinational logic
  3. A logic is a new data type in SystemVerilog that can be used to model both wires and state information (reg). It also is a 4 state variable and hence can hold 0, 1, x and z values. If a wire is declared as a logic (wire logic), then it can be used to model multiple drivers and the last assignment will take the value.
  4. 2. What is the difference between a bit and logic data type?

bit is a 2-state data type that can take only values 0 and 1, while logic is a 4-state data type which can take values 0, 1, x, and z.

2-state variables will help in a small simulation speed up but should not be used if it is used to drive or sample signals from RTL design in which uninitialized and unknown values will be missed.

3. What is the difference between logic[7:0] and byte variable in SystemVerilog?

byte is a signed variable which means it can only be used to count values till 127.

A logic[7:0] variable can be used for an unsigned 8 bit variable that can count up to 255.

4. Which of the array types: dynamic array or associative array, are good to model really large arrays, say: a huge memory array of 32KB?

Associative arrays are better to model large arrays as memory is allocated only when an entry is written into the array.

Dynamic arrays on the other hand need memory to be allocated and initialized before using.

For example: If you want a memory array of 32KB to be modelled using dynamic array, you would first need to allocate 32K entries and use the array for read/write. Associative arrays doesn’t need allocation and initialization of memory upfront and can be allocated

and initialized just when an entry of the 32K array needs to be referenced. However, associative arrays are also slowest as they internally implement search for elements in the array using a hash.

Suppose a dynamic array of integers ( myvalues ) is initialized to values as shown below. Write a code to find all elements greater than 3 in thearray using array locator method “ find ”?

int myvalues [] = ‘{9,1,8,3,2,4,6},
int match_q[$];
match_q = myvalues.find with (item > 3);
  1. 5. What is the difference between a struct and union in SystemVerilog?

A structure represents a collection of data types that can be referenced as a whole, or the individual data types that make up the structure can be referenced by name.

For example:

in the example below, we have a struct defined called instruction_s that groups a 24 bit address field and an 8 bit opcode field.

typedef struct {
bit [7:0] opcode;
bit [23:0] addr;
} instruction_s
instruction_s current_instruction;

The instruction_s struct can be referenced together or individual members can be accessed. The total memory allocated would be the sum of memory needed for all the data types. Hence in above example, the currect_instruction struct would take a total memory

of 32 bits (24 bit address and 8 bit opcode) A union is a data type which can be accessed using one and only one of the named member data type. Unlike struct you cannot access all member data types together. The memory allocated for the union would be the maximum of the memory needed for the member data types. Unions are normally useful if you want to model a hardware resource like register that can store values of different types. For example: if a register can store an integer and a real values, you can define a union as follows:

typedef union {
int data;
real f_data;
} state_u;
state_u reg_state;
reg_state.f_data = ‘hFFFF_FFFF_FFFF_FFFF;
$display(“ int_data =%h“,;

In this example, the union state_u can either hold a 32 bit integer data or it can hold 64 bit real data. Hence, the memory allocated for the union reg_state will be 64 bits (bigger of the two data types). Since, there is shared memory for all member data types, in above

example, if we assign a 64 bit value to reg_state.f_data, we will be also able to reference the 32 bit of same using the other data type.

6. What is the concept of a “ref” and “const ref” argument in SystemVerilog function or task?

A ref keyword is used to pass arguments by reference to a function instead of a value. The subroutine/function shares the reference handle with the caller to access values. This is an efficient way of passing arguments like class objects or arrays of objects where

otherwise creating a copy would consume more memory on the stack. Also, since the caller and the function/task shares same reference, any change done inside the function using the reference would also be visible to the caller.

For Example, Here is an example of a CRC function that needs a big packet as an argument to compute CRC. By passing as a reference, each call to CRC function doesn’t need to create a copy of the packet on stack memory.

function automatic int crc(ref byte packet [1000:1] );
for( int j= 1; j <= 1000; j++ ) begin
crc ^= packet[j];

A const keyword is used if user wants to make sure that the ref argument is not modified by the function. For example: in the same CRC function, the argument can be declared as a “ const ref “ argument as shown below to make sure that the original packet contents are not modified accidentally by the CRC function.

function automatic int crc( const ref byte packet [1000:1] );
for( int j= 1; j <= 1000; j++ ) begin
crc ^= packet[j];
  1. 7. What is the difference between a packed array and an unpacked array?

A packed array represents a contiguous set of bits while an unpacked array need not be represented as a contiguous set of bits. In terms of a difference in declarations, the following is how a packed and unpacked array is declared.

bit [7:0] data ; // packed array of scalar bit types
real latency [7:0]; // unpacked array of real types

Packed arrays can be made of only the single bit data types (bit, logic, reg), or enumerated types.

logic[31:0] addr; //packed array of logic type

Unpacked arrays can be made of any data type.

class record_c;
record_c table[7:0]; //unpacked array of record objects
  1. 8. Given a dynamic array of size 100, how can the array be re-sized to hold 200 elements while the lower 100 elements are preserved as original?

A dynamic array needs memory allocation using new[] to hold elements. Here is an example with an integer array that grows from an initial size of 100 elements to 200 elements.

integer addr[]; // Declare the dynamic array.
addr = new[100]; // Create a 100-element array.
// Double the array size, preserving previous values.
addr = new[200](addr);
  1. 9. What is the difference between “forever” and “for” in SystemVerilog ?

The “forever” loop repeatedly executes a statement without any limit. The only way execution can stop is by using a break statement. A forever loop if used without any timing controls (like clock or time delay) can result in a zero-delay infinite loop and cause hang in simulation.

The “for” loop is used for executing a statement for a defined number of times based on conditions that are defined.

  • 10. What is the difference between “case”, “casex” and “casez” in SystemVerilog?

The case statement is a multiway decision statement that tests whether an expression matches one of a number of possible values and branches accordingly. Following is a simple example to implement a 3:1 MUX using a case statement

case (select[1:0])
2’b00: out_sig = in0;
2’b01: out_sig = in1;
2’b10: out_sig = in2;
default: out_sig = ‘x

In the above example of using a “case” statement, the expression match happens exactly with what is specified. For example, in above case statement, if at least one of the select lines is X or Z, then it will not match any conditions and will execute the default statement.

“ casez ” is a special version of case expression which allows don’t cares in comparison of the expressions. These are typically useful in decoder logic which only treats fewer bits.

Here is an example where a 3 bit interrupt request queue (irq) bus is decoded into 3 separate interrupt pins based on which bit in the bus is high, while other bits are don’t care.

casez (irq)
3’b1?? : int2 = 1’b1;
3’b?1? : int1 = 1’b1;
3’b??1 : int0 = 1’b1;

“casex” is another special version where in addition to don’t cares, it also ignores X and Z values in comparison.

  1. 11. Which of the logical equality operators “==” or “===” are used in case expression conditions for case, casex, and casez?

All of the 3 case statements use “===” logical equality comparison to evaluate condition matches.

  • 12. What is the difference between $display, $write, $monitor, and $strobe in SystemVerilog?
  • $display: Print the values immediately when executed.
  • $strobe: Print the values at the end of the current timestep.
  • $monitor: Print the values at the end of the current timestep if any values change. If $monitor is called more than once, the last call will override the previous one.
  • $write: This is the same as $display but doesn’t terminate with a newline (/n).
  • 13. What is the difference between new() and new[] in SystemVerilog?

The function new() is the class constructor function in SystemVerilog. It is defined in a class to initialize data members of the class.

The new[] operator is used to allocate memory for a dynamic array. The size of the dynamic array that needs to be created is passed as an argument to the new[].

  • 14. What is the concept of forward declaration of a class in SystemVerilog?

Sometimes a class might reference another class that is not fully defined in the compile order. This can cause a compile error. For Example: If two classes Statistics and Packet are defined in the following order, then while compiling the Statistics class, the definition of a packet is not yet seen and the compiler will fail.

class Statistics;
Packet p1;
class Packet;
//full definition here

To avoid this problem, the Packet Class can be forward declared before the full definition. This concept is called forward declaration.

typedef Packet; //forward declaration
class Statistics;
Packet p1;
class Packet;
//full definition here
  1. 15. What is the difference between private, public, and protected data members of a SystemVerilog class?
  2. Private data members of a class can only be accessed from within the class. These data members will not be visible in derived classes.
  3. Public members can be accessed from within the class as well as outside the class also. These are also visible in derived classes.
  4. Protected data members are similar to private members in the sense that they are only accessible within the class. However, unlike private members, these are also visible in derived classes.
  5. 16. What are interfaces in SystemVerilog?

The interface constructs in SystemVerilog are a named bundle of nets of variables that helps in encapsulating communication between multiple design blocks. An interface can be instantiated in a design and can be connected using a single name instead of having all

the port names and connections. In addition to connectivity, functionality can also be abstracted in an interface as it supports defining functions that can be called by instantiating design for communication. Interfaces also support procedural ( always/initial blocks) and continuous assignments which are useful for verification in terms of adding protocol checks and assertions.

Following is a simple example on how an interface can be defined.

interface simple_bus; // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
endinterface: simple_bus
  1. 17. What is a modport construct in an interface?

modport (short form for module port) is a construct in an interface that lets you group signals and specify directions. Following is an example of how an interface can be further grouped using modports for connecting to different components.

interface arb_if(input bit clk);
logic [1:0] grant, request;
logic reset;
modport TEST (output request, reset, input grant, clk);
modport DUT (input request, reset, clk, output grant);
modport MONITOR (input request, grant, reset, clk);

In this example, you can see that the same signals are given different directions in different modports. A monitor component needs all signals as input and hence the modport MONITOR of interface can be used to connect to monitor. A test or a driver will need to drive some signals and sample other signals and above example shows a modport TEST that can be used

  1. 18. Are interfaces synthesizable?

Yes, interfaces are synthesizable.

  • 19. What is a clocking block and what are the benefits of using clocking blocks inside an interface?

A clocking block is a construct that assembles all the signals that are sampled or synchronized by a common clock and define their timing behaviors with respect to the clock.

Following example illustrates a simple clocking block.

clocking sample_cb @(posedge clk);
default input #2ns output #3ns;
input a1, a2;
output b1;

In above example, we have defined a clocking block with name sample_cb and the clock associated with this clocking block is clk. The default keyword defines the default skew for inputs (2 ns) and output (3 ns). The input skew defines how many time units before the

clock event the signal is sampled. The output skew defines how many time units after the clock event the signal will be driven.

A clocking block can be declared only inside a module or an interface.

  1. 20. What is the difference between the following two ways of specifying skews in a clocking block?
input #1 step req1;
input #1ns req1;

The clocking skew determines how many time units away from the clock event a signal is to be sampled (input skew) or driven (output skew). A skew can be specified in two forms – either explicitly in terms of time as in case 2) above, where the signal is sampled 1ns

before the clock, OR in terms of time step as in case 1) above, where the step corresponds to global time precision (defined using `timescale directive)

  • 21. What are the main regions inside a SystemVerilog simulation time step?

A SystemVerilog simulator is an event-driven simulator and as the simulator advances in time, it needs to have a well-defined manner in which all events are scheduled and executed. In any event simulation, all the scheduled events at a specific time define a time slot. A time slot is divided into a set of ordered regions to provide predictable interactions between the design and testbench code.

A timeslot can be broadly divided into 5 major regions as shown below and each of the regions can be further subdivided into sub-regions.

1) Prepone: The preponed region is executed only once and is the first phase of current time slot after advancing the simulation time. Sampling of signals from design for testbench input happens in this region.

2) Active: The active region set consists of the following subregions – Active, Inactive, and the NBA (Nonblocking assignment) regions. RTL code and behavioral code are scheduled in the Active region. All blocking assignments are executed in the Active region. For nonblocking assignments, evaluation of RHS happens in the Active region, while assignment happens in the NBA region. If there are any assignments with #0 delays, those happen in the Inactive region.

3) Observed: The Observed region is for evaluation of property expressions (used in concurrent assertions) when they are triggered. During property evaluation, pass/fail code is scheduled for later in the Reactive region of the current time slot

4) Reactive: The reactive region set (Re-active, Re-Inactive, and Re-NBA) is used to schedule blocking assignments, #0 blocking assignments, and nonblocking assignments included in SystemVerilog “program” blocks. This separate Reactive region ensures that all the design code evaluation (in Active region set) stabilizes before the testbench code in the program blocks is evaluated. With OVM/UVM methodologies, there is no need for program blocks (with the standard phasing of testbench code) and hence Reactive region may not be used much.

5) Postponed: There is also a postponed region which is the last phase of current time slot. $monitor, $strobe and other similar events are scheduled for execution in this region. $display events are scheduled for execution in Active and Reactive regions (if called in program blocks).

  1. 22. What is a unique constraint in SystemVerilog?

A unique constraint is used to randomize a group of variables such that no two members of the group have the same value. The following shows an example: Here a class has a random array of bytes ( a ) and one another byte ( b ). The unique constraint in this example shows how unique values can be generated for all of these.

class Test;
rand byte a[5];
rand byte b;
constraint ab_cons { unique {b, a[0:5]}; }

23. How can we disable or enable constraints selectively in a class?

<object>.constraint_mode(0) :: To disable all constraints
<object>.<constraint>.constraint_mode(0) :: To selectively disable specific constraints

class ABC;
rand int length;
rand byte SA;
constraint c_length { length inside [1:64];}
constraint c_sa {SA inside [1:16];}
ABC abc = new();
abc.constraint_mode(0) // will turn off all constraints
abc.c_length.constraint_mode(0) // will turn off only length constraint

24. Given a Packet class with following constraints, how can we generate a packet object with address value greater than 200?

class Packet;
rand bit[31:0] addr;
constraint c_addr { addr inside [0:100];}
Since default constraint restricts address to be less than 100, we will need to use inline constraints and turn off default constraint as below:
Packet p = new();
p.randomize with {addr > 200;};

25. What are pre_randomize() and post_randomize() ?

These are built-in callback functions supported in SystemVerilog language to perform an action immediately either before every randomizecall or immediately after randomize call.

A pre_randomize() is useful for setting or overriding any constraints while a post_randomize() is useful to override results of randomization.

26. Write constraints to generate elements of a dynamic array (abc as shown in code below) such that each element of the array is less than 10 and the array size is less than 10.

class dynamic_array;
rand unsigned int abc[];
For dynamic arrays, we can use a foreach constraint to constraint the value of each of the element of the array as shown below:
constraint c_abc_len {
abc.size() < 10;
foreach (abc[i])
abc[i] < 10;
  1. 27. Write constraints to create a random array of integers such that array size is between 10 and 20 and the values of the array are in descending order?
class array_abc;
rand unsigned int myarray[];
constraint c_abc_val {
myarray.size inside { [10:20] };
foreach (myarray[i])
if (i>0) myarray[i] < myarray[i-1];

28. How can we use constraints to generate a dynamic array with random but unique values ? Refer the code below:

class TestClass;
rand bit[3:0] my_array[]; //dynamic array of bit[3:0]

There are two ways in which this can be done – one using the SV unique constraint and one without using it as shown in 2) below.

1) Add a unique constraint to the class as below

constraint c_rand_array_uniq {
my_array.size == 6; //or any size constraint
unique {my_array}; //unique array values

2) Without using unique constraint, you can still generate incremental values and then do an array shuffle() in post_randomize() ;

constraint c_rand_array_inc {
my_array.size == 6 ;// or any size constraint
foreach (my_array[i])
if(i >0) my_array[i] > my_array[i-1];
function post_randomize();

29. Given a 32 bit address field as a class member, write a constraint to generate a random value such that it always has 10 bits as 1 and no two bits next to each other should be 1

class packet;
rand bit[31:0] addr;
constraint c_addr {
$countones(addr) ==10;
foreach (addr[i])
if(addr[i] && i>0)
addr[i] != addr[i-1];
  1. 30. What is the difference between hard and soft constraints?

The normal constraints that are written in SystemVerilog classes are known as hard constraints, and the constraint solver needs to always solve them or result in a failure if it cannot be solved.

On the other hand, if a constraint is defined as soft, then the solver will try to satisfy it unless contradicted by another hard constraint or another soft constraint with a higher priority.

Soft constraints are generally used to specify default values and distributions for random variables and can be overridden by specialized constraints.

class Packet;
rand int length;
constraint length_default_c { soft length inside {32,1024}; }
Packet p = new();
p.randomize() with { length == 1512; }

In the above example, if the default constraint was not defined as soft, then the call to randomize would have failed.

  1. 31. How many parallel processes does this code generate?
for (int i=0; i < 10; i++ ) begin

Since the “for” loop is inside the fork join, it executes as a single thread.

32. What is wrong with following SystemVerilog constraint?

class packet;
rand bit [15:0] a, b, c;
constraint pkt_c { 0 < a < b < c; }

There can be a maximum of only one relational operator (<, <=, ==, >=, or >) in an expression. If multiple variables need to be in some order, we will need to write multiple expressions as below.

constraint pkt_c { 0 < a; a < b ; b < c; }
  1. 33. What is difference between bounded and unbounded mailboxes? How can we create unbounded mailboxes?

A mailbox is called bounded if the size of mailbox is limited when created.

mailbox mbxRcv;
mbxRcv = new(10); //size bounded to 10

A mailbox is unbounded if the size is not limited when created.

mailbox mbxRcv;
mbxRcv = new(); //size is unbounded or infinite

A bounded mailbox becomes full when it contains the bounded number of messages and any further attempt to place a message will cause the process to be suspended while unbounded mailboxes never suspend a thread in a send operation.

  1. 34. How can we merge two events in SystemVerilog?

An event variable can be assigned to another event variable. When an event variable is assigned to other, both the events point to same synchronization object and are said to be merged.

  • 35. Is it possible to override a constraint defined in the base class in the derived class and if so how?

Yes, a constraint defined in the base class can be overridden in a derived class by changing the definition using the same constraint name.

For Example: Refer to the constraint c_a_b_const in the following code. In the base class, it is defined to always have a value of a< b, but in a derived class, it has been overridden to have always a > b.

class Base;
rand int a ;
rand int b;
constraint c_a_b_const {
a < b;
class Derived extends Base;
constraint c_a_b_const {
a > b;
  1. 36. What is the concept of callback?

A “callback” is any function that is called by another function which takes the first function as an argument. Most of the times, a callback function is called when some “event” happens.

In a Verification testbench, this feature is useful for several applications:

  • Calling back a function to inject error on transactions sent from a driver
  • When a simulation phase is ready to end, calling a function to drain all pending transactions in all sequence/driver.
  • Calling a coverage sample function on a specific event.

Most of the times, callback functions are implemented by registering them with a component/object that calls back on some defined conditions.

An example call back function in UVM is phase_ready_to_end() which is implemented in the base class and is registered with the UVM_component class. The function gets called when the current simulation phase is ready to end always. Hence, a user can implement any functionality that needs to be executed at end of a simulation phase by overriding this function definition

  • 37. What are system tasks and functions? Give some example of system tasks and functions with their purpose.

SystemVerilog language supports a number of built-in system tasks and functions for different utilities and are generally called with a “$” prefix to the task/function name. In addition, language also supports addition of user defined system tasks and functions.

Following are some examples of system tasks and functions (categorized based on functionality).

For a complete list, one should refer to LRM.

  • Simulation control tasks – $finish, $stop, $exit
  • Conversion functions – $bitstoreal, $itor, $cast
  • Bit vector system functions – $countones, $onehot, $isunknown
  • Severity tasks – $error, $fatal, $warning
  • Sampled value system functions – $rose, $fell, $changed
  • Assertion control tasks – $asserton, $assertoff

38. How would you track the progress of the verification project? What metrics would you use?
A number of metrics are used to track the progress of verification against a plan. A verification plan captures the scenarios/features to be verified in terms of directed tests or in terms of functional coverage monitor for detailed scenarios and corner cases. The plan also captures details on verification environment development which includes stimulus generation and checking methodologies.

Progress can be tracked in an early stage of a project by tracking the completeness of the environment development (stimulus generator, checker, monitor, etc.), test development, and functional coverage monitor development. Once most of the tests and a constrained random generator is developed, then tests are normally run as regressions on a farm of servers, and then progress is monitored in terms of regression pass rates, bug rate, and the functional
coverage numbers.

39. How do you measure the completeness of Verification OR when/how can you say that verification is complete?
Functional Verification can be called complete when the implemented behavior of a design matches with the design specification without any errors. To achieve this, we need to apply stimulus to the design to cover every possible input scenario and verify that the design meets specification without any errors. However, with ever increasing complexity
of the designs, it is practically not possible to define all the possible input stimulus scenarios. In addition, resource and time limitations also make this ideal definition of completeness impractical. Hence, in most of the projects, verification completeness is about the confidence achieved through a set of metrics and processes that minimizes the risk of having a design defect.

Following are some of the metrics and processes that are followed to achieve high
confidence with respect to verification completeness:
1) Reviewing Verification plan and design specification to make sure all details
are understood and captured.
2) Ensuring proper completeness in terms of environment development, test
development, and functional coverage monitor development against the reviewed
3) Review of testbench stimulus generator and constraints, checkers, assertions
and coverage monitor implementation.
4) Ensuring all tests are enabled in regression mode with consistently no failures
across weeks, all coverage metrics met and understood.
5) Ensuring that bug rates and unresolved bugs are zero or well understood to
have no impact on design.
6) Waveform Review of important scenarios.
7) Ensuring formal verification is done (wherever possible).
8) Comparing rate of incoming bugs and bug trend with that of past successful
projects of similar complexity.

40. What is GLS and why is it important?
GLS is an acronym for “Gate Level Simulation”. Gate Level Simulations are run after RTL code is synthesized into Gate Level Netlist. GLS forms an important part of the Verification lifecycle. It is required in addition to static verification tools like STA (Static Timing Analysis) and LEC (Logical Equivalence Checking) as STA and LEC don’t cover/report all issues. Majorly, GLS is used to:

Verify DFT scan chains.

Verify critical timing paths in asynchronous designs (this is not done by STA)

Verify Reset and Power Up flows.

Analyze X-Sate Optimism in RTL.

Collect Switching Activity for Power Estimation.

These are a few SV Interview Questions that will help you to crack interviews at entry-level and my other blog posts cover most of the fundamentals you can refer to those for more knowledge.

Books to refer to as a Verification Engineer to learn System Verilog in more detail:

  1. Writing Testbenches using SystemVerilog by Janick Bergeron –
  2. SystemVerilog for Verification by Chris Spear –
  3. Verilog and System Verilog Gotchas by Stuart Southerland –
  4. SystemVerilog Assertions Handbook: for Formal and Dynamic Verification By Ben Cohen, Srinivasan Venkataramanan, Ajeetha Kumari –
  5. Principles of Functional Verification by Andreas Meyer –
  6. System Verilog Assertions and Functional Coverage – Guide to Language Methodology and Applications by Ashok B Mehta –
  7. A Practical Guide for SystemVerilog Assertions by Srikanth Vijayaraghvan, Meyappan Ramanathan –
  8. Introduction to SystemVerilog by Ashok B Mehta –

About the author

Avatar photo

The Art of Verification

Hi, I’m Hardik, and welcome to The Art of Verification.

I’m a Verification Engineer who loves to crack complex designs and here to help others commit to mastering Verification Skills through self-learning, System Verilog, UVM, and most important to develop that thought process that every verification engineer should have.

I’ve made it my mission to give back and serve others beyond myself.

I will NEVER settle for less than I can be, do, give, or create.

View all posts


  • Maybe stupid, regarding the definition of a wire
    Wire a;
    Assign a=en? In : a; // can be used to model a hold on a value when en==0

  • Excellent post. I was checking continuously this blog and I am impressed!

    Extremely usefful information. I care for such innformation a lot.

    I was looking for this certain information for a very long
    time.Thahk you and goood luck.

  • I have a question on code coverage.
    there is a 4bit variable lets say [0:15]a, 0 to 11 values of code should get 100% coverage and 12 to 15 values must be excluded. How can we write this code coverage?

  • 29. Given a 32 bit address field as a class member, write a constraint to generate a random value such that it always has 10 bits as 1 and no two bits next to each other should be 1

    class packet;
    rand bit[31:0] addr;
    constraint c_addr {
    $countones(addr) ==10;
    foreach (addr[i])
    if(addr[i] && i>0)
    addr[i] != addr[i-1];
    Great job!.
    I don’t think this code works for the given question because foreach will be looking for a two dimensional array. Please correct me if I’m wrong.
    It would be great if you post more problem-solving questions for constraints