/*************************************************************************/
/*
* File name: FdkMinMaxPrimitive.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\solios\C++\SoliosFDK
 *             \MinMaxPrimitive
*
* Synopsis:  This example shows how to create an FPGA accelerated processing  
 *           function to find the Min and Max of an Image using the Mfpga API. 
 *           It also includes MIL Master and Slave functions that will be used 
 *           to call the processing primitive.
 *
 *           These Master and Slave functions allow the primitive to 
 *           become a true MIL function that does minimal parameter checking, 
 *           can log MIL errors and will make the custom FPGA function 
 *           visible to the MIL trace mechanism.
 */

/* Headers. */
#include <mil.h>
#include <milfpga.h>
#include <fpga_minmax.h>

/* Function definition */
#define FUNCTION_NB_PARAM                 3
#define FUNCTION_OPCODE_FPGA_MIN_MAX      2 

/* Error codes */
#define FUNCTION_PARAMETER_ERROR_CODE     1
#define FUNCTION_WRONG_FPGA_ERROR_CODE    2

/* Master, Slave and Primitive MIL functions declarations. */
void FPGAMinMax(MIL_ID SrcImageId, MIL_UINT32* pMinValue, MIL_UINT32* pMaxValue);
void MFTYPE FPGAMinMaxSlave(MIL_ID Func);
void FPGAMinMaxPrimitive(MIL_ID Func, MIL_ID MilSourceImage, MIL_UINT32* pMinValue, MIL_UINT32* pMaxValue);
void FPGAInitializeShadowRegisters(MIL_ID Func, FPGA_MINMAX_USER_ST* pShadow, MIL_BUFFER_INFO Buf);

/******************************************************************************
**              _______
**             | min   |
** SrcImage -->| max   |
**             |_______|
**
** Min = Min(SrcImage)
** Max = Max(SrcImage)
**
******************************************************************************/
void FPGAMinMaxPrimitive(MIL_ID Func, 
                         MIL_ID MilSourceImage,
                         MIL_UINT32* pMinValue,
                         MIL_UINT32* pMaxValue)
   {
   MIL_BUFFER_INFO Src;
   MIL_FPGA_CONTEXT MinMaxContext;
   FPGA_MINMAX_USER_ST ShadowRegisters;
   memset(&ShadowRegisters, 0, sizeof(ShadowRegisters));

   /* Inquire the buffer info object for the source buffer. */
   MfuncInquire(MilSourceImage, M_BUFFER_INFO, &Src);

   /* Allocate an synchronous command context on the MinMax processing unit. */
   /* This is important because this processing unit returns a result through a register read. */
   if(MfpgaCommandAlloc(MfuncBufOwnerSystemId(Src), M_DEV0, FPGA_MINMAX_FID,
               M_DEFAULT, M_DEV0, M_SYNCHRONOUS, M_DEFAULT, &MinMaxContext))
      {
      /* Initialize the shadow register structure. */
      FPGAInitializeShadowRegisters(Func, &ShadowRegisters, Src);

      /* Input0 of MinMax processing unit is connected to the Src buffer. */
      MfpgaSetSource(MinMaxContext, Src, M_INPUT0, M_DEFAULT);

      /* Pass the initialized shadow register structure, it will be programmed
         when the processing operation is dispatched (before processing starts).
      */
      MfpgaSetRegister(MinMaxContext, M_USER, 0, sizeof(ShadowRegisters),
                           (void*)(&ShadowRegisters), M_WHEN_DISPATCHED);

      /* Pass the initialized shadow register structure again, it will be read
         back when the processing operation is completed. */
      MfpgaGetRegister(MinMaxContext, M_USER, 0, sizeof(ShadowRegisters),
                            (void*)(&ShadowRegisters), M_WHEN_COMPLETED);

      /* Because this command context was allocated in synchronous mode,
         MfpgaCommandQueue will block until we get a result.
      */
      MfpgaCommandQueue(MinMaxContext, M_DEFAULT, M_DEFAULT);

      /* Retrieve the result from the FPGA. */
      *pMinValue = (MIL_UINT32)ShadowRegisters.min.f.min;
      *pMaxValue = (MIL_UINT32)ShadowRegisters.max.f.max;

      /* Free the allocated context. */
      MfpgaCommandFree(MinMaxContext, M_DEFAULT);
      }
   else
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
          MIL_TEXT("No MinMax acceleration supported by this FPGA configuration."),
          MIL_TEXT("Please select the proper FPGA configuration file."),
          MIL_TEXT("See the processing FPGA section of the milconfig utility."),
          M_NULL
          );
      }
   }

