Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Full Adder using Verilog HDL

Status
Not open for further replies.

David83

Advanced Member level 1
Advanced Member level 1
Joined
Jan 21, 2011
Messages
410
Helped
45
Reputation
92
Reaction score
45
Trophy points
1,308
Activity points
3,639
Hello all,

I was trying to write a synthesizable RTL code for a 3 1-bit full adder using Verilog, where the inputs are A, B, and C_in. In a full adder there are 2 1-bit outputs: the sum S and the carry out C_out, where S=A^B^C_in, and C_out=AB+BC+AC, where ^ the XOR logic operation and + is the logic OR operation. I wrote the following design (structural description) and stimulus blocks:

Code:
\\ Design Block 
module full_adder(A,B,C_in,S,C_out);

input A,B,C_in;
output S,C_out;

wire f1,f2,f3;

xor(S,A,B,C_in);
and(f1,A,B);
and(f2,B,C_in);
and(f3,A,C_in);
or(C_out,f1,f2,f3);

endmodule

\\Stimulus Block


`timescale 1ns/1ns
module full_adder_tb;

reg clk;
reg [2:0] count;

wire S,C;

initial
begin
clk=1;
count=3'b111;
end

always #5 clk=~clk;

always @(posedge clk)
count=count+1;

full_adder MUT(count[2],count[1],count[0],S,C);

endmodule

The simulation runs fine. The schematic diagram is attached and generated suing Vivado. I have a couple of questions:

1- Can I enhance my code? I guess I can learn some tricks to make more efficient.
2- what is `timescale 1ns/1ns? I understand that each time unit is 1ns, but what is the other 1ns in 1ns/1ns? I saw this in a tutorial, and I've kept using it.
3- Why Vivado didn't generate 1 3-input XOR gate, but instead generated 2 2-input XOR gate? isn't it practical?

Thanks
 

Attachments

  • FullAdderschematic.pdf
    17.3 KB · Views: 192
Last edited:

1.) for FPGA's, prefer using just A + B. Also prefer using named connections vs positional connections for instances.
2.) the second number is the precision that you can use for delays, etc...
3.) for the elaboration/synthesis, it doesn't really matter. After implementation it will be a single LUT (or something special, see below). A single LUT6 can replace several gates in a design.

In this case, there might be a difference between "+" and creating the full adders. FPGA's now have "Fast-Carry Chains", which are special pieces of logic in addition to the LUTs. The dedicated routing for the carry chains allow much higher performance than using general-purpose routing.
 

3- Why Vivado didn't generate 1 3-input XOR gate, but instead generated 2 2-input XOR gate? isn't it practical?
Don't confuse RTL schematic with implementation.
 

1.) for FPGA's, prefer using just A + B. Also prefer using named connections vs positional connections for instances.
2.) the second number is the precision that you can use for delays, etc...
3.) for the elaboration/synthesis, it doesn't really matter. After implementation it will be a single LUT (or something special, see below). A single LUT6 can replace several gates in a design.

In this case, there might be a difference between "+" and creating the full adders. FPGA's now have "Fast-Carry Chains", which are special pieces of logic in addition to the LUTs. The dedicated routing for the carry chains allow much higher performance than using general-purpose routing.

