59. Universal Barrel Shifter

module univ_barrel8 (
    input  [7:0] x,
    input  [2:0] sh,    // 0..7
    input  [2:0] mode,  // see table below
    output reg [7:0] y
);
    // mode encodings
    localparam [2:0]
        M_PASS = 3'b000,
        M_LSL  = 3'b001,
        M_LSR  = 3'b010,
        M_ASR  = 3'b011,
        M_ROL  = 3'b100,
        M_ROR  = 3'b101;

    always @* begin
        case (mode)
            M_PASS: y = x;                                        // pass-through
            M_LSL : y = (x << sh) & 8'hFF;                        // logical left
            M_LSR : y = (x >> sh);                                // logical right
            M_ASR : y = $signed(x) >>> sh;                        // arithmetic right
            M_ROL : y = ((x << sh) | (x >> (8 - sh))) & 8'hFF;    // rotate left
            M_ROR : y = ((x >> sh) | (x << (8 - sh))) & 8'hFF;    // rotate right
            default: y = x;                                       // reserved → PASS
        endcase
    end
endmodule

💡Remember

  • Logical vs Arithmetic: >> shifts in 0s; >>> keeps the sign (use $signed(x) to ensure correct behavior).
  • Rotate = combine complementary shifts; mask with 8'hFF after left shifts to keep 8 bits.
  • Shifting by 0 or by the full width (e.g., x << 8) yields defined zeros in Verilog-2005; the expressions above handle all sh 0..7 without special cases.
  • Unknown mode values in hardware should be mapped to a safe behavior (here, PASS) rather than x to keep designs synthesizable.

Testbench Code

`timescale 1ns/1ps

module tb_univ_barrel8;
    // 1) Inputs
    reg  [7:0] x;
    reg  [2:0] sh;
    reg  [2:0] mode;

    // 2) DUT outputs
    wire [7:0] y;

    // 3) Expected (purely combinational helper function)
    function [7:0] expected8;
        input [7:0] fx;
        input [2:0] fsh;
        input [2:0] fmode;
        reg   signed [7:0] sfx;
        begin
            sfx = fx; // signed view for ASR
            case (fmode)
                3'b000: expected8 = fx;                                        // PASS
                3'b001: expected8 = (fx << fsh) & 8'hFF;                       // LSL
                3'b010: expected8 = (fx >> fsh);                               // LSR
                3'b011: expected8 = (sfx >>> fsh);                             // ASR
                3'b100: expected8 = ((fx << fsh) | (fx >> (8 - fsh))) & 8'hFF; // ROL
                3'b101: expected8 = ((fx >> fsh) | (fx << (8 - fsh))) & 8'hFF; // ROR
                default: expected8 = fx;                                       // reserved → PASS
            endcase
        end
    endfunction

    wire [7:0] expected_y = expected8(x, sh, mode);

    // 4) Single mismatch flag (treat X/Z as mismatches)
    wire mismatch = (y !== expected_y);

    // Accounting
    integer TOTAL_TEST_CASES        = 0;
    integer TOTAL_PASSED_TEST_CASES = 0;
    integer TOTAL_FAILED_TEST_CASES = 0;

    // Limits
    integer VCD_MAX_CASES   = 32; // normal printed rows
    integer ERROR_MAX_CASES = 32; // error printed rows
    integer printed_rows    = 0;
    integer printed_errors  = 0;

    // DUT
    univ_barrel8 dut(.x(x), .sh(sh), .mode(mode), .y(y));

    // VCD dump
    initial begin
        $dumpfile("tb_univ_barrel8.vcd");
        $dumpvars(0,
            tb_univ_barrel8.x,
            tb_univ_barrel8.sh,
            tb_univ_barrel8.mode,
            tb_univ_barrel8.y,
            tb_univ_barrel8.expected_y,
            tb_univ_barrel8.mismatch
        );
        $dumpon;
    end

    // Header + init
    initial begin
        x=0; sh=0; mode=3'b000;
        $display("  x    sh mode |   y   | expected_y | mismatch");
        $display("----------------------------------------------");
    end

    // Apply + check (drive -> wait -> read combinational mismatch)
    task apply_and_check;
        input [7:0] tx;
        input [2:0] tsh;
        input [2:0] tmode;
        begin
            x = tx; sh = tsh; mode = tmode;
            #1; // settle

            TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
            if (!mismatch) begin
                TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
                if (printed_rows < VCD_MAX_CASES) begin
                    $display("0x%02h  %0d  %03b  | 0x%02h |    0x%02h   |    %0d",
                             x, sh, mode, y, expected_y, mismatch);
                    printed_rows = printed_rows + 1;
                end
            end else begin
                TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
                if (printed_errors < ERROR_MAX_CASES) begin
                    $display("ERR: x=0x%02h sh=%0d mode=%03b => y=0x%02h (exp 0x%02h)",
                             x, sh, mode, y, expected_y);
                    printed_errors = printed_errors + 1;
                end
            end

            // Optional: stop VCD after cap of total vectors
            if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
        end
    endtask

    integer ix, ish, im;
    initial begin
        // Directed: sanity per op
        // PASS
        apply_and_check(8'hA5, 3'd0, 3'b000);
        // LSL
        apply_and_check(8'h81, 3'd1, 3'b001);
        apply_and_check(8'h81, 3'd3, 3'b001);
        // LSR
        apply_and_check(8'h81, 3'd1, 3'b010);
        apply_and_check(8'hFF, 3'd7, 3'b010);
        // ASR (sign extend)
        apply_and_check(8'h81, 3'd1, 3'b011); // 1000_0001 >>> 1 = 1100_0000
        apply_and_check(8'hC0, 3'd3, 3'b011);
        // ROL
        apply_and_check(8'h96, 3'd2, 3'b100);
        // ROR
        apply_and_check(8'h96, 3'd3, 3'b101);

        // Full Sweep
        for (im = 0; im < 6; im = im + 1)         // test modes 000..101
          for (ish = 0; ish < 8; ish = ish + 1)   // shifts 0..7
            for (ix = 0; ix < 256; ix = ix + 1)  // sample inputs
              apply_and_check(ix[7:0], ish[2:0], im[2:0]);

        // Summary
        $display("----------------------------------------------");
        $display("TOTAL_TEST_CASES=%0d", TOTAL_TEST_CASES);
        $display("TOTAL_PASSED_TEST_CASES=%0d", TOTAL_PASSED_TEST_CASES);
        $display("TOTAL_FAILED_TEST_CASES=%0d", TOTAL_FAILED_TEST_CASES);
        $display("ALL_TEST_CASES_PASSED=%s", (TOTAL_FAILED_TEST_CASES==0) ? "true" : "false");
        #2 $finish;
    end
endmodule