View Course Path

Verilog code for NAND gate – All modeling styles

Let’s look into designing the NAND logic gate using all the three modeling styles in Verilog, i.e. Gate Level, Dataflow, and Behavioral modeling. The three different methods of modeling are based on the levels of abstraction. Let’s first start by understanding Gate Level Modeling.

Gate Level modeling

Gate level modeling allows us to design any digital logic circuit using basic logic gates. If you know the gate level circuit representation of any logic circuit, you can easily write the Verilog code for it using this modeling style. Verilog supports coding circuits using basic logic gates as predefined primitives. These primitives are simple commands that are understandable by the compiler. For example, the and command is available for implementing AND logic. Pretty straightforward isn’t it?

Logic Circuit of the NAND gate

The NAND gate is the complement of AND function. Its graphic symbol consists of an AND gate’s graphic symbol followed by a small circle. Here’s the logical representation of the NAND gate.

Verilog code for NAND gate using gate-level modeling

The code for the NAND gate would be as follows.

module NAND_2(output Y, input A, B);

We start by declaring the module. module, a basic building design unit in Verilog HDL, is a keyword to declare the module’s name. NAND_2 is the identifier. Identifiers is the name of the module. The module command instructs the compiler to create a block containing certain inputs and outputs. The list in parenthesis is known as the port list, it contains the input and output ports. Then we declare other datatypes required in our design as follows:

wire Yd;

wire in Verilog represents an electrical connection. It is internal, therefore not mentioned in the port list. Next,

and(Yd, A, B);
not(Y, Yd);

Here and is the operation performed on A, B, to get its output in Yd. Then Yd is passed through an inverter, and the output is obtained in Y. The compiler understands the and and the not operation the same way we do. endmodule terminates the module.

Here, you can look on the complete code:

module NAND_2_gate_level(output Y, input A, B);
    wire Yd;
    and(Yd, A, B);
    not(Y, Yd);

Data flow modeling

Compared to gate-level modeling, dataflow modeling is a higher level of abstraction. What this means is, we do not need to know the intricacies of the circuit. This helps as gate-level modeling becomes very complicated for large circuits.

Hence dataflow modeling is a very important way of implementing the design. All we need to know is the boolean logic equation of the output of the circuit in terms of its inputs. We use continuous assignments in dataflow modeling in most of the designs. The continuous assignments are done using the keyword assign.

Equation of the NAND gate

The boolean equation for a NAND gate is Y = (A.B)’ or ~(A & B).

Verilog code for NAND gate using data-flow modeling

We would again start by declaring the module.

module NAND_2_data_flow (output Y, input A, B);

A similar way is followed to list the ports. As it is data-flow modeling, assignment statements are the next,

 assign Y = ~(A & B);

This statement performs and operation, followed by negation on A and B, putting the output in Y.

Here is the complete code for data-flow modeling.

module NAND_2_data_flow (output Y, input A, B);
    assign Y = ~(A & B);    

Behavioral Modeling

Behavioral modeling allows the designer to not care about the circuit diagram. It is the highest level of abstraction in the Verilog HDL. All that a designer needs is the algorithm or the behavior of the circuit. This level simulates the behavior of the circuits. That’s helpful because the designer does not have to deal with complicated circuitry or equations. Just a simple truth table would suffice.

NAND gate’s truth table

A B Y(A nand B)
0 0 1
0 1 1
1 0 1
1 1 0

Equation from the truth table

Simply by minimization, (or you may arrive by k-maps), we can state that:

Y = (A.B)’ or say Y = (A & B)’.

Verilog code for NAND gate using behavioral modeling

Again, we begin by declaring module, setting up identifier as NAND_2_behavioral, and the port list.

module NAND_2_behavioral (output reg Y, input A, B);

In this case, the port list includes the output and input ports. When our level of abstraction is behavioral, then we use the reg datatype in the output ports. The reg data object holds its value from one procedural assignment statement to the next and means it holds its value over simulation data cycles.

Then we write,

always @ (A or B) begin

The always statement, a procedural statement in Verilog, runs the program sequentially.  (A, B) is known as the sensitivity list. It contains the input signals on which the statements of the block depends. An advantage of using a always block is that it gets triggered only if any of the input from sensitivity varies. In Verilog, begin embarks and  end concludes any block which contains more than one statement in it. We could also have written the always block as always @ * instead of always @(Y, A)* means that the code itself figures out the sensitivity list. Now, we have,

always @ (A or B) begin
   if (A == 1'b1 & B == 1'b1) begin
       Y = 1'b0
       Y = 1'b1; 

The condition for the NAND gate is that if both the inputs are high, then the output is low, else in every other condition that has to be high.  if (A == 1'b1 & B == 1'b1) states that if both A and B are 1, then Y has to be 0, else 1.

Here is the full Verilog code for the NAND gate using behavioral modeling:

module NAND_2_behavioral (output reg Y, input A, B);
always @ (A or B) begin
    if (A == 1'b1 & B == 1'b1) begin
        Y = 1'b0;
        Y = 1'b1; 

RTL schematic of the NAND gate

Have a look at the schematic.

Testbench of the NAND gate using Verilog

The file to be included and the name of the module changes, but the basic structure of the testbench remains the same in all the three modeling styles. We have an interesting guide on writing a testbench in Verilog that you might find helpful too.

`include "NAND_2_behavioral.v"
module NAND_2_behavioral_tb;
reg A, B;
wire Y;
NAND_2_behavioral Indtance0 (Y, A, B);
initial begin
    A = 0; B = 0;
 #1 A = 0; B = 1;
 #1 A = 1; B = 0;
 #1 A = 1; B = 1;   
initial begin
    $monitor ("%t | A = %d| B = %d| Y = %d", $time, A, B, Y);

Simulation Waveform

Correctly depicting that whenever both the inputs are high, the output is low else the output is high.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.