* INTC Contribution to the O-RAN F Release for O-DU Low
[o-du/phy.git] / fhi_lib / lib / src / xran_bfp_uplane.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
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 uint16_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     /// Now have 1 RB worth of bytes separated into 3 chunks (1 per lane)
125     /// Use three offset stores to join
126     _mm_mask_storeu_epi8(dataOut->dataCompressed + thisRBExpAddr + 1, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 0));
127     _mm_mask_storeu_epi8(dataOut->dataCompressed + thisRBExpAddr + 1 + dataIn.iqWidth, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 1));
128     _mm_mask_storeu_epi8(dataOut->dataCompressed + thisRBExpAddr + 1 + (2 * dataIn.iqWidth), rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 2));
129   }
130
131
132   /// Apply 9, 10, or 12bit compression to 16 RB
133   template<BlockFloatCompander::PackFunction networkBytePack>
134   void
135   compressN_16RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
136                  const __m512i totShiftBits, const int totNumBytesPerRB, const uint16_t rbWriteMask)
137   {
138     const auto exponents = computeExponent_16RB(dataIn, totShiftBits);
139 #pragma unroll(16)
140     for (int n = 0; n < 16; ++n)
141     {
142       applyCompressionN_1RB<networkBytePack>(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 4], n * totNumBytesPerRB, rbWriteMask);
143     }
144   }
145
146
147   /// Apply 9, 10, or 12bit compression to 4 RB
148   template<BlockFloatCompander::PackFunction networkBytePack>
149   void
150   compressN_4RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
151                 const __m512i totShiftBits, const int totNumBytesPerRB, const uint16_t rbWriteMask)
152   {
153     const auto exponents = computeExponent_4RB(dataIn, totShiftBits);
154 #pragma unroll(4)
155     for (int n = 0; n < 4; ++n)
156     {
157       applyCompressionN_1RB<networkBytePack>(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 16], n * totNumBytesPerRB, rbWriteMask);
158     }
159   }
160
161
162   /// Apply 9, 10, or 12bit compression to 1 RB
163   template<BlockFloatCompander::PackFunction networkBytePack>
164   void
165   compressN_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
166                 const __m512i totShiftBits, const int totNumBytesPerRB, const uint16_t rbWriteMask)
167   {
168     const auto thisExponent = computeExponent_1RB(dataIn, totShiftBits);
169     applyCompressionN_1RB<networkBytePack>(dataIn, dataOut, 0, thisExponent, 0, rbWriteMask);
170   }
171
172
173   /// Calls compression function specific to the number of RB to be executed. For 9, 10, or 12bit iqWidth.
174   template<BlockFloatCompander::PackFunction networkBytePack>
175   void
176   compressByAllocN(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
177                    const __m512i totShiftBits, const int totNumBytesPerRB, const uint16_t rbWriteMask)
178   {
179     switch (dataIn.numBlocks)
180     {
181     case 16:
182       compressN_16RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerRB, rbWriteMask);
183       break;
184
185     case 4:
186       compressN_4RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerRB, rbWriteMask);
187       break;
188
189     case 1:
190       compressN_1RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerRB, rbWriteMask);
191       break;
192     }
193   }
194
195
196   /// Apply compression to 1 RB
197   void
198   applyCompression8_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
199                         const int numREOffset, const uint8_t thisExp, const int thisRBExpAddr)
200   {
201     /// Get AVX512 pointer aligned to desired RB
202     const __m512i* rawDataIn = reinterpret_cast<const __m512i*>(dataIn.dataExpanded + numREOffset);
203     /// Apply the exponent shift
204     const auto compData = _mm512_srai_epi16(*rawDataIn, thisExp);
205     /// Store exponent first
206     dataOut->dataCompressed[thisRBExpAddr] = thisExp;
207     /// Now have 1 RB worth of bytes separated into 3 chunks (1 per lane)
208     /// Use three offset stores to join
209     constexpr uint32_t k_rbMask = 0x00FFFFFF; // Write mask for 1RB (24 values)
210     _mm256_mask_storeu_epi8(dataOut->dataCompressed + thisRBExpAddr + 1, k_rbMask, _mm512_cvtepi16_epi8(compData));
211   }
212
213
214   /// 8bit RB compression loop for 16 RB
215   void
216   compress8_16RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
217   {
218     const __m512i exponents = computeExponent_16RB(dataIn, totShiftBits);
219 #pragma unroll(16)
220     for (int n = 0; n < 16; ++n)
221     {
222       applyCompression8_1RB(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 4], n * (k_numREReal + 1));
223     }
224   }
225
226
227   /// 8bit RB compression loop for 4 RB
228   void
229   compress8_4RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
230   {
231     const __m512i exponents = computeExponent_4RB(dataIn, totShiftBits);
232 #pragma unroll(4)
233     for (int n = 0; n < 4; ++n)
234     {
235       applyCompression8_1RB(dataIn, dataOut, n * k_numREReal, ((uint8_t*)&exponents)[n * 16], n * (k_numREReal + 1));
236     }
237   }
238
239
240   /// 8bit RB compression loop for 4 RB
241   void
242   compress8_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
243   {
244     const auto thisExponent = computeExponent_1RB(dataIn, totShiftBits);
245     applyCompression8_1RB(dataIn, dataOut, 0, thisExponent, 0);
246   }
247
248
249   /// Calls compression function specific to the number of RB to be executed. For 8 bit iqWidth.
250   void
251   compressByAlloc8(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
252   {
253     switch (dataIn.numBlocks)
254     {
255     case 16:
256       compress8_16RB(dataIn, dataOut, totShiftBits);
257       break;
258
259     case 4:
260       compress8_4RB(dataIn, dataOut, totShiftBits);
261       break;
262
263     case 1:
264       compress8_1RB(dataIn, dataOut, totShiftBits);
265       break;
266     }
267   }
268
269
270   /// Apply compression to 1 RB
271   template<BlockFloatCompander::UnpackFunction networkByteUnpack>
272   void
273   applyExpansionN_1RB(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
274                       const int expAddr, const int thisRBAddr, const int maxExpShift)
275   {
276     /// Unpack network order packed data
277     const auto dataUnpacked = networkByteUnpack(dataIn.dataCompressed + expAddr + 1);
278     /// Apply exponent scaling (by appropriate arithmetic shift right)
279     const auto dataExpanded = _mm512_srai_epi16(dataUnpacked, maxExpShift - *(dataIn.dataCompressed + expAddr));
280     /// Write expanded data to output
281     static constexpr uint32_t k_WriteMask = 0x00FFFFFF;
282     _mm512_mask_storeu_epi16(dataOut->dataExpanded + thisRBAddr, k_WriteMask, dataExpanded);
283   }
284
285
286   /// Calls compression function specific to the number of RB to be executed. For 9, 10, or 12bit iqWidth.
287   template<BlockFloatCompander::UnpackFunction networkByteUnpack>
288   void
289   expandByAllocN(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
290                  const int totNumBytesPerRB, const int maxExpShift)
291   {
292     switch (dataIn.numBlocks)
293     {
294     case 16:
295 #pragma unroll(16)
296       for (int n = 0; n < 16; ++n)
297       {
298         applyExpansionN_1RB<networkByteUnpack>(dataIn, dataOut, n * totNumBytesPerRB, n * k_numREReal, maxExpShift);
299       }
300       break;
301
302     case 4:
303 #pragma unroll(4)
304       for (int n = 0; n < 4; ++n)
305       {
306         applyExpansionN_1RB<networkByteUnpack>(dataIn, dataOut, n * totNumBytesPerRB, n * k_numREReal, maxExpShift);
307       }
308       break;
309
310     case 1:
311       applyExpansionN_1RB<networkByteUnpack>(dataIn, dataOut, 0, 0, maxExpShift);
312       break;
313     }
314   }
315
316
317   /// Apply expansion to 1 RB and store
318   void
319   applyExpansion8_1RB(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
320                       const int expAddr, const int thisRBAddr)
321   {
322     const __m256i* rawDataIn = reinterpret_cast<const __m256i*>(dataIn.dataCompressed + expAddr + 1);
323     const auto compData16 = _mm512_cvtepi8_epi16(*rawDataIn);
324     const auto expData = _mm512_slli_epi16(compData16, *(dataIn.dataCompressed + expAddr));
325     constexpr uint8_t k_rbMask64 = 0b00111111; // 64b write mask for 1RB (24 int16 values)
326     _mm512_mask_storeu_epi64(dataOut->dataExpanded + thisRBAddr, k_rbMask64, expData);
327   }
328
329
330   /// Calls expansion function specific to the number of RB to be executed. For 8 bit iqWidth.
331   void
332   expandByAlloc8(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut)
333   {
334     switch (dataIn.numBlocks)
335     {
336     case 16:
337 #pragma unroll(16)
338       for (int n = 0; n < 16; ++n)
339       {
340         applyExpansion8_1RB(dataIn, dataOut, n * (k_numREReal + 1), n * k_numREReal);
341       }
342       break;
343
344     case 4:
345 #pragma unroll(4)
346       for (int n = 0; n < 4; ++n)
347       {
348         applyExpansion8_1RB(dataIn, dataOut, n * (k_numREReal + 1), n * k_numREReal);
349       }
350       break;
351
352     case 1:
353       applyExpansion8_1RB(dataIn, dataOut, 0, 0);
354       break;
355     }
356   }
357 }
358
359
360
361 /// Main kernel function for compression.
362 /// Starts by determining iqWidth specific parameters and functions.
363 void
364 BlockFloatCompander::BFPCompressUserPlaneAvx512(const ExpandedData& dataIn, CompressedData* dataOut)
365 {
366   /// Compensation for extra zeros in 32b leading zero count when computing exponent
367   const auto totShiftBits8 = _mm512_set1_epi32(25);
368   const auto totShiftBits9 = _mm512_set1_epi32(24);
369   const auto totShiftBits10 = _mm512_set1_epi32(23);
370   const auto totShiftBits12 = _mm512_set1_epi32(21);
371
372   /// Total number of compressed bytes per RB for each iqWidth option
373   constexpr int totNumBytesPerRB9 = 28;
374   constexpr int totNumBytesPerRB10 = 31;
375   constexpr int totNumBytesPerRB12 = 37;
376
377   /// Compressed data write mask for each iqWidth option
378   constexpr uint16_t rbWriteMask9 = 0x01FF;
379   constexpr uint16_t rbWriteMask10 = 0x03FF;
380   constexpr uint16_t rbWriteMask12 = 0x0FFF;
381
382   switch (dataIn.iqWidth)
383   {
384   case 8:
385     BFP_UPlane::compressByAlloc8(dataIn, dataOut, totShiftBits8);
386     break;
387
388   case 9:
389     BFP_UPlane::compressByAllocN<BlockFloatCompander::networkBytePack9b>(dataIn, dataOut, totShiftBits9, totNumBytesPerRB9, rbWriteMask9);
390     break;
391
392   case 10:
393     BFP_UPlane::compressByAllocN<BlockFloatCompander::networkBytePack10b>(dataIn, dataOut, totShiftBits10, totNumBytesPerRB10, rbWriteMask10);
394     break;
395
396   case 12:
397     BFP_UPlane::compressByAllocN<BlockFloatCompander::networkBytePack12b>(dataIn, dataOut, totShiftBits12, totNumBytesPerRB12, rbWriteMask12);
398     break;
399   }
400 }
401
402
403
404 /// Main kernel function for expansion.
405 /// Starts by determining iqWidth specific parameters and functions.
406 void
407 BlockFloatCompander::BFPExpandUserPlaneAvx512(const CompressedData& dataIn, ExpandedData* dataOut)
408 {
409   constexpr int k_totNumBytesPerRB9 = 28;
410   constexpr int k_totNumBytesPerRB10 = 31;
411   constexpr int k_totNumBytesPerRB12 = 37;
412
413   constexpr int k_maxExpShift9 = 7;
414   constexpr int k_maxExpShift10 = 6;
415   constexpr int k_maxExpShift12 = 4;
416
417   switch (dataIn.iqWidth)
418   {
419   case 8:
420     BFP_UPlane::expandByAlloc8(dataIn, dataOut);
421     break;
422
423   case 9:
424     BFP_UPlane::expandByAllocN<BlockFloatCompander::networkByteUnpack9b>(dataIn, dataOut, k_totNumBytesPerRB9, k_maxExpShift9);
425     break;
426
427   case 10:
428     BFP_UPlane::expandByAllocN<BlockFloatCompander::networkByteUnpack10b>(dataIn, dataOut, k_totNumBytesPerRB10, k_maxExpShift10);
429     break;
430
431   case 12:
432     BFP_UPlane::expandByAllocN<BlockFloatCompander::networkByteUnpack12b>(dataIn, dataOut, k_totNumBytesPerRB12, k_maxExpShift12);
433     break;
434   }
435 }