0
0
VerilogHow-ToBeginner · 4 min read

Verilog Code for ALU: Syntax, Example, and Common Pitfalls

An ALU in Verilog is created using a module with inputs for operands and operation code, and an output for the result. You define operations inside an always block using a case statement to select the operation based on the opcode. This structure allows the ALU to perform arithmetic and logic functions like addition, subtraction, AND, and OR.
📐

Syntax

The basic syntax of a Verilog ALU module includes defining inputs for two operands and an operation code, and an output for the result. Inside the module, an always @(*) block is used to react to any input changes. A case statement selects the operation based on the opcode.

  • module: Defines the ALU block.
  • input [width-1:0]: Defines operand bit widths.
  • input [opcode_width-1:0]: Defines operation selector bits.
  • output reg [width-1:0]: Holds the result.
  • always @(*): Runs whenever inputs change.
  • case: Chooses operation based on opcode.
verilog
module alu(
    input  [3:0] a,       // First operand
    input  [3:0] b,       // Second operand
    input  [2:0] opcode,  // Operation selector
    output reg [3:0] result // Result of operation
);

always @(*) begin
    case(opcode)
        3'b000: result = a + b;    // Addition
        3'b001: result = a - b;    // Subtraction
        3'b010: result = a & b;    // AND
        3'b011: result = a | b;    // OR
        3'b100: result = a ^ b;    // XOR
        3'b101: result = ~a;       // NOT a
        default: result = 4'b0000; // Default zero
    endcase
end

endmodule
💻

Example

This example shows a 4-bit ALU module that performs addition, subtraction, AND, OR, XOR, and NOT operations based on a 3-bit opcode. It demonstrates how to use the case statement inside an always block to select the operation.

verilog
module alu(
    input  [3:0] a,
    input  [3:0] b,
    input  [2:0] opcode,
    output reg [3:0] result
);

always @(*) begin
    case(opcode)
        3'b000: result = a + b;
        3'b001: result = a - b;
        3'b010: result = a & b;
        3'b011: result = a | b;
        3'b100: result = a ^ b;
        3'b101: result = ~a;
        default: result = 4'b0000;
    endcase
end

endmodule

// Testbench to demonstrate ALU
module testbench();
    reg [3:0] a, b;
    reg [2:0] opcode;
    wire [3:0] result;

    alu uut(.a(a), .b(b), .opcode(opcode), .result(result));

    initial begin
        a = 4'b0101; // 5
        b = 4'b0011; // 3

        opcode = 3'b000; #10; // Add
        $display("Add: %b + %b = %b", a, b, result);

        opcode = 3'b001; #10; // Subtract
        $display("Sub: %b - %b = %b", a, b, result);

        opcode = 3'b010; #10; // AND
        $display("AND: %b & %b = %b", a, b, result);

        opcode = 3'b011; #10; // OR
        $display("OR: %b | %b = %b", a, b, result);

        opcode = 3'b100; #10; // XOR
        $display("XOR: %b ^ %b = %b", a, b, result);

        opcode = 3'b101; #10; // NOT a
        $display("NOT: ~%b = %b", a, result);

        $finish;
    end
endmodule
Output
Add: 0101 + 0011 = 1000 Sub: 0101 - 0011 = 0010 AND: 0101 & 0011 = 0001 OR: 0101 | 0011 = 0111 XOR: 0101 ^ 0011 = 0110 NOT: ~0101 = 1010
⚠️

Common Pitfalls

Common mistakes when writing a Verilog ALU include:

  • Not using reg type for the output when assigning inside always blocks.
  • Forgetting to cover all opcode cases, which can cause latches or undefined behavior.
  • Using blocking assignments (=) incorrectly in sequential logic (should use non-blocking <= in clocked blocks).
  • Not using always @(*) for combinational logic, which can cause simulation mismatches.

Example of a common mistake and fix:

verilog
module alu_wrong(
    input [3:0] a, b,
    input [2:0] opcode,
    output [3:0] result
);

always @(*) begin
    case(opcode)
        3'b000: result = a + b; // Error: result not declared reg
        default: result = 4'b0000;
    endcase
end

endmodule

// Corrected version
module alu_right(
    input [3:0] a, b,
    input [2:0] opcode,
    output reg [3:0] result
);

always @(*) begin
    case(opcode)
        3'b000: result = a + b;
        default: result = 4'b0000;
    endcase
end

endmodule
📊

Quick Reference

Tips for writing a Verilog ALU:

  • Use reg type for outputs assigned in always blocks.
  • Use always @(*) for combinational logic to avoid latches.
  • Cover all possible opcode cases with a default case.
  • Use case statements for clear operation selection.
  • Test your ALU with a testbench to verify all operations.

Key Takeaways

Define ALU operations inside an always @(*) block using a case statement for opcode selection.
Declare output signals as reg when assigned inside always blocks to avoid errors.
Always include a default case to prevent unintended latches or undefined outputs.
Use a testbench to verify all ALU operations work as expected.
Use blocking assignments (=) for combinational logic inside always @(*) blocks.