/* ========================================================================
   Copyright (c) 2014 Advanced Micro Devices, Inc.  All rights reserved.
  ========================================================================*/

#ifndef HWDEBUG_H_
#define HWDEBUG_H_

#include "device.hpp"
#include "amdocl/cl_debugger_amd.h"

static const int TbaStartOffset = 256;

static const int RtTrapBufferWaveSize = 64;
static const int RtTrapBufferSeNum = 4;
static const int RtTrapBufferShNum = 2;
static const int RtTrapBufferCuNum = 16;
static const int RtTrapBufferSimdNum = 4;
static const int RtTrapBufferWaveNum = 16;
static const int RtTrapBufferTotalWaveNum =
    ((RtTrapBufferSeNum) * (RtTrapBufferShNum) * (RtTrapBufferCuNum) * (RtTrapBufferSimdNum) *
     (RtTrapBufferWaveNum));


/*!  \brief Debug trap handler location in the runtime trap buffer
 *
 *   This enumeration is used to indicate the location where the debug
 *   trap handler and debug trap buffer are set in the device trap buffer.
 */
enum DebugTrapLocation {
  kDebugTrapHandlerLocation = 0,  //! Debug Trap handler location, this location must be 0
  kDebugTrapBufferLocation = 1,   //! Debug Trap buffer location, this location must be 1
  kDebugTrapLocationMax = 2
};


/*!  \brief This structure is for the debug info in each kernel dispatch.
 *
 *   Contains the memory descriptor information of the scratch memory and the global
 *   memory
 */
struct DispatchDebugInfo {
  uint32_t scratchMemoryDescriptor_[4];  //! Scratch memory descriptor
  uint32_t globalMemoryDescriptor_[4];   //! Global memory descriptor
};

/*!  \brief Trap handler descriptor
 *
 *   The trap handler descriptor contains the details of a given trap handler.
 */
struct TrapHandlerInfo {
  amd::Memory* trapHandler_;  //!< Device memory for the trap handler
  amd::Memory* trapBuffer_;   //!< Device memory for the trap buffer
};

/*!  \brief Structure of the runtime trap handler buffer, which includes the following
 *   information: information of the runtime trap handler and buffer, information of
 *   the level-2 trap handlers and buffers.
 */
struct RuntimeTrapInfo {
  TrapHandlerInfo trap_;  //!< Structure of the address of all trap handlers
  uint32_t dispatchId_;   //!< Dispatch ID that signals the shader event
  uint32_t vgpr_backup_[RtTrapBufferTotalWaveNum][RtTrapBufferWaveSize];
  //!< Buffer to backup the VGPR used by the runtime trap handler
};

/**
 * Opaque pointer to trap event
 */
typedef uintptr_t DebugEvent;

namespace amd {


class Context;
class Device;
class HostQueue;


/*! \class HwDebugManager
 *
 *  \brief The device interface class for the hardware debug manager
 */
class HwDebugManager {
 public:
  //! Constructor for the Hardware Debug Manager
  HwDebugManager(amd::Device* device);

  //! Destructor for Hardware Debug Manager
  virtual ~HwDebugManager();

  //!  Setup the call back function pointer
  void setCallBackFunctions(cl_PreDispatchCallBackFunctionAMD preDispatchFn,
                            cl_PostDispatchCallBackFunctionAMD postDispatchFn);

  //!  Setup the call back argument pointers
  void setCallBackArguments(void* preDispatchArgs, void* postDispatchArgs);

  //!  Get dispatch debug info
  void getDispatchDebugInfo(void* debugInfo) const;

  //!  Set the kernel code address and its size
  void setKernelCodeInfo(address aqlCodeAddr, uint32_t aqlCodeSize);

  //!  Get the scratch ring
  void setScratchRing(address scratchRingAddr, uint32_t scratchRingSize);

  //!  Map the scratch ring for host access
  void mapScratchRing(uint64_t* scratchRingAddr, uint32_t* scratchRingSize) const;

  //!  Retrieve the pre-dispatch callback function
  cl_PreDispatchCallBackFunctionAMD preDispatchCallBackFunc() const {
    return preDispatchCallBackFunc_;
  }

  //!  Retrieve the post-dispatch callback function
  cl_PostDispatchCallBackFunctionAMD postDispatchCallBackFunc() const {
    return postDispatchCallBackFunc_;
  }

  //!  Retrieve the pre-dispatch callback function arguments
  void* preDispatchCallBackArgs() const { return preDispatchCallBackArgs_; }

  //!  Retrieve the post-dispatch callback function arguments
  void* postDispatchCallBackArgs() const { return postDispatchCallBackArgs_; }

  //!  Retrieve the memory pointer of the runtime trap handler code
  device::Memory* runtimeTBA() const { return runtimeTBA_; }

  //!  Retrieve the memory pointer of the runtime trap handler buffer
  device::Memory* runtimeTMA() const { return runtimeTMA_; }