1- I read in a book called Digital Logic Design using Verilog (I feel it's not my best choice. any suggestion of a good book to learn Digital logic design and verilog at the same time), it suggests using the following RTL statement

Code:
assign {C_out,Sum}=A+B+C_in;

But it didn't explain how this assignment works. In particular, why did they place {C_out,Sum} in this order, and not {Sum,C_out}? Is it a built in arrangement in Verilog for binary addition?

About the seconf part of your reply, what did you mean by: "Also prefer using named connections vs positional connections for instances"? Could you elaborate more? As I stated elsewhere, I'm still new to this field, and I'm not familiar with everything, yet.

2- Could you elaborate more on this point, too? I know for example that # is used to specify the number of time units, which is 1 ns in my example, but how to use the other 1 ns? Also, is it better to increase the precision as

Code:
`timescale 1ns/1ps;

3- I get that, but I was wondering since Verilog allows xor(Out,in1,in2,...), which is to say, N-input XOR gate for n>=2, why Vivado didn't follow the same rule?

Thanks

- - - Updated - - -

Don't confuse RTL schematic with implementation.

To my understanding, logic gates are the result of synthesis of a synthesizable RTL code, which is used (I mean the schematic) for implementation. So, you map your schematic to implementation. Am I right?
 

Code:
assign {C_out,Sum}=A+B+C_in;
it is concatenation of C_out with Sum. C_out is on the left because it is the carry out of the add operations and therefore resides in the MSB position (in this case bit-1 of [1:0] result bits)

About the seconf part of your reply, what did you mean by: "Also prefer using named connections vs positional connections for instances"? Could you elaborate more? As I stated elsewhere, I'm still new to this field, and I'm not familiar with everything, yet.
Unfortunately the built-in gate primitives don't have port names, they are defined by their ordering...
Code:
and(f1,A,B);
but if they did you would explicitly define the port connections.
Code:
my_and(.O(f1), .I1(A), .I2(B));
now each port is explicitly using a named association, so you could reorder and the connectivity is identical..
Code:
my_and(.I2(B), .O(f1), .I1(A));

2- Could you elaborate more on this point, too? I know for example that # is used to specify the number of time units, which is 1 ns in my example, but how to use the other 1 ns? Also, is it better to increase the precision as

Code:
`timescale 1ns/1ps;
Look at this page

3- I get that, but I was wondering since Verilog allows xor(Out,in1,in2,...), which is to say, N-input XOR gate for n>=2, why Vivado didn't follow the same rule?
because it's probably easier to deal with 2-input XOR than a multi-bit XOR gate as all you need to do in parsing is pick up each new input bit until you run out of inputs. I imagine there are probably no gates that have more than 2-input versions in the library as you can daisy chain the gates to make the multi-input gate.

To my understanding, logic gates are the result of synthesis of a synthesizable RTL code, which is used (I mean the schematic) for implementation. So, you map your schematic to implementation. Am I right?
the schematic is not the output of a synthesis tool, the synthesis tool generates a bunch of logic equations that are reduced using boolean transformations which are then mapped into the technology library. For a Xilinx design the technology schematic will no longer look anything like gates, but will be things like LUTs and FF primitives.
 
OK, 1 and 2 are now clear. But about point 3: the above expressions for the sum and carry out for the full adder were minimized using Boolean algebra prior to writing the code. So, how more optimization can be done after synthesis?

Thanks
 

I guess not much.
It also depends on the logic implemented in the FPGA.
Vivado has understanding of the device it is compiling the code for. So it probably has taken the best optimization already.
And remember; synthesis tools are better at optimizing than you are. People at e.q. Synopsys are getting paid to think of / out smart you at this ;-)

My question to you :
Besides that it is a nice scholary example, why would you like to implement an adder like this in an FPGA ?
Like vGoodTimes said, just write A + B.
Vivado will replace this expression with an appropriate IP module for your specific FPGA device.


