`timescale 1ns / 1ps

// =============================================================================
// Testbench for IPv4 Header Parser
// Extracts: total_length, ttl, protocol, src_ip, dst_ip, valid
// =============================================================================

module tb_ipv4_parser;

    // Input
    reg [159:0] header;
    
    // Outputs
    wire valid;
    wire [15:0] total_length;
    wire [7:0] ttl;
    wire [7:0] protocol;
    wire [31:0] src_ip;
    wire [31:0] dst_ip;
    
    // Instantiate DUT
    ipv4_parser uut (
        .header(header),
        .valid(valid),
        .total_length(total_length),
        .ttl(ttl),
        .protocol(protocol),
        .src_ip(src_ip),
        .dst_ip(dst_ip)
    );
    
    // ==============================================
    // Golden Vectors
    // ==============================================
    localparam NUM_TESTS = 25;
    
    reg [159:0] tb_header [0:24];
    reg         exp_valid [0:24];
    reg [15:0]  exp_total_length [0:24];
    reg [7:0]   exp_ttl [0:24];
    reg [7:0]   exp_protocol [0:24];
    reg [31:0]  exp_src_ip [0:24];
    reg [31:0]  exp_dst_ip [0:24];
    
    integer i;
    integer errors = 0;
    
    initial begin
        // Test 0: valid_tcp
        tb_header[0] = 160'h450000280000000040060000c0a801010a000001;
        exp_valid[0] = 1'b1;
        exp_total_length[0] = 16'd40;
        exp_ttl[0] = 8'd64;
        exp_protocol[0] = 8'd6;
        exp_src_ip[0] = 32'hc0a80101;
        exp_dst_ip[0] = 32'h0a000001;

        // Test 1: valid_udp
        tb_header[1] = 160'h4500003c00000000801100000808080801010101;
        exp_valid[1] = 1'b1;
        exp_total_length[1] = 16'd60;
        exp_ttl[1] = 8'd128;
        exp_protocol[1] = 8'd17;
        exp_src_ip[1] = 32'h08080808;
        exp_dst_ip[1] = 32'h01010101;

        // Test 2: valid_icmp
        tb_header[2] = 160'h4500005400000000ff010000ac100001ac100002;
        exp_valid[2] = 1'b1;
        exp_total_length[2] = 16'd84;
        exp_ttl[2] = 8'd255;
        exp_protocol[2] = 8'd1;
        exp_src_ip[2] = 32'hac100001;
        exp_dst_ip[2] = 32'hac100002;

        // Test 3: invalid_version
        tb_header[3] = 160'h650000280000000040060000c0a801010a000001;
        exp_valid[3] = 1'b0;
        exp_total_length[3] = 16'd40;
        exp_ttl[3] = 8'd64;
        exp_protocol[3] = 8'd6;
        exp_src_ip[3] = 32'hc0a80101;
        exp_dst_ip[3] = 32'h0a000001;

        // Test 4: invalid_ihl_6
        tb_header[4] = 160'h4600002c0000000040060000c0a801010a000001;
        exp_valid[4] = 1'b0;
        exp_total_length[4] = 16'd44;
        exp_ttl[4] = 8'd64;
        exp_protocol[4] = 8'd6;
        exp_src_ip[4] = 32'hc0a80101;
        exp_dst_ip[4] = 32'h0a000001;

        // Test 5: invalid_ihl_4
        tb_header[5] = 160'h440000280000000040060000c0a801010a000001;
        exp_valid[5] = 1'b0;
        exp_total_length[5] = 16'd40;
        exp_ttl[5] = 8'd64;
        exp_protocol[5] = 8'd6;
        exp_src_ip[5] = 32'hc0a80101;
        exp_dst_ip[5] = 32'h0a000001;

        // Test 6: ttl_one
        tb_header[6] = 160'h450000280000000001060000c0a801010a000001;
        exp_valid[6] = 1'b1;
        exp_total_length[6] = 16'd40;
        exp_ttl[6] = 8'd1;
        exp_protocol[6] = 8'd6;
        exp_src_ip[6] = 32'hc0a80101;
        exp_dst_ip[6] = 32'h0a000001;

        // Test 7: ttl_zero
        tb_header[7] = 160'h450000280000000000060000c0a801010a000001;
        exp_valid[7] = 1'b1;
        exp_total_length[7] = 16'd40;
        exp_ttl[7] = 8'd0;
        exp_protocol[7] = 8'd6;
        exp_src_ip[7] = 32'hc0a80101;
        exp_dst_ip[7] = 32'h0a000001;

        // Test 8: max_length
        tb_header[8] = 160'h4500ffff0000000040060000c0a801010a000001;
        exp_valid[8] = 1'b1;
        exp_total_length[8] = 16'd65535;
        exp_ttl[8] = 8'd64;
        exp_protocol[8] = 8'd6;
        exp_src_ip[8] = 32'hc0a80101;
        exp_dst_ip[8] = 32'h0a000001;

        // Test 9: zero_ips
        tb_header[9] = 160'h4500001400000000400600000000000000000000;
        exp_valid[9] = 1'b1;
        exp_total_length[9] = 16'd20;
        exp_ttl[9] = 8'd64;
        exp_protocol[9] = 8'd6;
        exp_src_ip[9] = 32'h00000000;
        exp_dst_ip[9] = 32'h00000000;

        // Test 10: broadcast
        tb_header[10] = 160'h450000640000000020110000c0a80164ffffffff;
        exp_valid[10] = 1'b1;
        exp_total_length[10] = 16'd100;
        exp_ttl[10] = 8'd32;
        exp_protocol[10] = 8'd17;
        exp_src_ip[10] = 32'hc0a80164;
        exp_dst_ip[10] = 32'hffffffff;

        // Test 11: random_0
        tb_header[11] = 160'h45006abb000000000559000098bc638adf52bf3f;
        exp_valid[11] = 1'b1;
        exp_total_length[11] = 16'd27323;
        exp_ttl[11] = 8'd5;
        exp_protocol[11] = 8'd89;
        exp_src_ip[11] = 32'h98bc638a;
        exp_dst_ip[11] = 32'hdf52bf3f;

        // Test 12: random_1
        tb_header[12] = 160'h4500dfdd00000000dd110000595fb52ed3554b69;
        exp_valid[12] = 1'b1;
        exp_total_length[12] = 16'd57309;
        exp_ttl[12] = 8'd221;
        exp_protocol[12] = 8'd17;
        exp_src_ip[12] = 32'h595fb52e;
        exp_dst_ip[12] = 32'hd3554b69;

        // Test 13: random_2
        tb_header[13] = 160'h4500b8220000000027060000aea40ceaad0dd401;
        exp_valid[13] = 1'b1;
        exp_total_length[13] = 16'd47138;
        exp_ttl[13] = 8'd39;
        exp_protocol[13] = 8'd6;
        exp_src_ip[13] = 32'haea40cea;
        exp_dst_ip[13] = 32'had0dd401;

        // Test 14: random_3
        tb_header[14] = 160'h450001c900000000535900005b9232d4365dc077;
        exp_valid[14] = 1'b1;
        exp_total_length[14] = 16'd457;
        exp_ttl[14] = 8'd83;
        exp_protocol[14] = 8'd89;
        exp_src_ip[14] = 32'h5b9232d4;
        exp_dst_ip[14] = 32'h365dc077;

        // Test 15: random_4
        tb_header[15] = 160'h4500f5e100000000a859000014763dff6e5f83de;
        exp_valid[15] = 1'b1;
        exp_total_length[15] = 16'd62945;
        exp_ttl[15] = 8'd168;
        exp_protocol[15] = 8'd89;
        exp_src_ip[15] = 32'h14763dff;
        exp_dst_ip[15] = 32'h6e5f83de;

        // Test 16: random_5
        tb_header[16] = 160'h450084bf0000000042010000549a044c75b3af50;
        exp_valid[16] = 1'b1;
        exp_total_length[16] = 16'd33983;
        exp_ttl[16] = 8'd66;
        exp_protocol[16] = 8'd1;
        exp_src_ip[16] = 32'h549a044c;
        exp_dst_ip[16] = 32'h75b3af50;

        // Test 17: random_6
        tb_header[17] = 160'h450062e500000000a5010000191fe328c65b05bc;
        exp_valid[17] = 1'b1;
        exp_total_length[17] = 16'd25317;
        exp_ttl[17] = 8'd165;
        exp_protocol[17] = 8'd1;
        exp_src_ip[17] = 32'h191fe328;
        exp_dst_ip[17] = 32'hc65b05bc;

        // Test 18: random_7
        tb_header[18] = 160'h45003735000000003e2f00001f07fba6d767ba3d;
        exp_valid[18] = 1'b1;
        exp_total_length[18] = 16'd14133;
        exp_ttl[18] = 8'd62;
        exp_protocol[18] = 8'd47;
        exp_src_ip[18] = 32'h1f07fba6;
        exp_dst_ip[18] = 32'hd767ba3d;

        // Test 19: random_8
        tb_header[19] = 160'h45008e8e000000004c010000fd54ab7668535393;
        exp_valid[19] = 1'b1;
        exp_total_length[19] = 16'd36494;
        exp_ttl[19] = 8'd76;
        exp_protocol[19] = 8'd1;
        exp_src_ip[19] = 32'hfd54ab76;
        exp_dst_ip[19] = 32'h68535393;

        // Test 20: random_9
        tb_header[20] = 160'h45002683000000002e320000014b01fd5e3187cf;
        exp_valid[20] = 1'b1;
        exp_total_length[20] = 16'd9859;
        exp_ttl[20] = 8'd46;
        exp_protocol[20] = 8'd50;
        exp_src_ip[20] = 32'h014b01fd;
        exp_dst_ip[20] = 32'h5e3187cf;

        // Test 21: random_invalid_0
        tb_header[21] = 160'h06000369000000005d7c00005e0dbea95aca5b5b;
        exp_valid[21] = 1'b0;
        exp_total_length[21] = 16'd873;
        exp_ttl[21] = 8'd93;
        exp_protocol[21] = 8'd124;
        exp_src_ip[21] = 32'h5e0dbea9;
        exp_dst_ip[21] = 32'h5aca5b5b;

        // Test 22: random_invalid_1
        tb_header[22] = 160'hff00021100000000d3d50000a63d22eb63674577;
        exp_valid[22] = 1'b0;
        exp_total_length[22] = 16'd529;
        exp_ttl[22] = 8'd211;
        exp_protocol[22] = 8'd213;
        exp_src_ip[22] = 32'ha63d22eb;
        exp_dst_ip[22] = 32'h63674577;

        // Test 23: random_invalid_2
        tb_header[23] = 160'h6f00026a00000000401e000085da16ba37224fbe;
        exp_valid[23] = 1'b0;
        exp_total_length[23] = 16'd618;
        exp_ttl[23] = 8'd64;
        exp_protocol[23] = 8'd30;
        exp_src_ip[23] = 32'h85da16ba;
        exp_dst_ip[23] = 32'h37224fbe;

        // Test 24: random_invalid_3
        tb_header[24] = 160'h040003a100000000bccd00009de5879ffcf51987;
        exp_valid[24] = 1'b0;
        exp_total_length[24] = 16'd929;
        exp_ttl[24] = 8'd188;
        exp_protocol[24] = 8'd205;
        exp_src_ip[24] = 32'h9de5879f;
        exp_dst_ip[24] = 32'hfcf51987;
        
        // Wait for inputs to settle
        #100;
        
        // Run tests
        for (i = 0; i < NUM_TESTS; i = i + 1) begin
            header = tb_header[i];
            
            #10;
            
            // Check all fields
            if (valid !== exp_valid[i]) begin
                $display("ERROR [Test %0d]: valid mismatch. Expected %b, Got %b", i, exp_valid[i], valid);
                errors = errors + 1;
            end
            if (total_length !== exp_total_length[i]) begin
                $display("ERROR [Test %0d]: total_length mismatch. Expected %d, Got %d", i, exp_total_length[i], total_length);
                errors = errors + 1;
            end
            if (ttl !== exp_ttl[i]) begin
                $display("ERROR [Test %0d]: ttl mismatch. Expected %d, Got %d", i, exp_ttl[i], ttl);
                errors = errors + 1;
            end
            if (protocol !== exp_protocol[i]) begin
                $display("ERROR [Test %0d]: protocol mismatch. Expected %d, Got %d", i, exp_protocol[i], protocol);
                errors = errors + 1;
            end
            if (src_ip !== exp_src_ip[i]) begin
                $display("ERROR [Test %0d]: src_ip mismatch. Expected %h, Got %h", i, exp_src_ip[i], src_ip);
                errors = errors + 1;
            end
            if (dst_ip !== exp_dst_ip[i]) begin
                $display("ERROR [Test %0d]: dst_ip mismatch. Expected %h, Got %h", i, exp_dst_ip[i], dst_ip);
                errors = errors + 1;
            end
        end
        
        // Final Results
        $display("");
        $display("===========================================");
        $display("  Tests Run: %0d", NUM_TESTS);
        $display("===========================================");
        
        if (errors == 0) begin
            $display("TEST_RESULT: PASS");
        end else begin
            $display("TEST_RESULT: FAIL (%0d errors)", errors);
        end
        
        $finish;
    end

endmodule
