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 in Verilog represents an electrical connection. It is internal, therefore not mentioned in the port list. Next,
and(Yd, A, B); not(Y, Yd); endmodule;
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); endmodule
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
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); endmodule
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)|
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 ..... end
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 end else Y = 1'b1; end
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; end else Y = 1'b1; end endmodule
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; end initial begin $monitor ("%t | A = %d| B = %d| Y = %d", $time, A, B, Y); $dumpfile("dump.vcd"); $dumpvars(); end endmodule
Correctly depicting that whenever both the inputs are high, the output is low else the output is high.