/*************************************************************************/
/*
* File name: FdkHistogramPrimitive.cpp
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\solios\C++\SoliosFDK
 *             \HistogramPrimitive
*
* Synopsis:  This example shows how to create an FPGA accelerated processing  
 *           function 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_hist.h>

#define FUNCTION_NB_PARAM                 3
#define FUNCTION_OPCODE_FPGA_HISTOGRAM    5

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

/* Master, Slave and Primitive MIL functions declarations. */
void MFTYPE FPGAHistogram(MIL_ID Func);
void MFTYPE FPGAHistogramSlave(MIL_ID Func);
void FPGAHistogramPrimitive(MIL_ID Func, MIL_ID SrcImageId, MIL_ID HistBufId, MIL_INT ClearFlag);
void FPGAHistogramClearPrimitive(MIL_ID Func, MIL_ID SrcImageId, MIL_ID HistBufId);
void FPGAInitializeShadowRegisters(MIL_ID Func, MIL_FPGA_CONTEXT HistContext, FPGA_HIST_USER_ST* pHistShadow,
   MIL_BUFFER_INFO Src, MIL_BUFFER_INFO Hist);
bool FindOffChipHistogram(MIL_ID MilSystem, MIL_FPGA_CONTEXT* pHistogram);

/******************************************************************************
**                _______
**               | hist- |
** SrcImage ---->| ogram |----> HistogramBuffer
**               |_______|
**
**
******************************************************************************/

/* MIL Primitive function definition. */
/* ---------------------------------- */

void FPGAHistogramPrimitive(MIL_ID Func, MIL_ID SrcImageId, MIL_ID HistBufId, MIL_INT ClearFlag)
   {
   MIL_BUFFER_INFO Src, Hist;
   MIL_FPGA_CONTEXT HistogramContext;
   FPGA_HIST_USER_ST HistShadow;
   memset(&HistShadow, 0, sizeof(HistShadow));

   if((ClearFlag == M_ENABLE) || (ClearFlag == M_DEFAULT))
      FPGAHistogramClearPrimitive(Func, SrcImageId, HistBufId);

   /* Inquire the buffer info object of all the buffers. */
   MfuncInquire(SrcImageId, M_BUFFER_INFO, &Src);
   MfuncInquire(HistBufId, M_BUFFER_INFO, &Hist);

   /* Allocate a synchronous command context on the Histogram processing unit. */
   if(FindOffChipHistogram(MfuncBufOwnerSystemId(Src), &HistogramContext))
      {
      /* Initialize the shadow register structure. */
      FPGAInitializeShadowRegisters(Func, HistogramContext, &HistShadow, Src, Hist);

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

      /* Output0 of the Histogram is connected to the Hist buffer. */
      MfpgaSetDestination(HistogramContext, Hist, M_OUTPUT0, M_DEFAULT);

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

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

      /* Free the allocated context. */
      MfpgaCommandFree(HistogramContext, M_DEFAULT);
      }
   else
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
          MIL_TEXT("This FPGA configuration does not support on-chip Histogram acceleration."),
          MIL_TEXT("Please select the proper FPGA configuration file."),
          MIL_TEXT("See the processing FPGA section of the milconfig utility."),
          M_NULL
          );
      }
   }

