View Course Path

Verilog Code for Half and Full Subtractor using Structural Modeling

Logic gates can be used for mathematical calculation and comparison. Half adder, full adder, half subtractor, full subtractor, multipliers, adder- subtractors are some very well defined combinational logic circuits that perform basic addition, subtraction, division, and multiplication. These circuits can be modeled or can be implemented in any hardware descriptive language.

 After reading this post, you’ll be able to

  1. Understand structural modeling.
  2. Become familiar with the half-subtractor and full-subtractor circuits.
  3. Design Half Subtractor and Full Subtractor in Verilog HDL.
  4. Simulate the circuits and generate RTL schematics.

Understanding the subtractor circuits

A subtractor circuit is a digital combinational circuit that is responsible for the subtraction of two or three-bit numbers. There are two different types of subtractors.

  1. Half- subtractor: subtracts two numbers and generates a difference output and a borrow output.
  2. Full- subtractor: performs subtraction of two bits, one is minuend, and the other is subtrahend, taking into account borrow of the previous adjacent lower minuend bit.

Here’s a detailed explanation of the half subtractor and full subtractor circuits if you would like to acquaint yourself with their working.

The general structure of the Verilog Code

The code structure of Verilog is as follows:

  1. Module Declaration
  2. Input-Output Delecaration
  3. Sensitivity list
  4. Procedural/ assignment/ continuous statements
  5. Ending module
 module module_name(port_list);

      declarations;
      statements;

endmodule

* Note: module and endmodule are the keywords defined in Verilog IEEE 1134.

Verilog supports three abstractions of modeling:

  1. Behavioral modeling
  2. Dataflow modeling
  3. Structural Modeling

In this post, we will be writing the Verilog code for the half subtractor and full subtractor using structural modeling. This type of modeling gives an idea about the actual elemental circuit involved in the system. It defines a circuit by explicitly showing how to construct it using logic gates, their predefined modules, and the connections between them

The Half-subtractor circuit

Let’s begin.

For the half- subtractor, suppose we have to subtract two numbers, say A and B, minuend and subtrahend respectively. So these will be the inputs to the half – subtractor circuit and the output generated will be a difference bit Diff and a borrow bit Borrow. Since we have two input variables, the maximum number of possible inputs can be calculated by using 2^n, where n is the number of inputs.

So we have 2^2 = 4 combinations for A and B i. e.  00  01  10  11.

Check out its circuit diagram.

Logic Diagram of Half- Subtractor

Logic Diagram for Half Subtractor Circuit
Logic Diagram for Half Subtractor Circuit

Verilog Code for Half Subtractor

To write the Verilog code, first, we need to analyze the logic diagram of half- subtractor. Especially when we are considering structural modeling. We can see three logic gates being used in the circuit. An XOR gate, an AND gate, and a NOT gate. So we’ll structurize these particular modules. Each module will define the functionality of one of the three logic gates using the assign statement.

  • The first module is for the XOR gate. The first two arguments in bracket a1, b1 are the input to the XOR gate, and the third one is output c1. After i/o declaration, it’s expression is assigned to the output variable c1 using assign statement.
  • Point to be noted here, the LHS entity of the assign statement must be a wire or vector net; it can’t be a register.

 

module xor_gate(a1, b1, c1);

input a1, b1;

output c1;

assign c1 = a1 ^ b1;

endmodule

 

  • Similarly, we will do the same for the rest of the two gates.

 

//NOT gate
module not_gate(a3, b3);

input a3;

output b3;

assign b3 = ~ a3;

endmodule
//AND gate
module and_gate(a2, b2, c2);

input a2, b2; 

output c2; 

assign c2 = a2 & b2; 

endmodule

 

  • After defining three modules, each for NOT, AND, XOR gate, we need to specify a module for HALF SUBTRACTOR which will contain module instantiation of these individual modules.
  • Module instantiation consists of module_name followed by instance_name and port_association_list. This module instance is useful if we want to call the same module several times in the same program.
  • One additional thing is done here; a temporary wire X is declared. This is due to the presence of an intermediate signal which is emerging from the output of NOT gate. u1, u2, u3 are the name of the instances for XOR, AND, and NOT gates, respectively.

 

