// retroDE_ps2 — gs_reciprocal_stub (Ch301)
//
// Pipelined fixed-point reciprocal unit for PERSPECTIVE-CORRECT texture
// interpolation. Computes  recip = floor(2**SCALE / q)  for an unsigned input
// q, with NO divider in the datapath — a serialized per-pixel divide would
// stall the ~1-pixel/cycle rasterizer (the architect's explicit constraint).
//
// Method — range-reduced table lookup (classic LUT reciprocal):
//   1. e   = position of q's most-significant set bit (0..Q_W-1).
//   2. M   = q normalized to an IDX_BITS-wide mantissa with its MSB at the top
//            (M in [2**(IDX_BITS-1) .. 2**IDX_BITS-1)), i.e. q ~= M * 2**(e-(IDX_BITS-1)).
//   3. recip = LUT[M] >> e, where LUT[M] = floor(2**(SCALE+IDX_BITS-1) / M).
//      Proof: LUT[M] >> e ~= 2**(SCALE+IDX_BITS-1)/(M * 2**e)
//                          = 2**SCALE / (M * 2**(e-(IDX_BITS-1)))
//                          = 2**SCALE / q.   ✓ (uniform for all e)
//
// Accuracy is ~1 part in 2**IDX_BITS (relative). For the first perspective
// rung (texel coords <= 63) an 8-bit mantissa gives sub-texel error; bump
// IDX_BITS for tighter precision later if real traces demand it.
//
// Pipeline: 3 stages (LAT=3), one result per cycle.
//   S0: register input q + valid.
//   S1: e = msb(q); M = normalize(q).
//   S2: lut_out = LUT[M]; carry e.
//   S3: recip = lut_out >> e; out_valid.
//
// q==0 saturates to all-ones (1/0 -> +inf), which is harmless for the demo
// (q = 1/w with w finite positive is always > 0).
//
// LUT init is a computed `initial` for-loop (Quartus infers ROM from it). If a
// future synth flow rejects it, switch to $readmemh of a generated .mem.

`timescale 1ns/1ps

module gs_reciprocal_stub #(
    parameter int Q_W      = 24,   // input width (q in [1, 2**Q_W))
    parameter int IDX_BITS = 8,    // mantissa / LUT-index width (256 entries)
    parameter int SCALE    = 24,   // output = floor(2**SCALE / q)
    parameter int OUT_W    = 25    // output width (recip <= 2**SCALE for q>=1)
) (
    input  logic              clk,
    input  logic              rst_n,
    input  logic              in_valid,
    input  logic [Q_W-1:0]    q,
    output logic              out_valid,
    output logic [OUT_W-1:0]  recip
);

    localparam int LUT_N    = (1 << IDX_BITS);
    localparam int TOP_BIT  = IDX_BITS - 1;          // mantissa MSB position
    // LUT entries: floor(2**(SCALE+TOP_BIT) / M). Only M in [2**TOP_BIT .. LUT_N-1]
    // are ever addressed (M always has its MSB set after normalization).
    localparam int LUT_W    = SCALE + 1;             // wide enough for M=2**TOP_BIT
    logic [LUT_W-1:0] lut [0:LUT_N-1];

    initial begin
        // 2**(SCALE+TOP_BIT) as a 64-bit constant numerator.
        longint unsigned num;
        num = (64'd1 << (SCALE + TOP_BIT));
        for (int m = 0; m < LUT_N; m++) begin
            if (m == 0) lut[m] = '0;
            else        lut[m] = LUT_W'(num / m);
        end
    end

    // --- combinational msb-detect + normalize (S0->S1 inputs) ---
    function automatic int unsigned msb_index(input logic [Q_W-1:0] v);
        msb_index = 0;
        for (int i = 0; i < Q_W; i++)
            if (v[i]) msb_index = i;
    endfunction

    // ---------------- S1: e + mantissa (from the LIVE input) ----------------
    // The msb-detect + normalize is combinational on the input q and registered
    // here, so the whole unit is exactly 3 register stages (S1/S2/S3) → LAT=3.
    logic                 s1_valid;
    logic [$clog2(Q_W):0] s1_e;
    logic [IDX_BITS-1:0]  s1_m;
    logic                 s1_zero;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            s1_valid <= 1'b0; s1_e <= '0; s1_m <= '0; s1_zero <= 1'b0;
        end else begin
            int unsigned e;
            logic [Q_W-1:0] norm;
            e        = msb_index(q);
            s1_valid <= in_valid;
            s1_zero  <= (q == '0);
            s1_e     <= ($clog2(Q_W)+1)'(e);
            // normalize so the mantissa MSB sits at bit TOP_BIT
            if (e >= TOP_BIT) norm = q >> (e - TOP_BIT);
            else              norm = q << (TOP_BIT - e);
            s1_m     <= norm[IDX_BITS-1:0];
        end
    end

    // ---------------- S2: LUT read ------------------------
    logic                 s2_valid;
    logic [$clog2(Q_W):0] s2_e;
    logic [LUT_W-1:0]     s2_lut;
    logic                 s2_zero;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            s2_valid <= 1'b0; s2_e <= '0; s2_lut <= '0; s2_zero <= 1'b0;
        end else begin
            s2_valid <= s1_valid;
            s2_e     <= s1_e;
            s2_lut   <= lut[s1_m];
            s2_zero  <= s1_zero;
        end
    end

    // ---------------- S3: shift back ----------------------
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            out_valid <= 1'b0; recip <= '0;
        end else begin
            logic [LUT_W-1:0] shifted;
            out_valid <= s2_valid;
            shifted   = s2_lut >> s2_e;
            if (s2_zero)              recip <= '1;                 // 1/0 -> saturate
            else if (shifted > OUT_W'('1)) recip <= '1;           // clamp to OUT_W
            else                      recip <= OUT_W'(shifted);
        end
    end

endmodule : gs_reciprocal_stub