/* MIL PU Register initialization function definition. */
/* ---------------------------------- */

void FPGAInitializeShadowRegisters(MIL_ID Func, FPGA_MINMAX_USER_ST* pShadow,
   MIL_BUFFER_INFO Buf)
   {
   MIL_INT BufferType = MfuncBufType(Buf);

   /* Set the shadow min and max values to extremes. This is a requirement of
      the MinMax processing unit.
   */
   pShadow->max.f.max = 0;
   pShadow->min.f.min = 0xffffffff;

   /* Program the source buffer type. */
   switch(BufferType)
      {
      case 8+M_SIGNED:
         pShadow->ctrl.f.srctype = 0;   break;
      case 8+M_UNSIGNED:
         pShadow->ctrl.f.srctype = 1;   break;
      case 16+M_SIGNED:
         pShadow->ctrl.f.srctype = 2;   break;
      case 16+M_UNSIGNED:
         pShadow->ctrl.f.srctype = 3;   break;
      case 32+M_SIGNED:
         pShadow->ctrl.f.srctype = 4;   break;
      case 32+M_UNSIGNED:
         pShadow->ctrl.f.srctype = 5;   break;
      default:
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Invalid source buffer type."),
             M_NULL, M_NULL, M_NULL
             );
         break;
      }

   if(MfuncBufSizeBand(Buf) == 3)
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
          MIL_TEXT("Only 1 band buffers are supported."),
          M_NULL, M_NULL, M_NULL
          );
   }

/* MIL Master function definition. */
/* ------------------------------- */

void FPGAMinMax(MIL_ID SrcImageId, MIL_UINT32* pMinValue, MIL_UINT32* pMaxValue)
   {
   MIL_ID   Func;

   /* Allocate a MIL function context that will be used to call a target 
      Slave function locally on the Host to do the processing.
   */
   MfuncAlloc(MIL_TEXT("FPGAMinMax"), 
              FUNCTION_NB_PARAM,
              FPGAMinMaxSlave, M_NULL, M_NULL, 
              M_USER_MODULE_1+FUNCTION_OPCODE_FPGA_MIN_MAX, 
              M_DEFAULT, 
              &Func
             );

   /* Register the parameters. */
   MfuncParamMilId      (Func, 1, SrcImageId, M_IMAGE, M_IN);
   MfuncParamDataPointer(Func, 2, pMinValue, sizeof(MIL_UINT32), M_OUT);
   MfuncParamDataPointer(Func, 3, pMaxValue, sizeof(MIL_UINT32), M_OUT);

   /* Call the target Slave function. */
   MfuncCall(Func);

   /* Free the MIL function context. */
   MfuncFree(Func);
   }

/* MIL Slave function definition. */
/* ------------------------------ */

void MFTYPE FPGAMinMaxSlave(MIL_ID Func)
   {
   MIL_ID      SrcImageId;
   MIL_UINT32  *pMinValue, *pMaxValue;

   /* Read the parameters. */
   MfuncParamValue(Func, 1, &SrcImageId);
   MfuncParamValue(Func, 2, &pMinValue); 
   MfuncParamValue(Func, 3, &pMaxValue);
    
   /* Call the primitive controling the processing FPGA using the Mfpga API. */
   FPGAMinMaxPrimitive(Func, SrcImageId, pMinValue, pMaxValue);
   }