module half_subtractor(a, b, difference, borrow);

input a, b;

output difference, borrow;

wire x;

xor_gate u1(a, b, difference);

and_gate u2(x, b, borrow);

not_gate u3(a, x);

endmodule

 

The final Verilog code for the half- subtractor is:

 

module xor_gate(a1, b1, c1);

input a1, b1;

output c1;

assign c1 = a1 ^ b1;

endmodule




module and_gate(a2, b2, c2);

input a2, b2;

output c2;

assign c2 = a2 & b2;

endmodule




module not_gate(a3, b3);

input a3;

output b3;

assign b3 = ~ a3;

endmodule




module half_subtractor(a, b, difference, borrow);

input a, b;

output difference, borrow;

wire x;

xor_gate u1(a, b, difference);

and_gate u2(x, b, borrow);

not_gate u3(a, x);

endmodule

 

Testbench for Half Subtractor

A simple testbench will instantiate a Unit Under Test (UUT) and drive the inputs. Your testbench should be able to test all the possible input conditions across every corner of your project. A good testbench should be self-checking in nature. It should generate input and automatically compare them with the expected results with that of the actual observations.

  • For writing the testbench, we’ll first add the timescale directive. Now, what are directives in Verilog? There are compiler directives, which are instructions to the Verilog compiler. They start with a grave accent ` and do not end with a semicolon.
  • Timescale directive is used for specifying the unit of time used in further modules and the time resolution. The time resolution is the precision factor that determines the degree of accuracy of the time unit in the modules.
  • In the code below, the reference time is one nanosecond, and time resolution is one picosecond.
`timescale reference_time_unit/time_resolution

 

  • Next is the reg and wire declaration. The register(reg) type holds the value until a next value is being driven by the clock pulse onto it and is always under initial or always block. It is used to apply a stimulus to the input. Hence A for first input and B for second input are declared as registers.
  • Wires(wire) are declared for the variables which are passive in nature. Their values don’t change, and they can’t be assigned inside always and initial block. Hence difference and borrow variables are declared as wires.

 

module top;
 reg  A, B;

 wire Difference, Borrow;

 

  • Next is the module UUT instantiation. The test bench applies stimulus to the Device Under Test DUT. To do this, the DUT must be instantiated under the testbench. The syntax for instantiation is given below. Port mapping is the linking of testbench’s modules with that of the design modules.

 

name_of_module name_of_instance(port_map)

 

  • Then comes the initialize block with keyword initial. This block of statements will give the stimulus to the input variables. Here, value 0 is given to the input signals at the very first time and the code gets finished executing after a delay of 100 ns; $finish statement is used for stopping the simulation after a specified delay.

 

initial     

begin       

A=0;       

B=0;       

#100 $finish;     

end

 

  • We can also consider system tasks for dumping the variables incorporated. $dumpfile and $dumpvars are used in this code.
  • $dumpfile is used to dump the changes in the values of net and registers in a VCD file (value change dump file). This particular attribute dumps all the changes that take place inside the variables (used in the program) throughout the simulation of the code. The syntax for $dumpfile is:

 

$dumpfile("name_of_the _file.vcd");

 

  • $dumpvars is used to specify which variables should be dumped in the filename specified by the argument. The simplest way to use it is without any arguments.
  • Now, it depends on the user whether he wants to display the simulation result on the TCL console or not. There are three ways to display the change in variable/ signal.
  1. $display: shows immediate values of signals.
  2. $monitor: displays the value of the signal whenever its value changes.
  3. $strobe: displays the value of the signal at the end of the current time.
  • I have used $monitor since our output changes on the change in the input value. It is always executed inside an always block. The sensitivity list contains the ports to which the following statements are sensitive. In most of the cases, input variables constitute the sensitivity list.

 

