Barrel Rotator in Verilog: Rotate Left/Right with Code

A barrel rotator performs a circular rotation of a data word by a variable amount in a single clock cycle, with no state and no sequential shifting. Unlike a shift, bits that fall off one end wrap around to the other end, so no information is lost. This page gives clean, simulator-verified barrel rotator Verilog code built from the classic logarithmic mux-stage structure.

The key idea is that a rotate by any amount from 0 to N-1 can be decomposed into a cascade of fixed rotates by powers of two (1, 2, 4, …), each gated by one bit of the rotate amount. For an N-bit word this needs only log2(N) stages of 2:1 multiplexers, which is why the barrel rotator is a staple of ALUs, cryptographic datapaths, and interview questions.

This parameterized 8-bit rotator rotates din left by amt using three mux stages (rotate by 1, 2, and 4). Each stage is a single conditional assignment gated by one bit of amt, and the testbench checks every rotate amount against the reference (in<<amt)|(in>>(N-amt)).

module barrel_rotate_left #(
    parameter N = 8,
    parameter S = 3            // S = log2(N)
) (
    input  wire [N-1:0] din,
    input  wire [S-1:0] amt,
    output wire [N-1:0] dout
);
    wire [N-1:0] stage [0:S];
    assign stage[0] = din;

    genvar i;
    generate
        for (i = 0; i < S; i = i + 1) begin : rot_stage
            // Each stage optionally rotates left by 2^i
            assign stage[i+1] = amt[i]
                ? ((stage[i] << (1 << i)) | (stage[i] >> (N - (1 << i))))
                : stage[i];
        end
    endgenerate

    assign dout = stage[S];
endmodule

module tb;
    localparam N = 8;
    localparam S = 3;
    reg  [N-1:0] din;
    reg  [S-1:0] amt;
    wire [N-1:0] dout;
    reg  [N-1:0] ref_val;
    integer k;
    integer errors;

    barrel_rotate_left #(.N(N), .S(S)) dut (.din(din), .amt(amt), .dout(dout));

    function [N-1:0] rotl;
        input [N-1:0] v;
        input integer a;
        begin
            if (a == 0) rotl = v;
            else        rotl = (v << a) | (v >> (N - a));
        end
    endfunction

    initial begin
        errors = 0;
        din = 8'b1011_0001;   // 0xB1
        for (k = 0; k < N; k = k + 1) begin
            amt = k[S-1:0];
            #1;
            ref_val = rotl(din, k);
            if (dout !== ref_val) begin
                $display("FAIL rotl amt=%0d din=%b got=%b exp=%b", k, din, dout, ref_val);
                errors = errors + 1;
            end else begin
                $display("PASS rotl amt=%0d din=%b dout=%b", k, din, dout);
            end
        end

        // second vector
        din = 8'b0000_1111;
        for (k = 0; k < N; k = k + 1) begin
            amt = k[S-1:0];
            #1;
            ref_val = rotl(din, k);
            if (dout !== ref_val) begin
                $display("FAIL rotl amt=%0d din=%b got=%b exp=%b", k, din, dout, ref_val);
                errors = errors + 1;
            end else begin
                $display("PASS rotl amt=%0d din=%b dout=%b", k, din, dout);
            end
        end

        if (errors == 0) $display("ALL TESTS PASSED");
        else             $display("TESTS FAILED: %0d", errors);
        $finish;
    end
endmodule
PASS rotl amt=0 din=10110001 dout=10110001
PASS rotl amt=1 din=10110001 dout=01100011
PASS rotl amt=2 din=10110001 dout=11000110
PASS rotl amt=3 din=10110001 dout=10001101
PASS rotl amt=4 din=10110001 dout=00011011
PASS rotl amt=5 din=10110001 dout=00110110
PASS rotl amt=6 din=10110001 dout=01101100
PASS rotl amt=7 din=10110001 dout=11011000
PASS rotl amt=0 din=00001111 dout=00001111
PASS rotl amt=1 din=00001111 dout=00011110
PASS rotl amt=2 din=00001111 dout=00111100
PASS rotl amt=3 din=00001111 dout=01111000
PASS rotl amt=4 din=00001111 dout=11110000
PASS rotl amt=5 din=00001111 dout=11100001
PASS rotl amt=6 din=00001111 dout=11000011
PASS rotl amt=7 din=00001111 dout=10000111
ALL TESTS PASSED

The log proves the staged-mux network matches the golden rotate for all eight amounts on two different input vectors: a sparse pattern (0xB1) and a block pattern (0x0F). Every bit wraps correctly, confirming it rotates rather than shifts.

