This conditional statement is used to make a decision on whether the statements within the if block should be executed or not.

  • If the expression evaluates to true (i.e. any non-zero value), all statements within that particular if block will be executed
  • If it evaluates to false (zero or 'x' or 'z'), the statements inside if block will not be executed
  • If there is an else statement and expression is false then statements within the else block will be executed.

Syntax

If multiple statements need to be placed inside the if or else part, it needs to be enclosed within begin and end.

  
  
	if ([expression])
		Single statement

	// Use "begin" and "end" blocks for more than 1 statements		
	if ([expression]) begin          
		Multiple statements
	end
	
	// Use else to execute statements for which expression is false
	if ([expression]) begin
		Multiple statements
	end else begin
		Multiple statements
	end
	
	// if-else-if style to check for more expressions if the previous one doesn't match
	if ([expression 1]) 
		Single statement
	else if ([expression 2]) begin
		Multiple Statements
	end else
		Single statement

  

Hardware Implementation

if without else

if without an else part implies that the value remain unchanged for any condition that does not satisfy the expression inside if.

  
  
module des (  input en,
              input d,
              output reg q);

    always @ (en or d)
        if (en)
            q = d;

endmodule

  

Value of output q is updated whenever d or en changes in value.

if with else

Output q will get the value of input d at the positive edge of clock if rstn is high and describes the behavior of a D flop.

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

  

Note that the synthesized output indicates a flop with an output q.

if else if

In the following example, the design module has a 4-bit output q that is incremented when mode is 1 and decrements when mode is 2 with if else construct. Note that the description does not specify what has to be done if mode is 0 or 3 which are valid values for a 2-bit variable. It is assumed that the circuit does nothing when mode is 1 and 3, but maintain exiting value of q. It is not recommended to leave such ambiguity in real design code, but is shown here to highlight the possibility.

  
  
module des ( input [1:0] mode,
             input clk,
            input rstn,
            output reg [3:0] q);

  always @ (posedge clk) begin
    if (! rstn)
      q <= 0;
    else begin
      if (mode == 1)
        q <= q + 1;
      else if (mode == 2)
        q <= q - 1;
    end
  end
endmodule

  

The synthesized output may differ with availability of cells for a given technology library

Shown below is the synthesized output and it is worth to note that q got implemented as a 4-bit flop which has a pin CE to enable the flop. Note that this flop is enabled only when mode is 1 or 2 and not for other values. Output q is fed back through an adder and subtractor block into the input of the same flop through a mux which is again controlled by mode.

Consider the same design from above with a 1-bit mode.

  
  
module des ( input mode,
             input clk,
            input rstn,
            output reg [3:0] q);

  always @ (posedge clk) begin
    if (! rstn)
      q <= 0;
    else begin
      if (mode)
        q <= q + 1;
      else 
        q <= q - 1;
    end
  end
    
endmodule

  

In this case, a regular flop without a CE pin is used along with a few multiplexers to choose the correct signal based on value of mode.

Examples

if without else for single statement

  
  
module tb;
  int a = 10;
  
  initial begin 
    if (a == 10)      // if block can have only one statement in it
      $display ("a is found to be 10");
    $display ("Always executed regardless of value of a");   // This statement is outside if block because 
  end
endmodule

  
Simulation Log
ncsim> run
a is found to be 10
Always executed regardless of value of a
ncsim: *W,RNQUIE: Simulation is complete.

if without else for multiple statements

  
  
module tb;
  int a = 10;
  
  initial begin
    if (a == 10) begin       // if block has begin end keywords, and can support multiple statements
       $display ("a is found to be 10");
       $display ("Its good to get 10");
       
       // Anything else can be done here until the "end" keyword
    end
    $display ("Always executed regardless of value of a");   // Statement is outside the if block, because of the closing "end" for the "begin" in if
  end
