Linduino  1.3.0
Linear Technology Arduino-Compatible Demonstration Board
LT_PMBusMath.cpp
Go to the documentation of this file.
1 /*!
2 LTC PMBus Support: Math conversion routines
3 
4 @verbatim
5 
6 This API is shared with Linduino and RTOS code.
7 
8 @endverbatim
9 
10 
11 Copyright 2018(c) Analog Devices, Inc.
12 
13 All rights reserved.
14 
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are met:
17  - Redistributions of source code must retain the above copyright
18  notice, this list of conditions and the following disclaimer.
19  - Redistributions in binary form must reproduce the above copyright
20  notice, this list of conditions and the following disclaimer in
21  the documentation and/or other materials provided with the
22  distribution.
23  - Neither the name of Analog Devices, Inc. nor the names of its
24  contributors may be used to endorse or promote products derived
25  from this software without specific prior written permission.
26  - The use of this software may or may not infringe the patent rights
27  of one or more patent holders. This license does not release you
28  from the requirement that you obtain separate licenses from these
29  patent holders to use this software.
30  - Use of the software either in source or binary form, must be run
31  on or directly connected to an Analog Devices Inc. component.
32 
33 THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
34 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
35 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
36 IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
37 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
38 LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
39 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
41 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
42 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 */
44 
45 //! @ingroup PMBus_SMBus
46 //! @{
47 //! @defgroup LT_PMBusMath LT_PMBusMath: Value conversion routines
48 //! @}
49 
50 /*! @file
51  @ingroup LT_PMBusMath
52  Library Header File for LT_PMBusMath
53 */
54 #include "LT_PMBusMath.h"
55 
56 // +---------------------------------------------------------------------------+
57 // | Define the Bit Widths and Exponent Properties |
58 // +---------------------------------------------------------------------------+
59 
60 // IEEE 754 Float32 Single Precision Floating Point Constants (Binary32)
61 #define fl32_width 32 // Bit width of the Float32
62 #define fl32_mant_width 23 // Bit width of the encoded mantissa
63 #define fl32_exp_bias 127 // IEEE 754 Float32 exponent bias
64 #define fl32_NaN_exp 1 // Exponent encodings used for NaN
65 
66 // PMBus Linear11 Floating Point Constants
67 #define lin11_width 16 // Bit width of the Linear11
68 #define lin11_mant_width 11 // Bit width of the Linear11 mantissa
69 
70 // PMBus Linear16 Format Constants
71 #define lin16_width 16 // Bit width of the Linear16
72 #define lin16_exp_width 5 // Bit width of the Linear16 exponent
73 
74 // +---------------------------------------------------------------------------+
75 // | Calculate the Float32 Exponent Ranges and Bit Masks |
76 // +---------------------------------------------------------------------------+
77 
78 // Float32 exponent constants
79 #define fl32_exp_width (fl32_width - fl32_mant_width - 1)
80 #define fl32_max_exp ((1UL << fl32_exp_width) - 1)
81 #define fl32_max_p_exp (fl32_max_exp - fl32_exp_bias - fl32_NaN_exp)
82 
83 // Define the IEEE 754 Float32 Single Precision Bit Masks
84 #define fl32_sign_mask (1UL << (fl32_width - 1))
85 #define fl32_exp_mask (((1UL << fl32_exp_width) - 1) << fl32_mant_width)
86 #define fl32_mant_mask ((1UL << fl32_mant_width) - 1)
87 
88 // +---------------------------------------------------------------------------+
89 // | PMBus Linear11 Definitions |
90 // +---------------------------------------------------------------------------+
91 
92 // Define the PMBus Linear11 Bit Masks
93 #define lin11_mant_sign_mask (1UL << (lin11_mant_width - 1))
94 #define lin11_mant_mask ((1UL << lin11_mant_width) - 1)
95 #define lin11_exp_width (lin11_width - lin11_mant_width)
96 #define lin11_exp_sign_mask (1UL << (lin11_exp_width - 1))
97 #define lin11_exp_mask ((1UL << lin11_exp_width) - 1)
98 #define lin11_ieee_mant_width (lin11_mant_width-2)
99 #define lin11_ieee_mant_mask ((1UL << lin11_ieee_mant_width) - 1)
100 #define lin11_max_p_exp ((1UL << (lin11_exp_width - 1)) - 1)
101 #define lin11_max_n_exp (1UL << (lin11_exp_width - 1))
102 #define lin11_bit_mask ((1UL << lin11_width) - 1)
103 
104 // Define the PMBus Linear11 normalized and denormal -1
105 #define lin11_den_n1 ((1UL<<(lin11_mant_width-1))|(1UL<<(lin11_mant_width-2)))
106 #define lin11_norm_n1 ( 1UL<<(lin11_mant_width-1))
107 
108 // +---------------------------------------------------------------------------+
109 // | PMBus Linear16 Definitions |
110 // +---------------------------------------------------------------------------+
111 
112 // Define the PMBus Linear16 Bit Masks
113 #define lin16_exp_sign_mask (1UL << (lin16_exp_width - 1))
114 #define lin16_exp_mask ((1UL << lin16_exp_width) - 1)
115 #define lin16_ieee_mant_width (lin16_width-1)
116 #define lin16_ieee_mant_mask ((1UL << lin16_ieee_mant_width) - 1)
117 #define lin16_max_p_exp ((1UL << (lin16_exp_width - 1)) - 1)
118 #define lin16_max_n_exp (1UL << (lin16_exp_width - 1))
119 #define lin16_max_value ((1UL << lin16_width) - 1)
120 
121 // Make sure the mantissa of the Float32 format is as wide as or wider than
122 // the mantissa of the Linear16 format
123 #if !(fl32_mant_width >= lin16_ieee_mant_width)
124 #error The Float32 mantissa does not have enough bits for L16!
125 #endif
126 
127 // +---------------------------------------------------------------------------+
128 // | Linear11 --> Float32 Conversion Function |
129 // +---------------------------------------------------------------------------+
130 
131 // The conversion function implementation assumes (checked at compile time)
132 // that all Linear11 numbers map to normalized Float32 floating point numbers
133 
135 {
136 
137  // Repackage the Linear11 in an IEEE754 format (sign, exponent, mantissa)
138  LT_PMBusMath::lin11_t lin11_sign, lin11_exp, lin11_exp_sign, lin11_mant, lin11_nmant;
140 
141  // Extract the sign, exponent and mantissa
142  lin11_sign = (xin & ((lin11_t) lin11_mant_sign_mask)) << lin11_exp_width;
143  lin11_exp = (xin >> lin11_mant_width) & ((lin11_t) lin11_exp_mask);
144  lin11_exp_sign = lin11_exp & ((lin11_t) lin11_exp_sign_mask);
145  lin11_mant = (xin & ((lin11_t) lin11_mant_mask));
146 
147  // Add the exponent bias of the target IEEE 754 format to the L11 exponent
148  if (lin11_exp_sign != 0)
149  {
150  lin11_exp = (lin11_exp ^ ((lin11_t) lin11_exp_mask)) + 1;
151  lin11_exp = fl32_exp_bias - lin11_exp + lin11_mant_width - 2;
152  }
153  else
154  {
155  lin11_exp = fl32_exp_bias + lin11_exp + lin11_mant_width - 2;
156  }
157 
158  // Invert the mantissa sign for the two's complement representation
159  if (lin11_sign != 0)
160  {
161  lin11_nmant = (lin11_mant ^ ((lin11_t) lin11_mant_mask)) + 1;
162 
163  // The most negative mantissa is a corner case and has to be shifted
164  if (lin11_nmant == lin11_mant)
165  {
166  lin11_mant = lin11_nmant >> 1;
167  lin11_exp++;
168  }
169  else
170  {
171  lin11_mant = lin11_nmant;
172  }
173  }
174 
175  // Normalize the Linear11 mantissa to fit the IEEE 754 format structure
176  if (lin11_mant != 0 )
177  {
178  while ((lin11_mant & (~((lin11_t) lin11_ieee_mant_mask))) == 0 )
179  {
180  lin11_mant = lin11_mant << 1;
181  lin11_exp--;
182  }
183  }
184  else
185  {
186  lin11_exp = 0; // Special case of Zero
187  }
188  lin11_mant = lin11_mant & ((lin11_t) lin11_ieee_mant_mask);
189 
190  // Shift the L11 sign, exponent and mantissa to the target IEEE format
191  xout = ((fl32_t) lin11_sign) << (fl32_width - lin11_width);
192  xout |= ((fl32_t) lin11_exp) << fl32_mant_width;
193  xout |= ((fl32_t) lin11_mant) << (fl32_mant_width - lin11_ieee_mant_width);
194 
195  return xout;
196 }
197 
198 // +---------------------------------------------------------------------------+
199 // | Linear16 --> Float32 Conversion Function |
200 // +---------------------------------------------------------------------------+
201 
202 // The conversion function implementation assumes (checked at compile time)
203 // that all Linear16 numbers map to normalized Float32 floating point numbers
204 
206 {
207 
208  // Repackage the Linear16 in an IEEE 754 format (sign, exponent, mantissa)
209  LT_PMBusMath::lin16m_t lin16_exp_sign;
210  LT_PMBusMath::fl32_t lin16_exp,xout;
211 
212  // Extract the exponent and mantissa (sign = 0)
213  lin16_exp = (fl32_t) (vout_mode & ((lin16m_t) lin16_exp_mask));
214  lin16_exp_sign = vout_mode & ((lin16m_t) lin16_exp_sign_mask);
215 
216  // Add the exponent bias of the target IEEE 754 format to the L16 exponent
217  if (lin16_exp_sign != (lin16m_t) 0)
218  {
219  lin16_exp = (lin16_exp ^ ((fl32_t) lin16_exp_mask)) + 1;
220  lin16_exp = (fl32_t)(fl32_exp_bias + lin16_ieee_mant_width) - lin16_exp;
221  }
222  else
223  {
224  lin16_exp = (fl32_t)(fl32_exp_bias + lin16_ieee_mant_width) + lin16_exp;
225  }
226 
227  // Normalize the Linear16 mantissa to fit the IEEE 754 format structure
228  if (lin16_mant != 0 )
229  {
230  while ((lin16_mant & (~((lin16_t) lin16_ieee_mant_mask))) == 0 )
231  {
232  lin16_mant = lin16_mant << 1;
233  lin16_exp--;
234  }
235  }
236  else
237  {
238  lin16_exp = 0; // Special case of Zero
239  }
240  lin16_mant = lin16_mant & ((lin16_t) lin16_ieee_mant_mask);
241 
242  // Shift the L16 mantissa and exponent to the target IEEE format (sign=0)
243  xout = lin16_exp << fl32_mant_width;
244  xout |= ((fl32_t) lin16_mant) << (fl32_mant_width - lin16_ieee_mant_width);
245 
246  return xout;
247 }
248 
249 // +---------------------------------------------------------------------------+
250 // | Float32 --> Linear16 Conversion Function |
251 // +---------------------------------------------------------------------------+
252 
253 // The conversion function implementation assumes (checked at compile time)
254 // that all Linear16 numbers map to normalized Float32 floating point numbers.
255 // The Linear16 exponent (VOUT_MODE) is assumed a constant of the chip.
256 
258 {
259 
260  // Variables for the exponent, mantissa and mantissa shift
261  LT_PMBusMath::fl32_t fl32_exp, fl32_mant, rnd_msb, tie_mask;
262  LT_PMBusMath::lin16m_t lin16_exp_sign, lin16_exp;
264  int shift;
265  unsigned int ushift;
266 
267  // Extract the sign, exponent and mantissa of the Float32 number
268  if ( ( xin >> (fl32_width - 1)) != 0 )
269  {
270  xout = 0; // All negative Float32 values get mapped to zero
271  }
272  else
273  {
274  // Extract the exponent and mantissa of the Float32 number
275  fl32_exp = (((fl32_t) fl32_exp_mask) & xin) >> fl32_mant_width;
276  fl32_mant = ((fl32_t) fl32_mant_mask) & xin;
277 
278  // Add the implicit 1.mmm to the mantissa (L16 is not normalized)
279  fl32_mant |= ((fl32_t) (((fl32_t) (1)) << fl32_mant_width));
280 
281  // Calculate the required shift for the mantissa
282  lin16_exp = vout_mode & ((lin16m_t) lin16_exp_mask);
283  lin16_exp_sign = lin16_exp & ((lin16m_t) lin16_exp_sign_mask);
284  shift = (int) (fl32_exp_bias + fl32_mant_width) - (int) fl32_exp;
285  if (lin16_exp_sign != (lin16m_t) 0)
286  {
287  shift -= (int)(lin16_exp ^ ((lin16m_t) lin16_exp_mask)) + 1;
288  }
289  else
290  {
291  shift += (int) lin16_exp;
292  }
293 
294  // Right shift the mantissa and round
295  if (shift < (fl32_mant_width + 1 - lin16_width))
296  {
297  xout = (lin16_t) lin16_max_value; // Saturate to 0xFFFF
298  }
299  else if (shift > (fl32_mant_width + 1))
300  {
301  xout = 0; // Underflow to zero
302  }
303  else // Shift and round
304  {
305  ushift = (unsigned) shift;
306  xout = (lin16_t) (fl32_mant >> ushift);
307  rnd_msb = (((fl32_t) (1)) << (ushift - 1));
308  tie_mask = ((((fl32_t) (1)) << (ushift + 1)) - 1);
309 
310  // When rounding, mantissa overflow is not possible (right shift>=1)
311  if (((rnd_msb & fl32_mant)!=0)&&((tie_mask & fl32_mant)!= rnd_msb))
312  {
313  xout++;
314  }
315  }
316  }
317  return xout;
318 }
319 
320 // +---------------------------------------------------------------------------+
321 // | Float32 --> Linear11 Conversion Function |
322 // +---------------------------------------------------------------------------+
323 
324 // The conversion function implementation assumes (checked at compile time)
325 // that all Linear11 numbers map to normalized Float32 floating point numbers.
326 
328 {
329 
330  // Variables for the floating point sign, exponent and mantissa bit fields
331  LT_PMBusMath::fl32_t fl32_exp, fl32_mant, rnd_msb, tie_mask;
332  LT_PMBusMath::lin11_t lin11_sign, lin11_mant;
333  LT_PMBusMath::slin11_t lin11_exp;
334  uchar_t mant_shift;
335  const LT_PMBusMath::lin11_t normal_n1 = (lin11_t) lin11_norm_n1;
336  const LT_PMBusMath::lin11_t denormal_n1 = (lin11_t) lin11_den_n1;
337 
338  // Extract the sign, exponent and mantissa of the Float32 number
339  fl32_exp = (((fl32_t) fl32_exp_mask) & xin) >> fl32_mant_width;
340  fl32_mant = ((fl32_t) fl32_mant_mask) & xin;
341  fl32_mant |= (((fl32_t) (1)) << fl32_mant_width); // Add the 1.mmm
342 
343  // Convert the extracted sign and signed exponent to Linear11
344  lin11_sign = ((lin11_t)(xin >> (fl32_width - lin11_width)));
345  lin11_sign &= (((lin11_t) (1)) << (lin11_width-1));
346  lin11_exp = (slin11_t)(fl32_exp) - fl32_exp_bias - lin11_ieee_mant_width;
347 
348  // Determine if the corresponding Linear11 number is normalized or denormal
349  mant_shift = (uchar_t) (fl32_mant_width - lin11_ieee_mant_width);
350  if (lin11_exp < (-(slin11_t) lin11_max_n_exp)) // Denormal Linear11
351  {
352  mant_shift -= (uchar_t) (lin11_exp + ((slin11_t) lin11_max_n_exp));
353  lin11_exp = -((slin11_t) lin11_max_n_exp);
354  }
355 
356  // Calculate the Linear11 mantissa and check if rounding up is needed
357  // If the mantissa overflowed after the rounding, increment the exponent
358  if (mant_shift > (uchar_t)(fl32_mant_width + 1))
359  {
360  lin11_mant = 0;
361  }
362  else
363  {
364  lin11_mant = (lin11_t) (fl32_mant >> mant_shift);
365  rnd_msb = (((fl32_t) (1)) << (fl32_t) (mant_shift - (uchar_t) 1));
366  tie_mask = ((((fl32_t) (1)) << (fl32_t) (mant_shift + (uchar_t) 1))-1);
367 
368  if (((rnd_msb & fl32_mant) !=0) && ((tie_mask & fl32_mant) != rnd_msb))
369  {
370  // Increment the mantissa and check for mantissa overflow
371  lin11_mant++;
372  if ((lin11_mant & ((lin11_t) lin11_ieee_mant_mask)) == 0)
373  {
374  lin11_exp++;
375  lin11_mant = lin11_mant >> 1;
376  }
377  }
378  }
379 
380  // Convert the mantissa to two's complement
381  if (lin11_sign != 0)
382  {
383  lin11_mant = ((~lin11_mant + 1) & (lin11_t) lin11_mant_mask);
384  }
385 
386  // Normalize the special case of -1 x 2^N
387  if ((lin11_mant == denormal_n1) && (lin11_exp > -(slin11_t)lin11_max_n_exp))
388  {
389  lin11_mant = normal_n1;
390  lin11_exp--;
391  }
392 
393  // Saturate to maximum if the exponent is too large
394  if (lin11_exp > (slin11_t) lin11_max_p_exp)
395  {
396  lin11_exp = (slin11_t) lin11_max_p_exp;
397  if (lin11_sign != 0)
398  {
399  lin11_mant = normal_n1;
400  }
401  else
402  {
403  lin11_mant = (((lin11_t) (1)) << (lin11_mant_width - 1)) - 1;
404  }
405  }
406 
407  // Select a unique representation for zero - if the mantissa is zero,
408  // return 0x0000 (zero exponent)
409  if (lin11_mant == 0) lin11_exp = 0;
410 
411  lin11_exp = (slin11_t)((lin11_t) lin11_exp << (lin11_t) lin11_mant_width);
412  lin11_exp &= ((lin11_t) lin11_bit_mask);
413  return ((lin11_t)(lin11_exp | lin11_mant));
414 }
415 
416 // +---------------------------------------------------------------------------+
417 // | Versions of the fl32_t Conversion Functions Interpreted as Float |
418 // +---------------------------------------------------------------------------+
419 // | |
420 // | NOTE: The functions below are for convenience only and are not as fast |
421 // | as calling the binary functions directly with the appropriate pointer |
422 // | assignments. |
423 // | |
424 // +---------------------------------------------------------------------------+
425 
426 // PMBus Linear11 to Single Precision Float
428 {
430  xout = lin11_to_fl32(xin);
431  return *((float *) &xout);
432 }
433 
434 // PMBus Linear16 to Single Precision Float
436 {
438  xout = lin16_to_fl32(lin16_mant, vout_mode);
439  return *((float *) &xout);
440 }
441 
442 // Single Precision Float to PMBus Linear11
444 {
445  LT_PMBusMath::fl32_t xin_fl32;
446  xin_fl32 = *((fl32_t *) &xin);
447  return fl32_to_lin11(xin_fl32);
448 }
449 
450 // Single Precision Float to PMBus Linear16
452 {
453  LT_PMBusMath::fl32_t xin_fl32;
454  xin_fl32 = *((fl32_t *) &xin);
455  return fl32_to_lin16 (xin_fl32, vout_mode);
456 }
457 
458 
459 
lin16_t float_to_lin16(float xin, lin16m_t vout_mode)
#define lin11_exp_width
float lin16_to_float(lin16_t lin16_mant, lin16m_t vout_mode)
unsigned int lin11_t
Definition: LT_PMBusMath.h:59
#define lin11_mant_sign_mask
#define fl32_width
#define fl32_mant_mask
lin16_t fl32_to_lin16(fl32_t xin, lin16_t lin16_exp)
#define lin16_exp_sign_mask
#define fl32_mant_width
LT_PMBusMath math_
#define lin11_max_p_exp
float lin11_to_float(lin11_t xin)
unsigned char uchar_t
Definition: LT_PMBusMath.h:63
lin11_t fl32_to_lin11(fl32_t xin)
#define lin16_ieee_mant_mask
#define lin16_width
#define lin16_exp_mask
fl32_t lin16_to_fl32(lin16_t lin16_mant, lin16_t lin16_exp)
#define fl32_exp_bias
#define lin11_max_n_exp
#define lin16_ieee_mant_width
#define lin11_ieee_mant_mask
#define lin11_width
#define lin11_norm_n1
unsigned int lin16m_t
Definition: LT_PMBusMath.h:61
#define lin16_max_value
#define lin11_exp_mask
fl32_t lin11_to_fl32(lin11_t xin)
#define lin11_ieee_mant_width
#define lin11_den_n1
unsigned int lin16_t
Definition: LT_PMBusMath.h:60
#define fl32_exp_mask
lin11_t float_to_lin11(float xin)
#define lin11_mant_mask
#define lin11_bit_mask
LTC PMBus Support: Math conversion routines.
#define lin11_mant_width
unsigned long fl32_t
Definition: LT_PMBusMath.h:58
#define lin11_exp_sign_mask