Digital elements are binary entities and can only hold either of the two values - 0 and 1. However the transition from 0 to 1 and 1 to 0 have a transitional delay and so does each gate element to propagate the value from input to its output.

For example, a two input AND gate has to switch the output to 1 if both inputs become 1 and back to 0 when any of its inputs become 0. These gate and pin to pin delays can be specified in Verilog when instantiating logic primitives.

## Rise, Fall and Turn-Off Delays

The time taken for the output of a gate to change from some value to 1 is called a rise delay. The time taken for the output of a gate to change fomr some value to 0 is called a fall delay. The time taken for the output of a gate to change from some value to high impedance is called turn-off delay.

These delays are actually applicable to any signal as they all can rise or fall anytime in real circuits and are not restricted to only outputs of gates. There are three ways to represent gate delays and the two delay format can be applied to most primitives whose outputs do not transition to high impedance. Like a three delay format cannot be applied to an AND gate because the output will not go to Z for any input combination.

```  ```

// Single delay specified - used for all three types of transition delays
or #(<delay>) o1 (out, a, b);

// Two delays specified - used for Rise and Fall transitions
or #(<rise>, <fall>) o1 (out, a, b);

// Three delays specified - used for Rise, Fall and Turn-off transitions
or #(<rise>, <fall>, <turn_off>) o1 (out, a, b);

```
```

If only a single delay is specified, all three types of delays will use the same given value. If there are two delays specified, the first one represents rise and the second one represents fall delay. If there are three delays specified, they represent rise, fall and turn-off delays respectively.

### One Delay Format

```  ```

module des (	input 	a, b,
output out1, out2);

// AND gate has 2 time unit gate delay
and 		#(2) o1 (out1, a, b);

// BUFIF0 gate has 3 time unit gate delay
bufif0 	#(3) b1 (out2, a, b);

endmodule

```
```
```  ```

module tb;
reg a, b;
wire out1, out2;

des d0 (.out1(out1), .out2(out2), .a(a), .b(b));

initial begin
{a, b} <= 0;

\$monitor ("T=%0t a=%0b b=%0b and=%0b bufif0=%0b", \$time, a, b, out1, out2);

#10 a <= 1;
#10 b <= 1;
#10 a <= 0;
#10 b <= 0;
end
endmodule

```
```

See that the output of AND gates change 2 time units after one of its inputs change. For example, b becomes 1 while a is already 1 at T=20. But the output becomes 1 only at T=22. Similarly, a goes back to zero at T=30 and the output gets the new value at T=32.

Gate delay is specified as 3 time units for BUFIF0 and hence when b changes from 0 to 1 while a is already at 1, output takes 3 time units to get updated to Z and finally does so at T=23.

Simulation Log
```ncsim> run
T=0 a=0 b=0 and=x bufif0=x
T=2 a=0 b=0 and=0 bufif0=x
T=3 a=0 b=0 and=0 bufif0=0
T=10 a=1 b=0 and=0 bufif0=0
T=13 a=1 b=0 and=0 bufif0=1
T=20 a=1 b=1 and=0 bufif0=1
T=22 a=1 b=1 and=1 bufif0=1
T=23 a=1 b=1 and=1 bufif0=z
T=30 a=0 b=1 and=1 bufif0=z
T=32 a=0 b=1 and=0 bufif0=z
T=40 a=0 b=0 and=0 bufif0=z
T=43 a=0 b=0 and=0 bufif0=0
ncsim: *W,RNQUIE: Simulation is complete.

```

### Two Delay Format

Let's apply the same testbench shown above to a different Verilog model shown below where rise and fall delays are explicitly mentioned.

```  ```

module des (	input 	a, b,
output out1, out2);

and #(2, 3) o1 (out1, a, b);
bufif0 #(4, 5) b1 (out2, a, b);

endmodule

