In this post, we will learn to write the Verilog code for the XNOR logic gate using the three modeling styles of Verilog, namely, Gate Level, Dataflow, and Behavioral modeling.

- In
**gate-level modeling**, the module is implemented in terms of concrete logic gates and interconnections between the gates. The designer must know the gate-level diagram of the design. - In
**dataflow level modeling**, a module is implemented by specifying how data flows between registers. - Whereas, in
**behavioral modeling**, the module’s high-level algorithm is implemented with little concern for the actual hardware.

In every model, we get the same results, but abstraction levels and approaches vary.

Let’s start with Gate Level modeling for the XNOR gate.

**Gate Level modeling**

Designing circuits using basic logic gates is known as gate-level modeling. As gates are the basic components of any logic diagram, gate primitives can be used to construct complex level logic like multiplexers, encoders. Gate level analysis also takes care of gate delays, which is not possible in a higher level of abstraction.

The process of automatically generating a gate-level model from either a dataflow or a behavioral model is called *Logic Synthesis*. We can create lower-level models from the higher-level models either manually or automatically. Verilog supports describing circuits even at the switch level (transistors). But that is more complex and requires more details; hence we restrict ourselves to the three popular styles of description.

Keep in mind that the primitives can be instantiated like modules except that they are predefined in Verilog and do not need a module definition.

**Logic Circuit of the XNOR**** gate**

The output of the XNOR gate is high if both the inputs are the same; otherwise, the output is low. An EXNOR gate is an equality detector. Here’s the logical representation of the XNOR gate.

**Verilog code for XNOR**** gate using gate-level modeling**

We begin the hardware description for the XNOR gate as follows:

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

We declare the module using the keyword module. `XNOR_2`

is the identifier here. The list in parenthesis contains input and output ports called the port list. As Verilog has primitives, we can then write:

xnor(Y, A, B); endmodule

XNOR is the operation performed on A, B, to get output Y. `endmodule`

terminates the module.

module XNOR_2(output Y, input A, B); xnor(Y, A, B); endmodule

**Data flow modeling**

Compared to gate-level modeling, dataflow modeling enables description which is more abstract.

**Dataflow modeling** describes the circuits by their function rather than by their gate structure. The module is implemented by specifying the way the data flows between registers. It’s a much easier method than gate-level modeling, which tends to become harder as with the circuit’s complexity.

Hence, dataflow modeling is a very important way of implementing the design. This style of design requires continuous assignment statements. The continuous assignments are created using the keyword `assign`

. Here’s a list of all the keywords in Verilog if you’d like to brush up your memory.

**Equation of the XNOR**** gate**

The boolean equation of an XNOR gate is Y = AB + A’B’.

**Verilog code for XNOR**** gate using dataflow modeling**

We would again start by declaring the module. The way it is done is

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

`module`

is a Verilog keyword, ` XNOR_2_data_flow`

is the identifier, ` (output Y, input A, B)`

is the port list. Then we have semicolon to end the statement.

Next is the assignment statement in data flow modeling.

assign Y = ~(A ^ B); endmodule

The `^`

operator performs the XOR operation on the inputs we provide. Then `~`

, the logic operator in Verilog for negation, compliments that value to spit out Y.

` endmodule`

is used to terminate the module.

You may look over the entire code in one piece here.

module XNOR_2_data_flow (output Y, input A, B); assign Y = ~(A ^ B); endmodule

**Behavioral Modeling**

Behavioral modeling is the highest level of abstraction in the Verilog HDL, whereas the other modeling techniques (like gate level, data flow level) are comparatively detailed.

Behavioral models in Verilog contain procedural statements, which control the simulation and manipulate variables of the data types. This level of abstraction simulates the behavior of the circuits without specifying the details. We are focussed on just the behavior of the design.

Most of the behavioral modeling is done using two important constructs: initial and always. All the other behavioral statements appear only inside these two structured procedure constructs. First of all, let’s have a look at the truth table.

**Truth Table for XNOR**** gate**

A |
B |
Y(A XNOR B) |

0 | 0 | 1 |

0 | 1 | 0 |

1 | 0 | 0 |

1 | 1 | 1 |

**Equation from the truth table**

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

Y = A.B + A’B’ or say Y = A XNOR B.

**Verilog code for XNOR**** gate using behavioral modeling**

Again, we begin by declaring module, setting up identifier as ` XNOR_2_behavioral`

, and the port list.

module XNOR_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 level, then we use `reg`

datatype in the output ports.

All assignments in an always block must be assigned to a reg variable – the reg variable may or may not actually represent a hardware register. If the `always`

block assigns a value to the reg variable for all possible executions, then the reg variable is not actually a hardware register. These types of data objects hold the value from one procedural assignment statement to the next and mean it holds its value over simulation data cycles. Then, we write,

always @ (A or B) begin.....end

An `always`

block is a behavioral block that contains the sensitivity list or the trigger list. `@`

is a part of the syntax, used before the sensitivity list. In Verilog, `begin`

embarks and `end`

concludes any block which contains more than one statement in itself.

You can also write the always statement ` always @ ( A, B)`

as ` @( *)`

. The ` @(*)`

construct creates a sensitivity list for all the signals in the always block.

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

The condition for the XNOR gate is that if both the inputs are the same, then the output is high, else in every other condition that has to be low.

if `(A == 1'b0 & B == 1'b0)`

states that if both A and B are 0, `Y = 1'b1;`

then Y has to be 1, else 0. `==`

is the symbol for testing logical equality, 1’b0 conveys that the number is of 1 bit in the binary number system, and its value is 0. Likewise, `&`

is the symbol for bitwise and operation.

Here is the full code:

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

**RTL schematic of ****XNOR**** gate**

**Testbench of the ****XNOR**** gate using Verilog**

The basic structure of the testbench remains the same in all the three modeling styles but, the file to be included and the name of the module changes.

`include "XNOR_2_behavioral.v" module XNOR_2_behavioral_tb; reg A, B;wire Y; XNOR_2_behavioral Instance0 (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

If you’re having trouble figuring out how to write testbenches, here’s a complete guide on Verilog testbenches.

**Simulation Waveform**

By observing the waveforms, we can derive that whenever both of the inputs A and B are the same, then the output is high, else in every other condition, the output is low. Thus, our code works!