`timescale 1ns / 1ps

module tb_systolic_mac2x2;
    reg clk;
    reg rst;
    reg weight_load;
    reg [31:0] weight_data;
    reg in_valid;
    reg [7:0] in_data;

    wire out_valid;
    wire [16:0] out_data;

    integer errors = 0;
    integer tests_run = 0;
    integer cycle_count = 0;
    integer i;

    systolic_mac2x2 uut (
        .clk(clk),
        .rst(rst),
        .weight_load(weight_load),
        .weight_data(weight_data),
        .in_valid(in_valid),
        .in_data(in_data),
        .out_valid(out_valid),
        .out_data(out_data)
    );

    initial begin
        clk = 1'b0;
        forever #5 clk = ~clk;
    end

// Generated by generate_golden.py and copied here. Do not edit by hand.
localparam integer NUM_TESTS = 42;
reg test_rst [0:NUM_TESTS-1];
reg test_weight_load [0:NUM_TESTS-1];
reg [31:0] test_weight_data [0:NUM_TESTS-1];
reg test_in_valid [0:NUM_TESTS-1];
reg [7:0] test_in_data [0:NUM_TESTS-1];
reg expected_out_valid [0:NUM_TESTS-1];
reg [16:0] expected_out_data [0:NUM_TESTS-1];
reg [255:0] test_tag [0:NUM_TESTS-1];

initial begin
    test_rst[0] = 1'b1;
    test_weight_load[0] = 1'b0;
    test_weight_data[0] = 32'h00000000;
    test_in_valid[0] = 1'b0;
    test_in_data[0] = 8'h00;
    expected_out_valid[0] = 1'b0;
    expected_out_data[0] = 17'h00000;
    test_tag[0] = "startup_reset";
    test_rst[1] = 1'b1;
    test_weight_load[1] = 1'b1;
    test_weight_data[1] = 32'h0705fd09;
    test_in_valid[1] = 1'b1;
    test_in_data[1] = 8'h55;
    expected_out_valid[1] = 1'b0;
    expected_out_data[1] = 17'h00000;
    test_tag[1] = "reset_ignore_both";
    test_rst[2] = 1'b0;
    test_weight_load[2] = 1'b0;
    test_weight_data[2] = 32'h00000000;
    test_in_valid[2] = 1'b0;
    test_in_data[2] = 8'h00;
    expected_out_valid[2] = 1'b0;
    expected_out_data[2] = 17'h00000;
    test_tag[2] = "post_reset_idle";
    test_rst[3] = 1'b0;
    test_weight_load[3] = 1'b1;
    test_weight_data[3] = 32'h01000001;
    test_in_valid[3] = 1'b1;
    test_in_data[3] = 8'h63;
    expected_out_valid[3] = 1'b0;
    expected_out_data[3] = 17'h00000;
    test_tag[3] = "load_identity_ignore_input";
    test_rst[4] = 1'b0;
    test_weight_load[4] = 1'b0;
    test_weight_data[4] = 32'h00000000;
    test_in_valid[4] = 1'b1;
    test_in_data[4] = 8'h07;
    expected_out_valid[4] = 1'b0;
    expected_out_data[4] = 17'h00000;
    test_tag[4] = "id_vec0_x0";
    test_rst[5] = 1'b0;
    test_weight_load[5] = 1'b0;
    test_weight_data[5] = 32'h00000000;
    test_in_valid[5] = 1'b1;
    test_in_data[5] = 8'hfd;
    expected_out_valid[5] = 1'b0;
    expected_out_data[5] = 17'h00000;
    test_tag[5] = "id_vec0_x1";
    test_rst[6] = 1'b0;
    test_weight_load[6] = 1'b0;
    test_weight_data[6] = 32'h00000000;
    test_in_valid[6] = 1'b1;
    test_in_data[6] = 8'h01;
    expected_out_valid[6] = 1'b1;
    expected_out_data[6] = 17'h00007;
    test_tag[6] = "id_vec1_x0";
    test_rst[7] = 1'b0;
    test_weight_load[7] = 1'b0;
    test_weight_data[7] = 32'h00000000;
    test_in_valid[7] = 1'b1;
    test_in_data[7] = 8'h02;
    expected_out_valid[7] = 1'b1;
    expected_out_data[7] = 17'h1fffd;
    test_tag[7] = "id_vec1_x1";
    test_rst[8] = 1'b0;
    test_weight_load[8] = 1'b0;
    test_weight_data[8] = 32'h00000000;
    test_in_valid[8] = 1'b0;
    test_in_data[8] = 8'h00;
    expected_out_valid[8] = 1'b1;
    expected_out_data[8] = 17'h00001;
    test_tag[8] = "id_drain0";
    test_rst[9] = 1'b0;
    test_weight_load[9] = 1'b0;
    test_weight_data[9] = 32'h00000000;
    test_in_valid[9] = 1'b0;
    test_in_data[9] = 8'h00;
    expected_out_valid[9] = 1'b1;
    expected_out_data[9] = 17'h00002;
    test_tag[9] = "id_drain1";
    test_rst[10] = 1'b0;
    test_weight_load[10] = 1'b1;
    test_weight_data[10] = 32'h05040302;
    test_in_valid[10] = 1'b0;
    test_in_data[10] = 8'h00;
    expected_out_valid[10] = 1'b0;
    expected_out_data[10] = 17'h00000;
    test_tag[10] = "load_dense";
    test_rst[11] = 1'b0;
    test_weight_load[11] = 1'b0;
    test_weight_data[11] = 32'h00000000;
    test_in_valid[11] = 1'b1;
    test_in_data[11] = 8'h01;
    expected_out_valid[11] = 1'b0;
    expected_out_data[11] = 17'h00000;
    test_tag[11] = "dense_vec0_x0";
    test_rst[12] = 1'b0;
    test_weight_load[12] = 1'b0;
    test_weight_data[12] = 32'h00000000;
    test_in_valid[12] = 1'b1;
    test_in_data[12] = 8'h02;
    expected_out_valid[12] = 1'b0;
    expected_out_data[12] = 17'h00000;
    test_tag[12] = "dense_vec0_x1";
    test_rst[13] = 1'b0;
    test_weight_load[13] = 1'b0;
    test_weight_data[13] = 32'h00000000;
    test_in_valid[13] = 1'b1;
    test_in_data[13] = 8'h03;
    expected_out_valid[13] = 1'b1;
    expected_out_data[13] = 17'h00008;
    test_tag[13] = "dense_vec1_x0";
    test_rst[14] = 1'b0;
    test_weight_load[14] = 1'b0;
    test_weight_data[14] = 32'h00000000;
    test_in_valid[14] = 1'b1;
    test_in_data[14] = 8'hff;
    expected_out_valid[14] = 1'b1;
    expected_out_data[14] = 17'h0000e;
    test_tag[14] = "dense_vec1_x1";
    test_rst[15] = 1'b0;
    test_weight_load[15] = 1'b0;
    test_weight_data[15] = 32'h00000000;
    test_in_valid[15] = 1'b1;
    test_in_data[15] = 8'h09;
    expected_out_valid[15] = 1'b1;
    expected_out_data[15] = 17'h00003;
    test_tag[15] = "partial_before_reset";
    test_rst[16] = 1'b1;
    test_weight_load[16] = 1'b1;
    test_weight_data[16] = 32'hfc0506f9;
    test_in_valid[16] = 1'b1;
    test_in_data[16] = 8'hf8;
    expected_out_valid[16] = 1'b0;
    expected_out_data[16] = 17'h00000;
    test_tag[16] = "reset_flush_pending";
    test_rst[17] = 1'b0;
    test_weight_load[17] = 1'b0;
    test_weight_data[17] = 32'h00000000;
    test_in_valid[17] = 1'b0;
    test_in_data[17] = 8'h00;
    expected_out_valid[17] = 1'b0;
    expected_out_data[17] = 17'h00000;
    test_tag[17] = "idle_after_reset";
    test_rst[18] = 1'b0;
    test_weight_load[18] = 1'b0;
    test_weight_data[18] = 32'h00000000;
    test_in_valid[18] = 1'b1;
    test_in_data[18] = 8'h04;
    expected_out_valid[18] = 1'b0;
    expected_out_data[18] = 17'h00000;
    test_tag[18] = "zero_weight_vec_x0";
    test_rst[19] = 1'b0;
    test_weight_load[19] = 1'b0;
    test_weight_data[19] = 32'h00000000;
    test_in_valid[19] = 1'b1;
    test_in_data[19] = 8'h05;
    expected_out_valid[19] = 1'b0;
    expected_out_data[19] = 17'h00000;
    test_tag[19] = "zero_weight_vec_x1";
    test_rst[20] = 1'b0;
    test_weight_load[20] = 1'b0;
    test_weight_data[20] = 32'h00000000;
    test_in_valid[20] = 1'b0;
    test_in_data[20] = 8'h00;
    expected_out_valid[20] = 1'b1;
    expected_out_data[20] = 17'h00000;
    test_tag[20] = "zero_weight_drain0";
    test_rst[21] = 1'b0;
    test_weight_load[21] = 1'b0;
    test_weight_data[21] = 32'h00000000;
    test_in_valid[21] = 1'b0;
    test_in_data[21] = 8'h00;
    expected_out_valid[21] = 1'b1;
    expected_out_data[21] = 17'h00000;
    test_tag[21] = "zero_weight_drain1";
    test_rst[22] = 1'b0;
    test_weight_load[22] = 1'b1;
    test_weight_data[22] = 32'h04fffe03;
    test_in_valid[22] = 1'b1;
    test_in_data[22] = 8'h4d;
    expected_out_valid[22] = 1'b0;
    expected_out_data[22] = 17'h00000;
    test_tag[22] = "load_mixed_ignore_input";
    test_rst[23] = 1'b0;
    test_weight_load[23] = 1'b0;
    test_weight_data[23] = 32'h00000000;
    test_in_valid[23] = 1'b1;
    test_in_data[23] = 8'hfb;
    expected_out_valid[23] = 1'b0;
    expected_out_data[23] = 17'h00000;
    test_tag[23] = "mixed_vec0_x0";
    test_rst[24] = 1'b0;
    test_weight_load[24] = 1'b0;
    test_weight_data[24] = 32'h00000000;
    test_in_valid[24] = 1'b1;
    test_in_data[24] = 8'h06;
    expected_out_valid[24] = 1'b0;
    expected_out_data[24] = 17'h00000;
    test_tag[24] = "mixed_vec0_x1";
    test_rst[25] = 1'b0;
    test_weight_load[25] = 1'b0;
    test_weight_data[25] = 32'h00000000;
    test_in_valid[25] = 1'b1;
    test_in_data[25] = 8'h7f;
    expected_out_valid[25] = 1'b1;
    expected_out_data[25] = 17'h1ffe5;
    test_tag[25] = "mixed_vec1_x0";
    test_rst[26] = 1'b0;
    test_weight_load[26] = 1'b0;
    test_weight_data[26] = 32'h00000000;
    test_in_valid[26] = 1'b1;
    test_in_data[26] = 8'h80;
    expected_out_valid[26] = 1'b1;
    expected_out_data[26] = 17'h0001d;
    test_tag[26] = "mixed_vec1_x1";
    test_rst[27] = 1'b0;
    test_weight_load[27] = 1'b0;
    test_weight_data[27] = 32'h00000000;
    test_in_valid[27] = 1'b0;
    test_in_data[27] = 8'h00;
    expected_out_valid[27] = 1'b1;
    expected_out_data[27] = 17'h0027d;
    test_tag[27] = "mixed_drain0";
    test_rst[28] = 1'b0;
    test_weight_load[28] = 1'b0;
    test_weight_data[28] = 32'h00000000;
    test_in_valid[28] = 1'b0;
    test_in_data[28] = 8'h00;
    expected_out_valid[28] = 1'b1;
    expected_out_data[28] = 17'h1fd81;
    test_tag[28] = "mixed_drain1";
    test_rst[29] = 1'b0;
    test_weight_load[29] = 1'b1;
    test_weight_data[29] = 32'hf609f807;
    test_in_valid[29] = 1'b1;
    test_in_data[29] = 8'hd4;
    expected_out_valid[29] = 1'b0;
    expected_out_data[29] = 17'h00000;
    test_tag[29] = "load_fixed_ignore_input";
    test_rst[30] = 1'b0;
    test_weight_load[30] = 1'b0;
    test_weight_data[30] = 32'h00000000;
    test_in_valid[30] = 1'b1;
    test_in_data[30] = 8'h0b;
    expected_out_valid[30] = 1'b0;
    expected_out_data[30] = 17'h00000;
    test_tag[30] = "fixed_vec0_x0";
    test_rst[31] = 1'b0;
    test_weight_load[31] = 1'b0;
    test_weight_data[31] = 32'h00000000;
    test_in_valid[31] = 1'b1;
    test_in_data[31] = 8'hf4;
    expected_out_valid[31] = 1'b0;
    expected_out_data[31] = 17'h00000;
    test_tag[31] = "fixed_vec0_x1";
    test_rst[32] = 1'b0;
    test_weight_load[32] = 1'b0;
    test_weight_data[32] = 32'h00000000;
    test_in_valid[32] = 1'b1;
    test_in_data[32] = 8'hf3;
    expected_out_valid[32] = 1'b1;
    expected_out_data[32] = 17'h000ad;
    test_tag[32] = "fixed_vec1_x0";
    test_rst[33] = 1'b0;
    test_weight_load[33] = 1'b0;
    test_weight_data[33] = 32'h00000000;
    test_in_valid[33] = 1'b1;
    test_in_data[33] = 8'h0e;
    expected_out_valid[33] = 1'b1;
    expected_out_data[33] = 17'h000db;
    test_tag[33] = "fixed_vec1_x1";
    test_rst[34] = 1'b0;
    test_weight_load[34] = 1'b0;
    test_weight_data[34] = 32'h00000000;
    test_in_valid[34] = 1'b1;
    test_in_data[34] = 8'h17;
    expected_out_valid[34] = 1'b1;
    expected_out_data[34] = 17'h1ff35;
    test_tag[34] = "fixed_vec2_x0";
    test_rst[35] = 1'b0;
    test_weight_load[35] = 1'b0;
    test_weight_data[35] = 32'h00000000;
    test_in_valid[35] = 1'b1;
    test_in_data[35] = 8'hef;
    expected_out_valid[35] = 1'b1;
    expected_out_data[35] = 17'h1feff;
    test_tag[35] = "fixed_vec2_x1";
    test_rst[36] = 1'b0;
    test_weight_load[36] = 1'b0;
    test_weight_data[36] = 32'h00000000;
    test_in_valid[36] = 1'b1;
    test_in_data[36] = 8'he1;
    expected_out_valid[36] = 1'b1;
    expected_out_data[36] = 17'h00129;
    test_tag[36] = "fixed_vec3_x0";
    test_rst[37] = 1'b0;
    test_weight_load[37] = 1'b0;
    test_weight_data[37] = 32'h00000000;
    test_in_valid[37] = 1'b1;
    test_in_data[37] = 8'h1d;
    expected_out_valid[37] = 1'b1;
    expected_out_data[37] = 17'h00179;
    test_tag[37] = "fixed_vec3_x1";
    test_rst[38] = 1'b0;
    test_weight_load[38] = 1'b0;
    test_weight_data[38] = 32'h00000000;
    test_in_valid[38] = 1'b0;
    test_in_data[38] = 8'h00;
    expected_out_valid[38] = 1'b1;
    expected_out_data[38] = 17'h1fe3f;
    test_tag[38] = "flush_idle0";
    test_rst[39] = 1'b0;
    test_weight_load[39] = 1'b0;
    test_weight_data[39] = 32'h00000000;
    test_in_valid[39] = 1'b0;
    test_in_data[39] = 8'h00;
    expected_out_valid[39] = 1'b1;
    expected_out_data[39] = 17'h1fdc7;
    test_tag[39] = "flush_idle1";
    test_rst[40] = 1'b0;
    test_weight_load[40] = 1'b0;
    test_weight_data[40] = 32'h00000000;
    test_in_valid[40] = 1'b0;
    test_in_data[40] = 8'h00;
    expected_out_valid[40] = 1'b0;
    expected_out_data[40] = 17'h00000;
    test_tag[40] = "flush_idle2";
    test_rst[41] = 1'b0;
    test_weight_load[41] = 1'b0;
    test_weight_data[41] = 32'h00000000;
    test_in_valid[41] = 1'b0;
    test_in_data[41] = 8'h00;
    expected_out_valid[41] = 1'b0;
    expected_out_data[41] = 17'h00000;
    test_tag[41] = "flush_idle3";
end

    task apply_cycle;
        input integer idx;
        begin
            rst = test_rst[idx];
            weight_load = test_weight_load[idx];
            weight_data = test_weight_data[idx];
            in_valid = test_in_valid[idx];
            in_data = test_in_data[idx];

            @(posedge clk);
            #1;

            cycle_count = cycle_count + 1;
            tests_run = tests_run + 1;

            if (out_valid !== expected_out_valid[idx]) begin
                $display("ERROR [cycle %0d][%0s]: out_valid mismatch. Expected %b, got %b (rst=%b weight_load=%b weight_data=0x%08h in_valid=%b in_data=0x%02h)",
                         cycle_count, test_tag[idx], expected_out_valid[idx], out_valid,
                         test_rst[idx], test_weight_load[idx], test_weight_data[idx],
                         test_in_valid[idx], test_in_data[idx]);
                errors = errors + 1;
            end

            if (out_data !== expected_out_data[idx]) begin
                $display("ERROR [cycle %0d][%0s]: out_data mismatch. Expected 0x%05h, got 0x%05h (rst=%b weight_load=%b weight_data=0x%08h in_valid=%b in_data=0x%02h)",
                         cycle_count, test_tag[idx], expected_out_data[idx], out_data,
                         test_rst[idx], test_weight_load[idx], test_weight_data[idx],
                         test_in_valid[idx], test_in_data[idx]);
                errors = errors + 1;
            end
        end
    endtask

    initial begin
        rst = 1'b0;
        weight_load = 1'b0;
        weight_data = 32'h00000000;
        in_valid = 1'b0;
        in_data = 8'h00;

        $display("===========================================");
        $display("        2x2 Weight-Loaded MAC Tile");
        $display("===========================================");

        for (i = 0; i < NUM_TESTS; i = i + 1) begin
            apply_cycle(i);
        end

        $display("");
        $display("===========================================");
        $display("  Tests Run: %0d", tests_run);
        $display("===========================================");

        if (errors == 0) begin
            $display("TEST_RESULT: PASS");
        end else begin
            $display("TEST_RESULT: FAIL (%0d errors)", errors);
        end

        $finish;
    end
endmodule
