`timescale 1ns / 1ps

// Testbench for Sobel Edge Detection
// 64x64 input, standard Gx/Gy kernels, |Gx|+|Gy| magnitude
// Golden values loaded from expected.hex

module tb_sobel_edge;
    reg clk, rst, start;
    reg [7:0] pixel_in;
    reg pixel_valid;
    wire pixel_ready;
    wire [7:0] pixel_out;
    wire out_valid;
    reg out_ready;
    wire done;
    
    integer errors = 0;
    integer pixels_sent = 0;
    integer pixels_received = 0;
    integer timeout_count;
    parameter MAX_TIMEOUT = 50000;
    parameter IMG_W = 64;
    parameter IMG_H = 64;
    parameter IMG_SIZE = IMG_W * IMG_H;  // 4096
    
    // Test image: gradient pattern (r*4 + c*2) % 256
    reg [7:0] test_image [0:4095];
    
    // Golden expected outputs loaded from expected.hex (Icarus-friendly).
    // Path can be overridden with +expected_hex=<path>.
    // Golden data is stored in expected.hex.
    reg [7:0] expected_output [0:4095];
    reg [8*256-1:0] expected_path;
    integer expected_file;
    integer i, r, c;
    
    // Instantiate UUT
    sobel_edge uut (
        .clk(clk), .rst(rst), .start(start),
        .pixel_in(pixel_in), .pixel_valid(pixel_valid), .pixel_ready(pixel_ready),
        .pixel_out(pixel_out), .out_valid(out_valid), .out_ready(out_ready),
        .done(done)
    );
    
    // Clock 100MHz
    initial begin clk = 0; forever #5 clk = ~clk; end
    
    // Initialize test image
    initial begin
        for (r = 0; r < IMG_H; r = r + 1)
            for (c = 0; c < IMG_W; c = c + 1)
                test_image[r * IMG_W + c] = (r * 4 + c * 2) % 256;
    end

    // Load golden output file with robust path fallbacks.
    initial begin : load_expected_output
        if ($value$plusargs("expected_hex=%s", expected_path)) begin
            expected_file = $fopen(expected_path, "r");
            if (!expected_file) begin
                $display("ERROR: +expected_hex file not found: %0s", expected_path);
                $finish;
            end
            $fclose(expected_file);
            $readmemh(expected_path, expected_output);
            $display("Loaded expected output from %0s", expected_path);
        end else begin
            expected_file = $fopen("expected.hex", "r");
            if (expected_file) begin
                $fclose(expected_file);
                $readmemh("expected.hex", expected_output);
                $display("Loaded expected output from expected.hex");
            end else begin
                expected_file = $fopen("benchmarks/problems/005_sobel_edge/expected.hex", "r");
                if (expected_file) begin
                    $fclose(expected_file);
                    $readmemh("benchmarks/problems/005_sobel_edge/expected.hex", expected_output);
                    $display("Loaded expected output from benchmarks/problems/005_sobel_edge/expected.hex");
                end else begin
                    expected_file = $fopen("/home/bubshait/Desktop/FYP/evaluationPipeline/benchmarks/problems/005_sobel_edge/expected.hex", "r");
                    if (expected_file) begin
                        $fclose(expected_file);
                        $readmemh("/home/bubshait/Desktop/FYP/evaluationPipeline/benchmarks/problems/005_sobel_edge/expected.hex", expected_output);
                        $display("Loaded expected output from absolute workspace path");
                    end else begin
                        $display("ERROR: Could not locate expected.hex.");
                        $display("ERROR: Tried +expected_hex, local, repo-relative, and absolute paths.");
                        $finish;
                    end
                end
            end
        end
    end
    
    // Test procedure
    initial begin
        rst = 1; start = 0; pixel_in = 0; pixel_valid = 0; out_ready = 1;
        $display("=== Sobel Edge Detection Testbench ===");
        
        repeat(5) @(posedge clk); rst = 0;
        repeat(2) @(posedge clk);
        @(negedge clk); start = 1;
        @(negedge clk); start = 0;
        
        // Stream input/output
        fork
            begin : send_block
                for (i = 0; i < IMG_SIZE; i = i + 1) begin
                    @(negedge clk);
                    pixel_in = test_image[i];
                    pixel_valid = 1;
                    timeout_count = 0;
                    begin : wait_for_accept
                        while (timeout_count < MAX_TIMEOUT) begin
                            @(posedge clk);
                            if (pixel_ready) begin
                                pixels_sent = pixels_sent + 1;
                                disable wait_for_accept;
                            end
                            timeout_count = timeout_count + 1;
                        end
                    end
                    if (timeout_count >= MAX_TIMEOUT) begin
                        $display("ERROR: Timeout at pixel %0d", i);
                        disable send_block;
                    end
                end
                @(negedge clk);
                pixel_valid = 0;
            end
            
            begin : recv_block
                integer local_timeout;
                local_timeout = 0;
                while (pixels_received < IMG_SIZE && local_timeout < MAX_TIMEOUT * 2) begin
                    @(posedge clk);
                    if (out_valid && out_ready) begin
                        if (pixel_out !== expected_output[pixels_received]) begin
                            if (errors < 20)
                                $display("ERROR: Pixel %0d: Expected 0x%02X, got 0x%02X",
                                         pixels_received, expected_output[pixels_received], pixel_out);
                            errors = errors + 1;
                        end
                        pixels_received = pixels_received + 1;
                        local_timeout = 0;
                    end else local_timeout = local_timeout + 1;
                end
                if (pixels_received < IMG_SIZE) begin
                    $display("ERROR: Timeout. Received %0d/%0d", pixels_received, IMG_SIZE);
                    errors = errors + (IMG_SIZE - pixels_received);
                end
            end
        join
        
        $display("Sent: %0d, Received: %0d", pixels_sent, pixels_received);
        if (errors == 0) $display("TEST_RESULT: PASS");
        else $display("TEST_RESULT: FAIL (%0d errors)", errors);
        $finish;
    end
endmodule
