
import random

def perceptron(x0, x1, x2, x3):
    """Compute perceptron output with fixed weights."""
    z = 3*x0 - 2*x1 + 4*x2 - x3 - 5
    y = 1 if z >= 0 else 0
    return y, z

def to_signed_12bit(val):
    if val < 0:
        return (1 << 12) + val
    return val & 0xFFF

vectors = []

# 1. Boundary Cases
vectors.append((0, 0, 0, 0))
vectors.append((255, 255, 255, 255))
vectors.append((255, 0, 0, 0))
vectors.append((0, 255, 0, 0))
vectors.append((0, 0, 255, 0))
vectors.append((0, 0, 0, 255))

# 2. Decision Boundary Cases
vectors.append((2, 0, 0, 1)) # z = 0
vectors.append((2, 0, 0, 2)) # z = -1

# 3. Random Cases
for _ in range(50):
    vectors.append((random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))

with open('testbench.v', 'w') as f:
    f.write("""`timescale 1ns/1ps

module tb_perceptron;

    // Inputs
    reg [7:0] x0;
    reg [7:0] x1;
    reg [7:0] x2;
    reg [7:0] x3;

    // Outputs
    wire y;
    wire [11:0] z;

    // Instantiate the Unit Under Test (UUT)
    perceptron uut (
        .x0(x0), 
        .x1(x1), 
        .x2(x2), 
        .x3(x3), 
        .y(y), 
        .z(z)
    );

    // Test vectors: 45 bits wide
    // {x0[7:0], x1[7:0], x2[7:0], x3[7:0], y, z[11:0]}
""")
    f.write(f"    localparam NUM_TESTS = {len(vectors)};\n")
    f.write("""    reg [44:0] test_vectors [0:NUM_TESTS-1];
    reg [44:0] vector;
    
    integer i;
    integer errors;

    initial begin
        // Initialize Inputs
        x0 = 0;
        x1 = 0;
        x2 = 0;
        x3 = 0;
        errors = 0;
        
        // Test Vectors
""")

    for i, (x0, x1, x2, x3) in enumerate(vectors):
        y, z = perceptron(x0, x1, x2, x3)
        z_hex = to_signed_12bit(z)
        vector_hex = f"{(x0<<37) | (x1<<29) | (x2<<21) | (x3<<13) | (y<<12) | z_hex:012x}"
        f.write(f"        test_vectors[{i}] = 45'h{vector_hex}; // x0={x0}, x1={x1}, x2={x2}, x3={x3}, y={y}, z={z}\n")

    f.write("""
        // Wait for global reset to finish
        #100;
        
        $display("Starting Perceptron Testbench...");

        for (i = 0; i < NUM_TESTS; i = i + 1) begin
            vector = test_vectors[i];
            x0 = vector[44:37];
            x1 = vector[36:29];
            x2 = vector[28:21];
            x3 = vector[20:13];
            
            #10; // Wait for combinational output
            
            if (y !== vector[12] || z !== vector[11:0]) begin
                $display("FAIL: Test %0d | Inputs: x0=%d x1=%d x2=%d x3=%d | Expected: y=%d z=%d | Got: y=%d z=%1d", 
                         i, x0, x1, x2, x3, vector[12], $signed(vector[11:0]), y, $signed(z));
                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
""")

print("Successfully generated testbench.v")
