// (C) 2001-2016 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 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 MegaCore Function 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/16.0/ip/merlin/altera_merlin_slave_translator/altera_merlin_slave_translator.sv#1 $
// $Revision: #1 $
// $Date: 2016/02/08 $
// $Author: swbranch $

// -------------------------------------
// Merlin Slave Translator
//
// Translates Universal Avalon  MM Slave
// to any Avalon MM Slave
// -------------------------------------
//
//Notable Note: 0 AV_READLATENCY is not allowed and will be converted to a 1 cycle readlatency in all cases but one
//If you declare a slave with fixed read timing requirements, the readlatency of such a slave will be allowed to be zero
//The key feature here is that no same cycle turnaround data is processed through the fabric.

//import avalon_utilities_pkg::*;

`timescale 1 ns / 1 ns

module altera_merlin_slave_translator #(
   parameter
   //Widths
   AV_ADDRESS_W           = 32,
   AV_DATA_W              = 32,
   AV_BURSTCOUNT_W        = 4,
   AV_BYTEENABLE_W        = 4,
   UAV_BYTEENABLE_W       = 4,

   //Read Latency
   AV_READLATENCY          = 1,

   //Timing
   AV_READ_WAIT_CYCLES     = 0,
   AV_WRITE_WAIT_CYCLES    = 0,
   AV_SETUP_WAIT_CYCLES    = 0,
   AV_DATA_HOLD_CYCLES     = 0,

   //Optional Port Declarations
   USE_READDATAVALID       = 1,
   USE_WAITREQUEST         = 1,
   USE_READRESPONSE        = 0,
   USE_WRITERESPONSE       = 0,

   //Variable Addressing
   AV_SYMBOLS_PER_WORD     = 4,
   AV_ADDRESS_SYMBOLS      = 0,
   AV_BURSTCOUNT_SYMBOLS   = 0,
   BITS_PER_WORD           = clog2_plusone(AV_SYMBOLS_PER_WORD - 1),
   UAV_ADDRESS_W           = 38,
   UAV_BURSTCOUNT_W        = 10,
   UAV_DATA_W              = 32,

   AV_CONSTANT_BURST_BEHAVIOR       = 0,
   UAV_CONSTANT_BURST_BEHAVIOR      = 0,
   CHIPSELECT_THROUGH_READLATENCY   = 0,

   // Tightly-Coupled Options
   USE_UAV_CLKEN           = 0,
   AV_REQUIRE_UNALIGNED_ADDRESSES = 0
) (

   // -------------------
   // Clock & Reset
   // -------------------
   input wire                             clk,
   input wire                             reset,

   // -------------------
   // Universal Avalon Slave
   // -------------------

   input wire [UAV_ADDRESS_W - 1 : 0]     uav_address,
   input wire [UAV_DATA_W - 1 : 0]        uav_writedata,
   input wire                             uav_write,
   input wire                             uav_read,
   input wire [UAV_BURSTCOUNT_W - 1 : 0]  uav_burstcount,
   input wire [UAV_BYTEENABLE_W - 1 : 0]  uav_byteenable,
   input wire                             uav_lock,
   input wire                             uav_debugaccess,
   input wire                             uav_clken,

   output logic                           uav_readdatavalid,
   output logic                           uav_waitrequest,
   output logic [UAV_DATA_W - 1 : 0]      uav_readdata,
   output logic [1:0]                     uav_response,
   // input wire                             uav_writeresponserequest,
   output logic                           uav_writeresponsevalid,

   // -------------------
   // Customizable Avalon Master
   // -------------------
   output logic [AV_ADDRESS_W - 1 : 0]    av_address,
   output logic [AV_DATA_W - 1 : 0]       av_writedata,
   output logic                           av_write,
   output logic                           av_read,
   output logic [AV_BURSTCOUNT_W - 1 : 0] av_burstcount,
   output logic [AV_BYTEENABLE_W - 1 : 0] av_byteenable,
   output logic [AV_BYTEENABLE_W - 1 : 0] av_writebyteenable,
   output logic                           av_begintransfer,
   output wire                            av_chipselect,
   output logic                           av_beginbursttransfer,
   output logic                           av_lock,
   output wire                            av_clken,
   output wire                            av_debugaccess,
   output wire                            av_outputenable,

   input logic [AV_DATA_W - 1 : 0]        av_readdata,
   input logic                            av_readdatavalid,
   input logic                            av_waitrequest,

   input logic [1:0]                      av_response,
   // output logic                           av_writeresponserequest,
   input wire                             av_writeresponsevalid

);

   function integer clog2_plusone;
      input [31:0] Depth;
      integer i;
      begin
         i = Depth;
         for(clog2_plusone = 0; i > 0; clog2_plusone = clog2_plusone + 1)
            i = i >> 1;
      end
   endfunction

   function integer max;
      //returns the larger of two passed arguments
      input [31:0] one;
      input [31:0] two;
      if(one > two)
         max=one;
      else
         max=two;
   endfunction // int

   localparam AV_READ_WAIT_INDEXED      = (AV_SETUP_WAIT_CYCLES + AV_READ_WAIT_CYCLES);
   localparam AV_WRITE_WAIT_INDEXED     = (AV_SETUP_WAIT_CYCLES + AV_WRITE_WAIT_CYCLES);
   localparam AV_DATA_HOLD_INDEXED      = (AV_WRITE_WAIT_INDEXED + AV_DATA_HOLD_CYCLES);
   localparam LOG2_OF_LATENCY_SUM       = max(clog2_plusone(AV_READ_WAIT_INDEXED + 1),clog2_plusone(AV_DATA_HOLD_INDEXED + 1));
   localparam BURSTCOUNT_SHIFT_SELECTOR = AV_BURSTCOUNT_SYMBOLS ? 0 : BITS_PER_WORD;
   localparam ADDRESS_SHIFT_SELECTOR    = AV_ADDRESS_SYMBOLS ? 0 : BITS_PER_WORD;
   localparam ADDRESS_HIGH              = ( UAV_ADDRESS_W > AV_ADDRESS_W + ADDRESS_SHIFT_SELECTOR ) ?
                                          AV_ADDRESS_W :
                                          UAV_ADDRESS_W - ADDRESS_SHIFT_SELECTOR;
   localparam BURSTCOUNT_HIGH           = ( UAV_BURSTCOUNT_W > AV_BURSTCOUNT_W + BURSTCOUNT_SHIFT_SELECTOR ) ?
                                          AV_BURSTCOUNT_W :
                                          UAV_BURSTCOUNT_W - BURSTCOUNT_SHIFT_SELECTOR;
   localparam BYTEENABLE_ADDRESS_BITS   = ( clog2_plusone(UAV_BYTEENABLE_W) - 1 ) >= 1 ? clog2_plusone(UAV_BYTEENABLE_W) - 1 : 1;


   // Calculate the symbols per word as the power of 2 extended symbols per word
   wire [31 : 0] symbols_per_word_int = 2**(clog2_plusone(AV_SYMBOLS_PER_WORD[UAV_BURSTCOUNT_W : 0] - 1));
   wire [UAV_BURSTCOUNT_W-1 : 0] symbols_per_word = symbols_per_word_int[UAV_BURSTCOUNT_W-1 : 0];

   // +--------------------------------
   // |Backwards Compatibility Signals
   // +--------------------------------
   assign av_clken = (USE_UAV_CLKEN) ? uav_clken : 1'b1;
   assign av_debugaccess = uav_debugaccess;

   // +-------------------
   // |Passthru Signals
   // +-------------------

   reg [1 : 0] av_response_delayed;

   always @(posedge clk, posedge reset) begin
      if (reset) begin
         av_response_delayed <= 2'b0;
      end else begin
         av_response_delayed <= av_response;
      end
   end

   always_comb
   begin
      if (!USE_READRESPONSE && !USE_WRITERESPONSE) begin
         uav_response = '0;
      end else begin
         if (AV_READLATENCY != 0 || USE_READDATAVALID) begin
            uav_response = av_response;
         end else begin
            uav_response = av_response_delayed;
         end
      end
   end
   // assign av_writeresponserequest = uav_writeresponserequest;
   assign uav_writeresponsevalid = av_writeresponsevalid;

   //-------------------------
   //Writedata and Byteenable
   //-------------------------

   always@* begin
      av_byteenable = '0;
      av_byteenable = uav_byteenable[AV_BYTEENABLE_W - 1 : 0];
   end

   always@* begin
      av_writedata = '0;
      av_writedata = uav_writedata[AV_DATA_W - 1 : 0];
   end

   // +-------------------
   // |Calculated Signals
   // +-------------------

   logic [UAV_ADDRESS_W - 1 : 0 ] real_uav_address;

   function [BYTEENABLE_ADDRESS_BITS - 1 : 0 ] decode_byteenable;
      input [UAV_BYTEENABLE_W - 1 : 0 ] byteenable;

      for(int i = 0 ; i < UAV_BYTEENABLE_W; i++ ) begin
         if(byteenable[i] == 1) begin
            return i;
         end
      end

      return '0;

   endfunction

   reg [AV_BURSTCOUNT_W - 1 : 0] burstcount_reg;
   reg [AV_ADDRESS_W    - 1 : 0] address_reg;
   always@(posedge clk, posedge reset) begin
      if(reset) begin
         burstcount_reg <= '0;
         address_reg    <= '0;
      end else begin
         burstcount_reg <= burstcount_reg;
         address_reg    <= address_reg;
         if(av_beginbursttransfer) begin
            burstcount_reg <= uav_burstcount [ BURSTCOUNT_HIGH - 1 + BURSTCOUNT_SHIFT_SELECTOR : BURSTCOUNT_SHIFT_SELECTOR ];
            address_reg    <= real_uav_address [ ADDRESS_HIGH - 1 + ADDRESS_SHIFT_SELECTOR : ADDRESS_SHIFT_SELECTOR ];
         end
      end
   end

   logic [BYTEENABLE_ADDRESS_BITS-1:0] temp_wire;

   always@* begin
      if( AV_REQUIRE_UNALIGNED_ADDRESSES == 1) begin
         temp_wire = decode_byteenable(uav_byteenable);
         real_uav_address = { uav_address[UAV_ADDRESS_W - 1 : BYTEENABLE_ADDRESS_BITS ], temp_wire[BYTEENABLE_ADDRESS_BITS - 1 : 0 ] };
      end else begin
         real_uav_address = uav_address;
      end

      av_address = real_uav_address[ADDRESS_HIGH - 1  + ADDRESS_SHIFT_SELECTOR : ADDRESS_SHIFT_SELECTOR ];
      if( AV_CONSTANT_BURST_BEHAVIOR && !UAV_CONSTANT_BURST_BEHAVIOR && ~av_beginbursttransfer )
         av_address = address_reg;
   end

   always@* begin
      av_burstcount=uav_burstcount[BURSTCOUNT_HIGH - 1 + BURSTCOUNT_SHIFT_SELECTOR : BURSTCOUNT_SHIFT_SELECTOR ];
      if( AV_CONSTANT_BURST_BEHAVIOR && !UAV_CONSTANT_BURST_BEHAVIOR && ~av_beginbursttransfer )
         av_burstcount = burstcount_reg;
   end

   always@* begin
      av_lock = uav_lock;
   end

   // -------------------
   // Writebyteenable Assignment
   // -------------------
   always@* begin
      av_writebyteenable = { (AV_BYTEENABLE_W){uav_write} } & uav_byteenable[AV_BYTEENABLE_W - 1 : 0];
   end

   // -------------------
   // Waitrequest Assignment
   // -------------------

   reg av_waitrequest_generated;
   reg av_waitrequest_generated_read;
   reg av_waitrequest_generated_write;
   reg waitrequest_reset_override;
   reg [ ( LOG2_OF_LATENCY_SUM ? LOG2_OF_LATENCY_SUM - 1 : 0 ) : 0 ] wait_latency_counter;

   always@(posedge reset, posedge clk) begin
      if(reset) begin
         wait_latency_counter <= '0;
         waitrequest_reset_override <= 1'h1;
      end else begin
         waitrequest_reset_override <= 1'h0;
         wait_latency_counter <= '0;
         if( ~uav_waitrequest | waitrequest_reset_override )
            wait_latency_counter <= '0;
         else if( uav_read | uav_write )
            wait_latency_counter <= wait_latency_counter + 1'h1;
      end
   end


   always @* begin

      av_read  = uav_read;
      av_write = uav_write;
      av_waitrequest_generated         = 1'h1;
      av_waitrequest_generated_read    = 1'h1;
      av_waitrequest_generated_write   = 1'h1;

      if(LOG2_OF_LATENCY_SUM == 1)
         av_waitrequest_generated = 0;

      if(LOG2_OF_LATENCY_SUM > 1 && !USE_WAITREQUEST) begin
         av_read  = wait_latency_counter >= AV_SETUP_WAIT_CYCLES && uav_read;
         av_write = wait_latency_counter >= AV_SETUP_WAIT_CYCLES && uav_write && wait_latency_counter <= AV_WRITE_WAIT_INDEXED;
         av_waitrequest_generated_read = wait_latency_counter != AV_READ_WAIT_INDEXED;
         av_waitrequest_generated_write = wait_latency_counter != AV_DATA_HOLD_INDEXED;

         if(uav_write)
            av_waitrequest_generated = av_waitrequest_generated_write;
         else
            av_waitrequest_generated = av_waitrequest_generated_read;

      end

      if(USE_WAITREQUEST) begin
         uav_waitrequest = av_waitrequest;
      end else begin
         uav_waitrequest = av_waitrequest_generated | waitrequest_reset_override;
      end

   end

   // --------------
   // Readdata Assignment
   // --------------

   reg[(AV_DATA_W ? AV_DATA_W -1 : 0 ): 0] av_readdata_pre;

   always@(posedge clk, posedge reset) begin
      if(reset)
         av_readdata_pre <= 'b0;
      else
         av_readdata_pre <= av_readdata;
   end

   always@* begin
      uav_readdata = {UAV_DATA_W{1'b0}};
      if( AV_READLATENCY != 0  || USE_READDATAVALID ) begin
         uav_readdata[AV_DATA_W-1:0] = av_readdata;
      end else begin
         uav_readdata[AV_DATA_W-1:0] = av_readdata_pre;
      end
   end
   
   // -------------------
   // Readdatavalid Assigment
   // -------------------
   reg[(AV_READLATENCY>0 ? AV_READLATENCY-1:0) :0] read_latency_shift_reg;
   reg top_read_latency_shift_reg;

   always@* begin
      uav_readdatavalid=top_read_latency_shift_reg;
      if(USE_READDATAVALID) begin
         uav_readdatavalid = av_readdatavalid;
      end
   end

   always@* begin
      top_read_latency_shift_reg = uav_read & ~uav_waitrequest & ~waitrequest_reset_override;
      if(AV_READLATENCY == 1 || AV_READLATENCY == 0 ) begin
         top_read_latency_shift_reg=read_latency_shift_reg;
      end
      if (AV_READLATENCY > 1) begin
         top_read_latency_shift_reg = read_latency_shift_reg[(AV_READLATENCY ? AV_READLATENCY-1 : 0)];
      end
   end

   always@(posedge reset, posedge clk) begin
      if (reset) begin
         read_latency_shift_reg <= '0;
      end else if (av_clken) begin
         read_latency_shift_reg[0] <= uav_read && ~uav_waitrequest & ~waitrequest_reset_override;
         for (int i=0; i+1 < AV_READLATENCY ; i+=1 ) begin
            read_latency_shift_reg[i+1] <= read_latency_shift_reg[i];
         end
      end
   end

   // ------------
   // Chipselect and OutputEnable
   // ------------
   reg av_chipselect_pre;
   wire cs_extension;
   reg av_outputenable_pre;
   
   assign av_chipselect  = (uav_read | uav_write) ? 1'b1 : av_chipselect_pre;
   assign cs_extension = ( (^ read_latency_shift_reg) & ~top_read_latency_shift_reg ) | ((| read_latency_shift_reg) & ~(^ read_latency_shift_reg));
   assign av_outputenable = uav_read ? 1'b1 : av_outputenable_pre;

   always@(posedge reset, posedge clk) begin
      if(reset)
         av_outputenable_pre <= 1'b0;
      else if( AV_READLATENCY == 0  && AV_READ_WAIT_INDEXED != 0 )
         av_outputenable_pre <= 0;
      else
         av_outputenable_pre <= cs_extension | uav_read;
   end

   always@(posedge reset, posedge clk) begin
      if(reset) begin
         av_chipselect_pre  <= 1'b0;
      end else begin
         av_chipselect_pre  <= 1'b0;
         if(AV_READLATENCY != 0 && CHIPSELECT_THROUGH_READLATENCY == 1) begin
            //The AV_READLATENCY term is only here to prevent chipselect from remaining asserted while read and write fall.
            //There is no functional impact as 0 cycle transactions are treated as 1 cycle on the other side of the translator.
            if(uav_read) begin
               av_chipselect_pre <= 1'b1;
            end else if(cs_extension == 1) begin
               av_chipselect_pre <= 1'b1;
            end
         end
      end
   end

   // -------------------
   // Begintransfer Assigment
   // -------------------
   reg end_begintransfer;

   always@* begin
      av_begintransfer = ( uav_write | uav_read ) & ~end_begintransfer;
   end

   always@ ( posedge clk or posedge reset ) begin
      if(reset) begin
         end_begintransfer <= 1'b0;
      end else begin
         if(av_begintransfer == 1 && uav_waitrequest && ~waitrequest_reset_override)
            end_begintransfer <= 1'b1;
         else if(uav_waitrequest)
            end_begintransfer <= end_begintransfer;
         else
            end_begintransfer <= 1'b0;
      end
   end

   // -------------------
   // Beginbursttransfer Assigment
   // -------------------
   reg end_beginbursttransfer;
   reg in_transfer;

   always@* begin
      av_beginbursttransfer = uav_read ? av_begintransfer : (av_begintransfer && ~end_beginbursttransfer && ~in_transfer);
   end

   always@ ( posedge clk or posedge reset ) begin
      if(reset) begin
         end_beginbursttransfer <= 1'b0;
         in_transfer <= 1'b0;
      end else begin
         end_beginbursttransfer <= uav_write & ( uav_burstcount != symbols_per_word );
         if(uav_write && uav_burstcount == symbols_per_word)
            in_transfer <=1'b0;
         else if(uav_write)
            in_transfer <=1'b1;
      end
   end

endmodule
