// Copyright (C) 2023-2025 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
namespace MVCNN;

enum MemoryLocation : byte {
  // Values indicating which type of memory a tensor resides in
  //
  NULL = 0, // This is either an invalid tensor, or a conceptual one that cannot be computed on.
  ProgrammableInput = 1,   // Memory Location retrieved/populated by device api users.
  ProgrammableOutput = 2,  // Memory Location retrieved/populated by device api users.
  VPU_DDR_Heap = 3,        // "Scratch Memory" for intermediary and temporal values needed for execution. No guarantee of initalized values.
  GraphFile = 4,           // Tensor is contained within the "binary_data" section of this graphfile
  VPU_CMX_NN = 5,          // Fast CMX Memory Connected to the NN Subprocessors
  VPU_DDR_BSS = 6,         // Similar "Scratch Memory" to "VPU_DDR_Heap", this explicitly guarantees zero initialization
  VPU_CSRAM = 7,           // Shared Memory
  AbsoluteAddr = 8,        // Absolute address
  MAC_Accumulators = 9,   // MAC accumulators
  ProfilingOutput = 10,    // Profiling output location retrieved/populated by device api users.
  GFEmbeddedKernel = 11,   // Tensor is contained within the "kernel_data" section of this graphfile
  KernelsBuffer = 12,      // An externally provided contiguous buffer of Activation Kernel ELFs
  ExternalFile = 13,        // Tensor is contained within weights.dat
}

enum DType : byte {
  // An enum to be used by a TensorReference Object, so that
  // it will know how to access data from its buffer.
  NOT_SET = 0,

  // Floating Point Types
  FP64 = 1,
  FP32 = 2,
  FP16 = 3,
  FP8 = 4,

  // Unsigned Integers
  U64 = 5,
  U32 = 6,
  U16 = 7,
  U8 = 8,

  // Signed Integers
  I64 = 9,
  I32 = 10,
  I16 = 11,
  I8 = 12,
  I4 = 13,
  I2 = 14,

  // Additional Sub-Byte Types
  I4X = 15,
  BIN = 16,
  LOG = 17,
  I2X = 18,

  // Variant Types
  BFP16 = 19,  // "Brain Floating Point" - https://en.wikipedia.org/wiki/Bfloat16_floating-point_format

  // Unsigned Integers (cont.)
  U4 = 20,
  
  // FP8 specific types
  BF8 = 21,  // (E5M2) "Brain Float 8"
  HF8 = 22,  // (E4M3) "Hybrid Float 8"
}

// Each field is optional
table BinaryData{
  // There can be many BinaryDatas as part of the graphfile.
  // Together they contain all known Tensors that are parameters
  // to the serialized network.

  // FlatBuffers internally align to the size of the datatype.
  underlying_type: DType;
  length: ulong;

  // This nasty padding is to ensure 64 Byte alignment, along with changes in the Fathom compiler.
  // Flatbuffers does not support it by default, so we have to work around it.
  padding: ulong = 0;
  padding2: ulong = 0;
  padding3: ulong = 0;
  padding4: ulong = 0;
  padding5: ulong = 0;
  padding6: ulong = 0;
  padding7: ulong = 0;
  padding8: ulong = 0;
  padding9: ulong = 0;
  padding10: ulong = 0;
  padding11: ulong = 0;
  padding12: ulong = 0;
  padding13: ulong = 0;
  padding14: ulong = 0;
  padding15: ulong = 0;
  padding16: ulong = 0;
  padding17: ulong = 0;
  padding18: ulong = 0;
  padding19: ulong = 0;
  padding20: ulong = 0;
  padding21: ulong = 0;
  padding22: ulong = 0;
  padding23: ulong = 0;
  padding24: ulong = 0;
  padding25: ulong = 0;
  padding26: ulong = 0;
  padding27: ulong = 0;
  padding28: ulong = 0;
  data: [uint64];

  // Marks tensors which should be copied to CSRAM
  // Only such tensors can be copied which will be read back by NN DMA
  // e.g. weights/parameters of SW layers read by shaves/DMA have to remain uncached
  csram_cacheable: bool;
}

table KernelData{
  // KernelData exists as a separately indexed collection of activation kernels for a
  // given inference. They can represent any kernel related buffer such as code or invocation data.
  // There can be many KernelDatas as part of the graphfile.

  // FlatBuffers internally align to the size of the datatype.
  // As there are several things that perform better (or perform at all)
  // when aligned to 64bit, we can pack values inside many unsigned longs.
  length: ulong;

  // This nasty padding is to ensure 64 Byte alignment, along with changes in the Fathom compiler.
  // Flatbuffers does not support it by default, so we have to work around it.
  padding: ulong = 0;
  padding2: ulong = 0;
  padding3: ulong = 0;
  padding4: ulong = 0;
  padding5: ulong = 0;
  padding6: ulong = 0;
  padding7: ulong = 0;
  padding8: ulong = 0;
  padding9: ulong = 0;
  padding10: ulong = 0;
  padding11: ulong = 0;
  padding12: ulong = 0;
  padding13: ulong = 0;
  padding14: ulong = 0;
  padding15: ulong = 0;
  padding16: ulong = 0;
  padding17: ulong = 0;
  padding18: ulong = 0;
  padding19: ulong = 0;
  padding20: ulong = 0;
  padding21: ulong = 0;
  padding22: ulong = 0;
  padding23: ulong = 0;
  padding24: ulong = 0;
  padding25: ulong = 0;
  padding26: ulong = 0;
  padding27: ulong = 0;
  padding28: ulong = 0;
  data: [ubyte];
}