This variant adds a dir control. Because rotating right by amt equals rotating left by N-amt, a single left-rotate network can serve both directions: when dir is high the amount is converted to (N-amt) mod N before entering the same three stages.

module barrel_rotator #(
    parameter N = 8,
    parameter S = 3            // S = log2(N)
) (
    input  wire [N-1:0] din,
    input  wire [S-1:0] amt,
    input  wire         dir,   // 0 = rotate left, 1 = rotate right
    output wire [N-1:0] dout
);
    // Rotating right by amt == rotating left by (N - amt).
    // Convert a right amount to the equivalent left amount, then do
    // one staged left-rotate network.
    wire [S-1:0] eff;          // effective left-rotate amount
    assign eff = dir ? ((~amt) + {{(S-1){1'b0}}, 1'b1}) : amt; // (N-amt) mod N

    wire [N-1:0] stage [0:S];
    assign stage[0] = din;

    genvar i;
    generate
        for (i = 0; i < S; i = i + 1) begin : rot_stage
            assign stage[i+1] = eff[i]
                ? ((stage[i] << (1 << i)) | (stage[i] >> (N - (1 << i))))
                : stage[i];
        end
    endgenerate

    assign dout = stage[S];
endmodule

module tb;
    localparam N = 8;
    localparam S = 3;
    reg  [N-1:0] din;
    reg  [S-1:0] amt;
    reg          dir;
    wire [N-1:0] dout;
    reg  [N-1:0] ref_val;
    integer k;
    integer errors;

    barrel_rotator #(.N(N), .S(S)) dut (.din(din), .amt(amt), .dir(dir), .dout(dout));

    function [N-1:0] rotl;
        input [N-1:0] v; input integer a;
        begin if (a == 0) rotl = v; else rotl = (v << a) | (v >> (N - a)); end
    endfunction
    function [N-1:0] rotr;
        input [N-1:0] v; input integer a;
        begin if (a == 0) rotr = v; else rotr = (v >> a) | (v << (N - a)); end
    endfunction

    initial begin
        errors = 0;
        din = 8'b1101_0010;   // 0xD2

        // Left direction
        dir = 1'b0;
        for (k = 0; k < N; k = k + 1) begin
            amt = k[S-1:0]; #1;
            ref_val = rotl(din, k);
            if (dout !== ref_val) begin
                $display("FAIL LEFT amt=%0d din=%b got=%b exp=%b", k, din, dout, ref_val);
                errors = errors + 1;
            end else
                $display("PASS LEFT  amt=%0d din=%b dout=%b", k, din, dout);
        end

        // Right direction
        dir = 1'b1;
        for (k = 0; k < N; k = k + 1) begin
            amt = k[S-1:0]; #1;
            ref_val = rotr(din, k);
            if (dout !== ref_val) begin
                $display("FAIL RIGHT amt=%0d din=%b got=%b exp=%b", k, din, dout, ref_val);
                errors = errors + 1;
            end else
                $display("PASS RIGHT amt=%0d din=%b dout=%b", k, din, dout);
        end

        if (errors == 0) $display("ALL TESTS PASSED");
        else             $display("TESTS FAILED: %0d", errors);
        $finish;
    end
endmodule
PASS LEFT  amt=0 din=11010010 dout=11010010
PASS LEFT amt=1 din=11010010 dout=10100101
PASS LEFT amt=2 din=11010010 dout=01001011
PASS LEFT amt=3 din=11010010 dout=10010110
PASS LEFT amt=4 din=11010010 dout=00101101
PASS LEFT amt=5 din=11010010 dout=01011010
PASS LEFT amt=6 din=11010010 dout=10110100
PASS LEFT amt=7 din=11010010 dout=01101001
PASS RIGHT amt=0 din=11010010 dout=11010010
PASS RIGHT amt=1 din=11010010 dout=01101001
PASS RIGHT amt=2 din=11010010 dout=10110100
PASS RIGHT amt=3 din=11010010 dout=01011010
PASS RIGHT amt=4 din=11010010 dout=00101101
PASS RIGHT amt=5 din=11010010 dout=10010110
PASS RIGHT amt=6 din=11010010 dout=01001011
PASS RIGHT amt=7 din=11010010 dout=10100101
ALL TESTS PASSED

The log sweeps all eight amounts in both directions. The left cases match rotl and the right cases match rotr, verifying the N-amt conversion trick reuses one rotate network for both directions with no extra mux layers.

How the staged-mux barrel rotator works

A barrel rotator exploits binary decomposition of the rotate amount. For an N-bit word, any rotate amount k in the range 0 to N-1 can be written in binary, and each set bit i contributes a fixed rotate by 2i. Cascading these conditional rotates produces the total rotate in one combinational pass.

  • Stage 0 rotates by 1 if amt[0] is set.
  • Stage 1 rotates by 2 if amt[1] is set.
  • Stage 2 rotates by 4 if amt[2] is set.

Each stage is just a row of N 2:1 multiplexers choosing between the pass-through value and the pre-wired rotated-by-2i value. The wiring of each fixed rotate is free in hardware — it is only wires — so the cost is dominated by the muxes.

Rotate vs shift (logical and arithmetic)

These operations are easy to confuse. A barrel rotator wraps; a shifter fills.

  • Rotate: bits leaving one end re-enter the other end. No bits are lost; the word is a permutation of itself. Used in cryptography (e.g. ROTL in SHA/ChaCha) and ALU rotate instructions.
  • Logical shift: vacated positions are filled with zeros. Bits shifted out are discarded.
  • Arithmetic shift right: vacated high bits are filled with copies of the sign bit to preserve signedness.

The same staged structure builds all three. A barrel shifter uses a constant fill (0 or sign) for the non-wrapped positions, while the barrel rotator feeds the wrapped bits back in. The mux-stage count — log2(N) stages — is identical; only the data feeding the muxes changes.

Area and timing: the log2(N) mux cost

The decisive advantage of the barrel structure is that it needs only log2(N) stages, not N. An 8-bit rotator uses 3 stages, a 32-bit rotator uses 5, a 64-bit rotator uses 6. Each stage is N 2:1 muxes, so total mux count is roughly N × log2(N) — for example 32 × 5 = 160 muxes for a 32-bit rotator.

Critical-path delay is log2(N) mux delays in series, which grows only logarithmically with width. This is far better than an iterative shift register (N cycles) or a naive N:1 mux per output bit. Because the amount decode is implicit in the per-stage gating, there is no separate wide decoder on the critical path.

Common mistakes

  • Using a shift instead of a rotate. Writing dout = din << amt; drops bits and zero-fills. You must OR in the wrapped part: (din << amt) | (din >> (N-amt)).
  • The amt==0 corner case. When the amount is zero, din >> (N-0) is a shift by N, which is undefined/zero behavior. In a per-stage design this is harmless because every stage simply passes through when its bit is 0, but a single-expression reference model must special-case zero (as the testbench functions do).
  • Mismatched amount width. The amount bus must be exactly log2(N) bits. A wider amount lets the rotate exceed N-1 and the extra bits are ignored only if you mask; an under-wide amount cannot reach all rotations.
  • Right-rotate sign extension. Rotate right is (din >> amt) | (din << (N-amt)). Using an arithmetic shift here would corrupt the wrap for signed-looking data.

Synthesis notes

The generate-for staged description synthesizes directly into a balanced tree of 2:1 multiplexers; synthesis tools recognize the pattern and map it to the technology's mux cells or to LUTs on an FPGA. Keep the rotate amount as a dedicated control input so the tool can share the gating logic across all N bit-slices of a stage.

For the left/right variant, performing the N-amt conversion once in front of a single network (as in Example 2) is cheaper than instantiating two full rotate networks and selecting between them, since it adds only a small adder/complementer on the amount rather than another N × log2(N) mux array. This is the standard way ALUs implement a bidirectional barrel shifter/rotator.

FAQ

Can N be non-power-of-two? The clean log2(N) staged structure assumes N is a power of two so that powers-of-two rotates compose exactly. For non-power-of-two widths you typically round the amount decode or use a different (often N:1 mux) structure.

Is it combinational or clocked? The rotator itself is purely combinational — it produces the result in one pass with no clock. You register its output if it sits on a timed datapath.

How do I make a barrel shifter instead? Replace the wrapped feedback term with a constant: zeros for a logical shift, or a sign-bit replica for an arithmetic right shift. The mux-stage skeleton is unchanged.

Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.
▶ Try it in the ChipXpert Lab

Open this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.

Run it in the Lab

Learn Verilog with mentors, labs and placement support

This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.

Explore VLSI Courses
Next batch starts 18th November 2026
Start your VLSI career with ChipXpert

Live online and classroom batches in Hyderabad & Bengaluru. Fill in your details and a counsellor will call you back.

  • Real EDA tools in your browser: industry-standard EDAReal EDA tools in your browser
  • Recorded sessions on the elearn portalRecorded sessions
  • Placement assistance: resume, mock interviewsPlacement assistance
  • Merit scholarship up to 60% · EMI optionsScholarship up to 60%
Hi! Ask me about courses, fees, batches or discounts. ×
BENGALURU
HYDERABAD