always @(A or B)      

  $monitor("At TIME(in ns)=%t, A=%d B=%d Difference = %d Borrow = %d", $time, A, B, Difference, Borrow);

 

The final testbench code for half subtractor:

 

`timescale 1ns / 1ps
module top;

 reg  A, B;

 wire Difference, Borrow; 

  half_subtractor instantiation(.a(A), .b(B), .difference(Difference), .borrow(Borrow));

  initial

    begin

      $dumpfile("xyz.vcd");

      $dumpvars;

      A=0;

      B=0;

      #100 $finish;

    end

always #10 A=~A;

always #5 B=~B;

     always @(A or B)

     $monitor("At TIME(in ns)=%t, A=%d B=%d Difference = %d Borrow = %d", $time, A, B, Difference, Borrow);  

endmodule

 

RTL Hardware Schematic for Half Subtractor

The RTL schematic is a design abstraction which models a synchronous digital circuit in terms of the flow of digital signals. Click on the elaborated design under RTL analysis, and you’ll get the schematic as shown below.

RTL Schematic for Half Adder Circuit
RTL Schematic for Half Subtractor Circuit

 

Simulation Waveform

I am using VIVADO 2019.1 software for the verification and testing of these circuits. This software gives us the simulation waveform, the RTL schematic, and has the console output for the user interface output. Once you’ve coded the logic diagram, you have to simulate both the design code as well as the testbench code.

The following window will appear, provided no errors or critical warning shows up after compilation. You can add/ move/ remove the signals/ variables according to your needs. You can cross-check this waveform with the truth table. This is executed till 100 ns, which is the delay for $finish statement.

Simulation waveform Half Subtractor
Simulation Waveform Output for Half Subtractor

 

TCL Console Output

Since we have used $monitor statement in testbench, the output console shows each clock transition and time control event change.

Console output
TCL Console Output for Half Subtractor Circuit

The Full-subtractor circuit

The full subtractor is one of the essential combinational logic circuits. It subtracts two one-bit numbers; one is minuend, other is subtrahend along with the borrow of the previous minuend bit. 

Logic Diagram of Full- Subtractor

Logic Diagram of Full Subtractor
Logic Diagram of Full Subtractor using two Half Subtractor Circuits

Can you notice the two half-subtractors in the above circuit?

full subtractor using two half subtractors
How about now?

As you can see from the above diagram, the full subtractor contains two individual half subtractor circuits, with the outputs of AND gate ORed together for the final borrow out Bout. Although there exists a simple logic diagram, in this article, we’re interested in using half subtractor for implementation of full subtractor.

Verilog Code for Full Subtractor using Half Subtractor:

For the coding part, as said earlier, we need to take a look at the logic diagram for the structural style of modeling. The logic diagram includes an AND gate and two half subtractor circuits, which are further an OR, XOR, AND, and NOT gate combination. Since in structural modeling, we define different modules for each basic elemental structure, we will start defining modules for each gate first.

  • This module is for the OR gate.
  • INPUT: a0, b0 OUTPUT: c0
module or_gate(a0, b0, c0);

input a0, b0;

output c0;

assign c0 = a0 | b0;

endmodule

 

We’ll repeat for the XOR, AND, and NOT gate.

 

module xor_gate(a1, b1, c1);

input a1, b1;

output c1;

assign c1 = a1 ^ b1;

endmodule
module and_gate(a2, b2, c2);

input a2, b2;

output c2;

assign c2 = a2 & b2;

endmodule
module not_gate(a3, b3);

input a3;

output b3;

assign b3 = ~ a3;

endmodule

Finally, we will combine these gate specific modules into a single module. For that, we will use module instantiation. Now instantiation is used when we want to repeat a particular function/ module for different sets of input. We’ll first construct a half subtractor, as discussed before, and then will use that module of half subtractor for implementing a full subtractor.

module half_subtractor(a4, b4, c4, d4); // module declaration

input a4, b4; // input variable

output c4, d4; // output variable

wire x; // temporary variable

xor_gate u1(a4, b4, c4); // XOR gate instance u1; a4, b4 will act as input and c4 will be the output

and_gate u2(x, b4, d4); // AND gate instance u2; x, b4 will act as input and d4 will be the output

not_gate u3(a4, x); // NOT gate instance u3; a4 will act as input and x will be the output

endmodule

 

Using this half_subtractor module for implementing a full subtractor, we need the OR gate for combining the outputs for the Bout variable.

 

module full_subtractor(A, B, Bin, D, Bout); // port declaration

input A, B, Bin; // input ports

output D, Bout; // output ports

wire p, q, r; // for intermediate signals, we use wire data type for temporary variables

half_subtractor u4(A, B, p, q); // half_subtractor instance u4; A and B will act as input and p, q will be the intermediate outputs

half_subtractor u5(p, Bin, D, r); // half_subtractor instance u5; wire p and Bin as input, D as final output and r as temporary output

or_gate u6(q, r, Bout); // or_gate instance u6; wire q and r as intermediate input and Bout as the final output

endmodule

 

The full Verilog code for the full-subtractor is:

 

module or_gate(a0, b0, c0);

input a0, b0;

output c0;

assign c0 = a0 | b0;

endmodule


module xor_gate(a1, b1, c1);

input a1, b1;

output c1;

assign c1 = a1 ^ b1;

endmodule


module and_gate(a2, b2, c2);

input a2, b2;

output c2;

assign c2 = a2 & b2;

endmodule


module not_gate(a3, b3);

input a3;

output b3;

assign b3 = ~ a3;

endmodule


module half_subtractor(a4, b4, c4, d4);

input a4, b4;

output c4, d4;

wire x;

xor_gate u1(a4, b4, c4);

and_gate u2(x, b4, d4);

not_gate u3(a4, x);

endmodule


module full_subtractor(A, B, Bin, D, Bout);

input A, B, Bin;

output D, Bout;

wire p, q, r;

half_subtractor u4(A, B, p, q);

half_subtractor u5(p, Bin, D, r);

or_gate u6(q, r, Bout);

endmodule

 

Testbench Code for Full Subtractor

The structure of the Verilog code for the testbench of full subtractor is almost the same as that for half subtractor. The only difference that gets showed up here is the change in the number of input and output ports.

 

`timescale 1ns / 1ps