table IndirectDataReference {
  /// Index/Offsets from the start of a memory location (see MemoryLocation)
  data_index: ulong = 999999999999999999 ;     // The data we want to access
  sparsity_index: ulong = 999999999999999999 ;  // Sparsity map associated with that data (optional)
  storage_element_index: ulong = 999999999999999999 ;  // Parameter sparsity maps tend to have their SEs in the weightTable, so this is mostly used for activations
  storage_element_size: uint = 0; // Tensor storage element size. In case of dense activations, this field is 0
}

table TensorReference {
  /// Information on how to access a Tensor

  name: string; // Field for easier debug

  dimensions: [uint];  // Logical dimensions

  strides: [float];    // The field will be deprecated, please use bit_strides instead.
                       // The first value of strides is always the stride in bytes of a single element. This is usually the size of the datatype.
                       // The subsequent values should be provided in the same order as the dimensions are specified.
                       // "Order" can then be derived from this array.
  leading_offset: uint;   // Amount of bytes to be left before the tensor is located
  trailing_offset: uint;  // Amount of bytes to be left after the tensor

  data: IndirectDataReference;  // This object contains the information on how to locate the physical tensors.

  locale: MemoryLocation; // What memory location the tensor resides in.
  // When multiple copies of a memory location exist (e.g. NN CMX Slices). This value is an index to the ones desires.
  locale_index: [uint];

  // Datatype information
  data_dtype: DType;

  // Quantization Meta values for uint8; programmed into PPE
  // For more details please refer to: https://arxiv.org/abs/1712.05877

  // Short summary:
  // Quantization formula -> real_value = scale * (quantized_value - zero_point)
  // Hardware scale multiplier -> M = input_scale * weight_scale / output_scale
  // M should be floating point but it is memorized as two integers M0 and shift such that
  // x * M =~ ((x * M0) >> shift)
  // Values are stored as vector for each tensor because they can be on a per channel basis.
  quant_zero: [ubyte];   // The quantized value that represents zero.
  quant_mult: [ushort];  // The difference between consecutive quantized values
  quant_shift: [ubyte];  // Used to resize operation accumulator results back to desired range
  quant_post_shift_right: byte = 0;
  order: uint64 = 0;    // Desribes permutation of logical dimensions to memory positions.
  // Density rate of the tensor [0-> fully sparse, 1-> fully dense]
  // This field is used only in simulation
  density_rate: float = 1;
  // Enable the swizzling key for the tensor.
  swizzling_key: ubyte = 0;

  // Alternative tensor shape representation for network with 'dynamic' data/behavior.
  dimsTensor: TensorReference; // alternative representation for dimensions (U32 1D tensor)
  stridesTensor: TensorReference; // alternative representation for strides (FP32 1D tensor)

  // Configuration for the IDU base_offset or ODU base_ptr registers.
  base_ptrs: [ushort];
  
  // If descriptor is set then it is an address of an indirect directml data descriptor.
  // The directml data descriptor points to the data resource at runtime.
  // This data resource pointer should be added into the memory location address to get the actual data reference.
  descriptor: ulong;

  bit_strides: [uint64];  // The effective bit width of float is smaller than uint64 and the int datatype can't effectively represent datatype.
                          // smaller than one byte, such as int4 and int1.
}

table KernelDataReference {
  // Information on how to access a Activation SHAVE Kernel
  // All underlying DTypes are always assumed to be U8

  // Field for easier debug
  // * Not to be confused with the layer name *
  // e.g This field may contain the kernel's function name "SoftMax",
  // rather than an arbitrary compiler name such as "classifier/softmax#12"
  name: string;

  // What memory location the ActKernel binary data resides in.
  locale: MemoryLocation;

  // When `locale = GFEmbeddedKernel`, this value indexes which KernelData is being referenced from GraphFile:kernel_data.
  // When `locale != GFEmbeddedKernel`, the offset from the start of a KernelsBuffer or AbsoluteAddr
  locale_offset: uint32;

  // The offset from the above selected `locale_offset`, i.e. where the .text or .data actually starts inside the ELF referenced by the KDR
  data_offset: uint32;

  // The size must be forward-declared because it may not be inferrable at parse time with certain locales
  referenced_data_size: uint32;
}
