FutureWiz
loading...

How to Write a Verilog Module for Design and Testbench

This article will give the way Module keywords are used to structure Verilog designs and how this relates to the hardware being described. The main purpose of this post is to introduce the skills which will allow us to model and test our digital design.Our discussion will start with the basic components of Verilog code.

A Verilog module is an essential building block that defines a design or testbench component by specifying the block's ports and internal behaviors. A module provides the necessary functionality to the higher-level block through its port interface. Module declaration comes on top of every Verilog program.To develop hierarchical architectures, higher-level modules can embed lower-level modules.

Verilog ports are the medium to connect Verilog modules with one another to communicate. Verilog is structured simply. Apart from system tasks and functions which are made global, all data, functions, and tasks are written in modules.

Components of Verilog Module:

A Verilog module is enclosed between the keywords module and endmodule. The Verilog module has several components such as:

  • module keyword to start the definition.
  • Name of the module as an identifier.
  • Port List along with the directions (In case of Testbench port list would be empty)
  • Parameters to declare the constants (Optional)
  • Module body which defines the logic to be implemented.
  • Optional Functions/ Tasks and System tasks
  • Endmodule

Lets understand each component one by one.

Module:Module is declared by the keyword module.A corresponding keyword endmodule must appear at the end of the module definition.Each module must have a module name, which is the identifierfor the module, and a port list, which describes the input and  output terminals of the module.

Ports: are the pins or signal which act as input or output of a specified module.Port list is an important component of Verilog module which provide a means for a module to communicate with the external world through input and output.There are three types of port available in Verilog:

  • input               // module can only receive information using its input ports
  • output            // module can only send value using its output ports
  • inout                     // module can either sends or receive values using its inout ports

All ports declared as one of the above are assumed to be awire by default, otherwise it is necessary to declare it again. Declaring multiple ports with same name is illegal. Module declaration syntax is shown below:

modulemodule_name (portdefinition);

// Body of Module

endmodule

modulemodule_name ;

//port list can be empty for testbench

// Body of Module

endmodule

 

So, module for given logical circuit can written as:

module mux1(input A, B, S, outputY)       ;

// Body of Module

endmodule

 

 

Parameter:is defining a constant which can be set when you use a module, which allows customization of module during the instantiation process.It can be a binary , Integer, real or string. 

 Syntax:parameter variable_name = value;

Module body: In essence, the "code that is inside the module" (following the port declaration) is referred to as a module body. It specifies what makes up the module and may contain a wide variety of declarations and definitions (such as net and variable declarations, always blocks and initial blocks, etc.).In Verilog, a module body can be defined using various levels of abstraction. There are four levels of abstraction in Verilog: -

  1. Gate level
  2. Register-Transfer Level
  3. Behavioral Level
  4. Switch level

Gate Level

Within the logical level, the characteristics of a system are described by logical links and their timing properties. All signals are discrete signals. They can only have definite logical values (`0', `1', `X', `Z`). The usable operations are predefined logic primitives (basic gates). Gate level modelling may not be a right idea for logic design. Gate level code is generated using tools like synthesis tools and this netlist is used for gate level simulation and for backend.

Register−Transfer Level

Designs using the Register−Transfer Level specify the characteristics of a circuit using operations and the transfer of data between the registers. The modern definition of an RTL code is "Any code that is synthesizable is called RTL code".

Behavioral level

This level describes a system by concurrent algorithms (Behavioural). Every algorithm is sequential, which means it consists of a set of instructions that are executed one by one. Functions, tasks, and procedural blocks are the main elements. There is no regard to the structural realization of the design.

This section will give you the flavor of Verilog coding for design with different levels of abstraction. For example, a 2:1 multiplexer as discussed above is taken to explain to you the different styles of coding using Verilog.

module mux1(input A, B, S, outputY)       ;

// using Gate level modeling

// Y= S’.A+ S.B

wire w1,w2,w3;

not (w1,S); // w1=S’

and (w2, w1,A);  //w2= S’.A

and (w3,S, B);    //w3= S.B

or (Y, w2,w3);  //Y= S’.A+S.B

endmodule

module mux1(input A, B, S, outputY)       ;

// Using RTL Coding

assign Y= S? B: A;

endmodule

 

module mux1(input A, B, S, outputregY);

// Using Behavioral Code

always @(*)

begin

if(S)  Y= B;

else  Y=A;

end

endmodule

 