```
```
Simulation Log
```ncsim> run
T=0 a=0 b=0 and=x bufif0=x
T=3 a=0 b=0 and=0 bufif0=x
T=5 a=0 b=0 and=0 bufif0=0
T=10 a=1 b=0 and=0 bufif0=0
T=14 a=1 b=0 and=0 bufif0=1
T=20 a=1 b=1 and=0 bufif0=1
T=22 a=1 b=1 and=1 bufif0=1
T=24 a=1 b=1 and=1 bufif0=z
T=30 a=0 b=1 and=1 bufif0=z
T=33 a=0 b=1 and=0 bufif0=z
T=40 a=0 b=0 and=0 bufif0=z
T=45 a=0 b=0 and=0 bufif0=0
ncsim: *W,RNQUIE: Simulation is complete.

```

### Three Delay Format

```  ```

module des (	input 	a, b,
output out1, out2);

and #(2, 3) o1 (out1, a, b);
bufif0 #(5, 6, 7) b1 (out2, a, b);

endmodule

```
```
Simulation Log
```ncsim> run
T=0 a=0 b=0 and=x bufif0=x
T=3 a=0 b=0 and=0 bufif0=x
T=6 a=0 b=0 and=0 bufif0=0
T=10 a=1 b=0 and=0 bufif0=0
T=15 a=1 b=0 and=0 bufif0=1
T=20 a=1 b=1 and=0 bufif0=1
T=22 a=1 b=1 and=1 bufif0=1
T=27 a=1 b=1 and=1 bufif0=z
T=30 a=0 b=1 and=1 bufif0=z
T=33 a=0 b=1 and=0 bufif0=z
T=40 a=0 b=0 and=0 bufif0=z
T=46 a=0 b=0 and=0 bufif0=0
ncsim: *W,RNQUIE: Simulation is complete.

```

## Min/Typ/Max Delays

Delays are not the same in different parts of the fabricated chip nor is it same for different temperatures and other variations. So Verilog also provides an extra level of control for each of the delay types mentioned above. Every digital gate and transistor cell has a minimum, typical and maximum delay specified based on process node and is typically provided by libraries from fabrication foundry.

For each type of delay - rise, fall, and turn-off - three values min, typ and max can be specified and stand for minimum, typical and maximum delays.

```  ```

module des (	input 	a, b,
output out1, out2);

and #(2:3:4, 3:4:5) o1 (out1, a, b);
bufif0 #(5:6:7, 6:7:8, 7:8:9) b1 (out2, a, b);

endmodule

```
```
Simulation Log
```ncsim> run
T=0 a=0 b=0 and=x bufif0=x
T=4 a=0 b=0 and=0 bufif0=x
T=7 a=0 b=0 and=0 bufif0=0
T=10 a=1 b=0 and=0 bufif0=0
T=16 a=1 b=0 and=0 bufif0=1
T=20 a=1 b=1 and=0 bufif0=1
T=23 a=1 b=1 and=1 bufif0=1
T=28 a=1 b=1 and=1 bufif0=z
T=30 a=0 b=1 and=1 bufif0=z
T=34 a=0 b=1 and=0 bufif0=z
T=40 a=0 b=0 and=0 bufif0=z
T=47 a=0 b=0 and=0 bufif0=0
ncsim: *W,RNQUIE: Simulation is complete.

```

A previous example explored a simple sequence detector. Here is another example for a pattern detector which detects a slightly longer pattern.

## Design

```  ```

module det_110101 ( input clk,
input rstn,
input in,
output out );

parameter IDLE 	= 0,
S1 		= 1,
S11 	= 2,
S110 	= 3,
S1101 	= 4,
S11010 	= 5,
S110101 = 6;

reg [2:0] cur_state, next_state;

assign out = cur_state == S110101 ? 1 : 0;

always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end

always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else 	next_state = IDLE;
end

S1: begin
if (in) next_state = S11;
else 	next_state = IDLE;
end

S11: begin
if (!in) next_state = S110;
else 	next_state = S11;
end

S110 : begin
if (in) next_state = S1101;
else 	next_state = IDLE;
end

S1101 : begin
if (!in) next_state = S11010;
else 	next_state = IDLE;
end

S11010: begin
if (in) next_state = S110101;
else 	next_state = IDLE;
end

S110101: begin
if (in) next_state = S1;
else 	next_state = IDLE; 		// Bug 2
end
endcase
end
endmodule

```
```

## Testbench

```  ```

module tb;
reg clk, in, rstn;
wire out;
integer l_dly;

always #10 clk = ~clk;

