Design

  
  
module modN_ctr 
  # (parameter N = 10,
     parameter WIDTH = 4)
  
  ( input   clk,
    input   rstn,
   	output  reg[WIDTH-1:0] out);
 
  always @ (posedge clk) begin
    if (!rstn) begin
      out <= 0;
    end else begin
      if (out == N-1) 
        out <= 0;
      else
        out <= out + 1;
    end
  end
endmodule

  

Testbench

  
  
module tb;
  parameter N = 10;
  parameter WIDTH = 4;
  
  reg clk;
  reg rstn;
  wire [WIDTH-1:0] out;
  
  modN_ctr u0  ( 	.clk(clk),
                	.rstn(rstn),
                	.out(out));

  always #10 clk = ~clk;
  
  initial begin
    {clk, rstn} <= 0;
    
    $monitor ("T=%0t rstn=%0b out=0x%0h", $time, rstn, out);
    repeat(2) @ (posedge clk);
    rstn <= 1;
    
    repeat(20) @ (posedge clk);
    $finish;  
  end
endmodule

  
Simulation Log
ncsim> run
T=0 rstn=0 out=0xx
T=10 rstn=0 out=0x0
T=30 rstn=1 out=0x0
T=50 rstn=1 out=0x1
T=70 rstn=1 out=0x2
T=90 rstn=1 out=0x3
T=110 rstn=1 out=0x4
T=130 rstn=1 out=0x5
T=150 rstn=1 out=0x6
T=170 rstn=1 out=0x7
T=190 rstn=1 out=0x8
T=210 rstn=1 out=0x9
T=230 rstn=1 out=0xa
T=250 rstn=1 out=0x0
T=270 rstn=1 out=0x1
T=290 rstn=1 out=0x2
T=310 rstn=1 out=0x3
T=330 rstn=1 out=0x4
T=350 rstn=1 out=0x5
T=370 rstn=1 out=0x6
T=390 rstn=1 out=0x7
T=410 rstn=1 out=0x8
Simulation complete via $finish(1) at time 430 NS + 0

The verilog assign statement is typically used to continuously drive a signal of wire datatype and gets synthesized as combinational logic. Here are some more design examples using the assign statement.

Example #1 : Simple combinational logic

The code shown below implements a simple digital combinational logic which has an output wire z that is driven continuously with an assign statement to realize the digital equation.

  
  
module combo ( 	input 	a, b, c, d, e,
								output 	z);

	assign z = ((a & b) | (c ^ d) & ~e);
	
endmodule

  

The module combo gets elaborated into the following hardware schematic using synthesis tools and can be seen that the combinational logic is implemented with digital gates.

simple combinational logic with assign

Testbench

The testbench is a platform for simulating the design to ensure that the design does behave as expected. All combinations of inputs are driven to the design module using a for loop with a delay statement of 10 time units so that the new value is applied to the inputs after some time.

  
  
module tb;
	// Declare testbench variables
  reg a, b, c, d, e;
  wire z;
  integer i;
  
  // Instantiate the design and connect design inputs/outputs with
  // testbench variables
  combo u0 ( .a(a), .b(b), .c(c), .d(d), .e(e), .z(z));
  
  initial begin
  	// At the beginning of time, initialize all inputs of the design
  	// to a known value, in this case we have chosen it to be 0.
    a <= 0;
    b <= 0;
    c <= 0;
    d <= 0;
    e <= 0;
    
    // Use a $monitor task to print any change in the signal to 
    // simulation console 
    $monitor ("a=%0b b=%0b c=%0b d=%0b e=%0b z=%0b", 
              a, b, c, d, e, z);
    
    // Because there are 5 inputs, there can be 32 different input combinations
    // So use an iterator "i" to increment from 0 to 32 and assign the value
    // to testbench variables so that it drives the design inputs
    for (i = 0; i < 32; i = i + 1) begin
      {a, b, c, d, e} = i;
      #10;
    end
  end
