3
\$\begingroup\$

I'm trying to learn digital design this summer and currently going through this excercise of creating a 32-bit ALU based on this schematic:

enter image description here

Im using the + operator to create an adder, but I need to grab the carry out for use in my carry flag. Should I just hard code an adder instead of using the + operator? I pretty much hard coded the equation for the carry out of a full adder in my else statement provided below. I'm pretty new to this, and any of type help would be appreciated. I'm also not sure if my code is well designed/structured. Any tips would be awesome!!

module alu(input logic [31:0] a, b,
           input logic [1:0] alu_operation,
           output logic [31:0] out,
           output logic [3:0] alu_flag);
           
           logic [31:0] sum, b_out;
           assign b_out = alu_operation[0] ? ~b : b; // if 0 bit of op = 1, then we invert b to subtract, otherwise we keep b and add.
           assign sum = a + b_out + alu_operation[0];
           
           always @ (alu_operation, a, b)
            case (alu_operation)
                    2'b00: out <= sum; // ADD
                    2'b01: out <= sum; // SUBTRACT
                    2'b10: out <= a & b; // AND
                    2'b11: out <= a | b; // OR
            endcase
            
            wire logic negative, zero, carry, overflow;
            
            assign negative = alu_flag[3];
            assign zero = alu_flag[2];
            assign carry = alu_flag[1];
            assign overflow = alu_flag[0];
            
            always @ (out, a, b)
             begin                                 
                if (out[31] == 1)
                    alu_flag[3] <= 1; // Negative
                    
                if (out == 0)
                    alu_flag[2] <= 1; // Zero

                if ( ~alu_operation[1] & ((a & b) | (alu_operation[0] & (a ^ b))))   // Carry = AB + Cin(A xor B) 
                    alu_flag[1] <= 1;                                                // Cin = alu_operation[0]
                                       
                if ((sum[31] & a[31]) & (~alu_operation[1]) & (b[31] ^~ a[31] ^~ alu_operation[0])) 
                    alu_flag[0] <= 1; // Overflow
             end
endmodule
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The layout of your code is good, and you chose meaningful names for your signals. There are some improvements you can make, however.

The following signals are essentially unused:

        wire logic negative, zero, carry, overflow;

You do make assignments to them, but you never use them otherwise. If you synthesize your code, they will be optimized away. Therefore, you can simply delete them and their assignments.

Good coding practices recommend that you should use blocking assignments (=) for combinational logic. Since you do not have any sequential logic, you should not use nonblocking assignments (<=) in your always blocks.

Your always blocks have incomplete sensitivity lists. This will lead to simulation mismatches before and after synthesis. You should use the implicit sensitivity list syntax:

always @*

Your second always block will infer latches because you do not make assignments to the signals under all conditions. One way to avoid latches is to use an else clause for each if statement.

Here is the new code, taking all of the above into account. I also made some minor changes regarding indentation and begin/end usage:

module alu (
    input  logic [31:0] a, b,
    input  logic [1:0]  alu_operation,
    output logic [31:0] out,
    output logic [3:0]  alu_flag
);

    logic [31:0] sum, b_out;
    assign b_out = alu_operation[0] ? ~b : b; // if 0 bit of op = 1, then we invert b to subtract, otherwise we keep b and add.
    assign sum = a + b_out + alu_operation[0];

    always @* begin
        case (alu_operation)
            2'b00: out = sum;   // ADD
            2'b01: out = sum;   // SUBTRACT
            2'b10: out = a & b; // AND
            2'b11: out = a | b; // OR
        endcase
    end

    always @* begin
        if (out[31]) begin
            alu_flag[3] = 1; // Negative
        end else begin
            alu_flag[3] = 0;
        end

        if (out == 0) begin
            alu_flag[2] = 1; // Zero
        end else begin
            alu_flag[2] = 0;
        end

        // Carry = AB + Cin(A xor B)
        if ( ~alu_operation[1] & ((a & b) | (alu_operation[0] & (a ^ b)))) begin
            alu_flag[1] = 1;
        end else begin
            alu_flag[1] = 0;
        end

        if ((sum[31] & a[31]) & (~alu_operation[1]) & (b[31] ^~ a[31] ^~ alu_operation[0])) begin
            alu_flag[0] = 1; // Overflow
        end else begin
            alu_flag[0] = 0;
        end
    end
endmodule

Regarding your question about the + operator, I think you should keep it because it is good to code at a high level of abstraction. Synthesis tools will optimize your code. If your synthesized design does not meet your goals (timing, power, area, etc.), then you can revisit the implementation.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you for the great answer, helped me a bunch. I also had another question, why do you create a begin/end block for each if block? What is the advantage to that as opposed to encapsulating it all under one begin/end block? \$\endgroup\$ Jun 15, 2021 at 23:09
  • \$\begingroup\$ @theonetrue2: You're welcome. All the if conditions are different from each other, which means you need different begin/end. In this case, since you only have one statement for each if, you don't really need begin/end, but it is a coding style that I recommend using. \$\endgroup\$
    – toolic
    Jun 16, 2021 at 10:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.