`timescale 1ns/1ps
module uart_tx_example_tb;
    // Short bit period keeps this regression quick; the ratio is parameterized.
    localparam integer N = 8;
    logic clk = 0, rst_n = 0, tx_start = 0;
    logic [7:0] tx_data = 0;
    wire tx_ready, tx_out;
    always #5 clk = ~clk;
    uart_tx_example #(.CLK_FREQ(80), .BAUD_RATE(10)) dut (.*);

    task automatic check_frame(input logic [7:0] data_byte);
        logic [10:0] expected;
        expected = {1'b1, ^data_byte, data_byte, 1'b0};
        @(negedge clk);
        if (!tx_ready) $fatal(1, "Not ready before request");
        tx_data = data_byte;
        tx_start = 1;
        @(posedge clk); #1;
        for (int bit_no = 0; bit_no < 11; bit_no++) begin
            for (int cycle_no = 0; cycle_no < N; cycle_no++) begin
                if (tx_ready !== 0 || tx_out !== expected[bit_no])
                    $fatal(1, "Frame/timing mismatch bit=%0d cycle=%0d", bit_no, cycle_no);
                @(negedge clk);
                // Change parity and data immediately after acceptance; inject
                // another one-clock request while busy. Neither may alter frame.
                tx_data = data_byte ^ 8'h01;
                tx_start = (bit_no == 2 && cycle_no == 0);
                @(posedge clk); #1;
            end
        end
        if (tx_ready !== 1 || tx_out !== 1) $fatal(1, "Stop duration/ready mismatch");
    endtask

    initial begin
        repeat (3) @(negedge clk);
        rst_n = 1;
        // Vary request phase and data/parity patterns.
        for (int phase = 0; phase < N; phase++) begin
            repeat (phase) @(negedge clk);
            check_frame(8'hA5);
            check_frame(8'h01);
            check_frame(8'h00);
            check_frame(8'hFF);
        end
        // Reset in the middle of a frame must restore idle.
        @(negedge clk); tx_start = 1;
        @(negedge clk); tx_start = 0;
        repeat (3) @(negedge clk);
        rst_n = 0;
        #1;
        if (tx_out !== 1 || tx_ready !== 1) $fatal(1, "Reset did not restore idle");
        @(negedge clk); rst_n = 1;
        check_frame(8'h3C);
        $display("PASS: 33 frames, request phases, input mutation, busy rejection, stop timing, reset");
        $finish;
    end
    initial begin
        #1000000;
        $fatal(1, "Timeout");
    end
endmodule
