Update to odulow per maintenance bronze
[o-du/phy.git] / fhi_lib / lib / src / xran_bfp_cplane8.cpp
1 /******************************************************************************
2 *
3 *   Copyright (c) 2019 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 for C-plane with 8T8R
21  *
22  * @file xran_bfp_cplane8.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 <complex>
30 #include <algorithm>
31 #include <immintrin.h>
32
33
34 namespace BFP_CPlane_8
35 {
36   /// Namespace constants
37   const int k_numDataElements = 16; /// 16 IQ pairs
38
39   inline __m512i
40   maxAbsOneReg(const __m512i maxAbs, const __m512i* inData, const int pairNum)
41   {
42     /// Compute abs of input data
43     const auto thisRegAbs = _mm512_abs_epi16(*inData);
44     /// Swap each IQ pair in each lane (via 32b rotation) and compute max of
45     /// each pair.
46     const auto maxRot16 = _mm512_rol_epi32(thisRegAbs, BlockFloatCompander::k_numBitsIQ);
47     const auto maxAbsIQ = _mm512_max_epi16(thisRegAbs, maxRot16);
48     /// Convert to 32b values
49     const auto maxAbsIQ32 = BlockFloatCompander::maskUpperWord(maxAbsIQ);
50     /// Swap 32b in each 64b chunk via rotation and compute 32b max
51     /// Results in blocks of 64b with 4 repeated 16b max values
52     const auto maxRot32 = _mm512_rol_epi64(maxAbsIQ32, BlockFloatCompander::k_numBitsIQPair);
53     const auto maxAbs32 = _mm512_max_epi32(maxAbsIQ32, maxRot32);
54     /// First 64b permute and max
55     /// Results in blocks of 128b with 8 repeated 16b max values
56     constexpr uint8_t k_perm64A = 0xB1;
57     const auto maxPerm64A = _mm512_permutex_epi64(maxAbs32, k_perm64A);
58     const auto maxAbs64 = _mm512_max_epi64(maxAbs32, maxPerm64A);
59     /// Second 64b permute and max
60     /// Results in blocks of 256b with 16 repeated 16b max values
61     constexpr uint8_t k_perm64B = 0x4E;
62     const auto maxPerm64B = _mm512_permutex_epi64(maxAbs64, k_perm64B);
63     const auto maxAbs128 = _mm512_max_epi64(maxAbs64, maxPerm64B);
64     /// Now register contains repeated max values for two compression blocks
65     /// Permute the desired results into maxAbs
66     const auto k_selectVals = _mm512_set_epi32(24, 16, 24, 16, 24, 16, 24, 16,
67                                                24, 16, 24, 16, 24, 16, 24, 16);
68     constexpr uint16_t k_2ValsMsk[8] = { 0x0003, 0x000C, 0x0030, 0x00C0, 0x0300, 0x0C00, 0x3000, 0xC000 };
69     return _mm512_mask_permutex2var_epi32(maxAbs, k_2ValsMsk[pairNum], k_selectVals, maxAbs128);
70   }
71
72   /// Compute exponent value for a set of 16 RB from the maximum absolute value.
73   inline __m512i
74   computeExponent_16RB(const BlockFloatCompander::ExpandedData& dataIn, const __m512i totShiftBits)
75   {
76     __m512i maxAbs = __m512i();
77     const __m512i* dataInAddr = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
78 #pragma unroll(8)
79     for (int n = 0; n < 8; ++n)
80     {
81       maxAbs = maxAbsOneReg(maxAbs, dataInAddr + n, n);
82     }
83     /// Calculate exponent
84     return BlockFloatCompander::expLzCnt(maxAbs, totShiftBits);
85   }
86
87   /// Compute exponent value for a set of 4 RB from the maximum absolute value.
88   inline __m512i
89   computeExponent_4RB(const BlockFloatCompander::ExpandedData& dataIn, const __m512i totShiftBits)
90   {
91     __m512i maxAbs = __m512i();
92     const __m512i* dataInAddr = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
93 #pragma unroll(2)
94     for (int n = 0; n < 2; ++n)
95     {
96       maxAbs = maxAbsOneReg(maxAbs, dataInAddr + n, n);
97     }
98     /// Calculate exponent
99     return BlockFloatCompander::expLzCnt(maxAbs, totShiftBits);
100   }
101
102   /// Compute exponent value for 1 RB from the maximum absolute value.
103   inline uint8_t
104   computeExponent_1RB(const BlockFloatCompander::ExpandedData& dataIn, const __m512i totShiftBits)
105   {
106     __m512i maxAbs = __m512i();
107     const __m512i* dataInAddr = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
108     maxAbs = maxAbsOneReg(maxAbs, dataInAddr, 0);
109     /// Calculate exponent
110     const auto exps = BlockFloatCompander::expLzCnt(maxAbs, totShiftBits);
111     return ((uint8_t*)&exps)[0];
112   }
113
114
115
116   /// Apply compression to one compression block
117   template<BlockFloatCompander::PackFunction networkBytePack>
118   inline void
119   applyCompressionN_1RB(const __m512i* dataIn, uint8_t* outBlockAddr,
120                         const int iqWidth, const uint8_t thisExp, const uint16_t rbWriteMask)
121   {
122     /// Store exponents first
123     *outBlockAddr = thisExp;
124     /// Apply the exponent shift
125     /// First Store the two exponent values in one register
126     const auto compData = _mm512_srai_epi16(*dataIn, thisExp);
127     /// Pack compressed data network byte order
128     const auto compDataBytePacked = networkBytePack(compData);
129     /// Now have 1 register worth of bytes separated into 2 chunks (1 per lane)
130     /// Use two offset stores to join
131     const auto thisOutRegAddr1 = outBlockAddr + 1;
132     _mm_mask_storeu_epi8(thisOutRegAddr1, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 0));
133     _mm_mask_storeu_epi8(thisOutRegAddr1 + iqWidth, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 1));
134   }
135
136   /// Apply compression to two compression blocks
137   template<BlockFloatCompander::PackFunction networkBytePack>
138   inline void
139   applyCompressionN_2RB(const __m512i* dataIn, uint8_t* outBlockAddr,
140                         const int totNumBytesPerBlock, const int iqWidth, const uint8_t* theseExps, const uint16_t rbWriteMask)
141   {
142     /// Store exponents first
143     *outBlockAddr = theseExps[0];
144     *(outBlockAddr + totNumBytesPerBlock) = theseExps[4];
145     /// Apply the exponent shift
146     /// First Store the two exponent values in one register
147     __m512i thisExp = __m512i();
148     constexpr uint32_t k_firstExpMask = 0x0000FFFF;
149     thisExp = _mm512_mask_set1_epi16(thisExp, k_firstExpMask, theseExps[0]);
150     constexpr uint32_t k_secondExpMask = 0xFFFF0000;
151     thisExp = _mm512_mask_set1_epi16(thisExp, k_secondExpMask, theseExps[4]);
152     const auto compData = _mm512_srav_epi16(*dataIn, thisExp);
153     /// Pack compressed data network byte order
154     const auto compDataBytePacked = networkBytePack(compData);
155     /// Now have 1 register worth of bytes separated into 4 chunks (1 per lane)
156     /// Use four offset stores to join
157     const auto thisOutRegAddr1 = outBlockAddr + 1;
158     const auto thisOutRegAddr2 = outBlockAddr + totNumBytesPerBlock + 1;
159     _mm_mask_storeu_epi8(thisOutRegAddr1, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 0));
160     _mm_mask_storeu_epi8(thisOutRegAddr1 + iqWidth, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 1));
161     _mm_mask_storeu_epi8(thisOutRegAddr2, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 2));
162     _mm_mask_storeu_epi8(thisOutRegAddr2 + iqWidth, rbWriteMask, _mm512_extracti64x2_epi64(compDataBytePacked, 3));
163   }
164
165   /// Derive and apply 9, 10, or 12bit compression to 16 compression blocks
166   template<BlockFloatCompander::PackFunction networkBytePack>
167   inline void
168   compressN_16RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
169                  const __m512i totShiftBits, const int totNumBytesPerBlock, const uint16_t rbWriteMask)
170   {
171     const auto exponents = computeExponent_16RB(dataIn, totShiftBits);
172     const __m512i* dataInAddr = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
173 #pragma unroll(8)
174     for (int n = 0; n < 8; ++n)
175     {
176       applyCompressionN_2RB<networkBytePack>(dataInAddr + n, dataOut->dataCompressed + n * 2 * totNumBytesPerBlock, totNumBytesPerBlock, dataIn.iqWidth, ((uint8_t*)&exponents) + n * 8, rbWriteMask);
177     }
178   }
179
180   /// Derive and apply 9, 10, or 12bit compression to 4 compression blocks
181   template<BlockFloatCompander::PackFunction networkBytePack>
182   inline void
183   compressN_4RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
184                 const __m512i totShiftBits, const int totNumBytesPerBlock, const uint16_t rbWriteMask)
185   {
186     const auto exponents = computeExponent_4RB(dataIn, totShiftBits);
187     const __m512i* dataInAddr = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
188 #pragma unroll(2)
189     for (int n = 0; n < 2; ++n)
190     {
191       applyCompressionN_2RB<networkBytePack>(dataInAddr + n, dataOut->dataCompressed + n * 2 * totNumBytesPerBlock, totNumBytesPerBlock, dataIn.iqWidth, ((uint8_t*)&exponents) + n * 8, rbWriteMask);;
192     }
193   }
194
195   /// Derive and apply 9, 10, or 12bit compression to 1 RB
196   template<BlockFloatCompander::PackFunction networkBytePack>
197   inline void
198   compressN_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
199                 const __m512i totShiftBits, const int totNumBytesPerBlock, const uint16_t rbWriteMask)
200   {
201     const auto thisExponent = computeExponent_1RB(dataIn, totShiftBits);
202     const __m512i* dataInAddr = reinterpret_cast<const __m512i*>(dataIn.dataExpanded);
203     applyCompressionN_1RB<networkBytePack>(dataInAddr, dataOut->dataCompressed, dataIn.iqWidth, thisExponent, rbWriteMask);
204   }
205
206   /// Calls compression function specific to the number of blocks to be executed. For 9, 10, or 12bit iqWidth.
207   template<BlockFloatCompander::PackFunction networkBytePack>
208   inline void
209   compressByAllocN(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut,
210                    const __m512i totShiftBits, const int totNumBytesPerBlock, const uint16_t rbWriteMask)
211   {
212     switch (dataIn.numBlocks)
213     {
214     case 16:
215       compressN_16RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerBlock, rbWriteMask);
216       break;
217
218     case 4:
219       compressN_4RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerBlock, rbWriteMask);
220       break;
221
222     case 1:
223       compressN_1RB<networkBytePack>(dataIn, dataOut, totShiftBits, totNumBytesPerBlock, rbWriteMask);
224       break;
225     }
226   }
227
228
229
230   /// Apply 8b compression to 1 compression block.
231   inline void
232   applyCompression8_1RB(const __m256i* dataIn, uint8_t* outBlockAddr, const uint8_t thisExp)
233   {
234     /// Store exponent first
235     *outBlockAddr = thisExp;
236     /// Apply the exponent shift
237     const auto compData = _mm256_srai_epi16(*dataIn, thisExp);
238     /// Truncate to 8bit and store
239     constexpr uint16_t k_writeMask = 0xFFFF;
240     _mm_mask_storeu_epi8(outBlockAddr + 1, k_writeMask, _mm256_cvtepi16_epi8(compData));
241   }
242
243   /// Derive and apply 8b compression to 16 compression blocks
244   inline void
245   compress8_16RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
246   {
247     const auto exponents = computeExponent_16RB(dataIn, totShiftBits);
248     const __m256i* dataInAddr = reinterpret_cast<const __m256i*>(dataIn.dataExpanded);
249 #pragma unroll(16)
250     for (int n = 0; n < 16; ++n)
251     {
252       applyCompression8_1RB(dataInAddr + n, dataOut->dataCompressed + n * (k_numDataElements + 1), ((uint8_t*)&exponents)[n * 4]);
253     }
254   }
255
256   /// Derive and apply 8b compression to 4 compression blocks
257   inline void
258   compress8_4RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
259   {
260     const auto exponents = computeExponent_4RB(dataIn, totShiftBits);
261     const __m256i* dataInAddr = reinterpret_cast<const __m256i*>(dataIn.dataExpanded);
262 #pragma unroll(4)
263     for (int n = 0; n < 4; ++n)
264     {
265       applyCompression8_1RB(dataInAddr + n, dataOut->dataCompressed + n * (k_numDataElements + 1), ((uint8_t*)&exponents)[n * 4]);
266     }
267   }
268
269   /// Derive and apply 8b compression to 1 compression block
270   inline void
271   compress8_1RB(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
272   {
273     const auto thisExponent = computeExponent_1RB(dataIn, totShiftBits);
274     const __m256i* dataInAddr = reinterpret_cast<const __m256i*>(dataIn.dataExpanded);
275     applyCompression8_1RB(dataInAddr, dataOut->dataCompressed, thisExponent);
276   }
277
278   /// Calls compression function specific to the number of RB to be executed. For 8 bit iqWidth.
279   inline void
280   compressByAlloc8(const BlockFloatCompander::ExpandedData& dataIn, BlockFloatCompander::CompressedData* dataOut, const __m512i totShiftBits)
281   {
282     switch (dataIn.numBlocks)
283     {
284     case 16:
285       compress8_16RB(dataIn, dataOut, totShiftBits);
286       break;
287
288     case 4:
289       compress8_4RB(dataIn, dataOut, totShiftBits);
290       break;
291
292     case 1:
293       compress8_1RB(dataIn, dataOut, totShiftBits);
294       break;
295     }
296   }
297
298
299   /// Expand 1 compression block
300   template<BlockFloatCompander::UnpackFunction256 networkByteUnpack>
301   inline void
302   applyExpansionN_1RB(const uint8_t* expAddr, __m256i* dataOutAddr, const int maxExpShift)
303   {
304     const auto thisExpShift = maxExpShift - *expAddr;
305     /// Unpack network order packed data
306     const auto inDataUnpacked = networkByteUnpack(expAddr + 1);
307     /// Apply exponent scaling (by appropriate arithmetic shift right)
308     const auto expandedData = _mm256_srai_epi16(inDataUnpacked, thisExpShift);
309     /// Write expanded data to output
310     static constexpr uint8_t k_WriteMask = 0x0F;
311     _mm256_mask_storeu_epi64(dataOutAddr, k_WriteMask, expandedData);
312   }
313
314   /// Calls expansion function specific to the number of blocks to be executed. For 9, 10, or 12bit iqWidth.
315   template<BlockFloatCompander::UnpackFunction256 networkByteUnpack>
316   void expandByAllocN(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut,
317                       const int totNumBytesPerBlock, const int maxExpShift)
318   {
319     __m256i* dataOutAddr = reinterpret_cast<__m256i*>(dataOut->dataExpanded);
320     switch (dataIn.numBlocks)
321     {
322     case 16:
323 #pragma unroll(16)
324       for (int n = 0; n < 16; ++n)
325       {
326         applyExpansionN_1RB<networkByteUnpack>(dataIn.dataCompressed + n * totNumBytesPerBlock, dataOutAddr + n, maxExpShift);
327       }
328       break;
329
330     case 4:
331 #pragma unroll(4)
332       for (int n = 0; n < 4; ++n)
333       {
334         applyExpansionN_1RB<networkByteUnpack>(dataIn.dataCompressed + n * totNumBytesPerBlock, dataOutAddr + n, maxExpShift);
335       }
336       break;
337
338     case 1:
339       applyExpansionN_1RB<networkByteUnpack>(dataIn.dataCompressed, dataOutAddr, maxExpShift);
340       break;
341     }
342   }
343
344
345   /// Apply expansion to 2 compression block
346   inline void
347   applyExpansion8_1RB(const uint8_t* expAddr, __m256i* dataOutAddr)
348   {
349     const __m128i* rawDataIn = reinterpret_cast<const __m128i*>(expAddr + 1);
350     const auto compData16 = _mm256_cvtepi8_epi16(*rawDataIn);
351     const auto expData = _mm256_slli_epi16(compData16, *expAddr);
352     static constexpr uint8_t k_WriteMask = 0x0F;
353     _mm256_mask_storeu_epi64(dataOutAddr, k_WriteMask, expData);
354   }
355
356   /// Calls expansion function specific to the number of RB to be executed. For 8 bit iqWidth.
357   void
358   expandByAlloc8(const BlockFloatCompander::CompressedData& dataIn, BlockFloatCompander::ExpandedData* dataOut)
359   {
360     __m256i* dataOutAddr = reinterpret_cast<__m256i*>(dataOut->dataExpanded);
361     switch (dataIn.numBlocks)
362     {
363     case 16:
364 #pragma unroll(16)
365       for (int n = 0; n < 16; ++n)
366       {
367         applyExpansion8_1RB(dataIn.dataCompressed + n * (k_numDataElements + 1), dataOutAddr + n);
368       }
369       break;
370
371     case 4:
372 #pragma unroll(4)
373       for (int n = 0; n < 4; ++n)
374       {
375         applyExpansion8_1RB(dataIn.dataCompressed + n * (k_numDataElements + 1), dataOutAddr + n);
376       }
377       break;
378
379     case 1:
380       applyExpansion8_1RB(dataIn.dataCompressed, dataOutAddr);
381       break;
382     }
383   }
384 }
385
386
387 /// Main kernel function for 8 antenna C-plane compression.
388 /// Starts by determining iqWidth specific parameters and functions.
389 void
390 BlockFloatCompander::BFPCompressCtrlPlane8Avx512(const ExpandedData& dataIn, CompressedData* dataOut)
391 {
392   /// Compensation for extra zeros in 32b leading zero count when computing exponent
393   const auto totShiftBits8 = _mm512_set1_epi32(25);
394   const auto totShiftBits9 = _mm512_set1_epi32(24);
395   const auto totShiftBits10 = _mm512_set1_epi32(23);
396   const auto totShiftBits12 = _mm512_set1_epi32(21);
397
398   /// Total number of data bytes per compression block is (iqWidth * numElements / 8) + 1
399   const auto totNumBytesPerBlock = ((BFP_CPlane_8::k_numDataElements * dataIn.iqWidth) >> 3) + 1;
400
401   /// Compressed data write mask for each iqWidth option
402   constexpr uint16_t rbWriteMask9 = 0x01FF;
403   constexpr uint16_t rbWriteMask10 = 0x03FF;
404   constexpr uint16_t rbWriteMask12 = 0x0FFF;
405
406   switch (dataIn.iqWidth)
407   {
408   case 8:
409     BFP_CPlane_8::compressByAlloc8(dataIn, dataOut, totShiftBits8);
410     break;
411
412   case 9:
413     BFP_CPlane_8::compressByAllocN<BlockFloatCompander::networkBytePack9b>(dataIn, dataOut, totShiftBits9, totNumBytesPerBlock, rbWriteMask9);
414     break;
415
416   case 10:
417     BFP_CPlane_8::compressByAllocN<BlockFloatCompander::networkBytePack10b>(dataIn, dataOut, totShiftBits10, totNumBytesPerBlock, rbWriteMask10);
418     break;
419
420   case 12:
421     BFP_CPlane_8::compressByAllocN<BlockFloatCompander::networkBytePack12b>(dataIn, dataOut, totShiftBits12, totNumBytesPerBlock, rbWriteMask12);
422     break;
423   }
424 }
425
426
427 /// Main kernel function for 8 antenna C-plane expansion.
428 /// Starts by determining iqWidth specific parameters and functions.
429 void
430 BlockFloatCompander::BFPExpandCtrlPlane8Avx512(const CompressedData& dataIn, ExpandedData* dataOut)
431 {
432   constexpr int k_maxExpShift9 = 7;
433   constexpr int k_maxExpShift10 = 6;
434   constexpr int k_maxExpShift12 = 4;
435
436   /// Total number of data bytes per compression block is (iqWidth * numElements / 8) + 1
437   const auto totNumBytesPerBlock = ((BFP_CPlane_8::k_numDataElements * dataIn.iqWidth) >> 3) + 1;
438
439   switch (dataIn.iqWidth)
440   {
441   case 8:
442     BFP_CPlane_8::expandByAlloc8(dataIn, dataOut);
443     break;
444
445   case 9:
446     BFP_CPlane_8::expandByAllocN<BlockFloatCompander::networkByteUnpack9b256>(dataIn, dataOut, totNumBytesPerBlock, k_maxExpShift9);
447     break;
448
449   case 10:
450     BFP_CPlane_8::expandByAllocN<BlockFloatCompander::networkByteUnpack10b256>(dataIn, dataOut, totNumBytesPerBlock, k_maxExpShift10);
451     break;
452
453   case 12:
454     BFP_CPlane_8::expandByAllocN<BlockFloatCompander::networkByteUnpack12b256>(dataIn, dataOut, totNumBytesPerBlock, k_maxExpShift12);
455     break;
456   }
457 }