Project 4: Arithmetic Logic Unit

B441/E315, Fall 2018                                                        Version 2018.8

Due Date: Sept 21, 2018

Overview

In this lab you will design and implement a complete 8-bit Arithmetic Logic Unit (ALU). You can use the same design procedure to expand to a functionally complete 16-, or 32-bit ALU.  You will also get to implement a basic D Flip Flop.  Flip Flops are used to add state to your digital systems.  

Background

ALU

You will build an ALU that takes two inputs, A and B, and produces three outputs, R, C and V.  Additionally, another input S will select which type of operation should be performed.  Your ALU should be capable of both Arithmetic and Logical operations.  

Arithmetic

Your ALU should be capable of both addition and subtraction.  Subtraction in the digital world can be implemented using addition and 2’s complement.

 

Thus, by inverting b and adding 1 (using a Carry-in), we can implement subtraction. Now that we are also doing subtraction, we have chosen to express the result as r rather than s  for sum.  

There are number of different ways to build adders.  In practice, it is typically easiest to use the built-in Verilog addition operator (+) or subtraction operator (-) and let the tools figure it out.  Unfortunately, Verilog’s build-in addition operator, by default, assumes an 8-bit addition results in an 8-bit sum.  However, we want the carry-out, so we need a 9-bit sum.   The solution is to prepend a 1-bit 0 to your 8-bit value to form a 9-bit value.  We can use the concatenation operator in Verilog to achieve this.  

            wire [7:0] a = 8’hff;

            wire [8:0] a9 = { 1’h0, a}; //concatenation of inputs

            wire [7:0] r;

            wire r9;

            {r9,r} = a9; //concatenation of outputs

Notice we’re also mashing the wire declaration and the assign-ment statement together into a single line.  

In addition, your ALU should be capable of several other helpful functions:

Transfer:                                  

Test:                                        

Logic

Your ALU should also be capable of 4 basic bitwise logic operations, AND, OR, NOT, and XOR.  These are computed to all bits of A and B.  

Operation Encoding

To select which operand your ALU should perform, you will need to read in a 4-bit “operation select” value ( here named s).  The mapping of s values to operations is given below.

s

Operation

Result

000

Addition

001

Subtraction

010

Transfer

011

Test

100

Bitwise AND

101

Bitwise OR

110

Bitwise XOR

111

Bitwise NOT

Carry

Recall that 8-bit unsigned addition can result in a 9-bit sum.  Therefore, the last bit should be returned in the c bit, short for Carry.  This bit must be 0 for transfer, test and all logic operations.  

Overflow

Additionally, signed arithmetic can result in overflows.  This can occur when adding two positive numbers, and receiving a negative result.  Or when adding two negative numbers resulting in a positive number.  This overflow condition should be captured in a v bit, short for oVerflow.  This bit must be correct for addition and subtraction and is undefined for all other operations.  If a signal is undefined, you can set it to whatever you want.  

There are two ways to calculate overflow.  The one discussed in class is using an XOR gate between the carry-out and the last internal carry bit of the addition/subtraction.  For an 8-bit addition, this would be .  Unfortunately, if you use the Verilog subtraction operator, -, you will not have access to .  

The second way is to look at both the inputs and the result.  If you construct a truth table for a[7], b[7], c[7], cout and result, it will look something like this:

a[7]

b[7]

c[7]

cout

result

0

0

0

0

0

0

0

1

0

1

0

1

0

0

1

0

1

1

1

0

1

0

0

0

1

1

0

1

1

0

1

1

0

1

0

1

1

1

1

1

Recall the v = c[7] ^ cout.  Which means it is true whenever c[7] and cout differ.  Notice, there are two cases above where c[7] and cout differ.  Those two cases are shown in red above.  The first case occurs when a[7] == 0, b[7] == 0, and result[7]==1.  This occurs when we are adding two positive numbers, and the result is negative.  The second case occurs when a[7] == 1, b[7] == 1, and result[7]==0. Now we are adding two negative numbers and getting a positive result. We can write a Verilog expression to capture these two cases:

 

wire oVerflowAdd = ~a[7] & ~b[7] & r[7] | a[7] & b[7] & ~r[7];

Recall with subtraction that.  Therefore, Verilog will implicitly flip the bits of b to form ~b. It will then perform {cout,result[7]} = a[7] + ~b[7] + c[7].  This is shown in a new truth table that looks like this:

a[7]

b[7]

~b[7]

c[7]

cout

result

0

0

1

0

0

1

0

0

1

1

1

0

0

1

0

0

0

0

0

1

0

1

0

1

1

0

1

0

1

0

1

0

1

1

1

1

1

1

0

0

0

1

1

1

0

1

1

0

Notice the two c[7] vs. cout discrepancies are now in different rows.  We can express that with the following Verilog expression.  

wire oVerflowSubtract = ~a[7] & b[7] & r[7] | a[7] & ~b[7] & ~r[7];

Verilog Case Statements and Always Blocks