det_110101 u0 ( .clk(clk), .rstn(rstn), .in(in), .out(out) );

initial begin
clk <= 0;
rstn <= 0;
in <= 0;

repeat (5) @ (posedge clk);
rstn <= 1;

@(posedge clk) in <= 1;
@(posedge clk) in <= 1;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;
@(posedge clk) in <= 1;
@(posedge clk) in <= 1;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;

#100 \$finish;
end
endmodule

```
```
Simulation Log
```ncsim> run
T=10 in=0 out=0
T=30 in=0 out=0
T=50 in=0 out=0
T=70 in=0 out=0
T=90 in=0 out=0
T=110 in=1 out=0
T=130 in=1 out=0
T=150 in=0 out=0
T=170 in=1 out=0
T=190 in=0 out=0
T=210 in=1 out=0
T=230 in=1 out=1
T=250 in=1 out=0
T=270 in=0 out=0
T=290 in=1 out=0
T=310 in=0 out=0
T=330 in=1 out=0
T=350 in=1 out=1
T=370 in=1 out=0
T=390 in=1 out=0
T=410 in=1 out=0
Simulation complete via \$finish(1) at time 430 NS + 0

```

A very common example of an FSM is that of a sequence detector where the hardware design is expected to detect when a fixed pattern is seen in a stream of binary bits that are input to it.

## Example

```  ```

module det_1011 ( input clk,
input rstn,
input in,
output out );

parameter IDLE 	= 0,
S1 		= 1,
S10 	= 2,
S101 	= 3,
S1011 	= 4;

reg [2:0] cur_state, next_state;

assign out = cur_state == S1011 ? 1 : 0;

always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end

always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else next_state = IDLE;
end

S1: begin
if (in) next_state = IDLE;
else 	next_state = S10;
end

S10 : begin
if (in) next_state = S101;
else 	next_state = IDLE;
end

S101 : begin
if (in) next_state = S1011;
else 	next_state = IDLE;
end

S1011: begin
next_state = IDLE;
end
endcase
end
endmodule

```
```

## Testbench

```  ```

module tb;
reg 			clk, in, rstn;
wire 			out;
reg [1:0] l_dly;
reg 			tb_in;
integer 	loop = 1;

always #10 clk = ~clk;

det_1011 u0 ( .clk(clk), .rstn(rstn), .in(in), .out(out) );

initial begin
clk <= 0;
rstn <= 0;
in <= 0;

repeat (5) @ (posedge clk);
rstn <= 1;

// Generate a directed pattern
@(posedge clk) in <= 1;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;
@(posedge clk) in <= 1; 		// Pattern is completed
@(posedge clk) in <= 0;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;
@(posedge clk) in <= 1;
@(posedge clk) in <= 0;
@(posedge clk) in <= 1;
@(posedge clk) in <= 1; 	 // Pattern completed again

// Or random stimulus using a for loop that drives a random
// value of input N times
for (int i = 0 ; i < loop; i ++) begin
l_dly = \$random;
repeat (l_dly) @ (posedge clk);
tb_in = \$random;
in <= tb_in;
end

// Wait for sometime before quitting simulation
#100 \$finish;
end
endmodule

```
```
Simulation Log
```ncsim> run
T=10 in=0 out=0
T=30 in=0 out=0
T=50 in=0 out=0
T=70 in=0 out=0
T=90 in=0 out=0
T=110 in=1 out=0
T=130 in=0 out=0
T=150 in=1 out=0
T=170 in=1 out=0
T=190 in=0 out=1
T=210 in=0 out=0
T=230 in=1 out=0
T=250 in=1 out=0
T=270 in=0 out=0
T=290 in=1 out=0
T=310 in=1 out=0
T=330 in=1 out=1
T=350 in=1 out=0
T=370 in=1 out=0
T=390 in=1 out=0
Simulation complete via \$finish(1) at time 410 NS + 0

```

There is a bug in the design. Can you find it ?

`uvm_object` has many common functions like print, copy and compare that are available to all its child classes and can be used out of the box if UVM automation macros are used inside the class definition. In a previous article, `print`, `do_print` and use of automation macros to print were discussed.

## Using automation macros