endmodule

  
Simulation Log
ncsim> run
a=0 b=0 c=0 d=0 e=0 z=0
a=0 b=0 c=0 d=0 e=1 z=0
a=0 b=0 c=0 d=1 e=0 z=1
a=0 b=0 c=0 d=1 e=1 z=0
a=0 b=0 c=1 d=0 e=0 z=1
a=0 b=0 c=1 d=0 e=1 z=0
a=0 b=0 c=1 d=1 e=0 z=0
a=0 b=0 c=1 d=1 e=1 z=0
a=0 b=1 c=0 d=0 e=0 z=0
a=0 b=1 c=0 d=0 e=1 z=0
a=0 b=1 c=0 d=1 e=0 z=1
a=0 b=1 c=0 d=1 e=1 z=0
a=0 b=1 c=1 d=0 e=0 z=1
a=0 b=1 c=1 d=0 e=1 z=0
a=0 b=1 c=1 d=1 e=0 z=0
a=0 b=1 c=1 d=1 e=1 z=0
a=1 b=0 c=0 d=0 e=0 z=0
a=1 b=0 c=0 d=0 e=1 z=0
a=1 b=0 c=0 d=1 e=0 z=1
a=1 b=0 c=0 d=1 e=1 z=0
a=1 b=0 c=1 d=0 e=0 z=1
a=1 b=0 c=1 d=0 e=1 z=0
a=1 b=0 c=1 d=1 e=0 z=0
a=1 b=0 c=1 d=1 e=1 z=0
a=1 b=1 c=0 d=0 e=0 z=1
a=1 b=1 c=0 d=0 e=1 z=1
a=1 b=1 c=0 d=1 e=0 z=1
a=1 b=1 c=0 d=1 e=1 z=1
a=1 b=1 c=1 d=0 e=0 z=1
a=1 b=1 c=1 d=0 e=1 z=1
a=1 b=1 c=1 d=1 e=0 z=1
a=1 b=1 c=1 d=1 e=1 z=1
ncsim: *W,RNQUIE: Simulation is complete.

Example #2: Half Adder

The half adder module accepts two scalar inputs a and b and uses combinational logic to assign the outputs sum and carry bit cout. The sum is driven by an XOR between a and b while the carry bit is obtained by an AND between the two inputs.

  
  
module ha ( input 	a, b,
						output	sum, cout);

	assign sum  = a ^ b;
	assign cout = a & b;
endmodule

  
half adder circuit with assign

Testbench

  
  
module tb;
	// Declare testbench variables
  reg a, b;
  wire sum, cout;
  integer i;

  // Instantiate the design and connect design inputs/outputs with
  // testbench variables  
  ha u0 ( .a(a), .b(b), .sum(sum), .cout(cout));
  
  initial begin
  	// At the beginning of time, initialize all inputs of the design
  	// to a known value, in this case we have chosen it to be 0.  
    a <= 0;
    b <= 0;
    
    // Use a $monitor task to print any change in the signal to 
    // simulation console     
    $monitor("a=%0b b=%0b sum=%0b cout=%0b", a, b, sum, cout);
    
    // Because there are only 2 inputs, there can be 4 different input combinations
    // So use an iterator "i" to increment from 0 to 4 and assign the value
    // to testbench variables so that it drives the design inputs    
    for (i = 0; i < 4; i = i + 1) begin
      {a, b} = i;
      #10;
    end
  end
endmodule

  
Simulation Log
ncsim> run
a=0 b=0 sum=0 cout=0
a=0 b=1 sum=1 cout=0
a=1 b=0 sum=1 cout=0
a=1 b=1 sum=0 cout=1
ncsim: *W,RNQUIE: Simulation is complete.

Example #3: Full Adder

A full adder can be built using the half adder module shown above or the entire combinational logic can be applied as is with assign statements to drive the outputs sum and cout.

  
  
module fa (	input 	a, b, cin,
						output 	sum, cout);

	assign sum  = (a ^ b) ^ cin;
	assign cout = (a & b) | ((a ^ b) & cin);
endmodule

  
full adder circuit with assign

Testbench

  
  