  //!  Set exception policy
  void setExceptionPolicy(void* policy);

  //!  Get exception policy
  void getExceptionPolicy(void* policy) const;

  //!  Set the kernel execution mode
  void setKernelExecutionMode(void* mode);

  //!  Get the kernel execution mode
  void getKernelExecutionMode(void* mode) const;

  //!  Setup the pointer to the aclBinary within the debug manager
  void setAclBinary(void* aclBinary);

  //!  Allocate storage to keep the memory pointers of the kernel parameters
  void allocParamMemList(uint32_t numParams);

  //!  Assign the kernel parameter memory
  void assignKernelParamMem(uint32_t paramIdx, amd::Memory* mem);

  //!  Get kernel parameter memory object
  cl_mem getKernelParamMem(uint32_t paramIdx) const;

  //!  Install trap handler
  void installTrap(cl_dbg_trap_type_amd trapType, amd::Memory* pTrapHandler,
                   amd::Memory* pTrapBuffer);

  //!  Flush cache
  virtual void flushCache(uint32_t mask) = 0;

  //!  Create the debug event
  virtual DebugEvent createDebugEvent(const bool autoReset) = 0;

  //!  Wait for the debug event
  virtual cl_int waitDebugEvent(DebugEvent pEvent, uint32_t timeOut) const = 0;

  //!  Destroy the debug event
  virtual void destroyDebugEvent(DebugEvent* pEvent) = 0;

  //!  Register the debugger
  virtual cl_int registerDebugger(amd::Context* context, uintptr_t pMessageStorage) = 0;

  //!  Unregister the debugger
  virtual void unregisterDebugger() = 0;

  //!  Send the wavefront control cmmand
  virtual void wavefrontControl(uint32_t waveAction, uint32_t waveMode, uint32_t trapId,
                                void* waveAddr) const = 0;

  //!  Set address watching point
  virtual void setAddressWatch(uint32_t numWatchPoints, void** watchAddress, uint64_t* watchMask,
                               uint64_t* watchMode, DebugEvent* event) = 0;

  //!  Map the shader (AQL code) for host access
  virtual void mapKernelCode(void* aqlCodeInfo) const = 0;

  //!  Get the packet information for dispatch
  virtual void getPacketAmdInfo(const void* aqlCodeInfo, void* packetInfo) const = 0;

  //!  Set global memory values
  virtual void setGlobalMemory(amd::Memory* memObj, uint32_t offset, void* srcPtr,
                               uint32_t size) = 0;

  //!  Execute the post-dispatch callback function
  virtual void executePostDispatchCallBack() = 0;

  //!  Execute the pre-dispatch callback function
  virtual void executePreDispatchCallBack(void* aqlPacket, void* toolInfo) = 0;

 protected:
  //!  Return the context
  const amd::Context* context() const { return context_; }

  //!  Get the debug device
  const amd::Device* device() const { return device_; }

  //!  Return the register flag
  bool isRegistered() const { return isRegistered_; }

 protected:
  const amd::Context* context_;  ///< context that used to create host queue for the debugger
  amd::Device* device_;          ///< Device to run the debugger

  cl_PreDispatchCallBackFunctionAMD preDispatchCallBackFunc_;  //!< pre-dispatch callback function
  cl_PostDispatchCallBackFunctionAMD
      postDispatchCallBackFunc_;    //!< post-dispatch callback function
  void* preDispatchCallBackArgs_;   //!< pre-dispatch callback function arguments
  void* postDispatchCallBackArgs_;  //!< post-dispatch callback function arguments

  DispatchDebugInfo debugInfo_;  //!< Debug setting/information for kernel dispatch
  amd::Memory* rtTrapInfo_[kDebugTrapLocationMax];  //!< Device trap buffer, to store various trap
                                                    //!handlers on the device

  amd::Memory** paramMemory_;  //!< list of memory pointers for kernel parameters
  uint32_t numParams_;         //!< number of kernel parameters

  void* aclBinary_;  //!< ACL binary

  address aqlCodeAddr_;   //!< The mapped AQL code to allow host access
  uint32_t aqlCodeSize_;  //!< The size of the AQL code info

  address scratchRingAddr_;   //!< The mapped address of the scratch buffer
  uint32_t scratchRingSize_;  //!< The size of the scratch ring

  bool isRegistered_;  //! flag to indicate the debugger has been registered

  cl_dbg_exception_policy_amd excpPolicy_;  //!< exception policy
  cl_dbg_kernel_exec_mode_amd execMode_;    //!< kernel execution mode
  RuntimeTrapInfo rtTrapHandlerInfo_;       //!< Runtime trap information

  //!  Runtime Trap handler pointer (TBA) & its buffer (TMA)
  device::Memory* runtimeTBA_;  //! runtime trap handler pointer
  device::Memory* runtimeTMA_;  //! runtime trap handler buffer
};


/**@}*/

/**
 * @}
 */
}  // namespace amd

#endif  // HWDEBUG_H_
