Skip to content
Diogo Valadares Reis dos Santos edited this page Aug 26, 2025 · 3 revisions

[Português]

[← Previous Page | Next Page →]

The Arithmetic and Logic Unit

The Arithmetic and Logic Unit (ALU) is essentially the calculator of the processor. It is responsible for executing the various mathematical operations defined in the ISA. An ALU can operate in different ways depending on the architecture, but in DRISC-V, we can think of it as performing a simple two-operand mathematical operation:

Source1 Operation Source2 = Result with CNZV

Where:

  • Source1 can be either the value from the A Bus or from the Current PC
  • Source2 can be either the value from the B Bus or the Immediate Bus
  • Operation represents a set of mathematical operations like +, -, *, >>, etc., encoded in 5 bits
  • Result is directed to the C Bus to be written back to the Register File
  • CNZV are 4 bits used to calculate comparisons for branch operations and are directed to the Operation Controller.
    • CNZV stands for:
      • Carry out
      • Negative
      • Zero
      • Overflow

The following figure shows the ALU component in the Logisim simulation. Note that the inputs for the A Bus and C Bus are labeled Source 1 and Source 2, and that there are two extra control signals to select the Program Counter and the Immediate Value:

ALU Component in Logisim

The following table Shows the available operations of the ALU: The following table shows the available operations of the ALU:
Encoding Operation Symbol Notes Example
0 + Addition with carry-out stored in cnzv[0] 5 + 3 = 8
1 << Logical left shift, equivalent to unsigned multiply by 2 5 << 1 = 10
2 < Signed less-than comparison (returns 1 if true, else 0) -2 < 3 → 1
3 <U Unsigned less-than comparison (returns 1 if true, else 0) 250 < 300 → 1
4 ^ Bitwise XOR 0101₂ ^ 0011₂ = 0110₂
5 >> Logical right shift 1000₂ >> 2 = 0010₂
6 | Bitwise OR 0101₂ | 0010₂ = 0111₂
7 & Bitwise AND 0101₂ & 0011₂ = 0001₂
8 * 32-bit multiplication (lower 32 bits of result) 6 * 7 = 42
9 *H Signed multiplication, returns upper 32 bits 100000 * 100000 → High bits
10 *HSU Signed × Unsigned multiplication, returns upper 32 bits -1 * 2^31 → High bits
11 *HU Unsigned multiplication, returns upper 32 bits 2^31 * 2^31 → High bits
16 - Subtraction with carry-out stored in cnzv[0], used for comparisons 10 - 3 = 7
21 >>> Arithmetic (signed) right shift, equivalent to dividing by 2 -8 >>> 1 = -4
default 0 Default case, result is zero Invalid op → 0

Note: DRISC-V, like most modern architectures, uses the two's complement number system for signed numbers (i.e., numbers that can go below 0).

When you see an operation labeled "unsigned," it means the number is treated as if the highest bit does not represent the sign.

For example, in the <U operation, testing whether -1 < 0 would result in false, because when treated as unsigned, the number -1 (0xffffffff) becomes 4,294,967,295, which is greater than 0.

According to the RISC-V Specification, part of the immediate value is used to encode the >>> (arithmetic right shift) operation.

This is because shifting more than 32 bits on a 32-bit value results in either 0 or 0xFFFFFFFF, depending on the sign. Therefore, only 5 bits are needed from the immediate value to specify the shift amount.

The OP-IMM instruction format provides only 3 bits to select the operation, which are already used for other instructions. To accommodate the SRAI (Shift Right Arithmetic Immediate) instruction, extra bits from the immediate field are repurposed.

However, the limited 3-bit operation selector prevents multiplication and subtraction from being supported directly in immediate form. There is a workaround for subtraction: you can use a negative immediate with the ADDI instruction to effectively perform subtraction.

Inside the ALU

Inside the ALU

We can divide the ALU operation into three parts:

1. Source Selection

This is where the operands are chosen. There is also a small circuit for the operation selection, which is responsible for ignoring the most significant 2 bits in immediate operations that are not Shift Arithmetic, preventing immediate values from affecting the current operation.

2. Result Calculation

All operations receive the same inputs and are calculated simultaneously.
Note that the adder/subtractor and the multiplier receive operation bits that allow their behavior to be modified. This enables reutilization of circuits, minimizing the number of required components.

Inside the Adder and Multiplier

3. Operation Selection and CNZV Calculation

The selection is done using two multiplexers:

  • The first is 1-bit wide and selects between standard operations and the arithmetic right shift (>>>).
  • The second is 4-bit wide and selects the final result from all computed operations.

The CNZV value is only truly valid for subtraction operations(and only used for with subration operations), but it can still return values for other operations, even if they are not meaningful in context.

System Verilog Code

The following code is the System Verilog version of the ALU:

`timescale 1s/1s
// Inputs and Outputs
module alu(
    input use_pc,
    input [31:0] a,
    input [31:0] program_counter,
    input use_immediate,
    input [31:0] b,
    input [31:0] immediate,
    input [4:0] operation,
    output logic [31:0] result,
    output logic [3:0] cnzv
    );
// First part, source selection and immediate operation bit masking.
    wire [4:0]op = (use_immediate & ~(operation == 1 | operation == 5 | operation == 21)) ?
                        {2'b0, operation[2:0]} :
                        operation;

    wire [31:0] source1 = use_pc ? program_counter : a;
    wire [31:0] source2 = use_immediate ? immediate : b;

    always@ * begin
//Second and Third parts(The Case operation generates the multiplexes)
        case(op)
            0: {cnzv[0], result} = source1 + source2;
            1: result = source1 << source2[4:0];
            2: result = ($signed(source1) < $signed(source2)) ? 1 : 0;
            3: result = ($unsigned(source1) < $unsigned(source2)) ? 1 : 0;
            4: result = source1 ^ source2;
            5: result = source1 >> source2[4:0];
            6: result = source1 | source2;
            7: result = source1 & source2;
            8: result = source1 * source2;
            9: begin
                logic [63:0] r64;
                r64 = $signed(source1) * $signed(source2);
                result = r64[63:32];
            end
            10: begin
                logic [63:0] r64;
                r64 = $signed({source1[31],source1}) * $signed({1'b0, source2});
                result = r64[63:32];
            end
            11: begin
                logic [63:0] r64;
                r64 = source1 * source2;
                result = r64[63:32];
            end
            16: {cnzv[0], result} = source1 - source2;
            21: result = $signed(source1) >>> source2[4:0];
            default: result = 32'b0;
        endcase
//CNZV Final calculation.
        cnzv[3:1] = {
            (operation == 16 ^ (source1[31] == source2[31]) && (result[31] != source1[31])),
            result == 0,
            result[31]
        };
    end
endmodule

Uses for ALU

The ALU is the primary component used for both OP and OP-IMM instructions. But beyond that, in DRISC-V, it is also utilized for branch instructions and the AUIPC instruction.

In branch instructions, the selected operation is always subtraction. This is because subtraction provides all the necessary information through the CNZV flags to evaluate comparisons effectively.

In addition to branch operations, the AUIPC instruction captures the value of the program counter and adds an immediate value to it.

In older versions of DRISC, the ALU's adder was used to calculate jump addresses for the program counter. However, to support pipelining during branch instructions, an additional adder circuit was introduced inside the Program Counter itself.

Clone this wiki locally