1 /******************************************************************************
2  *  Filename:       tempdiode.c
3  *
4  *  Description:    Driverlib for the high accuracy temperature diode
5  *
6  *  Copyright (c) 2023, Texas Instruments Incorporated
7  *  All rights reserved.
8  *
9  *  Redistribution and use in source and binary forms, with or without
10  *  modification, are permitted provided that the following conditions are met:
11  *
12  *  1) Redistributions of source code must retain the above copyright notice,
13  *     this list of conditions and the following disclaimer.
14  *
15  *  2) Redistributions in binary form must reproduce the above copyright notice,
16  *     this list of conditions and the following disclaimer in the documentation
17  *     and/or other materials provided with the distribution.
18  *
19  *  3) Neither the name of the ORGANIZATION nor the names of its contributors may
20  *     be used to endorse or promote products derived from this software without
21  *     specific prior written permission.
22  *
23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  *  POSSIBILITY OF SUCH DAMAGE.
34  *
35  ******************************************************************************/
36 
37 #include <stdint.h>
38 
39 #include "adc.h"
40 #include "hapi.h"
41 #include "tempdiode.h"
42 #include "interrupt.h"
43 
44 #include "../inc/hw_clkctl.h"
45 #include "../inc/hw_fcfg.h"
46 #include "../inc/hw_memmap.h"
47 #include "../inc/hw_pmud.h"
48 #include "../inc/hw_sys0.h"
49 #include "../inc/hw_types.h"
50 
51 #ifdef __IAR_SYSTEMS_ICC__
52 __ramfunc static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data);
53 #elif (defined(__GNUC__) || defined(__clang__))
54 static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data) __attribute__((section(".TI.ramfunc"), noinline));
55 #else
56     #error Unsupported Compiler
57 #endif
58 
59 extern int32_t voltageToTempHardcoded(uint32_t microVolts);
60 static void enableADC(void);
61 static int32_t voltageToTemp(uint32_t microVolts);
62 static uint64_t isqrt(uint64_t n);
63 
64 /* Macros for finding minimum between two and three numbers */
65 #define MIN2(a, b)    ((a) < (b) ? (a) : (b))
66 #define MIN3(a, b, c) MIN2(MIN2((a), (b)), (c))
67 
68 //*****************************************************************************
69 //
70 // Writes data to register while executing from RAM
71 //
72 //*****************************************************************************
73 #ifdef __IAR_SYSTEMS_ICC__
TempDiodeRamHWREG(uint32_t address,uint32_t data)74 __ramfunc static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data)
75 {
76     HWREG(address) = data;
77     /* Wait at least 11 us before proceeding with any flash operations */
78     HapiWaitUs(11);
79     return HWREG(address);
80 }
81 #elif (defined(__GNUC__) || defined(__clang__))
TempDiodeRamHWREG(uint32_t address,uint32_t data)82 static uint32_t TempDiodeRamHWREG(uint32_t address, uint32_t data)
83 {
84     HWREG(address) = data;
85     /* Wait at least 11 us before proceeding with any flash operations */
86     HapiWaitUs(11);
87     return HWREG(address);
88 }
89 #else
90     #error Unsupported Compiler
91 #endif
92 
93 //*****************************************************************************
94 //
95 // Enable and configure the ADC. Parameters are the same as used during
96 // trim-procedure.
97 //
98 //*****************************************************************************
enableADC(void)99 static void enableADC(void)
100 {
101     /* Enable ADC peripheral */
102     HWREG(CLKCTL_BASE + CLKCTL_O_CLKENSET0) = CLKCTL_CLKENSET0_ADC0;
103 
104     /* Configure ADC CTL-register 0 */
105     ADCSetMemctlRange(0, 0);
106 
107     /* Set maximum sampling duration for highest accuracy */
108     ADCSetSampleDuration(ADC_CLOCK_DIVIDER_48, 1023);
109 
110     /* Use 12-bit resolution */
111     ADCSetResolution(ADC_RESOLUTION_12_BIT);
112 
113     /* Use internal 1.4V reference, sample on internal channel 14 (VA_ATEST1) */
114     ADCSetInput(ADC_FIXED_REFERENCE_1V4, ADC_MEMCTL0_CHANSEL_CHAN_14, 0);
115 
116     /* Adjust ADC to 1.4V reference */
117     ADCSetAdjustmentOffset(ADC_FIXED_REFERENCE_1V4);
118 
119     /* Perform single conversion */
120     ADCSetSequence(ADC_SEQUENCE_SINGLE);
121 }
122 
123 //*****************************************************************************
124 //
125 // Converts a voltage (uV) measured across the diode to temperature (degC),
126 // with 4 fractional bits, using hardcoded coefficients. If the device has
127 // coefficients available in FCFG, or if different hardcoded values are needed,
128 // then this function can be overridden.
129 //
130 //*****************************************************************************
voltageToTempHardcoded(uint32_t microVolts)131 int32_t __attribute__((weak)) voltageToTempHardcoded(uint32_t microVolts)
132 {
133     /* The tempsense diode voltage (mV) as a function of temperature (degC) can
134      * be modeled as mV(T) = a*T^2 + b*T + c, where the coefficients are:
135      * a = -0.000327
136      * b = -1.435965
137      * c = 835.584465
138      *
139      * To improve the performance we find the delta between the curve fit, and
140      * the measured FCFG value. The FCFG temperature is not precisely 30 degC,
141      * but 29.5 degC instead.
142      * We calculate mV(29.5) = 792.938926 mV.
143      *
144      * The device-specific curve fit function then becomes
145      * mV(T) = a*T^2 + b*T + c + delta, where delta = (FCFG_value - mV(29.5))
146      *
147      * mV(T) = -0.000327*T^2 - 1.435965*T + 835.584465 + FCFG_value - 792.938926
148      *
149      * To bring all constants from floating-point to fixed-point integers we
150      * multiply the entire equation by a factor 2^24. This gives good enough
151      * resolution, and leaves a margin before overflowing 64 bit calculations.
152      * The shifted coefficients become as follows:
153      *
154      * aScaled = a * 2^24 = -5486
155      * bScaled = b * 2^24 = -24091495
156      * cScaled = c * 2^24 = 14018781056
157      * mV(29.5)Scaled = 792.938926 * 2^24 = 13303307632
158      *
159      */
160 
161     int64_t aScaled    = -5486LL;       /*  -0.000327 * 2^24 */
162     int64_t bScaled    = -24091495LL;   /*  -1.435965 * 2^24 */
163     int64_t cScaled    = 14018781056LL; /* 835.584465 * 2^24 */
164     int64_t mV28Scaled = 13303307632LL; /*   mV(29.5) * 2^24 */
165 
166     /* To find the temperature T, we solve the equation
167      * 0 = p2*T^2 + p1*T + p0, using the quadratic formula, where
168      * p2 = aScaled
169      * p1 = bScaled
170      * p0 = cScaled + fcfg_valueScaled - mV(29.5)Scaled - inputVoltageScaled
171      *
172      * T = (-p1 - sqrt(p1*p1 - 4*p2*p0)) / (2*p2)
173      */
174 
175     int64_t fcfgValue = fcfg->appTrims.cc23x0r5.auxDiodeCal30C.auxDiodeVoltage -
176                         fcfg->appTrims.cc23x0r5.auxDiodeCal30C.auxDiodeGnd;
177 
178     /* The FCFG voltage value is already left-shifted by 4, so we only shift it
179      * 20 more.
180      */
181     int64_t fcfgValueScaled = fcfgValue << 20;
182 
183     /* Convert input voltage (microvolts) to scaled millivolts */
184     int64_t inputVoltageScaled = (((int64_t)microVolts << 24) + 500) / 1000;
185 
186     int64_t p2 = aScaled;
187     int64_t p1 = bScaled;
188     int64_t p0 = cScaled + fcfgValueScaled - mV28Scaled - inputVoltageScaled;
189 
190     /* Apply quadratic formula, but scale numerator by a factor 16 to get 4
191      * fractional bits in the temperature result.
192      */
193     int32_t temperature = ((-p1 - (int64_t)isqrt(p1 * p1 - (4 * p2 * p0))) << 4) / (2 * p2);
194 
195     return temperature;
196 }
197 
198 //*****************************************************************************
199 //
200 // Converts a voltage (uV) measured across the diode to temperature (degC),
201 // with 4 fractional bits.
202 //
203 //*****************************************************************************
voltageToTemp(uint32_t microVolts)204 static int32_t voltageToTemp(uint32_t microVolts)
205 {
206     /* Check if coefficients are directly available in FCFG. If not, fall back
207      * on function using hardcoded values. Coefficients were only introduced in
208      * FCFG layout revision 6.
209      */
210     uint8_t fcfgRevision = fcfg->appTrims.revision;
211 
212     if (fcfgRevision < 0x06)
213     {
214         return voltageToTempHardcoded(microVolts);
215     }
216 
217     /* The tempsense diode voltage (mV) as a function of temperature (degC) can
218      * be modeled as mV(T) = a*T^2 + b*T + c, where the coefficients are:
219      * a = a_fcfg >> a_fcfg_shift
220      * b = b_fcfg >> b_fcfg_shift
221      * c = c_fcfg >> c_fcfg_shift
222      */
223 
224     int64_t aScaled = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP2;
225     int64_t bScaled = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP1;
226     int64_t cScaled = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP0;
227 
228     uint8_t aShiftFactor = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP2Shift;
229     uint8_t bShiftFactor = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP1Shift;
230     uint8_t cShiftFactor = fcfg->appTrims.cc23x0r5.auxDiodeCoeff.coeffP0Shift;
231 
232     /* Bring coefficients to the same scale. Try to shift towards the
233      * numerically highest coefficient (the one with the largest shift) in
234      * order to maintain highest possible resolution. Since coefficients are
235      * 16-bit, they should be scaled maximum by 16, otherwise two coefficients
236      * would overflow 64-bit when multiplied. If some coefficients need to be
237      * shifted more than 16 bits, a middle ground must be found and some
238      * coefficients must be shifted down instead.
239      */
240     uint8_t minShiftFactor = MIN3(aShiftFactor, bShiftFactor, cShiftFactor);
241 
242     /* Always shift up at least 16 bits */
243     uint8_t commonShiftFactor = minShiftFactor + 16;
244 
245     int64_t *coefficients[3] = {&aScaled, &bScaled, &cScaled};
246     uint8_t shiftFactors[3]  = {aShiftFactor, bShiftFactor, cShiftFactor};
247 
248     /* Loop over all three coefficients and scale each one. */
249     for (int i = 0; i < 3; i++)
250     {
251         if (shiftFactors[i] < commonShiftFactor)
252         {
253             /* Scale this coefficient up towards the common scale factor */
254             *coefficients[i] <<= (commonShiftFactor - shiftFactors[i]);
255         }
256         else if (shiftFactors[i] > commonShiftFactor)
257         {
258             /* Scale this coefficient down towards the common scale factor */
259             *coefficients[i] >>= (shiftFactors[i] - commonShiftFactor);
260         }
261         else
262         {
263             /* Do nothing. Scale neither up nor down */
264         }
265     }
266 
267     /* Convert input voltage (microvolts) to scaled millivolts. The common
268      * shift factor can be maximum 31 + 16 = 47, and microvolts can be maximum
269      * 1,400,000, which means that (microvolts << commonShiftFactor) can in
270      * theory overflow. Practically, the characteristics of the tempsense diode
271      * tell us this will never happen, but we add a check just in case.
272      */
273     int64_t inputVoltageScaled;
274 
275     if (commonShiftFactor >= 42)
276     {
277         /* If shitfting up by more than 42, this value might overflow. Divide
278          * by 1000 to convert to millivolts before shifting.
279          */
280         inputVoltageScaled = (int64_t)(microVolts / 1000) << commonShiftFactor;
281     }
282     else
283     {
284         inputVoltageScaled = (((int64_t)microVolts << commonShiftFactor) + 500) / 1000;
285     }
286 
287     /* To find the temperature T, we solve the equation
288      * 0 = p2*T^2 + p1*T + p0, using the quadratic formula, where
289      * p2 = aScaled
290      * p1 = bScaled
291      * p0 = cScaled - inputVoltageScaled
292      *
293      * T = (-p1 - sqrt(p1*p1 - 4*p2*p0)) / (2*p2)
294      */
295     int64_t p2 = aScaled;
296     int64_t p1 = bScaled;
297     int64_t p0 = cScaled - inputVoltageScaled;
298 
299     /* Apply quadratic formula, but scale numerator by a factor 16 to get 4
300      * fractional bits in the temperature result. Round to nearest integer.
301      */
302     int64_t dividend = (-p1 - (int64_t)isqrt(p1 * p1 - (4 * p2 * p0))) << 4;
303     int64_t divisor  = 2 * p2;
304     int32_t temperature;
305 
306     if ((dividend < 0LL) == (divisor < 0LL))
307     {
308         /* If dividend has same sign as divisor */
309         temperature = (dividend + (divisor / 2)) / divisor;
310     }
311     else
312     {
313         /* If dividend has opposite sign as divisor */
314         temperature = (dividend - (divisor / 2)) / divisor;
315     }
316 
317     return temperature;
318 }
319 
320 //*****************************************************************************
321 //
322 // Reads temperature from high accuracy temperature diode with 4 fractional bits
323 //
324 //*****************************************************************************
TempDiodeGetTemp(void)325 int32_t TempDiodeGetTemp(void)
326 {
327     uint32_t diodeVoltage = 0;
328     bool intAlreadyDisabled;
329 
330     /* Unlock ATESTCFG register and connect VR_ATEST to VA_ATEST */
331     HWREG(SYS0_BASE + SYS0_O_ATESTCFG) = 0x5A000000 | SYS0_ATESTCFG_VR2VA1 | SYS0_ATESTCFG_VR2VA0;
332 
333     /* Disable interrupts and keep track of whether interrupts were already
334      * disabled or not
335      */
336     intAlreadyDisabled = IntDisableMaster();
337 
338     /* Connect 1uA IREF to test bus. This is done via a function executing from
339      * RAM, while interrupts are disabled, to ensure that no flash-operations
340      * are active when the reference current is enabled.
341      */
342     TempDiodeRamHWREG(PMUD_BASE + PMUD_O_PREFSYS, PMUD_PREFSYS_TEST2);
343 
344     /* If interrupts were already disabled, then they should be left disabled.
345      * If interrupts were already enabled, then they should be re-enabled here.
346      */
347     if (intAlreadyDisabled == false)
348     {
349         IntEnableMaster();
350     }
351 
352     enableADC();
353 
354     /* Measure the high side of the diode */
355     HWREG(SYS0_BASE + SYS0_O_TSENSCFG) = SYS0_TSENSCFG_SEL_VALUE;
356 
357     /* Perform a dummy-read of the ADC for better settling */
358     ADCManualTrigger();
359     CPUDelay(3);
360     ADCReadResult(0);
361 
362     /* Do 4 ADC conversions for averaging */
363     for (uint32_t i = 0; i < 4; i++)
364     {
365         ADCManualTrigger();
366         CPUDelay(3);
367         diodeVoltage += ADCReadResult(0);
368     }
369 
370     /* Measure ground (low side of the diode) */
371     HWREG(SYS0_BASE + SYS0_O_TSENSCFG) = SYS0_TSENSCFG_SEL_GND;
372 
373     /* Perform a dummy-read of the ADC for better settling */
374     ADCManualTrigger();
375     CPUDelay(3);
376     ADCReadResult(0);
377 
378     /* Do 4 ADC conversions for averaging */
379     for (uint32_t i = 0; i < 4; i++)
380     {
381         ADCManualTrigger();
382         CPUDelay(3);
383         diodeVoltage -= ADCReadResult(0);
384     }
385 
386     /* Calculate an average of the 4 readings, rounded to the nearest integer */
387     diodeVoltage = (diodeVoltage + 2) >> 2;
388 
389     /* Adjust ADC value to compensate for device/reference specific gain */
390     uint16_t gain = ADCGetAdjustmentGain(ADC_FIXED_REFERENCE_1V4);
391     diodeVoltage  = ADCAdjustValueForGain(diodeVoltage, ADC_RESOLUTION_12_BIT, gain);
392 
393     /* Convert raw reading to microvolts */
394     diodeVoltage = ADCValueToMicrovolts(diodeVoltage, ADC_RESOLUTION_12_BIT, 1400000);
395 
396     /* Disable ADC */
397     HWREG(CLKCTL_BASE + CLKCTL_O_CLKENCLR0) = CLKCTL_CLKENCLR0_ADC0;
398 
399     /* Reset ATB */
400     HWREG(SYS0_BASE + SYS0_O_ATESTCFG) = 0x5A00000F;
401 
402     /* Disable temperature diode measurement */
403     HWREG(SYS0_BASE + SYS0_O_TSENSCFG) &= ~SYS0_TSENSCFG_SEL_M;
404 
405     /* Disconnect all test reference signals */
406     intAlreadyDisabled = IntDisableMaster();
407     TempDiodeRamHWREG(PMUD_BASE + PMUD_O_PREFSYS, 0);
408     if (intAlreadyDisabled == false)
409     {
410         IntEnableMaster();
411     }
412 
413     return voltageToTemp(diodeVoltage);
414 }
415 
416 //*****************************************************************************
417 //
418 // Compute the integer square root of a number n. This function returns the
419 // largest integer whose square is equal to or less than n.
420 //
421 //*****************************************************************************
isqrt(uint64_t n)422 static uint64_t isqrt(uint64_t n)
423 {
424     uint64_t remainder, root;
425 
426     /* Initialize the remainder and root to zero */
427     remainder = 0;
428     root      = 0;
429 
430     /* Loop over the 32 bits in the root */
431     for (uint32_t index = 0; index < 32; index++)
432     {
433         /*
434          * Shift the root up by a bit to make room for the new bit that is
435          * about to be computed.
436          */
437         root <<= 1;
438 
439         /* Get two more bits from the input into the remainder */
440         remainder = ((remainder << 2) + (n >> 62));
441         n <<= 2;
442 
443         /* Make the test root be 2n + 1 */
444         root++;
445 
446         /* See if the root is greater than the remainder */
447         if (root <= remainder)
448         {
449             /* Subtract the test root from the remainder */
450             remainder -= root;
451 
452             /* Increment the root, setting the second LSB */
453             root++;
454         }
455         else
456         {
457             /*
458              * The root is greater than the remainder, so the new bit of the
459              * root is actually zero
460              */
461             root--;
462         }
463     }
464 
465     /* Return the computed root */
466     return root >> 1;
467 }