endmodule

  
Simulation Log
ncsim> run
a is found to be 10
Its good to get 10
Always executed regardless of value of a
ncsim: *W,RNQUIE: Simulation is complete.

if-else for single statement

  
  
module tb;
  int a = 9;
 
  initial begin
    if (a == 10)
       $display ("a is found to be 10");    // Is executed when "if" expression is True
    else
       $display ("a is NOT 10 :(");         // Is executed when "if" expression is false
  end
endmodule

  
Simulation Log
ncsim> run
a is NOT 10 :(
ncsim: *W,RNQUIE: Simulation is complete.

if-else for multiple statements

  
  
module tb;
  int a = 9;
 
  initial begin
    if (a == 10) begin
       $display ("a is found to be 10");              // Is executed when "if" expression is True
       // Can have more additional statements here
       
    end else begin
       $display ("a is NOT 10 :(");                   // Is executed when "if" expression is false
       $display ("Why is a not 10 ?");
       // Can have more additional statements here
    end
  end
endmodule

  
Simulation Log
ncsim> run
a is NOT 10 :(
Why is a not 10 ?
ncsim: *W,RNQUIE: Simulation is complete.

All behavioral code is written inside module and endmodule. So, whatever digital design that you intend to create, it'll go inside a module block. It may or may not have ports defined - allow signals to enter the block as input or escape the block as output.

Module

The empty module in the example below is called testbench. You can name it whatever you like, except that it should be alphanumeric, and can contain '_'.

testbench module
  
  
module testbench;

endmodule

  

In the early days of integrated circuits, engineers had to sit down and physically draw transistors and their connections on paper to design them such that it can be fabricated on silicon. Bigger and complex circuits demanded more engineers, time and other resources and soon enough there was a need to have a better way of designing integrated circuits.

VHDL was soon developed to enhance the design process by allowing engineers to describe functionality of the desired hardware and let automation tools convert that behavior into actual hardware elements like combinational gates and sequential logic. Verilog was developed to simplify the process and make the Hardware Description Language (HDL) more robust and flexible. Today, Verilog is the most popular HDL used and practiced throughout the semiconductor industry.

How is Verilog useful ?

Verilog creates a level of abstraction that helps hide away the details of its implementation and technology.

For example, the design of a D flip-flop would require the knowledge of how the transistors need to be arranged to achieve a positive-edge triggered FF and what the rise, fall and clk-Q times required to latch the value onto a flop among many other technology oriented details. Power dissipation, timing and the ability to drive nets and other flops would also require a more thorough understanding of the physical characteristics of a transistor.

Verilog helps us to focus on the behavior and leave the rest to be sorted out later.

Example

The following code illustrates how a Verilog code looks like. We will delve into more details of the code in the next article. For the time being, let us simply understand that the behavior of a counter is described. The code essentially makes the counter count up if the up_down signal is 1, and down if its value is 0. It also resets the counter if the signal rstn becomes 0 making this an active-low reset.

  
  
	module ctr (input  				up_down,
									clk,
									rstn,
	            output reg [2:0] 	out);
		
		always @ (posedge clk)
			if (!rstn)
				out <= 0;
			else begin
				if (up_down)
					out <= out + 1;
				else
					out <= out - 1;
			end
	endmodule

  

The simple example shown above illustrates how all the physical implementation details have been hidden while still providing a clear idea of how the counter functions.

ctr is a module that represents an up/down counter, and it is possible to choose the actual physical implementation of the design from a wide variety of different styles of flops optimized for area, power and performance. They are usually compiled into libraries and will be available for us to select within EDA tools at a later stage in the design process.

Now that you know what Verilog is, let's start learning Verilog !

Design

  
  
module tff ( 	input clk,
            	input rstn,
            	input t,
            output reg q);
  
  always @ (posedge clk) begin
    if (!rstn) 
      q <= 0;
    else
    	if (t)
      		q <= ~q;
    	else
      		q <= q;
  end
endmodule

  

Testbench

  
  
module tb;
  reg clk;
  reg rstn;
  reg t;
  
  tff u0 (	.clk(clk),
          	.rstn(rstn),
          	.t(t),
          .q(q));
  
  always #5 clk = ~clk;
  
  initial begin  
    {rstn, clk, t} <= 0;
    
    $monitor ("T=%0t rstn=%0b t=%0d q=%0d", $time, rstn, t, q);
    repeat(2) @(posedge clk);
    rstn <= 1;
    
    for (integer i = 0; i < 20; i = i+1) begin
      reg [4:0] dly = $random;
      #(dly) t <= $random;
    end
	#20 $finish;
  end
endmodule

  
Simulation Log
ncsim> run
T=0 rstn=0 t=0 q=x
T=5 rstn=0 t=0 q=0
T=15 rstn=1 t=0 q=0
T=19 rstn=1 t=1 q=0
T=25 rstn=1 t=1 q=1
T=35 rstn=1 t=1 q=0
T=43 rstn=1 t=0 q=0
T=47 rstn=1 t=1 q=0
T=55 rstn=1 t=0 q=1
T=59 rstn=1 t=1 q=1
T=65 rstn=1 t=1 q=0
T=67 rstn=1 t=0 q=0
T=71 rstn=1 t=1 q=0
T=75 rstn=1 t=0 q=1
T=79 rstn=1 t=1 q=1
T=83 rstn=1 t=0 q=1
T=87 rstn=1 t=1 q=1
T=95 rstn=1 t=0 q=0
Simulation complete via $finish(1) at time 115 NS + 0

Design

  
  
module single_port_sync_ram 
  # (parameter ADDR_WIDTH = 4,
     parameter DATA_WIDTH = 32,
     parameter DEPTH = 16 
    )
  
  ( 	input 					clk,
   		input [ADDR_WIDTH-1:0]	addr,
   		inout [DATA_WIDTH-1:0]	data,
   		input 					cs,
   		input 					we,
   		input 					oe
  );
  
  reg [DATA_WIDTH-1:0] 	tmp_data;
  reg [DATA_WIDTH-1:0] 	mem [DEPTH];
  
  always @ (posedge clk) begin
    if (cs & we)
      mem[addr] <= data;
  end
  
  always @ (posedge clk) begin
    if (cs & !we)
    	tmp_data <= mem[addr];
  end
  
  assign data = cs & oe & !wr ? tmp_data : 'hz;
endmodule

  
Single Port RAM with async read and write

Testbench

  
  
module tb;
  parameter ADDR_WIDTH = 4;
  parameter DATA_WIDTH = 16;
  parameter DEPTH = 16;
  
  reg clk;
  reg cs;
  reg we;
  reg oe;
  reg [ADDR_WIDTH-1:0] addr;
  wire [DATA_WIDTH-1:0] data;
  reg [DATA_WIDTH-1:0] tb_data;
  
  single_port_sync_ram #(.DATA_WIDTH(DATA_WIDTH)) u0
  ( 	.clk(clk),
                        	.addr(addr),
                        	.data(data),
                        	.cs(cs),
   							.we(we),
   							.oe(oe)
                         );
  
  
  always #10 clk = ~clk;
  assign data = !oe ? tb_data : 'hz;
  
  initial begin
    {clk, cs, we, addr, tb_data, oe} <= 0;
    
    repeat (2) @ (posedge clk);
    
    for (integer i = 0; i < 2**ADDR_WIDTH; i= i+1) begin
      repeat (1) @(posedge clk) addr <= i; we <= 1; cs <=1; oe <= 0; tb_data <= $random;
    end
    
    for (integer i = 0; i < 2**ADDR_WIDTH; i= i+1) begin
      repeat (1) @(posedge clk) addr <= i; we <= 0; cs <= 1; oe <= 1;
    end
    
    #20 $finish;
  end
endmodule