/*************************************************************************/
/*
* File name: FdkOffsetGainPrimitive.cpp
* Location: See Matrox Example Launcher in the MIL Control Center
 * 
*
* Synopsis:  This example shows how to create an FPGA accelerated processing  
 *           function using the OffsetGain processing unit and 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.
 *
 * Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
 * All Rights Reserved
 */

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

/* Functions definition */
#define FUNCTION_NB_PARAM_OFFSET_GAIN           5
#define FUNCTION_OPCODE_FPGA_OFFSET_GAIN        8

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

/* Master, Slaves and Primitives MIL functions declarations. */
void MFTYPE FPGAOffsetGainSlave(MIL_ID Func);
void FPGAOffsetGainPrimitive(MIL_ID  Func, MIL_ID  SrcImageId,
     MIL_ID OffsetImageId, MIL_ID GainImageId, MIL_INT Src4, MIL_ID  DstImageId);
void FPGAInitializeShadowRegisters(MIL_ID Func, FPGA_GAINOFFSET_USER_ST* pOGShadow,
     MIL_BUFFER_INFO Src1, MIL_BUFFER_INFO Src2, MIL_BUFFER_INFO Src3, MIL_INT Src4,
     MIL_BUFFER_INFO Dst);

/* MIL Offset Gain primitive function definition. */
/* ---------------------------------------------------------- */

/***************************************************************
**                         _______ 
** SrcImage0         ---->| gain  |
** SrcImage1(Offset) ---->| offset|---->DstImage
** SrcImage2(Gain)   ---->|_______|
**                            ^
**                            |
**               Src4 (normalization factor register)
**
** DstImage = (((SrcImage0-SrcImage1)*SrcImage3)/Src4)
**
*****************************************************************/
void FPGAOffsetGainPrimitive(MIL_ID Func, MIL_ID SrcImageId,
                             MIL_ID OffsetImageId, MIL_ID GainImageId, 
                             MIL_INT Src4, MIL_ID  DstImageId)
   {
   MIL_BUFFER_INFO Src, Offset, Gain, Dst;
   MIL_FPGA_CONTEXT OffsetGainContext;
   FPGA_GAINOFFSET_USER_ST GOShadow;
   memset(&GOShadow, 0, sizeof(GOShadow));

   /* Inquire the buffer info object of all the buffers. */
   MfuncInquire(SrcImageId, M_BUFFER_INFO, &Src);
   MfuncInquire(OffsetImageId, M_BUFFER_INFO, &Offset);
   MfuncInquire(GainImageId, M_BUFFER_INFO, &Gain);
   MfuncInquire(DstImageId, M_BUFFER_INFO, &Dst);

   /* Allocate an asynchronous command context on the OffsetGain processing unit. */
   if(MfpgaCommandAlloc(MfuncBufOwnerSystemId(Src), M_DEV0, FPGA_GAINOFFSET_FID,
                        M_DEFAULT, M_DEV0, M_ASYNCHRONOUS, M_DEFAULT, &OffsetGainContext))
      {
      /* Initialize the shadow register structure. */
      FPGAInitializeShadowRegisters(Func, &GOShadow, Src, 
                                    Offset, Gain, Src4, Dst);

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

      /* Input1 of OffsetGain processing unit is connected to the Offset buffer. */
      MfpgaSetSource(OffsetGainContext, Offset, M_INPUT1, M_DEFAULT);

      /* Input2 of OffsetGain processing unit is connected to the Gain buffer. */
      MfpgaSetSource(OffsetGainContext, Gain, M_INPUT2, M_DEFAULT);

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

      /* Output0 of the LutMap processing unit is connected to the Dest buffer. */
      MfpgaSetDestination(OffsetGainContext, Dst, M_OUTPUT0, M_DEFAULT);

      /* Queue the processing operation, we use the M_DEFAULT flag. 
         This lets the software know that it can send the processing
         operation to the FPGA. */
      MfpgaCommandQueue(OffsetGainContext, M_DEFAULT, M_DEFAULT);

      /* Free the allocated context. */
      MfpgaCommandFree(OffsetGainContext, M_DEFAULT);
      }
   else
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
          MIL_TEXT("No OffsetGain 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_GAINOFFSET_USER_ST* pOGShadow,
                                    MIL_BUFFER_INFO Src1,
                                    MIL_BUFFER_INFO Src2,
                                    MIL_BUFFER_INFO Src3,
                                    MIL_INT         Src4,
                                    MIL_BUFFER_INFO Dst)
   {
   MIL_UINT lMaxValue, Src1Type, Src2Type, Src3Type, DstType, i, j;
   lMaxValue = (MIL_UINT)MfuncBufMaxValue(Src1);
   Src1Type = MfuncBufType(Src1);
   Src2Type = MfuncBufType(Src2);
   Src3Type = MfuncBufType(Src3);
   DstType = MfuncBufType(Dst);

   /*****************************
   ** Set OffsetGain registers
   *****************************/

   /*Set in-out types. */
   switch(Src1Type)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.srctype = 0x1; break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.srctype = 0x3; break;
      default:
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Invalid source buffer type."),
             M_NULL, M_NULL, M_NULL
             );
         break;
      }

   switch(Src2Type)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.offtype = 0x1; break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.offtype = 0x3; break;
      default:
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Invalid offset buffer type."),
             M_NULL, M_NULL, M_NULL
             );
         break;
      }

   switch(Src3Type)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.gaintype = 0x1;   break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.gaintype = 0x3;   break;
      default:
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Invalid gain buffer type."),
             M_NULL, M_NULL, M_NULL
             );
         break;
      }

   switch(DstType)
      {
      case 8+M_UNSIGNED:
         pOGShadow->ctrl.f.dsttype = 0x1;   break;
      case 16+M_UNSIGNED:
         pOGShadow->ctrl.f.dsttype = 0x3;   break;
      default:
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Invalid destination buffer type."),
             M_NULL, M_NULL, M_NULL
             );
         break;
      }

   /* Set the value we want to clip to. */
   pOGShadow->clipval.f.clipval  = lMaxValue;

   /* Compute the division factor. */
   j=1;
   for(i=0; i<32; i++)
      {
      if(Src4 & j)
         {
         pOGShadow->ctrl.f.shift = i;
         break;
         }
      j <<= 1;
      }
   }