Another more piece of information for this lab is the use of Verilog’s case statement.  Just like C, verilog lets you use case statements.  The syntax looks something like this:  

case(sel)

    2’b00: out = input[0]; //set out = input[0] when sel = 00

    2’b01: out = input[1]; //set out = input[1] when sel = 01

    2’b10: out = input[2];

    2’b11: out = input[3];

endcase

Unfortunately, you cannot use case statements with assign, it must be inside an always block.  There are many different ways to build always blocks in Verilog.  For now, we recommend the always @* block, which looks like this:

always @(*) // or just ‘always @*’

begin

    out = input[2];

end

The parenthesis on the first line defines the “sensitivity list”.  In this case, we’re using the wildcard “*” to mean “anything”.  Always blocks run all of the commands between begin/end every time something in the “sensitivity list” changes.  Here that means whenever anything changes, re-evaluate all the block’s assignments.  The above block is functionally equivalent to assign out = input[2]; 

If we put the always block and the case statement together, we get something that looks like this:

always @(*) // or just ‘always @*’

begin

    case(sel)

        2’b00: out = input[0];

        2’b01: out = input[1];

        2’b10: out = input[2];

        2’b11: out = input[3];

    endcase

end

This lets us set out to one of 4 values based on the value of sel (or select).  This is one way to build a 4-1 multiplexer.  We will cover more always blocks later, but for now stick with this one.  

Verilog D Flip Flop

One final piece is to building D Flip Flops in Verilog.  Here again we make use of an always block, but this one looks a little different.  

wire enable, D; //input signals

reg Q, Qbar; //must be reg to retain state

//D Flip Flop

always @(posedge enable)

begin

      Q    <=  D;

      Qbar <= ~D; //optional

end

Notice here that the sensitivity list only includes enable (not * like the last block).  It also includes posedge or “positive edge”.  This means only the positive (rising) edge of enable can cause signals inside the always block to change.  This is what makes this block into a flip flop.  As Q and Qbar can hold state, they must be declared as reg or register.  Also, notice the use of the Non-blocking assignment (<=).  We’ll discuss this more in class, but in general the rule is:

always @* blocks are for combinational logic and ONLY USE BLOCKING ASSIGNMENTS (=)

always @(posedge ...) blocks are for sequential logic and ONLY USE NON-BLOCKING ASSIGNMENTS (<=)

The tools will not check for this, and are more than happy to let you do whatever you want.  IT IS UP TO YOU NOT TO BREAK THE ABOVE RULES!

Assignment Description

8-bit ALU

Create a Verilog file named alu.v which defines a module as follows:

module alu(

   input   [7:0] a, //operand

   input   [7:0] b, //operand

   input   [2:0] s, //operation Select

   output  [7:0] r, //the Result value

   output        c,  //for unsigned Carry

   output        v  //for signed oVerflow

);

You task is to implement the ALU as per the specifications above.  

Top-Level

Your next task is to create a Verilog file named top.v defined as follows:

module top(

   input    [15:0] sw,  //operands a,b,s

   input           btnC, // middle button

   output   [15:0] led, //results c,v

);

This module should instantiate an ALU submodule and route the appropriate signals into/out of it as follows:

ALU Name

Top Name

a[7:0]

Q[7:0]

b[7:0]

sw[7:0]

s[2:0]

sw[10:8]

r[7:0]

led[7:0]

c

led[8]

v

led[9]

This module must also include an 8-bit register with the suggested name of Q.

reg [7:0] Q; //a state-holding register

Your module must  incorporate a D flip-flop that captures sw[7:0] and stores it to your register Q[7:0] when the btnC button is pressed.  This will capture the first input (a) for the ALU and store it, allowing you to reset sw[7:0] for the second input to the ALU (b).  

You will then need to reconfigure sw[7:0] for the second input, and set the select inputs to perform the appropriate ALU operation.  

It may be necessary to set all unused led values to 0 to placate the tools.  

Testbench

For this project, you only need to create two testbenches.  One testbench is for your ALU. It should be named alu_tb.v and should test each operation thoroughly, although it does not need to exhaustively test each input combination.  A second testbench is for your top level.  It should be named top_tb.v and should test the D flip flop circuit, and the connections to the ALU.  This testbench should not need to extensively test the ALU, as you already did that in your previous testbench.  

 Remember to select “System Verilog” from the “File Type” drop-down menu. 

Constraints

You will also need to reconfigure your constraints file to align with the top-level module declaration.  The names should line up properly by default.  A reference file is available in the Google Drive folder.  

Evaluation

The evaluation will have two steps, first submission of your source code and testbench to the autograder.  Second, you will need to synthesize your design, download it to the FPGa and do a demonstration for the TA.

Autograder (60%)  

Log on to https://autograder.sice.indiana.edu and submit your code as per Project 1.

Demonstration (40%)

Program your FPGA with your demultiplexer and demonstrate your working system to the TA.  You will not receive full points until the TA has approved your demonstration.