* INTC Contribution to the O-RAN F Release for O-DU Low
[o-du/phy.git] / fhi_lib / lib / src / xran_bfp_uplane_snc.cpp
1 /******************************************************************************
2 *
3 *   Copyright (c) 2020 Intel.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 *
17 *******************************************************************************/
18
19 /**
20  * @brief xRAN BFP compression/decompression U-plane implementation and interface functions
21  *
22  * @file xran_compression.cpp
23  * @ingroup group_source_xran
24  * @author Intel Corporation
25  **/
26
27 #include "xran_compression.hpp"
28 #include "xran_bfp_utils.hpp"
29 #include "xran_bfp_byte_packing_utils.hpp"
30 #include "xran_compression.h"
31 #include <complex>
32 #include <algorithm>
33 #include <immintrin.h>
34
35
36 namespace BFP_UPlane_SNC
37 {
38   /// Namespace constants
39   const int k_numREReal = 24; /// 12 IQ pairs
40
41
42   /// Compute exponent value for a set of 16 RB from the maximum absolute value.
43   /// Max Abs operates in a loop, executing 4 RB per iteration. The results are
44   /// packed into the final output register.
45   __m512i
46   computeExponent_16RB(const BlockFloatCompander::ExpandedData& dataIn, const __m512i totShiftBits)
47   {
48     __m512i maxAbs = __m512i();
49     const __m512i* rawData = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
50     /// Max Abs loop operates on 4RB at a time
51 #pragma unroll(4)
52     for (int n = 0; n < 4; ++n)
53     {
54       /// Re-order and vertical max abs
55       auto maxAbsVert = BlockFloatCompander::maxAbsVertical4RB(rawData[3 * n + 0], rawData[3 * n + 1], rawData[3 * n + 2]);
56       /// Horizontal max abs
57       auto maxAbsHorz = BlockFloatCompander::horizontalMax4x16(maxAbsVert);
58       /// Pack these 4 values into maxAbs
59       maxAbs = BlockFloatCompander::slidePermute(maxAbsHorz, maxAbs, n);
60     }
61     /// Calculate exponent
62     const auto maxAbs32 = BlockFloatCompander::maskUpperWord(maxAbs);
63     return BlockFloatCompander::expLzCnt(maxAbs32, totShiftBits);
64   }
65
66
67   /// Compute exponent value for a set of 4 RB from the maximum absolute value.
68   /// Note that we do not need to perform any packing of result as we are only
69   /// computing 4 RB. The appropriate offset is taken later when extracting the
70   /// exponent.
71   __m512i
72   computeExponent_4RB(const BlockFloatCompander::ExpandedData& dataIn, const __m512i totShiftBits)
73   {
74     const __m512i* rawData = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
75     /// Re-order and vertical max abs
76     const auto maxAbsVert = BlockFloatCompander::maxAbsVertical4RB(rawData[0], rawData[1], rawData[2]);
77     /// Horizontal max abs
78     const auto maxAbsHorz = BlockFloatCompander::horizontalMax4x16(maxAbsVert);
79     /// Calculate exponent
80     const auto maxAbs = BlockFloatCompander::maskUpperWord(maxAbsHorz);
81     return BlockFloatCompander::expLzCnt(maxAbs, totShiftBits);
82   }
83
84
85   /// Compute exponent value for 1 RB from the maximum absolute value.
86   /// This works with horizontal max abs only, and needs to include a
87   /// step to select the final exponent from the 4 lanes.
88   uint8_t
89   computeExponent_1RB(const BlockFloatCompander::ExpandedData& dataIn, const __m512i totShiftBits)
90   {
91     const __m512i* rawData = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
92     /// Abs
93     const auto rawDataAbs = _mm512_abs_epi16(rawData[0]);
94     /// No need to do a full horizontal max operation here, just do a max IQ step,
95     /// compute the exponents and then use a reduce max over all exponent values. This
96     /// is the fastest way to handle a single RB.
97     const auto rawAbsIQSwap = _mm512_rol_epi32(rawDataAbs, BlockFloatCompander::k_numBitsIQ);
98     const auto maxAbsIQ = _mm512_max_epi16(rawDataAbs, rawAbsIQSwap);
99     /// Calculate exponent
100     const auto maxAbsIQ32 = BlockFloatCompander::maskUpperWord(maxAbsIQ);
101     const auto exps = BlockFloatCompander::expLzCnt(maxAbsIQ32, totShiftBits);
102     /// At this point we have exponent values for the maximum of each IQ pair.
103     /// Run a reduce max step to compute the maximum exponent value in the first
104     /// three lanes - this will give the desired exponent for this RB.
105     constexpr uint16_t k_expMsk = 0x0FFF;
106     return (uint8_t)_mm512_mask_reduce_max_epi32(k_expMsk, exps);
107   }
108
109
110   /// Apply compression to 1 RB
111   template<BlockFloatCompander::PackFunction networkBytePack>
112   void
113   applyCompressionN_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
114                         const int numREOffset, const uint8_t thisExp, const int thisRBExpAddr, const uint64_t rbWriteMask)
115   {
116     /// Get AVX512 pointer aligned to desired RB
117     const __m512i* rawDataIn = reinterpret_cast<const __m512i*>(dataIn.dataExpanded + numREOffset);
118     /// Apply the exponent shift
119     const auto compData = _mm512_srai_epi16(*rawDataIn, thisExp);
120     /// Pack compressed data network byte order
121     const auto compDataBytePacked = networkBytePack(compData);
122     /// Store exponent first
123     dataOut->dataCompressed[thisRBExpAddr] = thisExp;
124     /// Store compressed data
125     _mm512_mask_storeu_epi8(dataOut->dataCompressed + thisRBExpAddr + 1, rbWriteMask, compDataBytePacked);
126   }
127
128
129   /// Apply 9, 10, or 12bit compression to 16 RB
130   template<BlockFloatCompander::PackFunction networkBytePack>
131   void
132   compressN_16RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
133                  const __m512i totShiftBits, const int totNumBytesPerRB, const uint64_t rbWriteMask)
134   {
135     const auto exponents = computeExponent_16RB(dataIn, totShiftBits);
136 #pragma unroll(16)
137     for (int n = 0; n < 16; ++n)
138     {
139       applyCompressionN_1RB<networkBytePack>(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 4], n * totNumBytesPerRB, rbWriteMask);
140     }
141   }
142
143
144   /// Apply 9, 10, or 12bit compression to 4 RB
145   template<BlockFloatCompander::PackFunction networkBytePack>
146   void
147   compressN_4RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
148                 const __m512i totShiftBits, const int totNumBytesPerRB, const uint64_t rbWriteMask)
149   {
150     const auto exponents = computeExponent_4RB(dataIn, totShiftBits);
151 #pragma unroll(4)
152     for (int n = 0; n < 4; ++n)
153     {
154       applyCompressionN_1RB<networkBytePack>(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 16], n * totNumBytesPerRB, rbWriteMask);
155     }
156   }
157
158
159   /// Apply 9, 10, or 12bit compression to 1 RB
160   template<BlockFloatCompander::PackFunction networkBytePack>
161   void
162   compressN_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
163                 const __m512i totShiftBits, const int totNumBytesPerRB, const uint64_t rbWriteMask)
164   {
165     const auto thisExponent = computeExponent_1RB(dataIn, totShiftBits);
166     applyCompressionN_1RB<networkBytePack>(dataIn, dataOut, 0, thisExponent, 0, rbWriteMask);
167   }
168
169
170   /// Calls compression function specific to the number of RB to be executed. For 9, 10, or 12bit iqWidth.
171   template<BlockFloatCompander::PackFunction networkBytePack>
172   void
173   compressByAllocN(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
174                    const __m512i totShiftBits, const int totNumBytesPerRB, const uint64_t rbWriteMask)
175   {
176     switch (dataIn.numBlocks)
177     {
178     case 16:
179       compressN_16RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerRB, rbWriteMask);
180       break;
181
182     case 4:
183       compressN_4RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerRB, rbWriteMask);
184       break;
185
186     case 1:
187       compressN_1RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerRB, rbWriteMask);
188       break;
189     }
190   }
191
192
193   /// Apply compression to 1 RB
194   void
195   applyCompression8_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
196                         const int numREOffset, const uint8_t thisExp, const int thisRBExpAddr)
197   {
198     /// Get AVX512 pointer aligned to desired RB
199     const __m512i* rawDataIn = reinterpret_cast<const __m512i*>(dataIn.dataExpanded + numREOffset);
200     /// Apply the exponent shift
201     const auto compData = _mm512_srai_epi16(*rawDataIn, thisExp);
202     /// Store exponent first
203     dataOut->dataCompressed[thisRBExpAddr] = thisExp;
204     /// Now have 1 RB worth of bytes separated into 3 chunks (1 per lane)
205     /// Use three offset stores to join
206     constexpr uint32_t k_rbMask = 0x00FFFFFF; // Write mask for 1RB (24 values)
207     _mm256_mask_storeu_epi8(dataOut->dataCompressed + thisRBExpAddr + 1, k_rbMask, _mm512_cvtepi16_epi8(compData));
208   }
209
210
211   /// 8bit RB compression loop for 16 RB
212   void
213   compress8_16RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
214   {
215     const __m512i exponents = computeExponent_16RB(dataIn, totShiftBits);
216 #pragma unroll(16)
217     for (int n = 0; n < 16; ++n)
218     {
219       applyCompression8_1RB(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 4], n * (k_numREReal + 1));
220     }
221   }
222
223
224   /// 8bit RB compression loop for 4 RB
225   void
226   compress8_4RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
227   {
228     const __m512i exponents = computeExponent_4RB(dataIn, totShiftBits);
229 #pragma unroll(4)
230     for (int n = 0; n < 4; ++n)
231     {
232       applyCompression8_1RB(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 16], n * (k_numREReal + 1));
233     }
234   }
235
236
237   /// 8bit RB compression loop for 4 RB
238   void
239   compress8_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
240   {
241     const auto thisExponent = computeExponent_1RB(dataIn, totShiftBits);
242     applyCompression8_1RB(dataIn, dataOut, 0, thisExponent, 0);
243   }
244
245
246   /// Calls compression function specific to the number of RB to be executed. For 8 bit iqWidth.
247   void
248   compressByAlloc8(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
249   {
250     switch (dataIn.numBlocks)
251     {
252     case 16:
253       compress8_16RB(dataIn, dataOut, totShiftBits);
254       break;
255
256     case 4:
257       compress8_4RB(dataIn, dataOut, totShiftBits);
258       break;
259
260     case 1:
261       compress8_1RB(dataIn, dataOut, totShiftBits);
262       break;
263     }
264   }
265
266
267   /// Apply compression to 1 RB
268   template<BlockFloatCompander::UnpackFunction networkByteUnpack>
269   void
270   applyExpansionN_1RB(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
271                       const int expAddr, const int thisRBAddr, const int maxExpShift)
272   {
273     /// Unpack network order packed data
274     const auto dataUnpacked = networkByteUnpack(dataIn.dataCompressed + expAddr + 1);
275     /// Apply exponent scaling (by appropriate arithmetic shift right)
276     const auto dataExpanded = _mm512_srai_epi16(dataUnpacked, maxExpShift - *(dataIn.dataCompressed + expAddr));
277     /// Write expanded data to output
278     static constexpr uint32_t k_WriteMask = 0x00FFFFFF;
279     _mm512_mask_storeu_epi16(dataOut->dataExpanded + thisRBAddr, k_WriteMask, dataExpanded);
280   }
281
282
283   /// Calls compression function specific to the number of RB to be executed. For 9, 10, or 12bit iqWidth.
284   template<BlockFloatCompander::UnpackFunction networkByteUnpack>
285   void
286   expandByAllocN(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
287                  const int totNumBytesPerRB, const int maxExpShift)
288   {
289     switch (dataIn.numBlocks)
290     {
291     case 16:
292 #pragma unroll(16)
293       for (int n = 0; n < 16; ++n)
294       {
295         applyExpansionN_1RB<networkByteUnpack>(dataIn, dataOut, n * totNumBytesPerRB, n * k_numREReal, maxExpShift);
296       }
297       break;
298
299     case 4:
300 #pragma unroll(4)
301       for (int n = 0; n < 4; ++n)
302       {
303         applyExpansionN_1RB<networkByteUnpack>(dataIn, dataOut, n * totNumBytesPerRB, n * k_numREReal, maxExpShift);
304       }
305       break;
306
307     case 1:
308       applyExpansionN_1RB<networkByteUnpack>(dataIn, dataOut, 0, 0, maxExpShift);
309       break;
310     }
311   }
312
313
314   /// Apply expansion to 1 RB and store
315   void
316   applyExpansion8_1RB(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
317                       const int expAddr, const int thisRBAddr)
318   {
319     const __m256i* rawDataIn = reinterpret_cast<const __m256i*>(dataIn.dataCompressed + expAddr + 1);
320     const auto compData16 = _mm512_cvtepi8_epi16(*rawDataIn);
321     const auto expData = _mm512_slli_epi16(compData16, *(dataIn.dataCompressed + expAddr));
322     constexpr uint8_t k_rbMask64 = 0b00111111; // 64b write mask for 1RB (24 int16 values)
323     _mm512_mask_storeu_epi64(dataOut->dataExpanded + thisRBAddr, k_rbMask64, expData);
324   }
325
326
327   /// Calls expansion function specific to the number of RB to be executed. For 8 bit iqWidth.
328   void
329   expandByAlloc8(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut)
330   {
331     switch (dataIn.numBlocks)
332     {
333     case 16:
334 #pragma unroll(16)
335       for (int n = 0; n < 16; ++n)
336       {
337         applyExpansion8_1RB(dataIn, dataOut, n * (k_numREReal + 1), n * k_numREReal);
338       }
339       break;
340
341     case 4:
342 #pragma unroll(4)
343       for (int n = 0; n < 4; ++n)
344       {
345         applyExpansion8_1RB(dataIn, dataOut, n * (k_numREReal + 1), n * k_numREReal);
346       }
347       break;
348
349     case 1:
350       applyExpansion8_1RB(dataIn, dataOut, 0, 0);
351       break;
352     }
353   }
354 }
355
356
357
358 /// Main kernel function for compression. This version uses instructions available in Sunny Cove.
359 /// Starts by determining iqWidth specific parameters and functions.
360 void
361 BlockFloatCompander::BFPCompressUserPlaneAvxSnc(const ExpandedData& dataIn, CompressedData* dataOut)
362 {
363   /// Compensation for extra zeros in 32b leading zero count when computing exponent
364   const auto totShiftBits8 = _mm512_set1_epi32(25);
365   const auto totShiftBits9 = _mm512_set1_epi32(24);
366   const auto totShiftBits10 = _mm512_set1_epi32(23);
367   const auto totShiftBits12 = _mm512_set1_epi32(21);
368
369   /// Total number of compressed bytes per RB for each iqWidth option
370   constexpr int totNumBytesPerRB9 = 28;
371   constexpr int totNumBytesPerRB10 = 31;
372   constexpr int totNumBytesPerRB12 = 37;
373
374   /// Compressed data write mask for each iqWidth option
375   constexpr uint64_t rbWriteMask9 = 0x0000000007FFFFFF;
376   constexpr uint64_t rbWriteMask10 = 0x000000003FFFFFFF;
377   constexpr uint64_t rbWriteMask12 = 0x0000000FFFFFFFFF;
378
379   switch (dataIn.iqWidth)
380   {
381   case 8:
382     BFP_UPlane_SNC::compressByAlloc8(dataIn, dataOut, totShiftBits8);
383     break;
384
385   case 9:
386     BFP_UPlane_SNC::compressByAllocN<BlockFloatCompander::networkBytePack9bSnc>(dataIn, dataOut, totShiftBits9, totNumBytesPerRB9, rbWriteMask9);
387     break;
388
389   case 10:
390     BFP_UPlane_SNC::compressByAllocN<BlockFloatCompander::networkBytePack10bSnc>(dataIn, dataOut, totShiftBits10, totNumBytesPerRB10, rbWriteMask10);
391     break;
392
393   case 12:
394     BFP_UPlane_SNC::compressByAllocN<BlockFloatCompander::networkBytePack12bSnc>(dataIn, dataOut, totShiftBits12, totNumBytesPerRB12, rbWriteMask12);
395     break;
396   }
397 }
398
399
400
401 /// Main kernel function for expansion.
402 /// Starts by determining iqWidth specific parameters and functions.
403 void
404 BlockFloatCompander::BFPExpandUserPlaneAvxSnc(const CompressedData& dataIn, ExpandedData* dataOut)
405 {
406   constexpr int k_totNumBytesPerRB9 = 28;
407   constexpr int k_totNumBytesPerRB10 = 31;
408   constexpr int k_totNumBytesPerRB12 = 37;
409
410   constexpr int k_maxExpShift9 = 7;
411   constexpr int k_maxExpShift10 = 6;
412   constexpr int k_maxExpShift12 = 4;
413
414   switch (dataIn.iqWidth)
415   {
416   case 8:
417     BFP_UPlane_SNC::expandByAlloc8(dataIn, dataOut);
418     break;
419
420   case 9:
421     BFP_UPlane_SNC::expandByAllocN<BlockFloatCompander::networkByteUnpack9bSnc>(dataIn, dataOut, k_totNumBytesPerRB9, k_maxExpShift9);
422     break;
423
424   case 10:
425     BFP_UPlane_SNC::expandByAllocN<BlockFloatCompander::networkByteUnpack10bSnc>(dataIn, dataOut, k_totNumBytesPerRB10, k_maxExpShift10);
426     break;
427
428   case 12:
429     BFP_UPlane_SNC::expandByAllocN<BlockFloatCompander::networkByteUnpack12bSnc>(dataIn, dataOut, k_totNumBytesPerRB12, k_maxExpShift12);
430     break;
431   }
432 }