// (C) 2001-2025 Altera Corporation. All rights reserved.
// Your use of Altera Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Altera Program License Subscription 
// Agreement, Altera IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Altera and sold by 
// Altera or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


// $Id: //acds/rel/25.1/ip/iconnect/merlin/altera_merlin_burst_adapter/new_source/altera_wrap_burst_converter.sv#1 $
// $Revision: #1 $
// $Date: 2025/02/06 $

// ------------------------------------------------------
// This component is specially for Wrapping Avalon slave.
// It converts burst length of input packet
// to match slave burst.
// ------------------------------------------------------

`timescale 1 ns / 1 ns

module altera_wrap_burst_converter
#(
  parameter
    // ----------------------------------------
    // Burst length Parameters
    // (real burst length value, not bytecount)
    // ----------------------------------------
    MAX_IN_LEN              = 16,
    MAX_OUT_LEN             = 4,
    ADDR_WIDTH              = 12,
    BNDRY_WIDTH             = 12,
    NUM_SYMBOLS             = 4,
    AXI_SLAVE               = 0,
    OPTIMIZE_WRITE_BURST    = 0,
    SYNC_RESET             =  0, 
    // ------------------
    // Derived Parameters
    // ------------------
    LEN_WIDTH       = log2ceil(MAX_IN_LEN) + 1,
    OUT_LEN_WIDTH   = log2ceil(MAX_OUT_LEN) + 1,
    LOG2_NUMSYMBOLS = log2ceil(NUM_SYMBOLS)
)
(
    input                           clk,
    input                           reset,
    input                           enable_write,
    input                           enable_read,

    input [LEN_WIDTH   - 1 : 0]     in_len,
    input [LEN_WIDTH   - 1 : 0]     first_len,
    input                           in_sop,

    input [ADDR_WIDTH  - 1 : 0]     in_addr,
    input [ADDR_WIDTH  - 1 : 0]     in_addr_reg,
    input [BNDRY_WIDTH - 1 : 0]     in_boundary,
    input [BNDRY_WIDTH - 1 : 0]     in_burstwrap,
    input [BNDRY_WIDTH - 1 : 0]     in_burstwrap_reg,

    // converted output length
    // out_len         : compressed burst, read
    // uncompressed_len: uncompressed, write
    output reg [LEN_WIDTH - 1 : 0]  out_len,
    output reg [LEN_WIDTH - 1 : 0]  uncompr_out_len,

    // Compressed address output
    output reg [ADDR_WIDTH - 1 : 0] out_addr,
    output reg                      new_burst_export
);

    // ------------------------------
    // Local parameters
    // ------------------------------
    localparam
        OUT_BOUNDARY        = MAX_OUT_LEN * NUM_SYMBOLS,
        ADDR_SEL            = log2ceil(OUT_BOUNDARY);

    // ----------------------------------------
    // Signals for wrapping support
    // ----------------------------------------
    reg [LEN_WIDTH - 1 : 0]        remaining_len;
    reg [LEN_WIDTH - 1 : 0]        next_out_len;
    reg [LEN_WIDTH - 1 : 0]        next_rem_len;
    reg [LEN_WIDTH - 1 : 0]        uncompr_remaining_len;
    reg                            new_burst;
    reg                            uncompr_sub_burst;
    reg [LEN_WIDTH - 1 : 0]        next_uncompr_out_len;
    reg [LEN_WIDTH - 1 : 0]        next_uncompr_sub_len;

    // Avoid QIS warning
    wire [OUT_LEN_WIDTH - 1 : 0]   max_out_length;
    assign max_out_length  = MAX_OUT_LEN[OUT_LEN_WIDTH - 1 : 0];

    // ----------------------------------------
    // Calculate aligned length for WRAP burst
    // ----------------------------------------
    reg [ADDR_WIDTH - 1 : 0]       extended_burstwrap;
    reg [ADDR_WIDTH - 1 : 0]       extended_burstwrap_reg;

    always_comb begin
        extended_burstwrap      = {{(ADDR_WIDTH - BNDRY_WIDTH) {in_burstwrap[BNDRY_WIDTH - 1]}}, in_burstwrap};
        extended_burstwrap_reg  = {{(ADDR_WIDTH - BNDRY_WIDTH) {in_burstwrap_reg[BNDRY_WIDTH - 1]}}, in_burstwrap_reg};
        new_burst_export        = new_burst;
    end

    // Generation of internal reset synchronization
   reg internal_sclr;
   generate if (SYNC_RESET == 1) begin : rst_syncronizer
      always @ (posedge clk) begin
         internal_sclr <= reset;
      end
   end
   endgenerate

    // -------------------------------------------
    // length calculation
    // -------------------------------------------
    reg [LEN_WIDTH -1 : 0] next_uncompr_remaining_len;
    always_comb begin
        // Signals name
        // *_uncompr_* --> uncompressed transaction
        // -------------------------------------------
        // Always use max_out_length as possible.
        // Else use the remaining length.
        // If in length smaller and not cross bndry or same, pass thru.

        if (in_sop) begin
            uncompr_remaining_len = in_len;
        end
        else begin
            uncompr_remaining_len = next_uncompr_remaining_len;
        end
    end // always_comb

    // compressed transactions
    always_comb begin : proc_compressed_read
        remaining_len  = in_len;
        if (!new_burst)
            remaining_len = next_rem_len;
    end

    always_comb begin
        next_uncompr_out_len = first_len;
        if (in_sop) begin
            next_uncompr_out_len = first_len;
        end
        else begin
            if (uncompr_sub_burst)
                next_uncompr_out_len = next_uncompr_sub_len;
            else begin
                if (uncompr_remaining_len < max_out_length)
                    next_uncompr_out_len = uncompr_remaining_len;
                else
                    next_uncompr_out_len = max_out_length;
            end
        end
    end

    // Compressed transaction: Always try to send MAX out_len then remaining length.
    // Seperate it as the main difference is the first out len.
    // For a WRAP burst, the first beat is the aligned length, then similar to INCR.
    always_comb begin
        if (new_burst) begin
            next_out_len = first_len;
        end
        else begin
            next_out_len = max_out_length;
            if (remaining_len < max_out_length) begin
                next_out_len = remaining_len;
            end
        end
    end // always_comb

    // --------------------------------------------------
    // Length remaining calculation : Compressed
    // --------------------------------------------------
    // length remaining for compressed transaction
    // for wrap, need special handling for first out length

    always_ff @(posedge clk) begin
        if (enable_read) begin
            if (new_burst)
                next_rem_len <= in_len - first_len;
            else
                next_rem_len <= next_rem_len - max_out_length;
        end
    end // always_ff @

    //generate
    //  if (SYNC_RESET == 0) begin : async_rst1
    //   always_ff @(posedge clk, posedge reset) begin
    //       if (reset)
    //           next_rem_len  <= 0;
    //       else if (enable_read) begin
    //           if (new_burst)
    //               next_rem_len <= in_len - first_len;
    //           else
    //               next_rem_len <= next_rem_len - max_out_length;
    //       end
    //   end // always_ff @
    // end // async_rst1
    // else begin // sync_rst1
    //   always_ff @(posedge clk) begin
    //       if (internal_sclr)
    //           next_rem_len  <= 0;
    //       else if (enable_read) begin
    //           if (new_burst)
    //               next_rem_len <= in_len - first_len;
    //           else
    //               next_rem_len <= next_rem_len - max_out_length;
    //       end
    //   end // always_ff @
    //  end // sync_rst1
    // endgenerate
    
    // --------------------------------------------------
    // Length remaining calculation : Uncompressed
    // --------------------------------------------------

    always_ff @(posedge clk) begin
        if (enable_write) begin
            if (in_sop)
                next_uncompr_remaining_len  <= in_len - first_len;
            else if (!uncompr_sub_burst)
                next_uncompr_remaining_len  <= next_uncompr_remaining_len - max_out_length;
        end
    end // always_ff @
    
    // length for each sub-burst if it needs to chop the burst
    always_ff @(posedge clk) begin
        if (enable_write) begin
            next_uncompr_sub_len  <= next_uncompr_out_len - 1'b1; // in term of length, it just reduces 1
        end
    end

   generate 
   if (SYNC_RESET == 0) begin : async_rst2
       //always_ff @(posedge clk, posedge reset) begin
       //    if (reset) begin
       //        next_uncompr_remaining_len  <= 0;
       //    end
       //    else if (enable_write) begin
       //        if (in_sop)
       //            next_uncompr_remaining_len  <= in_len - first_len;
       //        else if (!uncompr_sub_burst)
       //            next_uncompr_remaining_len  <= next_uncompr_remaining_len - max_out_length;
       //    end
       //end // always_ff @

       //// length for each sub-burst if it needs to chop the burst
       //always_ff @(posedge clk, posedge reset) begin
       //    if (reset) begin
       //        next_uncompr_sub_len  <= 0;
       //    end
       //    else if (enable_write) begin
       //        next_uncompr_sub_len  <= next_uncompr_out_len - 1'b1; // in term of length, it just reduces 1
       //    end
       //end

       // the sub-burst still active
       always_ff @(posedge clk, posedge reset) begin
           if (reset) begin
               uncompr_sub_burst <= 0;
           end
           else if (enable_write) begin
               uncompr_sub_burst <= (next_uncompr_out_len > 1'b1);
           end
       end
   end // async_rst1
   else begin // sync_rst2

       //always_ff @(posedge clk) begin
       //    if (internal_sclr) begin
       //        next_uncompr_remaining_len  <= 0;
       //    end
       //    else if (enable_write) begin
       //        if (in_sop)
       //            next_uncompr_remaining_len  <= in_len - first_len;
       //        else if (!uncompr_sub_burst)
       //            next_uncompr_remaining_len  <= next_uncompr_remaining_len - max_out_length;
       //    end
       //end // always_ff @
       //
       //// length for each sub-burst if it needs to chop the burst
       //always_ff @(posedge clk) begin
       //    if (internal_sclr) begin
       //        next_uncompr_sub_len  <= 0;
       //    end
       //    else if (enable_write) begin
       //        next_uncompr_sub_len  <= next_uncompr_out_len - 1'b1; // in term of length, it just reduces 1
       //    end
       //end

       // the sub-burst still active
       always_ff @(posedge clk) begin
           if (internal_sclr) begin
               uncompr_sub_burst <= 0;
           end
           else if (enable_write) begin
               uncompr_sub_burst <= (next_uncompr_out_len > 1'b1);
           end
       end
   end // sync_rst2
   endgenerate

    // --------------------------------------------------
    // Control signals
    // --------------------------------------------------
    wire end_compressed_sub_burst;
    assign end_compressed_sub_burst  = (remaining_len == next_out_len);

    // new_burst:
    //  the converter takes in_len for new caculation
    generate
     if (SYNC_RESET == 0) begin : async_rst3
       always_ff @(posedge clk, posedge reset) begin
           if (reset) begin
               new_burst   <= 1;
           end
           else if (enable_read) begin
               new_burst   <= end_compressed_sub_burst;
           end
       end
     end // async_rst3
     else begin // sync_rst3
       always_ff @(posedge clk) begin
           if (internal_sclr) begin
               new_burst   <= 1;
           end
           else if (enable_read) begin
               new_burst   <= end_compressed_sub_burst;
           end
       end
     end // sync_rst3
    endgenerate
    // --------------------------------------------------
    // Output length
    // --------------------------------------------------
    // register out_len for compressed trans
    always_ff @(posedge clk) begin
        if (enable_read) begin
            out_len <= next_out_len;
        end
    end

    //generate 
    // if (SYNC_RESET == 0) begin : async_rst4
    //   always_ff @(posedge clk, posedge reset) begin
    //       if (reset) begin
    //           out_len <= 0;
    //       end
    //       else if (enable_read) begin
    //           out_len <= next_out_len;
    //       end
    //   end
    //  end // async_rst4
    //  else begin // sync_rst4
    //    always_ff @(posedge clk) begin
    //       if (internal_sclr) begin
    //           out_len <= 0;
    //       end
    //       else if (enable_read) begin
    //           out_len <= next_out_len;
    //       end
    //   end
    // end // sync_rst4
    //endgenerate

    // register uncompr_out_len for uncompressed trans
    generate
        if (OPTIMIZE_WRITE_BURST) begin : optimized_write_burst_len
          always_ff @(posedge clk) begin
              if (enable_read) begin
                  uncompr_out_len <= first_len;
              end
          end

          //if (SYNC_RESET == 0) begin : async_rst5
          //  always_ff @(posedge clk, posedge reset) begin
          //      if (reset) begin
          //          uncompr_out_len <= '0;
          //      end
          //      //else if (enable_write) begin
          //      else if (enable_read) begin
          //          uncompr_out_len <= first_len;
          //      end
          //  end
          //end // async_rst5
          //else begin // sync_rst5
          //  always_ff @(posedge clk) begin
          //      if (internal_sclr) begin
          //          uncompr_out_len <= '0;
          //      end
          //      //else if (enable_write) begin
          //      else if (enable_read) begin
          //          uncompr_out_len <= first_len;
          //      end
          //  end
          //end // sync_rst5
        end
        else begin : unoptimized_write_burst_len
          always_ff @(posedge clk) begin
              if (enable_write) begin
                  uncompr_out_len <= next_uncompr_out_len;
              end
          end

          //if (SYNC_RESET == 0) begin : async_rst6
          //  always_ff @(posedge clk, posedge reset) begin
          //      if (reset) begin
          //          uncompr_out_len <= '0;
          //      end
          //      else if (enable_write) begin
          //          uncompr_out_len <= next_uncompr_out_len;
          //      end
          //  end
          // end // async_rst6
          // else begin // sync_rst6
          //  always_ff @(posedge clk) begin
          //      if (internal_sclr) begin
          //          uncompr_out_len <= '0;
          //      end
          //      else if (enable_write) begin
          //          uncompr_out_len <= next_uncompr_out_len;
          //      end
          //  end
          // end // sync_rst6
        end // else unoptimized
    endgenerate

    // --------------------------------------------------
    // Address calculation
    // --------------------------------------------------
    reg [ADDR_WIDTH - 1 : 0]        addr_incr;
    localparam [ADDR_WIDTH - 1 : 0] ADDR_INCR = MAX_OUT_LEN << LOG2_NUMSYMBOLS;
    assign addr_incr  = ADDR_INCR[ADDR_WIDTH - 1 : 0];

    reg [ADDR_WIDTH - 1 : 0]    next_out_addr;
    reg [ADDR_WIDTH - 1 : 0]    incremented_addr;
    
    always_ff @(posedge clk) begin
            if (enable_read) begin
                out_addr <=  (next_out_addr);
            end
    end // always_ff @

    //generate 
    // if (SYNC_RESET == 0) begin : async_rst7
    //   always_ff @(posedge clk, posedge reset) begin
    //       if (reset) begin
    //           out_addr <= '0;
    //       end
    //       else begin
    //           if (enable_read) begin
    //               out_addr <=  (next_out_addr);
    //           end
    //       end
    //   end // always_ff @
    //  end // async_rst7
    //  else begin // sync_rst7
    //   always_ff @(posedge clk) begin
    //       if (internal_sclr) begin
    //           out_addr <= '0;
    //       end
    //       else begin
    //           if (enable_read) begin
    //               out_addr <=  (next_out_addr);
    //           end
    //       end
    //   end // always_ff @
    //  end // sync_rst7
    // endgenerate

    // use burstwrap/burstwrap_reg to calculate address incrementing
    always_ff @(posedge clk) begin
        if (enable_read) begin
            incremented_addr <= ((next_out_addr + addr_incr) & extended_burstwrap_reg);
            if (new_burst) begin
                incremented_addr <= ((next_out_addr + (first_len << LOG2_NUMSYMBOLS)) & extended_burstwrap); //byte address
            end
        end
    end // always_ff @

    //generate
    // if (SYNC_RESET == 0) begin : async_rst8
    //   always_ff @(posedge clk, posedge reset) begin
    //       if (reset) begin
    //           incremented_addr <= '0;
    //       end
    //       else if (enable_read) begin
    //           incremented_addr <= ((next_out_addr + addr_incr) & extended_burstwrap_reg);
    //           if (new_burst) begin
    //               incremented_addr <= ((next_out_addr + (first_len << LOG2_NUMSYMBOLS)) & extended_burstwrap); //byte address
    //           end
    //       end
    //   end // always_ff @
    // end // async_rst8
    // else begin // sync_rst8
    //    always_ff @(posedge clk) begin
    //       if (internal_sclr) begin
    //           incremented_addr <= '0;
    //       end
    //       else if (enable_read) begin
    //           incremented_addr <= ((next_out_addr + addr_incr) & extended_burstwrap_reg);
    //           if (new_burst) begin
    //               incremented_addr <= ((next_out_addr + (first_len << LOG2_NUMSYMBOLS)) & extended_burstwrap); //byte address
    //           end
    //       end
    //   end // always_ff @
    // end // sync_rst8
    // endgenerate
  
    always_comb begin
        next_out_addr  = in_addr;
        if (!new_burst) begin
            next_out_addr = in_addr_reg & ~extended_burstwrap_reg | incremented_addr;
        end
    end

    // --------------------------------------------------
    // Calculates the log2ceil of the input value
    // --------------------------------------------------
    function integer log2ceil;
        input integer val;
        reg[31:0] i;

        begin
            i = 1;
            log2ceil = 0;

            while (i < val) begin
                log2ceil = log2ceil + 1;
                i = i[30:0] << 1;
            end
        end
    endfunction

endmodule
`ifdef QUESTA_INTEL_OEM
`pragma questa_oem_00 "STbanMqy8UpvYjUJXR/AuKniYmV/XZ0zJn/xzMvub2v72ixIJ7RUF/NrPoLZm9VpGYB+6/h3sGK2eBCGD8WgzYV4s04rwJV/gFlBTxheWC+sVCOTbBzPz91EJjQVEkx36RN/HJdQoD8GpU5YEjaU0qB+68AKbYYQBDCCuFskkWV1Ncy+T+wBSgFHyoxeoiMh9z1OKFVWiKp5JLyFZLmSp4sAD90mao8g5EAIvtCteI9pjfKDwWi8YWcPbQJmy6jGR3XEIa53IxxxuiWeCHZocpcCxlZohgRYz3QoJiutJZq9ZBC9BRyFM53huJ7hrVDXZud5ERf7cKlkPr5cN3isT/JfdL5N4jWcTBgoQLWj2w3e/R5rXotB/pv/cUaU/Y+723OAlZWZ/8//kCMIWbvTpLsJ+2dQWPy9S6dpYauAduqlW9tBjFA1vFcFSt/Sy6E82YwVvu+fRYNkEX3jtw2YhwUoL2NIeofWlA+0tuPqEvof+bxoUkZiyHE+aw0rYY8cxRLLZF1fji/PAtjjH5qgYSFVVBu7muIuLvUrON6350LGMcBdHRb9YKIN1EZlWmsMbxJRUBDBfWic6bsfurKUfgB/6kDB8XP6CN523zMk6g108h0sWtQEjIhayZ8SFtbHSG7TyeMkR8PVK0jQmp9Soqe+v7TCJ87cmzaScE2EdGWi17VYzphDPJ1P6WCbcibp/fbv2YH6hA6adit1aECO7HKhJ9yUjZmWyFcH0A+KMbx1IqlxVBlmgCH0tJj8CRpLUHvD/i2I/SPD4TRPbbBm/+AHhRHPVqWIlG5Vzb3oL5GqjGPIACOZSVxkowt8qYviklXCR9pI2SKwp2vkDFNBSPbf1QKtyBzn8+fCEWqgLtJeEJ1MSknNXi6STQ0xOlXYHoyndlyUzg92Kr/AN9IEBa6qDEvLri78JJGnCU8a6VvuYU1TWQAmyW2slozJnwrzh0jmtuHOffKRFWPRoKQNIPdCdBiAS5yruAQkbnx4BsNH2zyll2fHT2mS7e8J/UlV"
`endif