-
Notifications
You must be signed in to change notification settings - Fork 0
ALU
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:
Where:
-
Source1can be either the value from the A Bus or from the Current PC -
Source2can be either the value from the B Bus or the Immediate Bus -
Operationrepresents a set of mathematical operations like+,-,*,>>, etc., encoded in 5 bits -
Resultis directed to the C Bus to be written back to the Register File -
CNZVare 4 bits used to calculate comparisons for branch operations and are directed to the Operation Controller.-
CNZVstands 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:
| 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
<Uoperation, testing whether-1 < 0would result in false, because when treated as unsigned, the number-1(0xffffffff) becomes4,294,967,295, which is greater than0.
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.

We can divide the ALU operation into three parts:
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.
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.
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.
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
endmoduleThe 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.
-
- 1.1 Introduction
- 1.2 RISC-V Implementation
- 1.2.1 Available Instruction Set
- 1.2.2 Available Non-ISA Features
-
- 2.1 ALU
- 2.2 Register File
- 2.3 Program Counter
- 2.4 Input Buffer
- 2.5 RAM
- 2.6 Operation Controller
- 2.7 CSR Controller
-
- 3.1 Input Devices
- 3.1.1 Keyboard
- 3.1.2 Switches and Joystick
- 3.1.3 Random Number Generator
- 3.1.4 Real-Time Device
- 3.2 Output Devices
- 3.2.1 Screen
- 3.2.2 Terminal
- 3.2.3 Software Interrupt Register
- 3.1 Input Devices