Front Haul Interface Library update to first seed code contribution
[o-du/phy.git] / fhi_lib / lib / src / xran_compression.cpp
diff --git a/fhi_lib/lib/src/xran_compression.cpp b/fhi_lib/lib/src/xran_compression.cpp
new file mode 100644 (file)
index 0000000..8730a20
--- /dev/null
@@ -0,0 +1,185 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+#include "xran_compression.hpp"
+#include <complex>
+#include <algorithm>
+#include <immintrin.h>
+
+void
+BlockFloatCompander::BlockFloatCompress_AVX512(const ExpandedData& dataIn, CompressedData* dataOut)
+{
+  __m512i maxAbs = __m512i();
+
+  /// Load data and find max(abs(RB))
+  const __m512i* rawData = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
+  static constexpr int k_numInputLoopIts = BlockFloatCompander::k_numRB / 4;
+
+#pragma unroll(k_numInputLoopIts)
+  for (int n = 0; n < k_numInputLoopIts; ++n)
+  {
+    /// Re-order the next 4RB in input data into 3 registers
+    /// Input SIMD vectors are:
+    ///   [A A A A A A A A A A A A B B B B]
+    ///   [B B B B B B B B C C C C C C C C]
+    ///   [C C C C D D D D D D D D D D D D]
+    /// Re-ordered SIMD vectors are:
+    ///   [A A A A B B B B C C C C D D D D]
+    ///   [A A A A B B B B C C C C D D D D]
+    ///   [A A A A B B B B C C C C D D D D]
+    static constexpr uint8_t k_msk1 = 0b11111100; // Copy first lane of src
+    static constexpr int k_shuff1 = 0x41;
+    const auto z_w1 = _mm512_mask_shuffle_i64x2(rawData[3 * n + 0], k_msk1, rawData[3 * n + 1], rawData[3 * n + 2], k_shuff1);
+
+    static constexpr uint8_t k_msk2 = 0b11000011; // Copy middle two lanes of src
+    static constexpr int k_shuff2 = 0xB1;
+    const auto z_w2 = _mm512_mask_shuffle_i64x2(rawData[3 * n + 1], k_msk2, rawData[3 * n + 0], rawData[3 * n + 2], k_shuff2);
+
+    static constexpr uint8_t k_msk3 = 0b00111111; // Copy last lane of src
+    static constexpr int k_shuff3 = 0xBE;
+    const auto z_w3 = _mm512_mask_shuffle_i64x2(rawData[3 * n + 2], k_msk3, rawData[3 * n + 0], rawData[3 * n + 1], k_shuff3);
+
+    /// Perform max abs on these 3 registers
+    const auto abs16_1 = _mm512_abs_epi16(z_w1);
+    const auto abs16_2 = _mm512_abs_epi16(z_w2);
+    const auto abs16_3 = _mm512_abs_epi16(z_w3);
+    const auto maxAbs_12 = _mm512_max_epi16(abs16_1, abs16_2);
+    const auto maxAbs_123 = _mm512_max_epi16(maxAbs_12, abs16_3);
+
+    /// Perform horizontal max over each lane
+    /// Swap 64b in each lane and compute max
+    static const auto k_perm64b = _mm512_set_epi64(6, 7, 4, 5, 2, 3, 0, 1);
+    auto maxAbsPerm = _mm512_permutexvar_epi64(k_perm64b, maxAbs_123);
+    auto maxAbsHorz = _mm512_max_epi16(maxAbs_123, maxAbsPerm);
+
+    /// Swap each pair of 32b in each lane and compute max
+    static const auto k_perm32b = _mm512_set_epi32(14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1);
+    maxAbsPerm = _mm512_permutexvar_epi32(k_perm32b, maxAbsHorz);
+    maxAbsHorz = _mm512_max_epi16(maxAbsHorz, maxAbsPerm);
+
+    /// Swap each IQ pair in each lane (via 32b rotation) and compute max
+    maxAbsPerm = _mm512_rol_epi32(maxAbsHorz, BlockFloatCompander::k_numBitsIQ);
+    maxAbsHorz = _mm512_max_epi16(maxAbsHorz, maxAbsPerm);
+
+    /// Insert values into maxAbs
+    /// Use sliding mask to insert wanted values into maxAbs
+    /// Pairs of values will be inserted and corrected outside of loop
+    static const auto k_select4RB = _mm512_set_epi32(28, 24, 20, 16, 28, 24, 20, 16,
+                                                     28, 24, 20, 16, 28, 24, 20, 16);
+    static constexpr uint16_t k_expMsk[k_numInputLoopIts] = { 0x000F, 0x00F0, 0x0F00, 0xF000 };
+    maxAbs = _mm512_mask_permutex2var_epi32(maxAbs, k_expMsk[n], k_select4RB, maxAbsHorz);
+  }
+
+  /// Convert to 32b by removing repeated values in maxAbs
+  static const auto k_upperWordMask = _mm512_set_epi64(0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF,
+                                                       0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF,
+                                                       0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF,
+                                                       0x0000FFFF0000FFFF, 0x0000FFFF0000FFFF);
+  maxAbs = _mm512_and_epi64(maxAbs, k_upperWordMask);
+
+  /// Compute exponent and store for later use
+  static constexpr int k_expTotShiftBits = 32 - BlockFloatCompander::k_iqWidth + 1;
+  const auto totShiftBits = _mm512_set1_epi32(k_expTotShiftBits);
+  const auto lzCount = _mm512_lzcnt_epi32(maxAbs);
+  const auto exponent = _mm512_sub_epi32(totShiftBits, lzCount);
+  int8_t storedExp[BlockFloatCompander::k_numRB] = {};
+  static constexpr uint16_t k_expWriteMask = 0xFFFF;
+  _mm512_mask_cvtepi32_storeu_epi8(storedExp, k_expWriteMask, exponent);
+
+  /// Shift 1RB by corresponding exponent and write exponent and data to output
+  /// Output data is packed exponent first followed by corresponding compressed RB
+#pragma unroll(BlockFloatCompander::k_numRB)
+  for (int n = 0; n < BlockFloatCompander::k_numRB; ++n)
+  {
+    const __m512i* rawDataIn = reinterpret_cast<const __m512i*>(dataIn.dataExpanded + n * BlockFloatCompander::k_numREReal);
+    auto compData = _mm512_srai_epi16(*rawDataIn, storedExp[n]);
+
+    dataOut->dataCompressed[n * (BlockFloatCompander::k_numREReal + 1)] = storedExp[n];
+    static constexpr uint32_t k_rbMask = 0x00FFFFFF; // Write mask for 1RB (24 values)
+    _mm512_mask_cvtepi16_storeu_epi8(dataOut->dataCompressed + n * (BlockFloatCompander::k_numREReal + 1) + 1, k_rbMask, compData);
+  }
+}
+
+
+void
+BlockFloatCompander::BlockFloatExpand_AVX512(const CompressedData& dataIn, ExpandedData* dataOut)
+{
+#pragma unroll(BlockFloatCompander::k_numRB)
+  for (int n = 0; n < BlockFloatCompander::k_numRB; ++n)
+  {
+    /// Expand 1RB of data
+    const __m256i* rawDataIn = reinterpret_cast<const __m256i*>(dataIn.dataCompressed + n * (BlockFloatCompander::k_numREReal + 1) + 1);
+    const auto compData16 = _mm512_cvtepi8_epi16(*rawDataIn);
+    const auto expData = _mm512_slli_epi16(compData16, *(dataIn.dataCompressed + n * (BlockFloatCompander::k_numREReal + 1)));
+
+    /// Write expanded data to output
+    static constexpr uint8_t k_rbMask64 = 0b00111111; // 64b write mask for 1RB (24 int16 values)
+    _mm512_mask_storeu_epi64(dataOut->dataExpanded + n * BlockFloatCompander::k_numREReal, k_rbMask64, expData);
+  }
+}
+
+
+void
+BlockFloatCompander::BlockFloatCompress_Basic(const ExpandedData& dataIn, CompressedData* dataOut)
+{
+  int16_t maxAbs[BlockFloatCompander::k_numRB];
+  for (int rb = 0; rb < BlockFloatCompander::k_numRB; ++rb)
+  {
+    // Find max abs value for this RB
+    maxAbs[rb] = 0;
+    for (int re = 0; re < BlockFloatCompander::k_numREReal; ++re)
+    {
+      auto dataIdx = rb * BlockFloatCompander::k_numREReal + re;
+      int16_t dataAbs = (int16_t)std::abs(dataIn.dataExpanded[dataIdx]);
+      maxAbs[rb] = std::max(maxAbs[rb], dataAbs);
+    }
+
+    // Find exponent
+    static constexpr int k_expTotShiftBits16 = 16 - BlockFloatCompander::k_iqWidth + 1;
+    auto thisExp = (int8_t)(k_expTotShiftBits16 - __lzcnt16(maxAbs[rb]));
+    auto expIdx = rb * (BlockFloatCompander::k_numREReal + 1);
+    dataOut->dataCompressed[expIdx] = thisExp;
+
+    // ARS data by exponent
+    for (int re = 0; re < BlockFloatCompander::k_numREReal; ++re)
+    {
+      auto dataIdxIn = rb * BlockFloatCompander::k_numREReal + re;
+      auto dataIdxOut = (expIdx + 1) + re;
+      dataOut->dataCompressed[dataIdxOut] = (int8_t)(dataIn.dataExpanded[dataIdxIn] >> thisExp);
+    }
+  }
+}
+
+
+void
+BlockFloatCompander::BlockFloatExpand_Basic(const CompressedData& dataIn, ExpandedData* dataOut)
+{
+  // Expand data
+  for (int rb = 0; rb < BlockFloatCompander::k_numRB; ++rb)
+  {
+    for (int re = 0; re < BlockFloatCompander::k_numREReal; ++re)
+    {
+      auto dataIdxOut = rb * BlockFloatCompander::k_numREReal + re;
+      auto expIdx = rb * (BlockFloatCompander::k_numREReal + 1);
+      auto dataIdxIn = (expIdx + 1) + re;
+      auto thisData = (int16_t)dataIn.dataCompressed[dataIdxIn];
+      auto thisExp = (int16_t)dataIn.dataCompressed[expIdx];
+      dataOut->dataExpanded[dataIdxOut] = (int16_t)(thisData << thisExp);
+    }
+  }
+}