module tb;
  reg a, b, cin;
  wire sum, cout;
  integer i;
  
  fa u0 ( .a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
  
  initial begin
    a <= 0;
    b <= 0;
    
    $monitor("a=%0b b=%0b cin=%0b sum=%0b cout=%0b", a, b, cin, sum, cout);
    
    for (i = 0; i < 7; i = i + 1) begin
      {a, b, cin} = i;
      #10;
    end
  end
endmodule

  
Simulation Log
ncsim> run
a=0 b=0 cin=0 sum=0 cout=0
a=0 b=0 cin=1 sum=1 cout=0
a=0 b=1 cin=0 sum=1 cout=0
a=0 b=1 cin=1 sum=0 cout=1
a=1 b=0 cin=0 sum=1 cout=0
a=1 b=0 cin=1 sum=0 cout=1
a=1 b=1 cin=0 sum=0 cout=1
ncsim: *W,RNQUIE: Simulation is complete.

Example #4: 2x1 Multiplexer

The simple 2x1 multiplexer uses a ternary operator to decide which input should be assigned to the output c. If sel is 1, output is driven by a and if sel is 0 output is driven by b.

  
  
module mux_2x1 (input 	a, b, sel,
								output 	c);
				
	assign c = sel ? a : b;
endmodule

  
2x1 multiplexer

Testbench

  
  
module tb;
	// Declare testbench variables
  reg a, b, sel;
  wire c;
  integer i;
  
  // Instantiate the design and connect design inputs/outputs with
  // testbench variables  
  mux_2x1 u0 ( .a(a), .b(b), .sel(sel), .c(c));
  
  initial begin
  	// At the beginning of time, initialize all inputs of the design
  	// to a known value, in this case we have chosen it to be 0.    
    a <= 0;
    b <= 0;
    sel <= 0;
    
    $monitor("a=%0b b=%0b sel=%0b c=%0b", a, b, sel, c);

    for (i = 0; i < 3; i = i + 1) begin
      {a, b, sel} = i;
      #10;
    end
  end
endmodule

  
Simulation Log
ncsim> run
a=0 b=0 sel=0 c=0
a=0 b=0 sel=1 c=0
a=0 b=1 sel=0 c=1
ncsim: *W,RNQUIE: Simulation is complete.

Example #5: 1x4 Demultiplexer

The demultiplexer uses a combination of sel and f inputs to drive the different output signals. Each output signal is driven by a separate assign statement. Note that the same signal is generally not recommended to be driven by different assign statements.

  
  
module demux_1x4 (	input 				f,
										input [1:0]	 	sel,
										output 				a, b, c, d);

	assign a = f & ~sel[1] & ~sel[0];
	assign b = f &  sel[1] & ~sel[0];
	assign c = f & ~sel[1] &  sel[0];
	assign d = f &  sel[1] &  sel[0];

endmodule

  
1x4 demultiplexer

Testbench

  
  
module tb;
	// Declare testbench variables
  reg f;
  reg [1:0] sel;
  wire a, b, c, d;
  integer i;
  
  // Instantiate the design and connect design inputs/outputs with
  // testbench variables  
  demux_1x4 u0 ( .f(f), .sel(sel), .a(a), .b(b), .c(c), .d(d));
  
  // At the beginning of time, initialize all inputs of the design
  // to a known value, in this case we have chosen it to be 0.  
  initial begin
    f <= 0;
    sel <= 0;
    
    $monitor("f=%0b sel=%0b a=%0b b=%0b c=%0b d=%0b", f, sel, a, b, c, d);
    
    // Because there are 3 inputs, there can be 8 different input combinations
    // So use an iterator "i" to increment from 0 to 8 and assign the value
    // to testbench variables so that it drives the design inputs    
    for (i = 0; i < 8; i = i + 1) begin
      {f, sel} = i;
      #10;
    end
  end
endmodule

  
Simulation Log
ncsim> run
f=0 sel=0 a=0 b=0 c=0 d=0
f=0 sel=1 a=0 b=0 c=0 d=0
f=0 sel=10 a=0 b=0 c=0 d=0
f=0 sel=11 a=0 b=0 c=0 d=0
f=1 sel=0 a=1 b=0 c=0 d=0
f=1 sel=1 a=0 b=0 c=1 d=0
f=1 sel=10 a=0 b=1 c=0 d=0
f=1 sel=11 a=0 b=0 c=0 d=1
ncsim: *W,RNQUIE: Simulation is complete.

Example #6: 4x16 Decoder

  
  
module dec_3x8 ( 	input 					en,
									input 	[3:0] 	in,
									output  [15:0] 	out);

	assign out = en ? 1 << in: 0;
endmodule

  
4x16 decoder

Testbench

  
  
module tb;
  reg en;
  reg [3:0] in;
  wire [15:0] out;
  integer i;
  
  dec_3x8 u0 ( .en(en), .in(in), .out(out));
  
  initial begin
    en <= 0;
    in <= 0;
    
    $monitor("en=%0b in=0x%0h out=0x%0h", en, in, out);
    
    for (i = 0; i < 32; i = i + 1) begin
      {en, in} = i;
      #10;
    end
  end
endmodule

  
Simulation Log
ncsim> run
en=0 in=0x0 out=0x0
en=0 in=0x1 out=0x0
en=0 in=0x2 out=0x0
en=0 in=0x3 out=0x0
en=0 in=0x4 out=0x0
en=0 in=0x5 out=0x0
en=0 in=0x6 out=0x0
en=0 in=0x7 out=0x0
en=0 in=0x8 out=0x0
en=0 in=0x9 out=0x0
en=0 in=0xa out=0x0
en=0 in=0xb out=0x0
en=0 in=0xc out=0x0
en=0 in=0xd out=0x0
en=0 in=0xe out=0x0
en=0 in=0xf out=0x0
en=1 in=0x0 out=0x1
en=1 in=0x1 out=0x2
en=1 in=0x2 out=0x4
en=1 in=0x3 out=0x8
en=1 in=0x4 out=0x10
en=1 in=0x5 out=0x20
en=1 in=0x6 out=0x40
en=1 in=0x7 out=0x80
en=1 in=0x8 out=0x100
en=1 in=0x9 out=0x200
en=1 in=0xa out=0x400
en=1 in=0xb out=0x800
en=1 in=0xc out=0x1000
en=1 in=0xd out=0x2000
en=1 in=0xe out=0x4000
en=1 in=0xf out=0x8000
ncsim: *W,RNQUIE: Simulation is complete.

A for loop is the most widely used loop in software, but it is primarily used to replicate hardware logic in Verilog. The idea behind a for loop is to iterate a set of statements given within the loop as long as the given condition is true. This is very similar to the while loop, but is used more in a context where an iterator is available and the condition depends on the value of this iterator.

Syntax

  
  
	for (<initial_condition>; <condition>; <step_assignment>) begin
		// Statements
	end

  

The keyword for is used to specify this type of loop and has three parts:

  1. Initial condition to specify initial values of signals
  2. A check to evaluate if the given condition is true
  3. Update control variable for the next iteration

The initial condition and updates to control variable are included in the for loop and is not required to be specified separately unlike a while loop. A while loop is more general purpose and is mostly used only when the given statements are required to be repeated as long as a given condition. However the for loop tyically has a definite beginning and end controlled by the step variable.

Here is a simple example that illustrates the usage of a for loop.

  
  
module my_design;
	integer i;
	
	initial begin
		// Note that ++ operator does not exist in Verilog !
		for (i = 0; i < 10; i = i + 1) begin
			$display ("Current loop#%0d ", i);
		end
	end
endmodule

  
Simulation Log
ncsim> run
Current loop#0 
Current loop#1 
Current loop#2 
Current loop#3 
Current loop#4 
Current loop#5 
Current loop#6 
Current loop#7 
Current loop#8 
Current loop#9 
ncsim: *W,RNQUIE: Simulation is complete.

Design Example

Let us take a look at how an 8-bit left shift register can be implemented in Verilog without a for loop and then compare it with the code using a for loop just to appreciate the utility of a looping construct.

  
  
module lshift_reg (input 						clk,				// Clock input
                   input 						rstn,				// Active low reset input
                   input [7:0] 			load_val, 	// Load value 
                   input 						load_en, 		// Load enable
                   output reg [7:0] op); 				// Output register value

	 // At posedge of clock, if reset is low set output to 0
	 // If reset is high, load new value to op if load_en=1
	 // If reset is high, and load_en=0 shift register to left
	 always @ (posedge clk) begin
	    if (!rstn) begin
	      op <= 0;
	    end else begin
	    	if (load_en) begin
	      	op <= load_val;
	      end else begin
	        op[0] <= op[7];
	        op[1] <= op[0];
	        op[2] <= op[1];
	        op[3] <= op[2];
	        op[4] <= op[3];
	        op[5] <= op[4];
	        op[6] <= op[5];
	        op[7] <= op[6];
	      end
	    end
	  end
endmodule

  

The same behavior can be implemented using a for loop which will reduce the code and make it scalable for different register widths. If the width of the register is made a Verilog parameter, the design module will become scalable and the same parameter can be used inside the for loop.

  
  
module lshift_reg (input 						clk,				// Clock input
                   input    				rstn,				// Active low reset input
                   input [7:0] 			load_val, 	// Load value 
                   input 						load_en, 		// Load enable
                   output reg [7:0] op); 				// Output register value

	 integer i;
	 
	 // At posedge of clock, if reset is low set output to 0
	 // If reset is high, load new value to op if load_en=1
	 // If reset is high, and load_en=0 shift register to left
	 always @ (posedge clk) begin
	    if (!rstn) begin
	      op <= 0;
	    end else begin
	    
	    	// If load_en is 1, load the value to op
	    	// else keep shifting for every clock
	    	if (load_en) begin
	      	op <= load_val;
	      end else begin
            for (i = 0; i < 8; i = i + 1) begin
              op[i+1] <= op[i];
            end
            op[0] <= op[7];
	      end
	    end
	  end
endmodule

  

Testbench

The testbench code is shown below and instantiates the design.

  
  
module tb;
  reg clk;
  reg rstn;
  reg [7:0] load_val;
  reg load_en;
  wire [7:0] op;
  
  // Setup DUT clock
  always #10 clk = ~clk;
  
  // Instantiate the design
  lshift_reg u0 ( .clk(clk),
                 .rstn (rstn),
                 .load_val (load_val),
                 .load_en (load_en),
                 .op (op));
  
  initial begin
  	// 1. Initialize testbench variables
    clk <= 0;
    rstn <= 0;
    load_val <= 8'h01;
    load_en <= 0;
    
    // 2. Apply reset to the design
    repeat (2) @ (posedge clk);
    rstn <= 1;
    repeat (5) @ (posedge clk);
    
    // 3. Set load_en for 1 clk so that load_val is loaded
    load_en <= 1;
    repeat(1) @ (posedge clk);
    load_en <= 0;
    
    // 4. Let design run for 20 clocks and then finish
    repeat (20) @ (posedge clk);
    $finish;
  end
endmodule

  
hardware schematic for shift register

Verilog supports a few compiler directives that essentially direct the compiler to treat the code in a certain way. For example, a portion of the code may represent an implementation of a certain feature and there should be some way to not include the code in the design if the feature is not used.

This can be solved with conditional compilation where the designer can wrap the code within compiler directives which tell the compiler to either include or exclude the code for compilation when the given named flag is set.

Syntax

Conditional compilation can be achieved with Verilog `ifdef and `ifndef keywords. These keywords can appear anywhere in the design and can be nested one inside the other.

The keyword `ifdef simply tells the compiler to include the piece of code until the next `else or `endif if the given macro called FLAG is defined using a `define directive.

  
  
// Style #1: Only single `ifdef
`ifdef <FLAG>
	// Statements
`endif

// Style #2: `ifdef with `else part
`ifdef <FLAG>
	// Statements
`else
	// Statements
`endif

// Style #3: `ifdef with additional ifdefs
`ifdef <FLAG1>
	// Statements
`elsif <FLAG2>
	// Statements
`elsif <FLAG3>
	// Statements
`else
	// Statements
`endif

  

The keyword `ifndef simply tells the compiler to include the piece of code until the next `else or `endif if the given macro called FLAG is not defined using a `define directive.

Design Example with `ifdef

  
  
module my_design (input clk, d, 
`ifdef INCLUDE_RSTN
                  input rstn,
`endif                  
                  output reg q);
  
  always @ (posedge clk) begin
`ifdef INCLUDE_RSTN
    if (!rstn) begin
      q <= 0;
    end else 
`endif
    begin
      q <= d;
    end
  end
endmodule

  

Testbench

  
  
module tb;
  reg clk, d, rstn;
  wire q;
  reg [3:0] delay;
  
  my_design u0 ( .clk(clk), .d(d),
`ifdef INCLUDE_RSTN
                .rstn(rstn),
`endif
                .q(q));
  
  always #10 clk = ~clk;
  
  initial begin
    integer i;
    
    {d, rstn, clk} <= 0;
    
	#20 rstn <= 1;    
    for (i = 0 ; i < 20; i=i+1) begin
      delay = $random;
      #(delay) d <= $random;
    end
    
    #20 $finish;
  end
endmodule

  

Note that by default, rstn will not be included during compilation of the design and hence it will not appear in the portlist. However if a macro called INCLUDE_RSTN is either defined in any Verilog file that is part of the compilation list of files or passed through the command line to the compiler, rstn will be included in compilation and the design will have it.

Experiment by adding and removing +define+INCLUDE_RSTN from 'Compile & Run Options' on the left pane to know the difference.

Verilog `ifdef `elsif Example

The following example has two display statements inside separate `ifdef scopes which does not have a default `else part to it. So this means that by default nothing will be displayed. If the macro either MACRO is defined, the corresponding display message is included and will be displayed during simulation

  
  
module tb;
  initial begin

`ifdef MACRO1
    $display ("This is MACRO1");
    
`elsif MACRO2
    $display ("This is MACRO2");
    
`endif
  end
endmodule

  
Simulation Log
# With no macros defined
ncsim> run
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO1
ncsim> run
This is MACRO1
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO2
ncsim> run
This is MACRO2
ncsim: *W,RNQUIE: Simulation is complete.

Verilog `ifndef `elsif Example

The same code can be written with `ifndef and results will be just the opposite.

  
  
module tb;
  initial begin

`ifndef MACRO1
    $display ("This is MACRO1");
    
`elsif MACRO2
    $display ("This is MACRO2");
    
`endif
  end
endmodule

  
Simulation Log
# With no macros defined
ncsim> run
This is MACRO1
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO1
ncsim> run
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO2
ncsim> run
This is MACRO1
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+MACRO1 +define+MACRO2
ncsim> run
This is MACRO2
ncsim: *W,RNQUIE: Simulation is complete.

Verilog Nested `ifdef Example

`ifdef and its flavors can be nested one inside the other to create complex ways of code inclusion and exclusion with defined macros.

  
  
module tb;
  initial begin
    `ifdef FLAG
    	$display ("FLAG is defined");
    	`ifdef NEST1_A
    		$display ("FLAG and NEST1_A are defined");
    		`ifdef NEST2
    			$display ("FLAG, NEST1_A and NEST2 are defined");
    		`endif
    	`elsif NEST1_B
    		$display ("FLAG and NEST1_B are defined");
    		`ifndef WHITE
    			$display ("FLAG and NEST1_B are defined, but WHITE is not");
    		`else
    			$display ("FLAG, NEST1_B and WHITE are defined");
    		`endif
    	`else
    		$display ("Only FLAG is defined");
    	`endif
    `else
    	$display ("FLAG is not defined");
    `endif
  end
endmodule

  
Simulation Log
# Without defining any macro
ncsim> run
FLAG is not defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+FLAG +define+NEST1_B
ncsim> run
FLAG is defined
FLAG and NEST1_B are defined
FLAG and NEST1_B are defined, but WHITE is not
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+FLAG +define+NEST1_B +define+WHITE
ncsim> run
FLAG is defined
FLAG and NEST1_B are defined
FLAG, NEST1_B and WHITE are defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+FLAG
ncsim> run
FLAG is defined
Only FLAG is defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+WHITE
ncsim> run
FLAG is not defined
ncsim: *W,RNQUIE: Simulation is complete.

# With +define+NEST1_A
ncsim> run
FLAG is not defined
ncsim: *W,RNQUIE: Simulation is complete.

Note that as long as the parent macro is not defined, definition of any other nested macro within it does not get compiled. For example, NEST1_A or WHITE macro definitions without FLAG does not make the compiler pick up the nested code.

Straight Ring Counter

Design

  
  
module ring_ctr  #(parameter WIDTH=4) 
  (  
	input clk,                
	input rstn,
  	output reg [WIDTH-1:0] out
  );    
 
  always @ (posedge clk) begin
      if (!rstn)
         out <= 1;
      else begin
        out[WIDTH-1] <= out[0];
        for (int i = 0; i < WIDTH-1; i=i+1) begin
          out[i] <= out[i+1];
        end
      end
  end
endmodule

  

Testbench

  
  
module tb;
  parameter WIDTH = 4;
  
  reg clk;
  reg rstn;
  wire [WIDTH-1:0] out;
  
  ring_ctr 	u0 (.clk (clk),
                .rstn (rstn),
                .out (out));
  
  always #10 clk = ~clk;
  
  initial begin
    {clk, rstn} <= 0;

    $monitor ("T=%0t out=%b", $time, out);
    repeat (2) @(posedge clk);
    rstn <= 1;
    repeat (15) @(posedge clk);
    $finish;
  end
endmodule

  
Simulation Log
ncsim> run
T=0 out=xxxx
T=10 out=0001
T=50 out=1000
T=70 out=0100
T=90 out=0010
T=110 out=0001
T=130 out=1000
T=150 out=0100
T=170 out=0010
T=190 out=0001
T=210 out=1000
T=230 out=0100
T=250 out=0010
T=270 out=0001
T=290 out=1000
T=310 out=0100
Simulation complete via $finish(1) at time 330 NS + 0