A class called Packet is defined with a single variable and registered using UVM automation macros between `uvm_object_utils_begin` and `uvm_object_utils_end`. A variable used with UVM_DEFAULT setting means that this variable will be included in all automated methods like copy, print, etc unless specifically mentioned.

An object of Packet is instantiated in another class called Object along with a bunch of other variables of different data types. Similar to the Packet class, all variables in Object are registered with UVM automation macros with the corresponding macro type. For example, `string` variables require ``uvm_field_string`.

```  ```

typedef enum {FALSE, TRUE} e_bool;

class Packet extends uvm_object;

// Automation macros
`uvm_object_utils_begin(Packet)
`uvm_object_utils_end

function new(string name = "Packet");
super.new(name);
endfunction
endclass

class Object extends uvm_object;
rand e_bool 				m_bool;
rand bit[3:0] 			m_mode;
rand byte 				m_data[4];
rand shortint 			m_queue[\$];
string 					m_name;
rand Packet 				m_pkt;

constraint c_queue { m_queue.size() == 3; }

function new(string name = "Object");
super.new(name);
m_name = name;
m_pkt = Packet::type_id::create("m_pkt");
m_pkt.randomize();
endfunction

`uvm_object_utils_begin(Object)
`uvm_field_enum(e_bool, m_bool, UVM_DEFAULT)
`uvm_field_int (m_mode, 		UVM_DEFAULT)
`uvm_field_sarray_int(m_data, 	UVM_DEFAULT)
`uvm_field_queue_int(m_queue, 	UVM_DEFAULT)
`uvm_field_string(m_name, 		UVM_DEFAULT)
`uvm_field_object(m_pkt, 		UVM_DEFAULT)
`uvm_object_utils_end
endclass

```
```

Let us create a test class and create two objects of type Object, randomize both and display them first. Then contents of obj1 is copied into obj2 using the `copy` function. Implementation of this copy function is taken care of by the automation macros.

```  ```

class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
Object obj1 = Object::type_id::create("obj1");
Object obj2 = Object::type_id::create("obj2");
obj1.randomize();
obj1.print();
obj2.randomize();
obj2.print();

obj2.copy(obj1);
`uvm_info("TEST", "After copy", UVM_LOW)
obj2.print();
endfunction
endclass

module tb;
initial begin
run_test("base_test");
end
endmodule

```
```

The first table shows the values of obj1 after randomization, the second table that of obj2 after randomization and the third one shows contents of obj2 after the copy method is called.

Simulation Log
```ncsim> run
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
--------------------------------------
Name        Type          Size  Value
--------------------------------------
obj1        Object        -     @1903
m_bool    e_bool        32    TRUE
m_mode    integral      4     'he
m_data    sa(integral)  4     -
[0]     integral      8     'hf4
[1]     integral      8     'he
[2]     integral      8     'h58
[3]     integral      8     'hbd
m_queue   da(integral)  3     -
[0]     integral      16    'h9ae9
[1]     integral      16    'hd31d
[2]     integral      16    'ha96c
m_name    string        4     obj1
m_pkt     Packet        -     @1906
--------------------------------------
--------------------------------------
Name        Type          Size  Value
--------------------------------------
obj2        Object        -     @1908
m_bool    e_bool        32    FALSE
m_mode    integral      4     'he
m_data    sa(integral)  4     -
[0]     integral      8     'h60
[1]     integral      8     'h24
[2]     integral      8     'h27
[3]     integral      8     'hb5
m_queue   da(integral)  3     -
[0]     integral      16    'he17f
[1]     integral      16    'h98e6
[2]     integral      16    'h5a41
m_name    string        4     obj2
m_pkt     Packet        -     @1910
--------------------------------------
UVM_INFO testbench.sv(60) @ 0: uvm_test_top [TEST] After copy
--------------------------------------
Name        Type          Size  Value
--------------------------------------
obj2        Object        -     @1908
m_bool    e_bool        32    TRUE
m_mode    integral      4     'he
m_data    sa(integral)  4     -
[0]     integral      8     'hf4
[1]     integral      8     'he
[2]     integral      8     'h58
[3]     integral      8     'hbd
m_queue   da(integral)  3     -
[0]     integral      16    'h9ae9
[1]     integral      16    'hd31d
[2]     integral      16    'ha96c
m_name    string        4     obj1
m_pkt     Packet        -     @1909
--------------------------------------
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---

```

