// comp_decomp_sync.v - ML-KEM coefficient compression/decompression
//
// Streaming: one coefficient per cycle through pipeline_reg.
// mode=0: compress   — round((2^d * x) / Q) mod 2^d
// mode=1: decompress — round((Q * x) / 2^d) mod Q
// Uses round-half-up (round(2.5)=3, round(3.5)=4).
// Integer arithmetic:
//   compress:   (x * 2^d + Q/2) / Q,  lower d bits as result
//   decompress: (x * Q + 2^(d-1)) / 2^d, result (always < Q)

`include "sync_rtl/common/defines.vh"

module comp_decomp_sync (
    input         clk,
    input         rst_n,
    input  [11:0] coeff_in,
    input  [4:0]  d,
    input         mode,          // 0=compress, 1=decompress
    input         valid_i,
    output        ready_o,
    output [11:0] coeff_out,
    output        valid_o,
    input         ready_i
);

    // 2^d for d in {4,5,10,11}; fits in 12 bits (max 2048)
    wire [11:0] two_pow_d;
    assign two_pow_d = 12'd1 << d;

    // Product: 12-bit * 12-bit = max 2048*3328 = 6,815,744 → 23 bits (use 24)
    wire [23:0] product;

    // compress: x * 2^d;  decompress: x * Q
    assign product = mode ? {12'b0, coeff_in} * {12'b0, 12'(`Q)}
                          : {12'b0, coeff_in} * {12'b0, two_pow_d};

    // Rounding offset: compress→Q/2=1664; decompress→2^(d-1)
    wire [11:0] round_off;
    assign round_off = mode ? (two_pow_d >> 1) : 12'd1664;

    // Dividend = product + round_off  (max ~ 6,817,408 fits in 24 bits)
    wire [23:0] dividend;
    assign dividend = product + {12'b0, round_off};

    // Divisor: compress→Q=3329; decompress→2^d
    wire [11:0] divisor;
    assign divisor = mode ? two_pow_d : 12'(`Q);

    // Integer division (both ops positive → floor)
    // Quotient is computed as 24b (widest operand) but fits in 12b
    /* verilator lint_off WIDTHTRUNC */
    wire [11:0] div_result;
    assign div_result = dividend / {12'b0, divisor};
    /* verilator lint_on WIDTHTRUNC */

    // Final modular reduction
    // compress: lower d bits  (mask with 2^d - 1)
    // decompress: result < Q always, but apply mod Q for safety
    wire [11:0] mod_result;
    assign mod_result = mode ? (div_result % 12'(`Q)) : (div_result & (two_pow_d - 1'b1));

    // Pipeline through valid/ready stage
    pipeline_reg #(.DW(12)) u_pipe (
        .clk    (clk),
        .rst_n  (rst_n),
        .data_i (mod_result),
        .valid_i(valid_i),
        .ready_o(ready_o),
        .data_o (coeff_out),
        .valid_o(valid_o),
        .ready_i(ready_i)
    );

endmodule