/* MIL Master function definition. */
/* ------------------------------- */
void FPGAOffsetGain(MIL_ID  SrcImageId, MIL_ID  OffsetImageId,
    MIL_ID GainImageId, MIL_INT Src4, MIL_ID  DstImageId)
   {
   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("FPGAOffsetGain"), 
              FUNCTION_NB_PARAM_OFFSET_GAIN,
              FPGAOffsetGainSlave, M_NULL, M_NULL, 
              M_USER_MODULE_1+FUNCTION_OPCODE_FPGA_OFFSET_GAIN,
              M_DEFAULT, 
              &Func
             );

   /* Register the parameters. */
   MfuncParamMilId (Func, 1, SrcImageId,    M_IMAGE, M_IN);
   MfuncParamMilId (Func, 2, OffsetImageId, M_IMAGE, M_IN);
   MfuncParamMilId (Func, 3, GainImageId,   M_IMAGE, M_IN);
   MfuncParamMilInt(Func, 4, Src4);
   MfuncParamMilId (Func, 5, DstImageId,    M_IMAGE, M_OUT);

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

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

/* MIL Slave function definition. */
/* ------------------------------ */
void MFTYPE FPGAOffsetGainSlave(MIL_ID Func)
   {
   MIL_ID SrcImageId, OffsetImageId, GainImageId, DstImageId;
   MIL_INT Src4;

   /* Read the parameters. */
   MfuncParamValue(Func, 1, &SrcImageId);
   MfuncParamValue(Func, 2, &OffsetImageId); 
   MfuncParamValue(Func, 3, &GainImageId);
   MfuncParamValue(Func, 4, &Src4);
   MfuncParamValue(Func, 5, &DstImageId);
    
   /* Call the primitive controling the processing FPGA using the Mfpga API. */
   FPGAOffsetGainPrimitive(Func, SrcImageId, OffsetImageId, GainImageId, Src4, DstImageId);
   }