Automation macros introduce a lot of additional code and is not generally recommended

## Using do_copy

Another way is for the user to implement `do_copy` method inside the child class and assign the value from each variable of the class to be copied into the current one. Note in the following example that `uvm_object_utils_*` macros are not used and hence the `print()` method we used above will not work as is. Instead another method called `convert2string` is implemented for both classes so that it returns the contents in a string format when called.

Just like the way `print` calls `do_print` method, a `copy` calls the `do_copy` method and implementation of the function is all that is required to be done.

```  ```

typedef enum {FALSE, TRUE} e_bool;

class Packet extends uvm_object;

// Function is used to return contents of this class in a
// string format
virtual function string convert2string();
string contents;
endfunction

`uvm_object_utils(Packet)

// Implementation of "do_copy". A generic uvm_object called "rhs"
// is received and type casted into Packet called "_pkt". Then
// m_addr is copied from _pkt to the variable in current class
virtual function void do_copy(uvm_object rhs);
Packet _pkt;
super.do_copy(rhs);
\$cast(_pkt, rhs);
`uvm_info(get_name(), "In Packet::do_copy()", UVM_LOW)
endfunction

function new(string name = "Packet");
super.new(name);
endfunction
endclass

class Object extends uvm_object;
rand e_bool 				m_bool;
rand bit[3:0] 			m_mode;
rand byte 				m_data[4];
rand shortint 			m_queue[\$];
string 					m_name;
rand Packet 				m_pkt;

constraint c_queue { m_queue.size() == 3; }

function new(string name = "Object");
super.new(name);
m_name = name;
m_pkt = Packet::type_id::create("m_pkt");
m_pkt.randomize();
endfunction

// Function used to return contents of this class in a
// string format
virtual function string convert2string();
string contents = "";
\$sformat(contents, "%s m_name=%s", contents, m_name);
\$sformat(contents, "%s m_bool=%s", contents, m_bool.name());
\$sformat(contents, "%s m_mode=0x%0h", contents, m_mode);
foreach(m_data[i]) begin
\$sformat(contents, "%s m_data[%0d]=0x%0h", contents, i, m_data[i]);
end
return contents;
endfunction

`uvm_object_utils(Object)

// "rhs" does not contain m_bool, m_mode, etc since its a parent
// handle. So cast into child data type and access using child handle
// Copy each field from the casted handle into local variables
virtual function void do_copy(uvm_object rhs);
Object _obj;
super.do_copy(rhs);
\$cast(_obj, rhs);
m_bool 	= _obj.m_bool;
m_mode 	= _obj.m_mode;
m_data 	= _obj.m_data;
m_queue = _obj.m_queue;
m_name 	= _obj.m_name;
m_pkt.copy(_obj.m_pkt);
`uvm_info(get_name(), "In Object::do_copy()", UVM_LOW)
endfunction
endclass

```
```

Like in the earlier example, two objects are created and contents of one is copied into another. Because `do_print` method is not implemented and automation macros are not used, `convert2string` will be used to print contents of each class.

```  ```

class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
Object obj1 = Object::type_id::create("obj1");
Object obj2 = Object::type_id::create("obj2");
obj1.randomize();
`uvm_info("TEST", \$sformatf("Obj1.print: %s", obj1.convert2string()), UVM_LOW)
obj2.randomize();
`uvm_info("TEST", \$sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)