module top;

 reg a, b, bin;

 wire d, bout; 


 full_subtractor instantiation(.A(a), .B(b), .Bin(bin), .D(d), .Bout(bout));

 initial

 begin

   $dumpfile("xyz.vcd");

   $dumpvars;

   a=0; 

   b=0; 

   bin=0;

   #100 $finish;

 end


always #40 a=~a;

always #20 b=~b;

always #10 bin=~bin;

 always @(a or b or bin)

   $monitor("At TIME(in ns)=%t, A=%d B=%d Bin=%d D = %d Bout = %d", $time, a, b, bin, d, bout);

endmodule

 

RTL Hardware Schematic for Full Subtractor using Half Subtractor

The hardware schematic for half subtractor is shown below:

RTL Schematic of Full Subractor Circuit
RTL Schematic of Full Subtractor Circuit

 

Simulation Waveform

The simulation window opens up like the image below. We can append the signals according to our convenience. This waveform should verify the truth-table. This is executed till 100 ns, which is the delay for $finish statement.

Simulation waveform Full Subtractor Circuit
Simulation Waveform Full Subtractor Circuit

TCL Console Output

The final output on the TCL screen looks like this. We are looking at the transition of variables after every change in the input due to the $monitor function.

TCL Console Output for Full Subtractor Circuit
TCL Console Output for Full Subtractor Circuit

I hope this post was sufficient for you to fully grasp the process of writing the Verilog code for half and full subtractors. If you have any queries regarding any of the concepts we saw above, let us know in the comments section.

Leave a Reply

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