Now we understand the approach to writing a testbench for the Design / top-level module, which we have learned in the above section. This section will discuss the basic architecture of Verilog testbench. When a digital design is being implemented using Verilog, we normally also create a testbench to stimulate the code and ensure that it functions as expected.The testbench can be written in several languages, the most popular being VHDL, Verilog, and System Verilog.Testbenches contain non-synthesizable Verilog code that generates inputs for the design and verifies that the outputs are accurate. A Verilog testbench is consisting of:

  • Testbench module:The very first thing we do in the testbench is declare an empty module to write the testbench code followed by a declaration of reg/wires to connect the testbench with DUT as shown below.

module mux1_tb;

reg A,B,S;

wire Y;

// Testbench Code

endmodule

 

  • Instantiation of DUT/ Top-Module: Since we have an empty testbench module to work with, now we need to instantiate the design we are going to verify. Instantiation is specified −By name, using a dot “. template port name (name of wire connected to port)” OrBy position, placing the ports at the same place in the port lists of both template and the instance.

//DUT Instantiation with positional Port

mux1 DUT (A,B,S,Y);

//DUT Instantiation with named Port

mux1 DUT (.A(A),.B(B),.S(S),.Y(Y));

 

  • Generation of Clock and Reset (In case of Sequential Circuits): The next step is to generate a clock and reset signal in the Verilog testbench. We can write the code for this within an initial block. Next, we schedule the state changes using the Verilog delay operator. A clock signal is generatedusing the forever keyword by scheduling inversion after a specific time delay. The below Verilog codesillustrates the generation of the clock and reset in the testbench. 

// Generate Clock

initial begin

clk= 1’b0;

forever #2 clk= ~clk;

end

// Generatethe Reset

initial begin

reset= 1’b1;

#8 reset= 1’b0;

end

 

Writing the stimulus: The next part of the testbench is stimulus generation. We must generate each of the possible input combinations to test the circuit. After every combination, we then need to wait for a short time while the signals propagate through our code block. To do this, we assign the values to inputs and then use the Verilog delay operator. The below code snippet illustrates the code for this.

// Generate the input combination after 2 unit delay

initial begin

A= 1’b0; B= 1’b0; S= 1’b0;

#2 A= 1’b0; B= 1’b0; S= 1’b1;

#2 A= 1’b0; B= 1’b1; S= 1’b0;

#2 A= 1’b0; B= 1’b1; S= 1’b1;

#2 A= 1’b1; B= 1’b0; S= 1’b0;

#2 A= 1’b1; B= 1’b0; S= 1’b1;

#2 A= 1’b1; B= 1’b1; S= 1’b0;

#2 A= 1’b1; B= 1’b1; S= 1’b1;

end

 

  • Monitoring the Input and Outputs: Finally,we monitorthe values of the inputs and outputs, which can be done with the $monitorVerilog system task.

// Monitor the inputs and outputs

initial begin

$monitor (“ Sim Time=%t, A=%b, B=%b, S=%b, Y=%b”, $time, A, B, S, Y);

end

 

Complete Verilog code for Design and Testbench: By combining all parts of Testbench, full Verilog code for 2:1 MUX Design and Testbench is shown below.

// Testbench Code for 2:1 MUX DUT

module mux1_tb;

reg A,B,S;

wire Y;

//DUT Instantiation

mux1 DUT (.A(A),.B(B),.S(S),.Y(Y));

// Generate the input combination after 2 unit delay

initial begin

A= 1’b0; B= 1’b0; S= 1’b0;

#2 A= 1’b0; B= 1’b0; S= 1’b1;

#2 A= 1’b0; B= 1’b1; S= 1’b0;

#2 A= 1’b0; B= 1’b1; S= 1’b1;

#2 A= 1’b1; B= 1’b0; S= 1’b0;

#2 A= 1’b1; B= 1’b0; S= 1’b1;

#2 A= 1’b1; B= 1’b1; S= 1’b0;

#2 A= 1’b1; B= 1’b1; S= 1’b1;

end

// Monitor the inputs and outputs

initial begin

$monitor (“ Sim Time=%t, A=%b, B=%b, S=%b, Y=%b”, $time, A, B, S, Y);

end

endmodule

// Verilog code for 2:1 MUX

module mux1(input A, B, S, outputY);

// Using RTL Coding Ternary operator

assign Y= S? B: A;

endmodule

 

// Verilog code for 2:1 MUX

module mux1(input A, B, S, outputY);

// Using RTL Coding using Data flow

assign Y= (~S & A) | (S & B);

endmodule

 

// Verilog code for 2:1 MUX

module mux1(input A, B, S, outputregY);

// Using Behavioral Code

always @(*)

begin

if(S)  Y= B;

else  Y=A;

end

endmodule

 

×

Register for on Call Counselling.


×

Talk To Advisor


×

Enroll Now