obj2.copy(obj1);
`uvm_info("TEST", "After copy", UVM_LOW)
`uvm_info("TEST", \$sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
endfunction
endclass

module tb;
initial begin
run_test("base_test");
end
endmodule

```
```
Simulation Log
```ncsim> run
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO testbench.sv(93) @ 0: uvm_test_top [TEST] Obj1.print:  m_name=obj1 m_bool=TRUE m_mode=0xe m_data[0]=0xf4 m_data[1]=0xe m_data[2]=0x58 m_data[3]=0xbd
UVM_INFO testbench.sv(95) @ 0: uvm_test_top [TEST] Obj2.print:  m_name=obj2 m_bool=FALSE m_mode=0xe m_data[0]=0x60 m_data[1]=0x24 m_data[2]=0x27 m_data[3]=0xb5
UVM_INFO testbench.sv(26) @ 0: reporter [m_pkt] In Packet::do_copy()
UVM_INFO testbench.sv(79) @ 0: reporter [obj2] In Object::do_copy()
UVM_INFO testbench.sv(98) @ 0: uvm_test_top [TEST] After copy
UVM_INFO testbench.sv(99) @ 0: uvm_test_top [TEST] Obj2.print:  m_name=obj1 m_bool=TRUE m_mode=0xe m_data[0]=0xf4 m_data[1]=0xe m_data[2]=0x58 m_data[3]=0xbd
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]
--- UVM Report Summary ---

```

## Using clone method

`clone` method works exactly the same as a `copy` method, the difference being that a clone will return an object with the copied contents. So this saves some trouble of creating the second object before copy.

```  ```

class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
// Create obj1, but only declare handle for obj2
Object obj2;
Object obj1 = Object::type_id::create("obj1");
obj1.randomize();
`uvm_info("TEST", \$sformatf("Obj1.print: %s", obj1.convert2string()), UVM_LOW)

// Use \$cast to clone obj1 into obj2
\$cast(obj2, obj1.clone());
`uvm_info("TEST", "After clone", UVM_LOW)
`uvm_info("TEST", \$sformatf("Obj2.print: %s", obj2.convert2string()), UVM_LOW)
endfunction
endclass

module tb;
initial begin
run_test("base_test");
end
endmodule

```
```
Simulation Log
```ncsim> run
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO testbench.sv(105) @ 0: uvm_test_top [TEST] Obj1.print:  m_name=obj1 m_bool=TRUE m_mode=0xe m_data[0]=0xf4 m_data[1]=0xe m_data[2]=0x58 m_data[3]=0xbd
UVM_INFO testbench.sv(20) @ 0: reporter [m_pkt] In Packet::do_copy()
UVM_INFO testbench.sv(86) @ 0: reporter [obj1] In Object::do_copy()
UVM_INFO testbench.sv(108) @ 0: uvm_test_top [TEST] After clone
UVM_INFO testbench.sv(109) @ 0: uvm_test_top [TEST] Obj2.print:  m_name=obj1 m_bool=TRUE m_mode=0xe m_data[0]=0xf4 m_data[1]=0xe m_data[2]=0x58 m_data[3]=0xbd
UVM_INFO /playground_lib/uvm-1.2/src/base/uvm_report_server.svh(847) @ 0: reporter [UVM/REPORT/SERVER]

```

A given set of statements can be executed N number of times with a `repeat` construct.

## Syntax

```  ```

repeat (<number>)
// Single Statement

repeat (<number>) begin
// Multiple Statements
end

```
```

## Example #1

```  ```

module tb;
initial begin
repeat (5) begin
\$display ("Repeat this statement");
end
end
endmodule

```
```
Simulation Log
```ncsim> run
Repeat this statement
Repeat this statement
Repeat this statement
Repeat this statement
Repeat this statement
ncsim: *W,RNQUIE: Simulation is complete.

```

A `repeat` loop can also be implemented using a `for` loop but is more verbose. If the variable i is not required to be referenced inside the loop, a `repeat` loop would be more suitable.

```  ```

for (int i = 0; i < number; i++) begin
// Code
end

```
```

In the code shown below, we have a `repeat` loop to wait for a given number of clock cycles.

```  ```

module tb;
bit clk;
always #10 clk = ~clk;

initial begin
bit [2:0] num = \$random;

\$display ("[%0t] Repeat loop is going to start with num = %0d", \$time, num);
repeat (num) @(posedge clk);
\$display ("[%0t] Repeat loop has finished", \$time);
\$finish;
end
endmodule

```
```

In this example, the clock period is 20 ns, and the first posedge of clock happens at 10 ns. Next 3 posedge of clock happens at 30ns, 50ns and 70ns after which the initial block ends. So, this `repeat` loop successfully waits until 4 posedge of clocks are over.

Simulation Log
```ncsim> run