`timescale 1ns / 1ps

module tb_timestamp_delta_encoder;
    reg clk;
    reg rst;
    reg in_valid;
    reg [31:0] in_timestamp;

    wire out_valid;
    wire [1:0] out_tag;
    wire [31:0] out_value;

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

    timestamp_delta_encoder uut (
        .clk(clk),
        .rst(rst),
        .in_valid(in_valid),
        .in_timestamp(in_timestamp),
        .out_valid(out_valid),
        .out_tag(out_tag),
        .out_value(out_value)
    );

    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 = 27;
reg test_rst [0:NUM_TESTS-1];
reg test_in_valid [0:NUM_TESTS-1];
reg [31:0] test_in_timestamp [0:NUM_TESTS-1];
reg expected_out_valid [0:NUM_TESTS-1];
reg [1:0] expected_out_tag [0:NUM_TESTS-1];
reg [31:0] expected_out_value [0:NUM_TESTS-1];
reg [255:0] test_tag [0:NUM_TESTS-1];

initial begin
    test_rst[0] = 1'b1;
    test_in_valid[0] = 1'b0;
    test_in_timestamp[0] = 32'h00000000;
    expected_out_valid[0] = 1'b0;
    expected_out_tag[0] = 2'b00;
    expected_out_value[0] = 32'h00000000;
    test_tag[0] = "startup_reset";
    test_rst[1] = 1'b1;
    test_in_valid[1] = 1'b1;
    test_in_timestamp[1] = 32'h12345678;
    expected_out_valid[1] = 1'b0;
    expected_out_tag[1] = 2'b00;
    expected_out_value[1] = 32'h00000000;
    test_tag[1] = "reset_ignored_input";
    test_rst[2] = 1'b0;
    test_in_valid[2] = 1'b0;
    test_in_timestamp[2] = 32'h00000000;
    expected_out_valid[2] = 1'b0;
    expected_out_tag[2] = 2'b00;
    expected_out_value[2] = 32'h00000000;
    test_tag[2] = "post_reset_idle";
    test_rst[3] = 1'b0;
    test_in_valid[3] = 1'b1;
    test_in_timestamp[3] = 32'h00001000;
    expected_out_valid[3] = 1'b0;
    expected_out_tag[3] = 2'b00;
    expected_out_value[3] = 32'h00000000;
    test_tag[3] = "first_sample";
    test_rst[4] = 1'b0;
    test_in_valid[4] = 1'b1;
    test_in_timestamp[4] = 32'h00001000;
    expected_out_valid[4] = 1'b1;
    expected_out_tag[4] = 2'b10;
    expected_out_value[4] = 32'h00001000;
    test_tag[4] = "zero_delta";
    test_rst[5] = 1'b0;
    test_in_valid[5] = 1'b1;
    test_in_timestamp[5] = 32'h000010ff;
    expected_out_valid[5] = 1'b1;
    expected_out_tag[5] = 2'b00;
    expected_out_value[5] = 32'h00000000;
    test_tag[5] = "delta_255";
    test_rst[6] = 1'b0;
    test_in_valid[6] = 1'b1;
    test_in_timestamp[6] = 32'h000011ff;
    expected_out_valid[6] = 1'b1;
    expected_out_tag[6] = 2'b00;
    expected_out_value[6] = 32'h000000ff;
    test_tag[6] = "delta_256";
    test_rst[7] = 1'b0;
    test_in_valid[7] = 1'b1;
    test_in_timestamp[7] = 32'h000111fe;
    expected_out_valid[7] = 1'b1;
    expected_out_tag[7] = 2'b01;
    expected_out_value[7] = 32'h00000100;
    test_tag[7] = "delta_65535";
    test_rst[8] = 1'b0;
    test_in_valid[8] = 1'b1;
    test_in_timestamp[8] = 32'h000211fe;
    expected_out_valid[8] = 1'b1;
    expected_out_tag[8] = 2'b01;
    expected_out_value[8] = 32'h0000ffff;
    test_tag[8] = "delta_65536";
    test_rst[9] = 1'b0;
    test_in_valid[9] = 1'b0;
    test_in_timestamp[9] = 32'h00000000;
    expected_out_valid[9] = 1'b1;
    expected_out_tag[9] = 2'b10;
    expected_out_value[9] = 32'h00010000;
    test_tag[9] = "idle_gap";
    test_rst[10] = 1'b0;
    test_in_valid[10] = 1'b1;
    test_in_timestamp[10] = 32'h00021210;
    expected_out_valid[10] = 1'b0;
    expected_out_tag[10] = 2'b00;
    expected_out_value[10] = 32'h00000000;
    test_tag[10] = "small_after_gap";
    test_rst[11] = 1'b0;
    test_in_valid[11] = 1'b1;
    test_in_timestamp[11] = 32'h00030000;
    expected_out_valid[11] = 1'b1;
    expected_out_tag[11] = 2'b00;
    expected_out_value[11] = 32'h00000012;
    test_tag[11] = "flush_candidate";
    test_rst[12] = 1'b1;
    test_in_valid[12] = 1'b0;
    test_in_timestamp[12] = 32'h00000000;
    expected_out_valid[12] = 1'b0;
    expected_out_tag[12] = 2'b00;
    expected_out_value[12] = 32'h00000000;
    test_tag[12] = "flush_reset";
    test_rst[13] = 1'b0;
    test_in_valid[13] = 1'b0;
    test_in_timestamp[13] = 32'h00000000;
    expected_out_valid[13] = 1'b0;
    expected_out_tag[13] = 2'b00;
    expected_out_value[13] = 32'h00000000;
    test_tag[13] = "post_flush_idle";
    test_rst[14] = 1'b0;
    test_in_valid[14] = 1'b1;
    test_in_timestamp[14] = 32'habcd0000;
    expected_out_valid[14] = 1'b0;
    expected_out_tag[14] = 2'b00;
    expected_out_value[14] = 32'h00000000;
    test_tag[14] = "post_reset_first";
    test_rst[15] = 1'b0;
    test_in_valid[15] = 1'b1;
    test_in_timestamp[15] = 32'habcd0000;
    expected_out_valid[15] = 1'b1;
    expected_out_tag[15] = 2'b10;
    expected_out_value[15] = 32'habcd0000;
    test_tag[15] = "post_reset_zero";
    test_rst[16] = 1'b0;
    test_in_valid[16] = 1'b1;
    test_in_timestamp[16] = 32'habcd0005;
    expected_out_valid[16] = 1'b1;
    expected_out_tag[16] = 2'b00;
    expected_out_value[16] = 32'h00000000;
    test_tag[16] = "rand_small_5";
    test_rst[17] = 1'b0;
    test_in_valid[17] = 1'b1;
    test_in_timestamp[17] = 32'habcd0125;
    expected_out_valid[17] = 1'b1;
    expected_out_tag[17] = 2'b00;
    expected_out_value[17] = 32'h00000005;
    test_tag[17] = "rand_medium_288";
    test_rst[18] = 1'b0;
    test_in_valid[18] = 1'b1;
    test_in_timestamp[18] = 32'habcd0125;
    expected_out_valid[18] = 1'b1;
    expected_out_tag[18] = 2'b01;
    expected_out_value[18] = 32'h00000120;
    test_tag[18] = "rand_zero";
    test_rst[19] = 1'b0;
    test_in_valid[19] = 1'b1;
    test_in_timestamp[19] = 32'habce0125;
    expected_out_valid[19] = 1'b1;
    expected_out_tag[19] = 2'b00;
    expected_out_value[19] = 32'h00000000;
    test_tag[19] = "rand_full_65536";
    test_rst[20] = 1'b0;
    test_in_valid[20] = 1'b0;
    test_in_timestamp[20] = 32'h00000000;
    expected_out_valid[20] = 1'b1;
    expected_out_tag[20] = 2'b10;
    expected_out_value[20] = 32'h00010000;
    test_tag[20] = "rand_idle_gap";
    test_rst[21] = 1'b0;
    test_in_valid[21] = 1'b1;
    test_in_timestamp[21] = 32'habce0136;
    expected_out_valid[21] = 1'b0;
    expected_out_tag[21] = 2'b00;
    expected_out_value[21] = 32'h00000000;
    test_tag[21] = "rand_small_17";
    test_rst[22] = 1'b0;
    test_in_valid[22] = 1'b1;
    test_in_timestamp[22] = 32'habcf0135;
    expected_out_valid[22] = 1'b1;
    expected_out_tag[22] = 2'b00;
    expected_out_value[22] = 32'h00000011;
    test_tag[22] = "rand_medium_65535";
    test_rst[23] = 1'b0;
    test_in_valid[23] = 1'b1;
    test_in_timestamp[23] = 32'habcf0138;
    expected_out_valid[23] = 1'b1;
    expected_out_tag[23] = 2'b01;
    expected_out_value[23] = 32'h0000ffff;
    test_tag[23] = "rand_small_3";
    test_rst[24] = 1'b0;
    test_in_valid[24] = 1'b1;
    test_in_timestamp[24] = 32'habd012a8;
    expected_out_valid[24] = 1'b1;
    expected_out_tag[24] = 2'b00;
    expected_out_value[24] = 32'h00000003;
    test_tag[24] = "rand_full_70000";
    test_rst[25] = 1'b0;
    test_in_valid[25] = 1'b0;
    test_in_timestamp[25] = 32'h00000000;
    expected_out_valid[25] = 1'b1;
    expected_out_tag[25] = 2'b10;
    expected_out_value[25] = 32'h00011170;
    test_tag[25] = "drain_last";
    test_rst[26] = 1'b0;
    test_in_valid[26] = 1'b0;
    test_in_timestamp[26] = 32'h00000000;
    expected_out_valid[26] = 1'b0;
    expected_out_tag[26] = 2'b00;
    expected_out_value[26] = 32'h00000000;
    test_tag[26] = "final_idle";
end

    task apply_cycle;
        input integer idx;
        begin
            rst = test_rst[idx];
            in_valid = test_in_valid[idx];
            in_timestamp = test_in_timestamp[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 in_valid=%b in_timestamp=0x%08h",
                         cycle_count, test_tag[idx], expected_out_valid[idx], out_valid,
                         test_rst[idx], test_in_valid[idx], test_in_timestamp[idx]);
                errors = errors + 1;
            end

            if (out_tag !== expected_out_tag[idx]) begin
                $display("ERROR [cycle %0d][%0s]: out_tag mismatch. expected=%b got=%b rst=%b in_valid=%b in_timestamp=0x%08h",
                         cycle_count, test_tag[idx], expected_out_tag[idx], out_tag,
                         test_rst[idx], test_in_valid[idx], test_in_timestamp[idx]);
                errors = errors + 1;
            end

            if (out_value !== expected_out_value[idx]) begin
                $display("ERROR [cycle %0d][%0s]: out_value mismatch. expected=0x%08h got=0x%08h rst=%b in_valid=%b in_timestamp=0x%08h",
                         cycle_count, test_tag[idx], expected_out_value[idx], out_value,
                         test_rst[idx], test_in_valid[idx], test_in_timestamp[idx]);
                errors = errors + 1;
            end

            if ((out_valid === 1'b1) && (out_tag === 2'b11)) begin
                $display("ERROR [cycle %0d][%0s]: out_tag must never be 2'b11 when out_valid=1",
                         cycle_count, test_tag[idx]);
                errors = errors + 1;
            end
        end
    endtask

    initial begin
        rst = 1'b0;
        in_valid = 1'b0;
        in_timestamp = 32'h00000000;

        $display("==============================================");
        $display("   Streaming Timestamp Delta Encoder Testbench");
        $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