Read some technical notes on your chosen FPGA and how they are constructed. (LUTs, CLBs, LAB's etc)
Or read "The Design Warrior's Guide to FPGAs: Devices, Tools and Flows" by Clive Maxfield.
 

Are you saying that if I gave Vivado a Boolean expression that is not optimal, say S=A'B'C_in+A'BC'_in+AB'C'_in+ABC_in then the synthesis tool will optimize it? I thought Synthesis tool will take the RTL code as it is and construct the netlist from it.

When I used the + operation I got the attached schematic diagram. I guess in this case Vivado treats a full adder as a cascade of two half adders, where half adders are considered as black boxes.

Actually, I'm using Vivado just out of curiosity. I use ModelSim for simulation and functional verification, but sometimes I use Vivado for simulation as well and look at the schematic diagrams it produces.

Thanks for the suggested book.
 

Attachments

  • FullAdderschematic.pdf
    9.8 KB · Views: 115

I mean; do not worry about boolean expressions.
Just write behavioral code. The tool will indeed optimize your RTL in the best way it can.
 
When I used the + operation I got the attached schematic diagram. I guess in this case Vivado treats a full adder as a cascade of two half adders, where half adders are considered as black boxes.

Actually, I'm using Vivado just out of curiosity. I use ModelSim for simulation and functional verification, but sometimes I use Vivado for simulation as well and look at the schematic diagrams it produces.

As FvM has mentioned earlier, for the time, keep simulation + functional verification separate from synthesis.

ModelSim is used for simulation/functional-verification (I am not sure if it can produce you a schematic; I don't use it).

As for Vivado, it is a complete suite. It contains simulation + synthesis + PnR engines, which helps you to translate a design from RTL to bit-stream. Many engineers use a separate simulation s/w (ModelSim/VCS), separate synth s/w (Synopsys Synplify) and just do the final PnR with Vivado. It depends on the amount of $ you have at your disposal.
 
Actually, I'm using Vivado just out of curiosity. I use ModelSim for simulation and functional verification, but sometimes I use Vivado for simulation as well and look at the schematic diagrams it produces.

Remember that simulation and synthesis are two very different things.
Simulation of RTL code (which is what modelsim and Vivado simulation are doing) will literally simulate exactly what you wrote - for better or for worse. It wont tell you of design flaws in your code that wont map to an FPGA for example.

Synthesis will produce a netlist that should match your intended functionality - as best it can. If you follow the required templates then the logic should match the code exactly at a functional level. It doesnt just do logic optimisation, it will do other things like:
1. Register merging (two registers have exactly the same output - why have 2 when you only need 1)
2. Ram merging when it determines your two rams use the same addres - why not merge the data pins into a single wide bus and use a single ram instead of two.
3. Register retiming - move registers around the logic to improve the timing.
 
As FvM has mentioned earlier, for the time, keep simulation + functional verification separate from synthesis.

ModelSim is used for simulation/functional-verification (I am not sure if it can produce you a schematic; I don't use it).

As for Vivado, it is a complete suite. It contains simulation + synthesis + PnR engines, which helps you to translate a design from RTL to bit-stream. Many engineers use a separate simulation s/w (ModelSim/VCS), separate synth s/w (Synopsys Synplify) and just do the final PnR with Vivado. It depends on the amount of $ you have at your disposal.

Thanks. Could you please tell me a little more about Synonsys? Is it used only for synthesis? I read in many jobs descriptions that this program is an asset in hardware design but I'm not sure why.
 

Could you please tell me a little more about Synonsys? Is it used only for synthesis?

Synopsys is the name of an EDA software vendor. They have various EDA tools catering to different purpose.
None of them are free and Synopsys tools are very expensive.
Synopsys Synplify Pro and Synopsys Synplify Premier® are the ones specific for FPGA synthesis.

If your are in a self-learning mode, just forget about using Synopsys tools. Use whatever is provided by Xilinx/Altear/Microsemi/etc.
 
I was reading this **broken link removed**, which implements a full adder. The design block and the stimulus block are shown below:

Code:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// SingleStage.v
//
//////////////////////////////////////////////////////////////////////////////////
module singleStage (
         input a,
         input b,
         input cin,
         output s,
         output cout );
         
        assign {cout,s} = a + b + cin;

endmodule

`timescale 1ns / 1ps

////////////////////////////////////////////////////////////////////////////////
// Module Name:   Y:/Desktop/Xilinx Stuff/Projects/NBitAdder/test.v
// 
////////////////////////////////////////////////////////////////////////////////

module test;

        // Inputs
        reg a;
        reg b;
        reg cin;

        // Outputs
        wire s;
        wire cout;

        integer i;
        
        // Instantiate the Unit Under Test (UUT)
        SingleStage uut (
                .a(a), 
                .b(b), 
                .cin(cin), 
                .s(s), 
                .cout(cout)
        );

        initial begin
                // Initialize Inputs
                a = 0;
                b = 0;
                cin = 0;
        end

        always @ ( a, b, cin )
                begin
       
                // generate truth table
                for ( i = 0; i < 8; i = i + 1 )
                        // every 10 ns set a, b, and cin to the binary rep. of i
                        #10 {a, b, cin} = i;
                        
                // stop 10ns after last change of inputs
                #10 $stop;
                end

      
endmodule

The first thing that caught my attention is that the instantiation happens before the final always block, as if any change in a, b, and cin in the final always block makes a new instantiation automatically. Is that right? If yes, how so?
 

no, it just changes the values of a, b, cin -- the inputs to the single instance of "singleStage".
 

no, it just changes the values of a, b, cin -- the inputs to the single instance of "singleStage".

Why? does it have anything to do with the way the ports of the instance are defined? If I used the following instantiation, would it work the same way?

Code:
SingleStage uut (
                a, 
                b, 
                c, 
                s, 
                cout
        );
 

An additional comment to your previous question.

The first thing that caught my attention is that the instantiation happens before the final always block, as if any change in a, b, and cin in the final always block makes a new instantiation automatically.

The order of continuous assignments, module instantiations and procedural blocks in a Verilog design is irrelevant. Statement order only matters inside a procedural block (e.g. always block) or subroutine.

You are still missing basic understanding how Verilog works, should have a text book or tutorial at hand when browsing arbitrary designs.
 

An additional comment to your previous question.



The order of continuous assignments, module instantiations and procedural blocks in a Verilog design is irrelevant. Statement order only matters inside a procedural block (e.g. always block) or subroutine.

You are still missing basic understanding how Verilog works, should have a text book or tutorial at hand when browsing arbitrary designs.

Right. I admit I lack that. Do you have any good suggestion as a book or tutorial? I'm using this book, but it doesn't explain everything in detail. It says the truth table of X system is given in Fig Y and the code in Fig Z and the schematic in Fig M.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top