In this project, we will design arithmetic circuits using an FPGA. We will build a 4-bit magnitude comparator, a ripple-carry adder, and a multiplier circuit. You can challenge yourself by integrating all of those circuits together with some multiplexers to build an arithmetic logic unit (ALU).
|1||Digilent Nexys™4, Nexys™3, Nexys™2, or Basys™2 FPGA Board|
|1||Xilinx ISE Design Suite: WebPACK (14.6 Recommended)|
A magnitude comparator is a device that receives two N-bit inputs and asserts one of three possible outputs depending on whether one input is greater than, less than, or equal to the other (simpler comparators, called equality comparators, provide a single output that is asserted whenever the two inputs are equal). The truth table of a bit-sliced magnitude comparator and the block diagram of a magnitude comparator are shown in Figs. 1 and 2 below.
module cmp_bitslice( input A, input B, input LT_I, input EQ_I, input GT_I, output LT_O, output EQ_O, output GT_O ); assign GT_O = ( A & ~B ) | GT_I; assign EQ_O = EQ_I & (( A & B ) | (~A & ~B)); assign LT_O = ( B & ~A ) | LT_I; endmodule
module cmp( input [3:0] A, input [3:0] B, output LT_O, output EQ_O, output GT_O ); wire [3:0] GT_int; wire [3:0] EQ_int; wire [3:0] LT_int; cmp_bitslice slice_0 ( .A(A), .B(B), .LT_I(1'b0), .EQ_I(1'b1), .GT_I(1'b0), .LT_O(LT_int), .EQ_O(EQ_int), .GT_O(GT_int) ); cmp_bitslice slice_1 ( .A(A), .B(B), .LT_I(LT_int), .EQ_I(EQ_int), .GT_I(GT_int), .LT_O(LT_int), .EQ_O(EQ_int), .GT_O(GT_int) ); cmp_bitslice slice_2 ( .A(A), .B(B), .LT_I(LT_int), .EQ_I(EQ_int), .GT_I(GT_int), .LT_O(LT_int), .EQ_O(EQ_int), .GT_O(GT_int) ); cmp_bitslice slice_3 ( .A(A), .B(B), .LT_I(LT_int), .EQ_I(EQ_int), .GT_I(GT_int), .LT_O(LT_int), .EQ_O(EQ_int), .GT_O(GT_int) ); assign LT_O = LT_int; assign EQ_O = EQ_int; assign GT_O = GT_int; endmodule
module cmp( input [3:0] A, input [3:0] B, output LT_O, output EQ_O, output GT_O ); assign LT_O = (A < B) ? 1'b1 : 1'b0; assign EQ_O = (A == B) ? 1'b1 : 1'b0; assign GT_O = (A > B) ? 1'b1 : 1'b0; endmodule
Adder circuits add two N-bit operands to produce an N-bit result and a carry out signal (the carry out is a '1' only when the addition result requires more than N-bits). The logic graph in Fig. 3 below shows the eight different cases that may be encountered when adding two binary numbers. The highlighted bit pairs and the associated carries show that a bit-slice adder circuit must process three inputs (the two addend bits and a carry-in from the previous stage) and produce two outputs (the sum bit and a carry out bit). The circuit block that implements the bit-sliced adder is called a “full adder” (FA).
Note the carry-in of the RCA LSB is connected directly to ground, because (by definition) the carry-in to the LSB of any adder must be logic '0'. It is possible to capitalize on this observation, and create a smaller bit-slice circuit for use in the LSB position that does not have a carry-in input. Called a half adder (HA), this reduced-function adder circuit is often used in the LSB position. The ripple carry adder block diagram is displayed in Fig. 4 below.
module FA( input A, input B, input Cin, output S, output Cout ); assign S = A ^ B ^ Cin; assign Cout = (A & B) | ((A ^ B) & Cin); endmodule
module HA( input A, input B, output S, output Cout ); assign S = A ^ B; assign Cout = A & B; endmodule
module adder( input [3:0] A, input [3:0] B, output [3:0] S, output Cout ); wire [3:0] Carry; HA add_0 ( .A(A), .B(B), .S(S), .Cout(Carry) ); FA add_1 ( .A(A), .B(B), .Cin(Carry), .S(S), .Cout(Carry) ); FA add_2 ( .A(A), .B(B), .Cin(Carry), .S(S), .Cout(Carry) ); FA add_3 ( .A(A), .B(B), .Cin(Carry), .S(S), .Cout(Carry) ); assign Cout = Carry; endmodule
module adder( input [3:0] A, input [3:0] B, output [3:0] S, output Cout ); wire [4:0] Result; assign Result = A + B; assign S = Result[3:0]; assign Cout = Result; endmodule
Hardware multipliers, based directly on adder architectures, have become indispensable in modern computers. Multiplier circuits are modeled after the “shift and add” algorithm, as shown below. In this algorithm, one partial product is created for each bit in the multiplier—the first partial product is created by the LSB of the multiplier, the second partial product is created by the second bit in the multiplier, and so forth. The partial product bits need to be fed to an array of full adders (and half adders where appropriate), with the adders shifted to the left as indicated by the multiplication example. The final partial products are added with a CLA circuit. Note that some full-adder circuits bring signal values into the carry-in inputs (instead of carry's from the neighboring stage). This is a valid use of the full-adder circuit; the full adder simply adds any three bits applied to its inputs. The circuit for a partial product and the block diagram of the multiplier is shown in Fig. 5 below.
Up to this point, you are expected to be able to describe circuits structurally. Based on the block diagram shown above in Fig. 5, you can use assignment statements to implement the circuit for partial products, and use the FA, HA, and the adder you implemented in previous steps to add those partial products together. Before deploying your circuit on your board, write a test bench to verify that your circuit is correct. Use SW3-0 as A and SW7-4 as B (A and B are multiplicants), and show the result of multiplication on 8-bit LEDs.
Unlike the adder and subtractor, multipliers do not have an operator support in Verilog, mostly due to the fact that there are various ways to implement a multiplier which trade off power, hardware resource, and speed. So implementing a multiplier structurally is the only solution.
Now that you've completed this project, try these modifications:
|000||Addition||$A + B$|
|001||Increment||$A + 1$|
|010||Subtract||$A - B$|
|011||Bit- wise XOR||$A \oplus B$|
|100||Bit- wise OR||$A | B$|
|101||Bit-wise AND||$A \& B$|