 # Copyright 2019 Xilinx, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
 #     http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.

from ctypes import *
import numpy as np
import sys
import argparse
import os

class XFBLASManager:
  def __init__(self,libFile):
    self._lib = cdll.LoadLibrary(libFile)
    self._lib.xfblasCreate.argtypes = [c_char_p,c_char_p,c_uint,c_uint]
    self._lib.xfblasCreate.restype = c_bool
    self._lib.xfblasSend.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_ulonglong,c_uint,c_uint,c_uint]
    self._lib.xfblasSend.restype = c_bool
    self._lib.xfblasGet.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_uint,c_uint]
    self._lib.xfblasGet.restype = c_bool
    self._lib.xfblasFreeInstr.argtypes = [c_uint,c_uint]
    self._lib.xfblasDestroy.argtypes = [c_uint,c_uint]
    self._lib.xfblasFree.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_uint,c_uint]
    self._lib.xfblasGemm.argtypes = [c_uint,c_uint,c_uint,c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,c_uint]
    self._lib.xfblasGemm.restype = c_bool
    self._lib.xfblasGemv.argtypes = [c_uint,c_uint,c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,c_uint]
    self._lib.xfblasGemv.restype = c_bool
    self._lib.xfblasFcn.argtypes = [c_uint,c_uint,c_uint,c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_int,c_int,
                                     c_short,c_short,
                                     c_uint,c_uint]
    self._lib.xfblasFcnByAddress.argtypes = [c_uint,c_uint,c_uint,c_uint,
                                             c_uint,c_uint,c_uint,c_uint,
                                             c_uint,c_uint,c_uint,
                                             c_int,c_int,
                                             c_short,c_short,
                                             c_uint,c_uint]
    self._lib.xfblasGemm.restype = c_bool
    self._lib.xfblasGetByAddress.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_ulonglong,c_uint,c_uint,c_uint]
    self._lib.xfblasGetByAddress.restype = c_bool
    self._lib.xfblasExecuteAsync.argtypes = [c_uint,c_uint]
    self._lib.xfblasExecute.argtypes = [c_uint,c_uint]
    
  def createGemm(self,xclbin,numKernel,idxDevice):
    '''
    create Gemm Handle
    
    
    Parameters
    
    xclbin
                file path for FPGA bitstream
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    b_xclbin = xclbin.encode('utf-8')
    b_log = xclbin.encode('utf-8')
    return self._lib.xfblasCreate(b_xclbin,b'Gemm',numKernel,idxDevice)
    
  def createGemv(self,xclbin,numKernel,idxDevice):
    '''
    create Gemv Handle
    
    Parameters
    
    xclbin
                file path for FPGA bitstream
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    b_xclbin = xclbin.encode('utf-8')
    b_log = xclbin.encode('utf-8')
    return self._lib.xfblasCreate(b_xclbin,b'Gemv',numKernel,idxDevice)
  
  def createFcn(self,xclbin,numKernel,idxDevice):
    '''
    create Fcn Handle
    
    Parameters
    
    xclbin
                file path for FPGA bitstream
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    b_xclbin = xclbin.encode('utf-8')
    b_log = xclbin.encode('utf-8')
    return self._lib.xfblasCreate(b_xclbin,b'Fcn',numKernel,idxDevice)
    
  def sendMat(self,A,idxKernel,idxDevice):
    '''
    send mat from host to device
    
    Parameters
    
    A:          ndarray
                matrix in host memory
    idxKernel:  int
                index of kernel to be used
    idxDeivce:  int
                index of local device to be used
    '''
    return self._lib.xfblasSend(A,c_ulonglong(A.size),c_uint(A.itemsize),idxKernel,idxDevice)
    
  def getMat(self,A,idxKernel,idxDevice):
    '''
    get mat from device to host
    
    Parameters
    
    A:          ndarray
                matrix in host memory
    idxKernel:  int
                index of kernel to be used
    idxDeivce:  int
                index of local device to be used
    '''
    return self._lib.xfblasGet(A,idxKernel,idxDevice)
  
  def freeInstr(self,idxKernel,idxDevice):
    '''
    free memory for instructions
    
    Parameters
    
    idxKernel
                index of kernel to be used
    idxDeivce
                index of local device to be used
    '''
    return self._lib.xfblasFreeInstr(idxKernel,idxDevice)
  
  def freeMat(self,A,idxKernel,idxDevice):
    '''
    free device memory for mat A
    
    Parameters
    
    A:          ndarray
                matrix in host memory
    idxKernel:  int
                index of kernel to be used
    idxDeivce:  int
                index of local device to be used
    '''
    return self._lib.xfblasFree(A,idxKernel,idxDevice)
  
  def destroy(self,numKernel,idxDevice):
    '''
    release handle used by the XFBLAS library
    
    Parameters
    
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''    
    return self._lib.xfblasDestroy(numKernel,idxDevice)
    
  def gemmOp(self,A,B,C,idxKernel,idxDevice):
    '''
    perform matrix-matrix multiplication of C=A*B
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    B:              ndarray
                    matrix in host memory
    C:              ndarray
                    matrix in host memory
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''      
    return self._lib.xfblasGemm(c_uint(A.shape[0]), c_uint(B.shape[1]), c_uint(A.shape[1]), 1, A, c_uint(A.shape[1]), B, c_uint(B.shape[1]), 1, C, c_uint(B.shape[1]),idxKernel,idxDevice)
  
  def gemvOp(self,A,x,y,idxKernel,idxDevice):
    '''
    perform matrix-vector multiplication of y=A*x
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    x:              ndarray
                    vector in host memory
    y:              ndarray
                    vector in host memory
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''      
    return self._lib.xfblasGemv(c_uint(A.shape[0]), c_uint(A.shape[1]),1, A, c_uint(A.shape[1]), x, 1, y, 1, idxKernel,idxDevice)
  
  def fcnOp(self,A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice):
    '''
    perform matrix-matrix multiplication with p_relu, and quantization support of C = relu ((A * B + X) * postScale >> postShift) 
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    B:              ndarray
                    matrix in host memory
    C:              ndarray
                    matrix in host memory
    X:              ndarray
                    matrix in host memory   
    postScale:      int
                    multiply the output values with specific scalar
    postShift:      int
                    shift the output values with specific scalar
    PReLUScale:     int
                    multiply the output values with specific scalar when output values < 0 
    PReLUAlpha:     int
                    shift the output values with specific scalar when output values < 0              
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''      
    return self._lib.xfblasFcn(c_uint(A.shape[0]), c_uint(B.shape[1]), c_uint(A.shape[1]), 1, A, c_uint(A.shape[1]), B, c_uint(B.shape[1]), 1, C, c_uint(C.shape[1]),X,c_uint(X.shape[1]),postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
  def fcnOpByAddress(self,a,b,c,x,A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice):
    '''
    send intructions for matrix-matrix multiplication with p_relu, and quantization support of C = relu ((A * B + X) * postScale >> postShift) 
    Using this function, could avoid sending matrices that will be over-written in device, but users need to provide offsets
    
    Parameters
    
    a:              int
                    offset of memory for A on device
    b:              int
                    offset of memory for B on device
    c:              int
                    offset of memory for C on device  
    A:              ndarray
                    matrix in host memory
    B:              ndarray
                    matrix in host memory
    C:              ndarray
                    matrix in host memory
    X:              ndarray
                    matrix in host memory   
    postScale:      int
                    multiply the output values with specific scalar
    postShift:      int
                    shift the output values with specific scalar
    PReLUScale:     int
                    multiply the output values with specific scalar when output values < 0 
    PReLUAlpha:     int
                    shift the output values with specific scalar when output values < 0              
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''
    return self._lib.xfblasFcnByAddress(c_uint(a),c_uint(b),c_uint(c),c_uint(x),c_uint(A.shape[0]), c_uint(B.shape[1]), c_uint(A.shape[1]), c_uint(A.shape[1]),c_uint(B.shape[1]),c_uint(C.shape[1]),c_uint(X.shape[1]),postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
  def getMatByAddress(self,A,offset,idxKernel,idxDevice):
    '''
    get mat from device by offset
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    offset:         int
                    offset of memory for A on device
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''
    return self._lib.xfblasGetByAddress(A,c_ulonglong(A.size*A.itemsize),offset,idxKernel,idxDevice)
  
  def executeAsync(self,numKernel,idxDevice):
    '''
    run number of kernels async
    
    Parameters
    
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    return self._lib.xfblasExecuteAsync(numKernel,idxDevice)
  
  def execute(self,idxKernel,idxDevice):
    '''
    run ith kernel
    
    Parameters
    
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''
    return self._lib.xfblasExecute(idxKernel,idxDevice)
  
_xfblasManager = None
  
def createGemm(args,xclbin_opts,numKernel=1,idxDevice=0):
    if int(xclbin_opts['GEMX_runGemm'])!= 1:
        raise Exception('The xclbin does not include gemm engine.')
    createManager(args.lib)
    return _xfblasManager.createGemm(args.xclbin,numKernel,idxDevice)
  
def createGemv(args,xclbin_opts,numKernel=1,idxDevice=0):
    if int(xclbin_opts['GEMX_runGemv'])!= 1:
        raise Exception('The xclbin does not include gemv engine.')
    createManager(args.lib)
    return _xfblasManager.createGemv(args.xclbin,numKernel,idxDevice)
  
def createFcn(args,xclbin_opts,numKernel=1,idxDevice=0):
    if int(xclbin_opts['GEMX_runFcn'])!= 1:
        raise Exception('The xclbin does not include fcn engine.')
    createManager(args.lib)
    return _xfblasManager.createFcn(args.xclbin,numKernel,idxDevice)
  
def sendMat(A,idxKernel=0,idxDevice=0):
    return _xfblasManager.sendMat(A,idxKernel,idxDevice)
  
def getMat(A,idxKernel=0,idxDevice=0):
    return _xfblasManager.getMat(A,idxKernel,idxDevice)
  
def freeInstr(idxKernel=0,idxDevice=0):
    return _xfblasManager.freeInstr(idxKernel,idxDevice)

def freeMat(A,idxKernel=0,idxDevice=0):
    return _xfblasManager.freeMat(A,idxKernel,idxDevice)
  
def destroy(numKernel=1,idxDevice=0):
    return _xfblasManager.destroy(numKernel,idxDevice)
  
def gemmOp(A,B,C,idxKernel=0,idxDevice=0):
    return _xfblasManager.gemmOp(A,B,C,idxKernel,idxDevice)
  
def gemvOp(A,x,y,idxKernel=0,idxDevice=0):
    return _xfblasManager.gemvOp(A,x,y,idxKernel,idxDevice)
  
def fcnOp(A,B,C,X,postScale=1,postShift=0,preluScale=1,preluAlpha=0,idxKernel=0,idxDevice=0):
    return _xfblasManager.fcnOp(A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
def fcnOpByAddress(a,b,c,x,A,B,C,X,postScale=1,postShift=0,preluScale=1,preluAlpha=0,idxKernel=0,idxDevice=0):
    return _xfblasManager.fcnOpByAddress(a,b,c,x,A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
def getMatByAddress(A,offset,idxKernel=0,idxDevice=0):
    return _xfblasManager.getMatByAddress(A,offset,idxKernel,idxDevice)
  
def executeAsync(numKernel=1,idxDevice=0):
    return _xfblasManager.executeAsync(numKernel,idxDevice)
  
def execute(idxKernel=0,idxDevice=0):
    return _xfblasManager.execute(idxKernel,idxDevice)
  
def createManager ( libFile ):
  global _xfblasManager
  if not _xfblasManager:
    _xfblasManager = XFBLASManager(libFile)    
  return True  
  
def parse_cfg(filename):
    myvars = {}
    with open(filename) as myfile:
        for line in myfile:
            for word in line.split():
               name, var = word.split("=")
               myvars[name.strip()] = var.rstrip()  
    return myvars

def default_args():
    parser = argparse.ArgumentParser(description='xfblas')
    parser.add_argument('--xclbin', required = True, help='file path to FPGA bitstream')
    parser.add_argument('--lib', required = True, help='file path to xfblas shared library')
    parser.add_argument('--cfg', required=True, help='file describing .xclbin properties')
    return parser
      
def processCommandLine():
    parser = default_args()
    args = parser.parse_args()
    xclbin_opts = parse_cfg ( args.cfg ) 
    return args, xclbin_opts
 # Copyright 2019 Xilinx, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
 #     http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.

from ctypes import *
import numpy as np
import sys
import argparse
import os

class XFBLASManager:
  def __init__(self,libFile):
    self._lib = cdll.LoadLibrary(libFile)
    self._lib.xfblasCreate.argtypes = [c_char_p,c_char_p,c_uint,c_uint]
    self._lib.xfblasCreate.restype = c_bool
    self._lib.xfblasSend.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_ulonglong,c_uint,c_uint,c_uint]
    self._lib.xfblasSend.restype = c_bool
    self._lib.xfblasGet.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_uint,c_uint]
    self._lib.xfblasGet.restype = c_bool
    self._lib.xfblasFreeInstr.argtypes = [c_uint,c_uint]
    self._lib.xfblasDestroy.argtypes = [c_uint,c_uint]
    self._lib.xfblasFree.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_uint,c_uint]
    self._lib.xfblasGemm.argtypes = [c_uint,c_uint,c_uint,c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,c_uint]
    self._lib.xfblasGemm.restype = c_bool
    self._lib.xfblasGemv.argtypes = [c_uint,c_uint,c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,c_uint]
    self._lib.xfblasGemv.restype = c_bool
    self._lib.xfblasFcn.argtypes = [c_uint,c_uint,c_uint,c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"), c_uint,
                                     c_int,c_int,
                                     c_short,c_short,
                                     c_uint,c_uint]
    self._lib.xfblasFcnByAddress.argtypes = [c_uint,c_uint,c_uint,c_uint,
                                             c_uint,c_uint,c_uint,c_uint,
                                             c_uint,c_uint,c_uint,
                                             c_int,c_int,
                                             c_short,c_short,
                                             c_uint,c_uint]
    self._lib.xfblasGemm.restype = c_bool
    self._lib.xfblasGetByAddress.argtypes = [np.ctypeslib.ndpointer(flags="C_CONTIGUOUS"),c_ulonglong,c_uint,c_uint,c_uint]
    self._lib.xfblasGetByAddress.restype = c_bool
    self._lib.xfblasExecuteAsync.argtypes = [c_uint,c_uint]
    self._lib.xfblasExecute.argtypes = [c_uint,c_uint]
    
  def createGemm(self,xclbin,numKernel,idxDevice):
    '''
    create Gemm Handle
    
    
    Parameters
    
    xclbin
                file path for FPGA bitstream
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    b_xclbin = xclbin.encode('utf-8')
    b_log = xclbin.encode('utf-8')
    return self._lib.xfblasCreate(b_xclbin,b'Gemm',numKernel,idxDevice)
    
  def createGemv(self,xclbin,numKernel,idxDevice):
    '''
    create Gemv Handle
    
    Parameters
    
    xclbin
                file path for FPGA bitstream
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    b_xclbin = xclbin.encode('utf-8')
    b_log = xclbin.encode('utf-8')
    return self._lib.xfblasCreate(b_xclbin,b'Gemv',numKernel,idxDevice)
  
  def createFcn(self,xclbin,numKernel,idxDevice):
    '''
    create Fcn Handle
    
    Parameters
    
    xclbin
                file path for FPGA bitstream
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    b_xclbin = xclbin.encode('utf-8')
    b_log = xclbin.encode('utf-8')
    return self._lib.xfblasCreate(b_xclbin,b'Fcn',numKernel,idxDevice)
    
  def sendMat(self,A,idxKernel,idxDevice):
    '''
    send mat from host to device
    
    Parameters
    
    A:          ndarray
                matrix in host memory
    idxKernel:  int
                index of kernel to be used
    idxDeivce:  int
                index of local device to be used
    '''
    return self._lib.xfblasSend(A,c_ulonglong(A.size),c_uint(A.itemsize),idxKernel,idxDevice)
    
  def getMat(self,A,idxKernel,idxDevice):
    '''
    get mat from device to host
    
    Parameters
    
    A:          ndarray
                matrix in host memory
    idxKernel:  int
                index of kernel to be used
    idxDeivce:  int
                index of local device to be used
    '''
    return self._lib.xfblasGet(A,idxKernel,idxDevice)
  
  def freeInstr(self,idxKernel,idxDevice):
    '''
    free memory for instructions
    
    Parameters
    
    idxKernel
                index of kernel to be used
    idxDeivce
                index of local device to be used
    '''
    return self._lib.xfblasFreeInstr(idxKernel,idxDevice)
  
  def freeMat(self,A,idxKernel,idxDevice):
    '''
    free device memory for mat A
    
    Parameters
    
    A:          ndarray
                matrix in host memory
    idxKernel:  int
                index of kernel to be used
    idxDeivce:  int
                index of local device to be used
    '''
    return self._lib.xfblasFree(A,idxKernel,idxDevice)
  
  def destroy(self,numKernel,idxDevice):
    '''
    release handle used by the XFBLAS library
    
    Parameters
    
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''    
    return self._lib.xfblasDestroy(numKernel,idxDevice)
    
  def gemmOp(self,A,B,C,idxKernel,idxDevice):
    '''
    perform matrix-matrix multiplication of C=A*B
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    B:              ndarray
                    matrix in host memory
    C:              ndarray
                    matrix in host memory
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''      
    return self._lib.xfblasGemm(c_uint(A.shape[0]), c_uint(B.shape[1]), c_uint(A.shape[1]), 1, A, c_uint(A.shape[1]), B, c_uint(B.shape[1]), 1, C, c_uint(B.shape[1]),idxKernel,idxDevice)
  
  def gemvOp(self,A,x,y,idxKernel,idxDevice):
    '''
    perform matrix-vector multiplication of y=A*x
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    x:              ndarray
                    vector in host memory
    y:              ndarray
                    vector in host memory
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''      
    return self._lib.xfblasGemv(c_uint(A.shape[0]), c_uint(A.shape[1]),1, A, c_uint(A.shape[1]), x, 1, y, 1, idxKernel,idxDevice)
  
  def fcnOp(self,A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice):
    '''
    perform matrix-matrix multiplication with p_relu, and quantization support of C = relu ((A * B + X) * postScale >> postShift) 
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    B:              ndarray
                    matrix in host memory
    C:              ndarray
                    matrix in host memory
    X:              ndarray
                    matrix in host memory   
    postScale:      int
                    multiply the output values with specific scalar
    postShift:      int
                    shift the output values with specific scalar
    PReLUScale:     int
                    multiply the output values with specific scalar when output values < 0 
    PReLUAlpha:     int
                    shift the output values with specific scalar when output values < 0              
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''      
    return self._lib.xfblasFcn(c_uint(A.shape[0]), c_uint(B.shape[1]), c_uint(A.shape[1]), 1, A, c_uint(A.shape[1]), B, c_uint(B.shape[1]), 1, C, c_uint(C.shape[1]),X,c_uint(X.shape[1]),postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
  def fcnOpByAddress(self,a,b,c,x,A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice):
    '''
    send intructions for matrix-matrix multiplication with p_relu, and quantization support of C = relu ((A * B + X) * postScale >> postShift) 
    Using this function, could avoid sending matrices that will be over-written in device, but users need to provide offsets
    
    Parameters
    
    a:              int
                    offset of memory for A on device
    b:              int
                    offset of memory for B on device
    c:              int
                    offset of memory for C on device  
    A:              ndarray
                    matrix in host memory
    B:              ndarray
                    matrix in host memory
    C:              ndarray
                    matrix in host memory
    X:              ndarray
                    matrix in host memory   
    postScale:      int
                    multiply the output values with specific scalar
    postShift:      int
                    shift the output values with specific scalar
    PReLUScale:     int
                    multiply the output values with specific scalar when output values < 0 
    PReLUAlpha:     int
                    shift the output values with specific scalar when output values < 0              
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''
    return self._lib.xfblasFcnByAddress(c_uint(a),c_uint(b),c_uint(c),c_uint(x),c_uint(A.shape[0]), c_uint(B.shape[1]), c_uint(A.shape[1]), c_uint(A.shape[1]),c_uint(B.shape[1]),c_uint(C.shape[1]),c_uint(X.shape[1]),postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
  def getMatByAddress(self,A,offset,idxKernel,idxDevice):
    '''
    get mat from device by offset
    
    Parameters
    
    A:              ndarray
                    matrix in host memory
    offset:         int
                    offset of memory for A on device
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''
    return self._lib.xfblasGetByAddress(A,c_ulonglong(A.size*A.itemsize),offset,idxKernel,idxDevice)
  
  def executeAsync(self,numKernel,idxDevice):
    '''
    run number of kernels async
    
    Parameters
    
    numKernel
                number of CUs in the xclbin
    idxDeivce
                index of local device to be used
    '''
    return self._lib.xfblasExecuteAsync(numKernel,idxDevice)
  
  def execute(self,idxKernel,idxDevice):
    '''
    run ith kernel
    
    Parameters
    
    idxKernel:      int
                    index of kernel to be used
    idxDeivce:      int
                    index of local device to be used
    '''
    return self._lib.xfblasExecute(idxKernel,idxDevice)
  
_xfblasManager = None
  
def createGemm(args,xclbin_opts,numKernel=1,idxDevice=0):
    if int(xclbin_opts['GEMX_runGemm'])!= 1:
        raise Exception('The xclbin does not include gemm engine.')
    createManager(args.lib)
    return _xfblasManager.createGemm(args.xclbin,numKernel,idxDevice)
  
def createGemv(args,xclbin_opts,numKernel=1,idxDevice=0):
    if int(xclbin_opts['GEMX_runGemv'])!= 1:
        raise Exception('The xclbin does not include gemv engine.')
    createManager(args.lib)
    return _xfblasManager.createGemv(args.xclbin,numKernel,idxDevice)
  
def createFcn(args,xclbin_opts,numKernel=1,idxDevice=0):
    if int(xclbin_opts['GEMX_runFcn'])!= 1:
        raise Exception('The xclbin does not include fcn engine.')
    createManager(args.lib)
    return _xfblasManager.createFcn(args.xclbin,numKernel,idxDevice)
  
def sendMat(A,idxKernel=0,idxDevice=0):
    return _xfblasManager.sendMat(A,idxKernel,idxDevice)
  
def getMat(A,idxKernel=0,idxDevice=0):
    return _xfblasManager.getMat(A,idxKernel,idxDevice)
  
def freeInstr(idxKernel=0,idxDevice=0):
    return _xfblasManager.freeInstr(idxKernel,idxDevice)

def freeMat(A,idxKernel=0,idxDevice=0):
    return _xfblasManager.freeMat(A,idxKernel,idxDevice)
  
def destroy(numKernel=1,idxDevice=0):
    return _xfblasManager.destroy(numKernel,idxDevice)
  
def gemmOp(A,B,C,idxKernel=0,idxDevice=0):
    return _xfblasManager.gemmOp(A,B,C,idxKernel,idxDevice)
  
def gemvOp(A,x,y,idxKernel=0,idxDevice=0):
    return _xfblasManager.gemvOp(A,x,y,idxKernel,idxDevice)
  
def fcnOp(A,B,C,X,postScale=1,postShift=0,preluScale=1,preluAlpha=0,idxKernel=0,idxDevice=0):
    return _xfblasManager.fcnOp(A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
def fcnOpByAddress(a,b,c,x,A,B,C,X,postScale=1,postShift=0,preluScale=1,preluAlpha=0,idxKernel=0,idxDevice=0):
    return _xfblasManager.fcnOpByAddress(a,b,c,x,A,B,C,X,postScale,postShift,preluScale,preluAlpha,idxKernel,idxDevice)
  
def getMatByAddress(A,offset,idxKernel=0,idxDevice=0):
    return _xfblasManager.getMatByAddress(A,offset,idxKernel,idxDevice)
  
def executeAsync(numKernel=1,idxDevice=0):
    return _xfblasManager.executeAsync(numKernel,idxDevice)
  
def execute(idxKernel=0,idxDevice=0):
    return _xfblasManager.execute(idxKernel,idxDevice)
  
def createManager ( libFile ):
  global _xfblasManager
  if not _xfblasManager:
    _xfblasManager = XFBLASManager(libFile)    
  return True  
  
def parse_cfg(filename):
    myvars = {}
    with open(filename) as myfile:
        for line in myfile:
            for word in line.split():
               name, var = word.split("=")
               myvars[name.strip()] = var.rstrip()  
    return myvars

def default_args():
    parser = argparse.ArgumentParser(description='xfblas')
    parser.add_argument('--xclbin', required = True, help='file path to FPGA bitstream')
    parser.add_argument('--lib', required = True, help='file path to xfblas shared library')
    parser.add_argument('--cfg', required=True, help='file describing .xclbin properties')
    return parser
      
def processCommandLine():
    parser = default_args()
    args = parser.parse_args()
    xclbin_opts = parse_cfg ( args.cfg ) 
    return args, xclbin_opts
# Copyright 2019 Xilinx, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # #     http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. create Gemm Handle               Parameters          xclbin                 file path for FPGA bitstream     numKernel                 number of CUs in the xclbin     idxDeivce                 index of local device to be used create Gemv Handle          Parameters          xclbin                 file path for FPGA bitstream     numKernel                 number of CUs in the xclbin     idxDeivce                 index of local device to be used create Fcn Handle          Parameters          xclbin                 file path for FPGA bitstream     numKernel                 number of CUs in the xclbin     idxDeivce                 index of local device to be used send mat from host to device          Parameters          A:          ndarray                 matrix in host memory     idxKernel:  int                 index of kernel to be used     idxDeivce:  int                 index of local device to be used get mat from device to host          Parameters          A:          ndarray                 matrix in host memory     idxKernel:  int                 index of kernel to be used     idxDeivce:  int                 index of local device to be used free memory for instructions          Parameters          idxKernel                 index of kernel to be used     idxDeivce                 index of local device to be used free device memory for mat A          Parameters          A:          ndarray                 matrix in host memory     idxKernel:  int                 index of kernel to be used     idxDeivce:  int                 index of local device to be used release handle used by the XFBLAS library          Parameters          numKernel                 number of CUs in the xclbin     idxDeivce                 index of local device to be used perform matrix-matrix multiplication of C=A*B          Parameters          A:              ndarray                     matrix in host memory     B:              ndarray                     matrix in host memory     C:              ndarray                     matrix in host memory     idxKernel:      int                     index of kernel to be used     idxDeivce:      int                     index of local device to be used perform matrix-vector multiplication of y=A*x          Parameters          A:              ndarray                     matrix in host memory     x:              ndarray                     vector in host memory     y:              ndarray                     vector in host memory     idxKernel:      int                     index of kernel to be used     idxDeivce:      int                     index of local device to be used perform matrix-matrix multiplication with p_relu, and quantization support of C = relu ((A * B + X) * postScale >> postShift)           Parameters          A:              ndarray                     matrix in host memory     B:              ndarray                     matrix in host memory     C:              ndarray                     matrix in host memory     X:              ndarray                     matrix in host memory        postScale:      int                     multiply the output values with specific scalar     postShift:      int                     shift the output values with specific scalar     PReLUScale:     int                     multiply the output values with specific scalar when output values < 0      PReLUAlpha:     int                     shift the output values with specific scalar when output values < 0                   idxKernel:      int                     index of kernel to be used     idxDeivce:      int                     index of local device to be used send intructions for matrix-matrix multiplication with p_relu, and quantization support of C = relu ((A * B + X) * postScale >> postShift)      Using this function, could avoid sending matrices that will be over-written in device, but users need to provide offsets          Parameters          a:              int                     offset of memory for A on device     b:              int                     offset of memory for B on device     c:              int                     offset of memory for C on device       A:              ndarray                     matrix in host memory     B:              ndarray                     matrix in host memory     C:              ndarray                     matrix in host memory     X:              ndarray                     matrix in host memory        postScale:      int                     multiply the output values with specific scalar     postShift:      int                     shift the output values with specific scalar     PReLUScale:     int                     multiply the output values with specific scalar when output values < 0      PReLUAlpha:     int                     shift the output values with specific scalar when output values < 0                   idxKernel:      int                     index of kernel to be used     idxDeivce:      int                     index of local device to be used get mat from device by offset          Parameters          A:              ndarray                     matrix in host memory     offset:         int                     offset of memory for A on device     idxKernel:      int                     index of kernel to be used     idxDeivce:      int                     index of local device to be used run number of kernels async          Parameters          numKernel                 number of CUs in the xclbin     idxDeivce                 index of local device to be used run ith kernel          Parameters          idxKernel:      int                     index of kernel to be used     idxDeivce:      int                     index of local device to be used