From HacDC Wiki
Jump to: navigation, search
Digital Design and FPPGA Workshop
William Gibb (teachmeFPGA@gmail.com)
Week 3 - Introduction to Combinatorial Verilog
Full adder and ALU exercise

For our exercises this week, we'll be working with a few different modeling
styles, focused around a 4-bit full adder.  We'll be working with a model
where the 1 bit full adder circuit is explicitly defined, and break that down
into half adders, behavioral and structural models.

The full adder circuit can be constructed from 1-bit half adder circuits.
The truth table for the half adder is as follows.
A B | S Cout
0 0 | 0 0
0 1 | 1 0
1 0 | 1 0
1 1 | 0 1
S = A xor B
Cout = A and B

Two of these cells, plus an additional OR gate, can be used to implement a
full adder.  The S output of the first half adder cell is connected to the
input of the second cell, as is the Cin.  The S output of the second cell is
the sum S.  The Cout is computed by ORing the Cout of both cells.

Likewise, the truth table for the Full Adder is as follows, for reference.
Cin A B | S Cout
 0 0 0 | 0 0
 0 0 1 | 1 0
 0 1 0 | 1 0
 0 1 1 | 0 1
 1 0 0 | 1 0
 1 0 1 | 0 1
 1 1 0 | 0 1
 1 1 1 | 1 1

To do
0) Copy the full adder codes below to files in your working directory.
Be sure to copy a makefile to the directory.
0.a) mkdir a new directory for this example.  name it something useful, such as "adder_exercise"
0.b) create a new file for each verilog module, in the form modulename.v where module name is the name of the module. We'll need full_adder.v, full_adder_4bit.v, add test_adder.v.
0.c) copy and paste the code for each module into the file.  be sure that anything in the file that isn't verilog code is commented out.
0.d) if your in the vm, type copymake.sh into the command line to copy a makefile into your working directory

1) Change the values of the makefile to test the full adder.
1.a) Change the line SRC and TESTBENCH to include only the .v files created earlier.  if you need to add a new source to a project, add it to your makefile as well.

2) Verify the full adder works by simulation.

3) Write a description of the half adder, at gate level

4) Write a full adder description, using the half adders you wrote in step
3.  Give this full adder module a different module name than the provided

5) Write a 4 bit full adder module using the new 1 bit full adder.  Be sure to
give it a module name different than the name of the provided module.

6) Take a break if you feel needed...or keep on coding

7) Write a 4 bit full adder module using the new 1 bit full adder from step 6.
Be sure to give it a module name different than the name of the provided

8) One last adder - Write a 4 bit behavioral adder module.  Use 4 bit inputs
and outputs in this module.  Again, give it a new name.

9) Add the new models to the makefile.
       -half adder
       -full adder from half adders
       -4 bit full adder from step 5
       -full adder from step 6
       -4 bit full adder from step 7
       -4 bit full adder from step 8

9) Modify the testbench to instantiate the new modules.  You'll need to
instantiate the 4 bit full adder from step 5, step 7 and step 8.  This is why
they all need to have unique module names.  You'll need to add three 4 bit
wires for connecting the sum output of these new adders, as well as
three 1 bit wires for connecting the cout output of these new adders.

10) Simulate and verify all of the models behave the same way.

Once this is done, and you have simulated the adders and shown that they all
behave the same way, you'll have built the following types of modules
- Structural Model (step 5)
- Behavioral & Hierarchical Model (step 7)
- Behavioral Model (step 8)

After that is done, people are encouraged to take the ALU presented in the
slides and are encouraged to expand the functionality of the ALU to 8 total
functions.  The functions that should be added to the ALU are as follows:
- r = ~A (R equals the complement of A)
- r = A ^ B (R equals A xor B)
- r = A << B (R equals A left shifted B bits)

After this functionality has been added to the ALU model, the test bench used
for the 4 bit full adder can be modified to simulate the ALU.
Steps to do this
0) Copy the test bench and makefile to your working directory
1) Add/remove wires and regs as needed
2) Replace the DUT with the ALU
3) Expand the inputs in order to test all of the ALU functions.

Full Adder Code
module full_adder(a, b, cin, sum, cout);
       input    a, b, cin;
       output  sum, cout;

       reg sum, cout;

       always @(a or b or cin)
               sum = a ^ b ^ cin;
               cout= (a & b) | (a & cin) | (b & cin);
module full_adder_4bit (a, b, cin, sum, cout);
       input[3:0]    a, b;
       input           cin;
       output [3:0] sum;
       output cout;

       wire            c1, c2, c3;

       // instantiate 1-bit adders
       full_adder FA0(a[0],b[0], cin, sum[0], c1);
       full_adder FA1(a[1],b[1], c1, sum[1], c2);
       full_adder FA2(a[2],b[2], c2, sum[2], c3);
       full_adder FA3(a[3],b[3], c3, sum[3], cout);

module test_adder;
       reg[3:0]  a, b;
       reg cin;
       wire [3:0] sum;
       wire         cout;

       full_adder_4bit dut(a, b, cin,
       sum, cout);

               a = 4'b0000;
               b = 4'b0000;
               cin= 1'b0;
               a = 4'b0101;
               b = 4'b1010;
               // sum = 1111, cout= 0
               a = 4'b1111;
               b = 4'b0001;
               // sum = 0000, cout= 1
               #50;a = 4'b0000;
               b = 4'b1111;
               cin= 1'b1;
               // sum = 0000, cout= 1
               a = 4'b0110;
               b = 4'b0001;
               // sum = 1000, cout= 0
       end // initial begin

                $dumpfile ("waves.lxt");

endmodule// test_adder
ALU Code
module mul16(i0,i1,prod);
       input [15:0] i0,i1;
       output [31:0] prod;
       // this is a magnitude multiplier
       // signed arithmetic later
       assign prod = i0 * i1;
module add32(i0,i1,sum);
       input [31:0] i0,i1;
       output [31:0] sum;
       assign sum = i0 + i1;
module sub32(i0,i1,diff);
       input [31:0] i0,i1;
       output [31:0] diff;
       assign diff = i0 - i1;
module mux32two(i0,i1,sel,out);
       input [31:0] i0,i1;
       input sel;
       output [31:0] out;

       assign out = sel? i1 : i0;
module mux32three(i0,i1,i2,sel,out);
       input [31:0] i0,i1,i2;
       input [1:0] sel;
       output [31:0] out;

       reg[31:0] out;

       always @ (i0 or i1 or i2 or sel)
               case (sel)
               2’b00: out = i0;
               2’b01: out = i1;
               2’b10: out = i2;
               default: out = 32’bx;
module alu(a, b, f, r);
       input [31:0] a, b;
       input [2:0] f;
       output [31:0] r;

       // wire declarations
       wire [31:0] addmux_out, submux_out;
       wire [31:0] add_out, sub_out, mul_out;

       // module declarations
       mux32two   adder_mux(b, 32'd1, f[0], addmux_out);
       mux32two   sub_mux(b, 32'd1, f[0], submux_out);
       add32      our_adder(a, addmux_out, add_out);
       sub32      our_subtracter(a, submux_out, sub_out);
       mul16      our_multiplier(a[15:0], b[15:0], mul_out);
       mux32three output_mux(add_out, sub_out, mul_out, f[2:1], r);

Personal tools