`timescale 1ns / 1ps

module tb_crc32_generator;

    // Clock and Reset
    reg clk;
    reg rst;
    
    // Inputs
    reg [7:0] data_in;
    reg       data_valid;
    
    // Outputs
    wire [31:0] crc_out;
    wire        crc_valid;
    
    // Instantiate DUT
    crc32_generator uut (
        .clk(clk),
        .rst(rst),
        .data_in(data_in),
        .data_valid(data_valid),
        .crc_out(crc_out),
        .crc_valid(crc_valid)
    );
    
    // Clock generation
    initial clk = 0;
    always #5 clk = ~clk;
    
    // Golden Vectors
    localparam NUM_TESTS = 12;
    reg [31:0] expected_crc [0:NUM_TESTS-1];
    localparam TOTAL_BYTES = 355;
    reg [7:0] test_data [0:TOTAL_BYTES-1];
    reg [31:0] test_len [0:NUM_TESTS-1];
    reg [31:0] test_start_idx [0:NUM_TESTS-1];
    
    initial begin
        // Test 0: 'b'123456789'...' -> 0xCBF43926
        expected_crc[0] = 32'hCBF43926;
        test_len[0] = 9;
        test_start_idx[0] = 0;
        test_data[0] = 8'h31;    test_data[1] = 8'h32;    test_data[2] = 8'h33;    test_data[3] = 8'h34;    test_data[4] = 8'h35;    test_data[5] = 8'h36;    test_data[6] = 8'h37;    test_data[7] = 8'h38;    test_data[8] = 8'h39;
        // Test 1: 'b''...' -> 0x00000000
        expected_crc[1] = 32'h00000000;
        test_len[1] = 0;
        test_start_idx[1] = 9;
    
        // Test 2: 'b'\x00'...' -> 0xD202EF8D
        expected_crc[2] = 32'hD202EF8D;
        test_len[2] = 1;
        test_start_idx[2] = 9;
        test_data[9] = 8'h00;
        // Test 3: 'b'\xff'...' -> 0xFF000000
        expected_crc[3] = 32'hFF000000;
        test_len[3] = 1;
        test_start_idx[3] = 10;
        test_data[10] = 8'hFF;
        // Test 4: 'b'A'...' -> 0xD3D99E8B
        expected_crc[4] = 32'hD3D99E8B;
        test_len[4] = 1;
        test_start_idx[4] = 11;
        test_data[11] = 8'h41;
        // Test 5: 'b'CRC'...' -> 0xEAC5C577
        expected_crc[5] = 32'hEAC5C577;
        test_len[5] = 3;
        test_start_idx[5] = 12;
        test_data[12] = 8'h43;    test_data[13] = 8'h52;    test_data[14] = 8'h43;
        // Test 6: 'b'Hello'...' -> 0xF7D18982
        expected_crc[6] = 32'hF7D18982;
        test_len[6] = 5;
        test_start_idx[6] = 15;
        test_data[15] = 8'h48;    test_data[16] = 8'h65;    test_data[17] = 8'h6C;    test_data[18] = 8'h6C;    test_data[19] = 8'h6F;
        // Test 7: 'b'\x00\x01\x02\x03\...
        expected_crc[7] = 32'h29058C73;
        test_len[7] = 256;
        test_start_idx[7] = 20;
        test_data[20] = 8'h00;    test_data[21] = 8'h01;    test_data[22] = 8'h02;    test_data[23] = 8'h03;    test_data[24] = 8'h04;    test_data[25] = 8'h05;    test_data[26] = 8'h06;    test_data[27] = 8'h07;    test_data[28] = 8'h08;    test_data[29] = 8'h09;    test_data[30] = 8'h0A;    test_data[31] = 8'h0B;    test_data[32] = 8'h0C;    test_data[33] = 8'h0D;    test_data[34] = 8'h0E;    test_data[35] = 8'h0F;    test_data[36] = 8'h10;    test_data[37] = 8'h11;    test_data[38] = 8'h12;    test_data[39] = 8'h13;    test_data[40] = 8'h14;    test_data[41] = 8'h15;    test_data[42] = 8'h16;    test_data[43] = 8'h17;    test_data[44] = 8'h18;    test_data[45] = 8'h19;    test_data[46] = 8'h1A;    test_data[47] = 8'h1B;    test_data[48] = 8'h1C;    test_data[49] = 8'h1D;    test_data[50] = 8'h1E;    test_data[51] = 8'h1F;    test_data[52] = 8'h20;    test_data[53] = 8'h21;    test_data[54] = 8'h22;    test_data[55] = 8'h23;    test_data[56] = 8'h24;    test_data[57] = 8'h25;    test_data[58] = 8'h26;    test_data[59] = 8'h27;    test_data[60] = 8'h28;    test_data[61] = 8'h29;    test_data[62] = 8'h2A;    test_data[63] = 8'h2B;    test_data[64] = 8'h2C;    test_data[65] = 8'h2D;    test_data[66] = 8'h2E;    test_data[67] = 8'h2F;    test_data[68] = 8'h30;    test_data[69] = 8'h31;    test_data[70] = 8'h32;    test_data[71] = 8'h33;    test_data[72] = 8'h34;    test_data[73] = 8'h35;    test_data[74] = 8'h36;    test_data[75] = 8'h37;    test_data[76] = 8'h38;    test_data[77] = 8'h39;    test_data[78] = 8'h3A;    test_data[79] = 8'h3B;    test_data[80] = 8'h3C;    test_data[81] = 8'h3D;    test_data[82] = 8'h3E;    test_data[83] = 8'h3F;    test_data[84] = 8'h40;    test_data[85] = 8'h41;    test_data[86] = 8'h42;    test_data[87] = 8'h43;    test_data[88] = 8'h44;    test_data[89] = 8'h45;    test_data[90] = 8'h46;    test_data[91] = 8'h47;    test_data[92] = 8'h48;    test_data[93] = 8'h49;    test_data[94] = 8'h4A;    test_data[95] = 8'h4B;    test_data[96] = 8'h4C;    test_data[97] = 8'h4D;    test_data[98] = 8'h4E;    test_data[99] = 8'h4F;    test_data[100] = 8'h50;    test_data[101] = 8'h51;    test_data[102] = 8'h52;    test_data[103] = 8'h53;    test_data[104] = 8'h54;    test_data[105] = 8'h55;    test_data[106] = 8'h56;    test_data[107] = 8'h57;    test_data[108] = 8'h58;    test_data[109] = 8'h59;    test_data[110] = 8'h5A;    test_data[111] = 8'h5B;    test_data[112] = 8'h5C;    test_data[113] = 8'h5D;    test_data[114] = 8'h5E;    test_data[115] = 8'h5F;    test_data[116] = 8'h60;    test_data[117] = 8'h61;    test_data[118] = 8'h62;    test_data[119] = 8'h63;    test_data[120] = 8'h64;    test_data[121] = 8'h65;    test_data[122] = 8'h66;    test_data[123] = 8'h67;    test_data[124] = 8'h68;    test_data[125] = 8'h69;    test_data[126] = 8'h6A;    test_data[127] = 8'h6B;    test_data[128] = 8'h6C;    test_data[129] = 8'h6D;    test_data[130] = 8'h6E;    test_data[131] = 8'h6F;    test_data[132] = 8'h70;    test_data[133] = 8'h71;    test_data[134] = 8'h72;    test_data[135] = 8'h73;    test_data[136] = 8'h74;    test_data[137] = 8'h75;    test_data[138] = 8'h76;    test_data[139] = 8'h77;    test_data[140] = 8'h78;    test_data[141] = 8'h79;    test_data[142] = 8'h7A;    test_data[143] = 8'h7B;    test_data[144] = 8'h7C;    test_data[145] = 8'h7D;    test_data[146] = 8'h7E;    test_data[147] = 8'h7F;    test_data[148] = 8'h80;    test_data[149] = 8'h81;    test_data[150] = 8'h82;    test_data[151] = 8'h83;    test_data[152] = 8'h84;    test_data[153] = 8'h85;    test_data[154] = 8'h86;    test_data[155] = 8'h87;    test_data[156] = 8'h88;    test_data[157] = 8'h89;    test_data[158] = 8'h8A;    test_data[159] = 8'h8B;    test_data[160] = 8'h8C;    test_data[161] = 8'h8D;    test_data[162] = 8'h8E;    test_data[163] = 8'h8F;    test_data[164] = 8'h90;    test_data[165] = 8'h91;    test_data[166] = 8'h92;    test_data[167] = 8'h93;    test_data[168] = 8'h94;    test_data[169] = 8'h95;    test_data[170] = 8'h96;    test_data[171] = 8'h97;    test_data[172] = 8'h98;    test_data[173] = 8'h99;    test_data[174] = 8'h9A;    test_data[175] = 8'h9B;    test_data[176] = 8'h9C;    test_data[177] = 8'h9D;    test_data[178] = 8'h9E;    test_data[179] = 8'h9F;    test_data[180] = 8'hA0;    test_data[181] = 8'hA1;    test_data[182] = 8'hA2;    test_data[183] = 8'hA3;    test_data[184] = 8'hA4;    test_data[185] = 8'hA5;    test_data[186] = 8'hA6;    test_data[187] = 8'hA7;    test_data[188] = 8'hA8;    test_data[189] = 8'hA9;    test_data[190] = 8'hAA;    test_data[191] = 8'hAB;    test_data[192] = 8'hAC;    test_data[193] = 8'hAD;    test_data[194] = 8'hAE;    test_data[195] = 8'hAF;    test_data[196] = 8'hB0;    test_data[197] = 8'hB1;    test_data[198] = 8'hB2;    test_data[199] = 8'hB3;    test_data[200] = 8'hB4;    test_data[201] = 8'hB5;    test_data[202] = 8'hB6;    test_data[203] = 8'hB7;    test_data[204] = 8'hB8;    test_data[205] = 8'hB9;    test_data[206] = 8'hBA;    test_data[207] = 8'hBB;    test_data[208] = 8'hBC;    test_data[209] = 8'hBD;    test_data[210] = 8'hBE;    test_data[211] = 8'hBF;    test_data[212] = 8'hC0;    test_data[213] = 8'hC1;    test_data[214] = 8'hC2;    test_data[215] = 8'hC3;    test_data[216] = 8'hC4;    test_data[217] = 8'hC5;    test_data[218] = 8'hC6;    test_data[219] = 8'hC7;    test_data[220] = 8'hC8;    test_data[221] = 8'hC9;    test_data[222] = 8'hCA;    test_data[223] = 8'hCB;    test_data[224] = 8'hCC;    test_data[225] = 8'hCD;    test_data[226] = 8'hCE;    test_data[227] = 8'hCF;    test_data[228] = 8'hD0;    test_data[229] = 8'hD1;    test_data[230] = 8'hD2;    test_data[231] = 8'hD3;    test_data[232] = 8'hD4;    test_data[233] = 8'hD5;    test_data[234] = 8'hD6;    test_data[235] = 8'hD7;    test_data[236] = 8'hD8;    test_data[237] = 8'hD9;    test_data[238] = 8'hDA;    test_data[239] = 8'hDB;    test_data[240] = 8'hDC;    test_data[241] = 8'hDD;    test_data[242] = 8'hDE;    test_data[243] = 8'hDF;    test_data[244] = 8'hE0;    test_data[245] = 8'hE1;    test_data[246] = 8'hE2;    test_data[247] = 8'hE3;    test_data[248] = 8'hE4;    test_data[249] = 8'hE5;    test_data[250] = 8'hE6;    test_data[251] = 8'hE7;    test_data[252] = 8'hE8;    test_data[253] = 8'hE9;    test_data[254] = 8'hEA;    test_data[255] = 8'hEB;    test_data[256] = 8'hEC;    test_data[257] = 8'hED;    test_data[258] = 8'hEE;    test_data[259] = 8'hEF;    test_data[260] = 8'hF0;    test_data[261] = 8'hF1;    test_data[262] = 8'hF2;    test_data[263] = 8'hF3;    test_data[264] = 8'hF4;    test_data[265] = 8'hF5;    test_data[266] = 8'hF6;    test_data[267] = 8'hF7;    test_data[268] = 8'hF8;    test_data[269] = 8'hF9;    test_data[270] = 8'hFA;    test_data[271] = 8'hFB;    test_data[272] = 8'hFC;    test_data[273] = 8'hFD;    test_data[274] = 8'hFE;    test_data[275] = 8'hFF;
        // Test 8: 'b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'...' -> 0xC79B40E0
        expected_crc[8] = 32'hC79B40E0;
        test_len[8] = 16;
        test_start_idx[8] = 276;
        test_data[276] = 8'hAA;    test_data[277] = 8'hAA;    test_data[278] = 8'hAA;    test_data[279] = 8'hAA;    test_data[280] = 8'hAA;    test_data[281] = 8'hAA;    test_data[282] = 8'hAA;    test_data[283] = 8'hAA;    test_data[284] = 8'hAA;    test_data[285] = 8'hAA;    test_data[286] = 8'hAA;    test_data[287] = 8'hAA;    test_data[288] = 8'hAA;    test_data[289] = 8'hAA;    test_data[290] = 8'hAA;    test_data[291] = 8'hAA;
        // Test 9: 'b'UUUUUUUUUUUUUUUU'...' -> 0x1493CDAF
        expected_crc[9] = 32'h1493CDAF;
        test_len[9] = 16;
        test_start_idx[9] = 292;
        test_data[292] = 8'h55;    test_data[293] = 8'h55;    test_data[294] = 8'h55;    test_data[295] = 8'h55;    test_data[296] = 8'h55;    test_data[297] = 8'h55;    test_data[298] = 8'h55;    test_data[299] = 8'h55;    test_data[300] = 8'h55;    test_data[301] = 8'h55;    test_data[302] = 8'h55;    test_data[303] = 8'h55;    test_data[304] = 8'h55;    test_data[305] = 8'h55;    test_data[306] = 8'h55;    test_data[307] = 8'h55;
        // Test 10: 'b'\xde\xad\xbe\xef'...' -> 0x7C9CA35A
        expected_crc[10] = 32'h7C9CA35A;
        test_len[10] = 4;
        test_start_idx[10] = 308;
        test_data[308] = 8'hDE;    test_data[309] = 8'hAD;    test_data[310] = 8'hBE;    test_data[311] = 8'hEF;
        // Test 11: 'b'The quick brown fox '...' -> 0x414FA339
        expected_crc[11] = 32'h414FA339;
        test_len[11] = 43;
        test_start_idx[11] = 312;
        test_data[312] = 8'h54;    test_data[313] = 8'h68;    test_data[314] = 8'h65;    test_data[315] = 8'h20;    test_data[316] = 8'h71;    test_data[317] = 8'h75;    test_data[318] = 8'h69;    test_data[319] = 8'h63;    test_data[320] = 8'h6B;    test_data[321] = 8'h20;    test_data[322] = 8'h62;    test_data[323] = 8'h72;    test_data[324] = 8'h6F;    test_data[325] = 8'h77;    test_data[326] = 8'h6E;    test_data[327] = 8'h20;    test_data[328] = 8'h66;    test_data[329] = 8'h6F;    test_data[330] = 8'h78;    test_data[331] = 8'h20;    test_data[332] = 8'h6A;    test_data[333] = 8'h75;    test_data[334] = 8'h6D;    test_data[335] = 8'h70;    test_data[336] = 8'h73;    test_data[337] = 8'h20;    test_data[338] = 8'h6F;    test_data[339] = 8'h76;    test_data[340] = 8'h65;    test_data[341] = 8'h72;    test_data[342] = 8'h20;    test_data[343] = 8'h74;    test_data[344] = 8'h68;    test_data[345] = 8'h65;    test_data[346] = 8'h20;    test_data[347] = 8'h6C;    test_data[348] = 8'h61;    test_data[349] = 8'h7A;    test_data[350] = 8'h79;    test_data[351] = 8'h20;    test_data[352] = 8'h64;    test_data[353] = 8'h6F;    test_data[354] = 8'h67;
    end
    
    integer i, j;
    integer errors = 0;
    
    initial begin
        // Initialize inputs
        rst = 1;
        data_in = 0;
        data_valid = 0;
        
        // Wait for reset
        #20;

        // Loop through tests
        for (i = 0; i < NUM_TESTS; i = i + 1) begin
            // Sync to negedge
            @(negedge clk);
            rst = 1;
            @(negedge clk);
            rst = 0;
            @(negedge clk);
            
            // Send Data
            // If length is 0, we still want to check initial value.
            for (j = 0; j < test_len[i]; j = j + 1) begin
                data_valid = 1;
                data_in = test_data[test_start_idx[i] + j];
                @(negedge clk);
            end
            
            data_valid = 0;
            data_in = 0;
            
            // Wait for result (at posedge)
            // One cycle latency means value is ready at NEXT posedge.
            // We just finished negedge. Next posedge is 5ns away.
            @(posedge clk); 
            // At this posedge, result should be latched.
            
            #1; // Wait for output to settle
            
            // Check result
            if (test_len[i] > 0) begin
                // Check Valid
                if (crc_valid !== 1'b1) begin
                     $display("ERROR: Test %0d failed. crc_valid should be 1. Got %b", i, crc_valid);
                     errors = errors + 1;
                end
                
                // Check Value
                if (crc_out !== expected_crc[i]) begin
                     $display("ERROR: Test %0d failed. Expected CRC 0x%h, Got 0x%h", i, expected_crc[i], crc_out);
                     errors = errors + 1;
                end else begin
                     $display("PASS: Test %0d. CRC 0x%h matches.", i, crc_out);
                end
            end else begin
                 // Empty case logic
                 if (crc_valid !== 1'b0) begin
                      $display("ERROR: Test %0d (Empty) failed. crc_valid should be 0. Got %b", i, crc_valid);
                      errors = errors + 1;
                 end else begin
                      $display("PASS: Test %0d (Empty). crc_valid is 0.", i);
                 end
            end
            
            // Wait a bit
            #10;
        end
        
        // Test Hold functionality
        $display("Testing Data Valid toggle...");
        // Reset
        @(negedge clk); rst = 1; @(negedge clk); rst = 0; @(negedge clk);
        
        // Feed "123" (31 32 33) with pauses
        // '1'
        data_valid = 1; data_in = 8'h31; @(negedge clk);
        // Pause
        data_valid = 0; @(negedge clk); @(negedge clk);
        // '2'
        data_valid = 1; data_in = 8'h32; @(negedge clk);
        // Pause
        data_valid = 0; @(negedge clk);
        // '3'
        data_valid = 1; data_in = 8'h33; @(negedge clk);
        // End
        data_valid = 0; @(negedge clk);
        
        // Check "123" -> CRC used python to verify: binascii.crc32(b'123') = 0x884863D2
        @(posedge clk);
        #1;
        if (crc_out !== 32'h884863D2) begin
            $display("ERROR: Hold test failed. Expected 0x884863D2, Got 0x%h", crc_out);
            errors = errors + 1;
        end else begin
            $display("PASS: Hold test passed.");
        end
        
        // Summary
        if (errors == 0) begin
            $display("TEST_RESULT: PASS");
        end else begin
            $display("TEST_RESULT: FAIL (%0d errors)", errors);
        end
        
        $finish;
    end

endmodule
