Modeling ALUs
Arithmetic Logic Unit implementation.
The Arithmetic Logic Unit (ALU) is the computational core of every processor, responsible for performing arithmetic and logical operations on binary operands. Modeling an ALU in Verilog requires understanding how to use a selection signal to multiplex between different operations within a single combinational always block. This is one of the most exam-relevant Verilog design exercises.
Core Concept: ALU Architecture and Operation Selection
An ALU accepts two operands and a function select input. The select input chooses which of several operations is performed and whose result is passed to the output. In a 3-bit select ALU, up to 8 distinct operations can be supported. The design philosophy in Verilog is to evaluate all operations implicitly through the case statement and route the correct result to the output based on sel.
The output width must be planned carefully. For addition and subtraction of two 4-bit numbers, the result can be 5 bits due to carry or borrow. The carry flag is extracted as the most significant bit of the extended result. The zero flag is asserted when the result equals zero, which is critical in processor branch instructions like BEQ (branch if equal) in MIPS and RISC-V architectures.
The behavioral modeling approach uses a single always@(*) block with a case statement on the sel input. Each case branch assigns the result. Because this is purely combinational logic, the sensitivity list must include all inputs (using the wildcard * is the safe practice). The case statement should include a default branch to avoid latch inference for undefined select values.
Mathematical Expression
For a 4-bit ALU with operands A and B and 3-bit select sel, the output function is defined as a piecewise expression: result = f(A, B) where f is determined by sel. For arithmetic operations, the result is represented in (n+1) bits to accommodate carry. The carry out Cout = result[n] where n is the operand width. The zero flag Z = NOR of all result bits = ~|result[n-1:0]. The overflow flag for signed addition is V = A[n-1] XOR B[n-1] XOR result[n-1] XOR Cout.
Practical Understanding
In processor datapaths, the ALU is controlled by the control unit which decodes the instruction opcode and drives the ALU select lines accordingly. For a simple RISC processor, an ADD instruction maps to sel=000, a SUB maps to sel=001, and logical operations map to their respective codes. The ALU output feeds the register file write-back path and the branch condition logic simultaneously.
Shift operations in the ALU are particularly important. Left shift by 1 bit is equivalent to multiplication by 2 and is implemented in hardware without an actual multiplier. Similarly, right shift implements integer division by powers of 2. These operations execute in a single clock cycle in most RISC processors, making them far more efficient than full multiplication.
Given:
A = 4b1010 (decimal 10), B = 4b0011 (decimal 3), sel = 3b000 (ADD)
Why this formula applies:
sel = 000 selects the ADD operation in the ALU case statement.
Result must be 5 bits to capture carry.
Formula:
result[4:0] = {1b0, A} + {1b0, B}
carry = result[4]
zero = ~|result[3:0]
Substitution:
{1b0, 4b1010} + {1b0, 4b0011}
= 5b01010 + 5b00011
Calculation:
01010
+ 00011
-------
01101
Final Answer:
result[4:0] = 5b01101 (decimal 13)
carry flag = 0 (no overflow beyond 4 bits)
zero flag = 0 (result is non-zero)Exam Tip: In GATE questions on Verilog ALU modeling, watch for the result bit-width. Declaring result as a 4-bit reg when adding two 4-bit numbers silently truncates the carry bit. Always declare result as (n+1) bits or use a separate carry output. Also, missing the default in a case statement causes latch inference in synthesis, which is a common synthesis-related MCQ.
- The ALU uses always@(*) with a case(sel) block; each branch computes one operation and assigns to the result register.
- Result must be declared (n+1) bits for n-bit operands to correctly capture the carry out from addition and subtraction.
- The zero flag is computed as the bitwise NOR reduction of the result: zero = ~|result.
- The carry flag is simply the MSB of the extended result: carry = result[n].
- A default branch in the case statement is mandatory to prevent latch inference during synthesis.
- Shift operations (left and right) implement multiply/divide by powers of 2 without a hardware multiplier.
Quick Revision
- ALU selects one of multiple operations using a case(sel) in always@(*); this is purely combinational.
- Result bit-width: for n-bit operands, declare result as (n+1) bits to capture carry.
- Zero flag: zero = ~|result[n-1:0] (reduction NOR).
- Carry flag: carry = result[n].
- Default branch in case is mandatory to avoid latch inference.
- Left shift by k = multiply by 2^k; right shift by k = divide by 2^k.
- Exam trap: 4-bit result reg for 4-bit ADD silently discards carry; always size result correctly.
ALU Modeling Quiz
Test functional knowledge of Arithmetic Logic Unit design.
Q1.What parameter primarily controls the specific operation executed by an ALU during a given clock cycle?
Related Articles
Modeling Comparators
Comparing vectors.
10 min read
Modeling Multiplexers
Using if-else, case, assign statements.
12 min read
Modeling Combinational Logic
Boolean equations using dataflow.
7 min read
Modeling Latches
Level sensitive behavior, inferring latches accident.
6 min read
FSM Behavioral Modeling
Mealy vs Moore coding styles.
11 min read