void FPGAHistogramClearPrimitive(MIL_ID Func, MIL_ID SrcImageId, MIL_ID HistBufId)
   {
   MIL_BUFFER_INFO Src, Hist;
   MIL_FPGA_CONTEXT HistogramContext;
   FPGA_HIST_USER_ST HistShadow;
   memset(&HistShadow, 0, sizeof(HistShadow));

   /* Inquire the buffer info object of all the buffers. */
   MfuncInquire(SrcImageId, M_BUFFER_INFO, &Src);
   MfuncInquire(HistBufId, M_BUFFER_INFO, &Hist);

   /* Allocate a synchronous command context on the Histogram processing unit. */
   if(FindOffChipHistogram(MfuncBufOwnerSystemId(Src), &HistogramContext))
      {
      /* Initialize the shadow register structure. */
      FPGAInitializeShadowRegisters(Func, HistogramContext, &HistShadow, Src, Hist);

      /* Overwrite default shadow register initialization to allow for
         clearing of histogram buffer.
      */
      HistShadow.ctrl.f.hoaclr = 1;

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

      /* Output0 of the Histogram is connected to the Hist buffer. */
      MfpgaSetDestination(HistogramContext, Hist, M_OUTPUT0, M_DEFAULT);

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

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

      /* Free the allocated context. */
      MfpgaCommandFree(HistogramContext, M_DEFAULT);
      }
   else
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_WRONG_FPGA_ERROR_CODE,
          MIL_TEXT("This FPGA configuration does not support on-chip Histogram acceleration."),
          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, MIL_FPGA_CONTEXT HistContext,
   FPGA_HIST_USER_ST* pHistShadow, MIL_BUFFER_INFO Src, MIL_BUFFER_INFO Hist)
   {

   /* Do error checking. */
   if(!(MfuncBufAttribute(Hist) & M_HIST_LIST))
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
          MIL_TEXT("Destination buffer must be a M_HIST_LIST."),
          M_NULL,
          M_NULL,
          M_NULL
          );
      }

   if((MfuncBufSizeX(Hist)-1) < (MIL_INT)MfuncBufMaxValue(Src))
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
          MIL_TEXT("Destiantion histogram buffer too small."),
          M_NULL,
          M_NULL,
          M_NULL
          );
      }

   if(MfuncBufSizeBand(Src) != 1)
      {
      /* Report a MIL error. */
      MfuncErrorReport( 
          Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
          MIL_TEXT("Only monochrome source buffers are supported for Histogram."),
          M_NULL,
          M_NULL,
          M_NULL
          );
      }

   /* Program the source buffer type */
   switch(MfuncBufType(Src))
      {
      case 8+M_UNSIGNED:
      case 8+M_SIGNED:
         pHistShadow->ctrl.f.srctype = 0; break;
      case 16+M_UNSIGNED:
      case 16+M_SIGNED:
         pHistShadow->ctrl.f.srctype = 1; break;
      default:
         /* Report a MIL error. */
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Only 8 and 16 bit source buffers are supported for Histogram."),
             M_NULL,
             M_NULL,
             M_NULL
             );
         break;
      }

   /* Program the data range */
   switch((MIL_INT)MfuncBufMaxValue(Src))
      {
      case 0xFFFF:
         pHistShadow->ctrl.f.drange = 0;    break;
      case 0x7FFF:
         pHistShadow->ctrl.f.drange = 1;    break;
      case 0x3FFF:
         pHistShadow->ctrl.f.drange = 2;    break;
      case 0x1FFF:
         pHistShadow->ctrl.f.drange = 3;    break;
      case 0x0FFF:
         pHistShadow->ctrl.f.drange = 4;    break;
      case 0x07FF:
         pHistShadow->ctrl.f.drange = 5;    break;
      case 0x03FF:
         pHistShadow->ctrl.f.drange = 6;    break;
      case 0x01FF:
         pHistShadow->ctrl.f.drange = 7;    break;
      case 0x00FF:
         pHistShadow->ctrl.f.drange = 8;    break;
      case 0x007F:
         pHistShadow->ctrl.f.drange = 9;    break;
      case 0x003F:
         pHistShadow->ctrl.f.drange = 10;   break;
      case 0x001F:
         pHistShadow->ctrl.f.drange = 11;   break;
      case 0x000F:
         pHistShadow->ctrl.f.drange = 12;   break;
      case 0x0007:
         pHistShadow->ctrl.f.drange = 13;   break;
      case 0x0003:
         pHistShadow->ctrl.f.drange = 14;   break;
      default:
         /* Report a MIL error. */
         MfuncErrorReport( 
             Func, M_FUNC_ERROR+FUNCTION_PARAMETER_ERROR_CODE,
             MIL_TEXT("Source buffer data range must be between 4 and 65536."),
             MIL_TEXT("It must also be a power of two."),
             M_NULL,
             M_NULL
             );
         break;
      }
   }


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

void FPGAHistogram(MIL_ID SrcImageId, MIL_ID HistBufId, MIL_INT ClearFlag)
   {
   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("FPGAHistogram"), 
              FUNCTION_NB_PARAM,
              FPGAHistogramSlave, M_NULL, M_NULL, 
              M_USER_MODULE_1+FUNCTION_OPCODE_FPGA_HISTOGRAM, 
              M_DEFAULT, 
              &Func
             );

   /* Register the parameters. */
   MfuncParamMilId (Func, 1, SrcImageId, M_IMAGE, M_IN);
   MfuncParamMilId (Func, 2, HistBufId,  M_HIST_LIST, M_OUT);
   MfuncParamMilInt(Func, 3, ClearFlag);

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

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

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

void MFTYPE FPGAHistogramSlave(MIL_ID Func)
   {
   MIL_ID SrcImageId, HistBufId;
   MIL_INT ClearFlag;

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

/* MIL Routine used to find a specific instance of a command context. */
/* ------------------------------ */

bool FindOffChipHistogram(MIL_ID MilSystem, MIL_FPGA_CONTEXT* pHistogram)
   {
   MIL_FPGA_CONTEXT HistogramContext = 0;
   bool Done = false;
   MIL_INT InstanceNbr = M_DEV0, Capability;
   do
      {
      if(MfpgaCommandAlloc(MilSystem, M_DEV0, FPGA_HIST_FID,
            M_DEFAULT, InstanceNbr++, M_SYNCHRONOUS, M_DEFAULT, &HistogramContext))
         {
         MfpgaCommandInquire(HistogramContext, M_CAPABILITY, &Capability);
         /* This example uses the on-chip histogram Processing Unit. */
         if((Capability & FPGA_HIST_CPBLTY_EXTERNAL_SRAM_SUPPORT) == 0)
            {
            Done = true;
            }
         else
            {
            MfpgaCommandFree(HistogramContext, M_DEFAULT);
            HistogramContext = 0;
            }
         }
      else
         Done = true;
      }
   while(!Done);

   *pHistogram = HistogramContext;
   return ((*pHistogram) != 0);
   }