Skip to main content
Albi G.
Associate III
October 7, 2019
Question

[SILICON BUG ?] Why does OPAMP fail to calibrate with internal output routing? (STM32G474)

  • October 7, 2019
  • 14 replies
  • 4703 views

Hi,

I am using the relatively new STM32G4(74).

I am currently implementing the OPAMP-abstraction and i am having issues with calibration.

The calibration works in general, if the output routing goes to the pin (OPAINTOEN = 0). (n&p is around 15/16)

The calibration does not work (OUTCAL always 0, no matter which trimming values) if the opamp output is routed to the internal connection (OPAINTOEN=1) for e.g. measuring it with the ADC.

Therefore i need to make sure that during calibration OPAINTOEN=0. this->behavior = !documented;

Further i noticed another issue:

When i calibrate (with OPAINTOEN=0), then switch to OPAINTOEN=1 to connect to the ADC, the ADC-Values arent even close to what I expect.

For example, with VREF=3.3V, the OPAMP in FollowerMode, external Voltage=2.413V, i expect a reading of 2995. What I really read is 3060 while the OPAMP is in highspeed-mode and 3420 while the OPAMP is in normal mode. (Sampling time is at maximum, but shorter sampling time does not worsen the behavior)

It seems like the internal connection is straining the OPAMP far beyond its output current capabilities. This would also explain why the reading changes, if high-speed mode is enabled.

And this output strain would also explain why the calibration fails.

I however dont understand why calibration is setting-depended, since the documentation states, that all switches should be opened.

I have tested all OPAMPs 1..6. They are all the same concerning the calibration miss-behavior.

The ADC-value desaster is tested with:

  • OPAMP2 connected to ADC2/Ch16 as Follower.
  • OPAMP6 connected to ADC4/Ch17 as Follower.

The ADC value seems independend of highspeed mode with

  • OPAMP4 connected to ADC5/Ch5 as Follower
  • OPAMP3 connected to ADC2/Ch18 as Gain x32

Does anyone know what is wrong here?

 It really seems strange, since the OPAMP->ADC connection is a highly advertised/mentioned feature in those chips. Yet, it really sounds like a silicon bug. Specially the calibration dependence on output routing....

This topic has been closed for replies.

14 replies

Albi G.
Albi G.Author
Associate III
October 8, 2019

Meanwhile i have tested the self calibration behavior on another chip. Same behavior.

Except OPAMP3... there changing the output routing just highly shifts the calibration values around.

Is this as silicon bug? It is clearly against the documentation.

Which information are needed to confirm this issue or diagnose the problem?

Is there any specific setting needed to operate on 3.3V @ VDDA and VDD besides disabling the analog boost in SYSCFG? What could lead to this misbehavior..

I am just not finding any information and the HAL-Code from CubeMX is some utter ********. There is only a comment /* Self calibration is successful */ but this fact is not checked at all. I think it is reasonable to consider any calibration a failure if the calibration value result is at its extremes (either 0 or 31) since there is no proof, that not a lower or higher value would be necessary.

The demo project under

\STM32Cube_FW_G4_V1.1.0\Projects\B-G474E-DPOW1\Demonstrations\Src\ledRGB.c

does have a sequence in which calibration should fail:

OpampRedHandle.Instance = OPAMP3;
...
 OpampRedHandle.Init.InternalOutput = ENABLE;
...
 OpampRedHandle.Init.UserTrimming = OPAMP_TRIMMING_USER;
 OpampRedHandle.Init.TrimmingValueP = 0;
 OpampRedHandle.Init.TrimmingValueN = 0;
 HAL_OPAMP_Init(&OpampRedHandle);
 /* Calibrate OPAMP */
 HAL_OPAMP_SelfCalibrate(&OpampRedHandle);
 
/* To avoid blink of orange led during opamp calibration, the output is enabled only after calbration */
 
OpampRedHandle.Init.InternalOutput = DISABLE;
 
 HAL_OPAMP_Init(&OpampRedHandle);

I havent run this example, but I expect this calibration to not work at all in this circumstance.

Also, if there is an LED on the OPAMP-output and the output can only drive a few µA as per datasheet, i dont understand how this code ever worked or did what it is supposed to do (Reflect the DAC to OPAMP-out). Very strange.

If no one else participates here, i will try my best to run this example. But if this calibration fails, we have an extremely cripling silicon bug.

Albi G.
Albi G.Author
Associate III
October 8, 2019

I have verified it with CubeMX. I made a project which enables OPAMPS, configured the Outputs to internal and turned on self calibration.

I have updated the self calibration function to check if CALOUT has seen both states (1/0) since the datasheet says that the transition from 1->0 is the signal for successful calibration. If no transition happened, then the calibration is a failure. Currently i only run into a "asm volatile ("bkpt 0") instruction to trigger the debugger. This is the new cal-function with actual error checking.

HAL_StatusTypeDef HAL_OPAMP_SelfCalibrate(OPAMP_HandleTypeDef *hopamp)
{
 
 HAL_StatusTypeDef status = HAL_OK;
 
 uint32_t trimmingvaluen;
 uint32_t trimmingvaluep;
 uint32_t delta;
 char nWas0 = 0;
 char nWas1 = 0;
 char pWas0 = 0;
 char pWas1 = 0;
 
 
 
 /* Check the OPAMP handle allocation */
 /* Check if OPAMP locked */
 if (hopamp == NULL)
 {
 status = HAL_ERROR;
 }
 else if (hopamp->State == HAL_OPAMP_STATE_BUSYLOCKED)
 {
 status = HAL_ERROR;
 }
 else
 {
 
 /* Check if OPAMP in calibration mode and calibration not yet enable */
 if (hopamp->State == HAL_OPAMP_STATE_READY)
 {
 /* Check the parameter */
 assert_param(IS_OPAMP_ALL_INSTANCE(hopamp->Instance));
 
 /* Set Calibration mode */
 /* Non-inverting input connected to calibration reference voltage. */
 SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);
 
 /* user trimming values are used for offset calibration */
 SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_USERTRIM);
 
 /* Enable calibration */
 SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_CALON);
 
 /* 1st calibration - N */
 /* Select 90% VREF */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_90VDDA);
 
 /* Enable the selected opamp */
 SET_BIT(hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);
 
 /* Init trimming counter */
 /* Medium value */
 trimmingvaluen = 16UL;
 delta = 8UL;
 
 while (delta != 0UL)
 {
 /* Set candidate trimming */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
 
 /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
 /* Offset trim time: during calibration, minimum time needed between */
 /* two steps to have 1 mV accuracy */
 HAL_Delay(2);
 
 if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
 {
 	nWas1 = 1;
 /* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
 trimmingvaluen += delta;
 }
 else
 {
 	nWas0 = 1;
 	/* OPAMP_CSR_OUTCAL is LOW try lower trimming */
 trimmingvaluen -= delta;
 }
 
 delta >>= 1;
 }
 
 /* Still need to check if righ calibration is current value or un step below */
 /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0 */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
 
 /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
 /* Offset trim time: during calibration, minimum time needed between */
 /* two steps to have 1 mV accuracy */
 HAL_Delay(2);
 
 if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
 {
 	nWas1 = 1;
 	 /* OPAMP_CSR_OUTCAL is actually one value more */
 trimmingvaluen++;
 /* Set right trimming */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
 }
 
 /* 2nd calibration - P */
 /* Select 10% VREF */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_CALSEL, OPAMP_VREF_10VDDA);
 
 /* Init trimming counter */
 /* Medium value */
 trimmingvaluep = 16UL;
 delta = 8UL;
 
 while (delta != 0UL)
 {
 /* Set candidate trimming */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
 
 /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
 /* Offset trim time: during calibration, minimum time needed between */
 /* two steps to have 1 mV accuracy */
 HAL_Delay(2);
 
 if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
 {
 pWas1 = 1;
 	/* OPAMP_CSR_OUTCAL is HIGH try higher trimming */
 trimmingvaluep += delta;
 }
 else
 {
 	pWas0 = 1;
			trimmingvaluep -= delta;
 }
 
 delta >>= 1;
 }
 
 /* Still need to check if righ calibration is current value or un step below */
 /* Indeed the first value that causes the OUTCAL bit to change from 1 to 0U */
 /* Set candidate trimming */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
 
 /* OFFTRIMmax delay 2 ms as per datasheet (electrical characteristics */
 /* Offset trim time: during calibration, minimum time needed between */
 /* two steps to have 1 mV accuracy */
 HAL_Delay(2);
 
 if ((hopamp->Instance->CSR & OPAMP_CSR_OUTCAL) != 0UL)
 {
 	pWas1 = 1;
 	 /* OPAMP_CSR_OUTCAL is actually one value more */
 trimmingvaluep++;
 /* Set right trimming */
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
 }
 
 /* Disable calibration */
 CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_CALON);
 
 /* Disable the OPAMP */
 CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_OPAMPxEN);
 
 /* Set operating mode */
 /* Non-inverting input connected to calibration reference voltage. */
 CLEAR_BIT(hopamp->Instance->CSR, OPAMP_CSR_FORCEVP);
 
 /* Self calibration is successful */
 /* Store calibration(user timming) results in init structure. */
 
 /* Write calibration result N */
 hopamp->Init.TrimmingValueN = trimmingvaluen;
 
 /* Write calibration result P */
 hopamp->Init.TrimmingValueP = trimmingvaluep;
 
 if (!pWas1 || !pWas0 || !nWas0 || !nWas1)
 	 asm volatile ("bkpt 0");
 /* Select user timming mode */
 /* And updated with calibrated settings */
 hopamp->Init.UserTrimming = OPAMP_TRIMMING_USER;
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETP, trimmingvaluep << OPAMP_INPUT_NONINVERTING);
 MODIFY_REG(hopamp->Instance->CSR, OPAMP_CSR_TRIMOFFSETN, trimmingvaluen << OPAMP_INPUT_INVERTING);
 }
 
 else
 {
 /* OPAMP can not be calibrated from this mode */
 status = HAL_ERROR;
 }
 }
 return status;
}

I think the next step is that others could reproduce this hardware bug or maybe just by chips are bad.

I have attached the CubeMX project file (hopefully it is enough).

You can generate code, load the project, replace the HAL_OPAMP_SelfCalibrate with what i just posted above and just run. If the calibration fails, the debugger will automatically stop.

Can please anyone try this? I am quite desperate by now since i cant get usefull ADC data from the OPAMPs. :(

Can someone from ST jump in here?

Albi G.
Albi G.Author
Associate III
October 20, 2019

Hello? ST?

waclawek.jan
Super User
October 20, 2019

This is primarily a user-driven forum, with casual ST presence.

And probably there is not much knowledge amongst users who are willing to respond here, about the intimate details of the 'G4, yet.

You may want to contact ST directly.

JW

PS. @Vincent Onde​ , is this covered in some of the G4 appnotes?

Albi G.
Albi G.Author
Associate III
October 20, 2019

Thanks for your reply. To be honest, i am unsure how to get technical support. Contacting ST leads either to a general purpose contact form (where the categories do not include anything technical) or it is this forum. For new processors that is a big problem so i would expect they monitor feeback here.

Vincent Onde
ST Employee
October 23, 2019

​Hi,

We have tested this and confirm it is a silicon limitation (this will be documented). As it is done today, the calibration method here-above only works with external output routing.

The work-around is to use the ADC to read the internal OpAmp connection, instead of the CALOUT bit, and decode the ADC value as a logical one or zero. The algorithm  is the very same, including the 2ms delay for taking a decision on the right trimming value.

Thanks for your feedback!

Vincent

PS: in parallel, we are also double checking your ADC reading error, we'll keep you posted.

Singh.Harjit
Senior
October 26, 2019

What is the ADC value that one must use?

Albi G.
Albi G.Author
Associate III
October 26, 2019

I think the spirit of Vincents answer is to use the MSB of the ADC-reading as the CALOUT equivalent.

I wonder if one could actually extrapolate some ADC-Readings and predict the final reading and get a much faster calibration that way.

If the derivation over time of the ADC reading is not large enough (or has the wrong sign) to flip the MSB within 2ms, that would be interesting

Albi G.
Albi G.Author
Associate III
October 23, 2019

Thank you Vincent, that takes a lot off my mind.

Concerning the ADC reading error, i try to help you there. I have a STM32G747 in my circuit that has an external driven analog Voltage at PB12.

That pin can connect to ADC4(in3) and to OPAMP6 (VINP0) which gives the opportunity to test it all:

1) reading directly via ADC4 Ch3.

2) Configuring OPA6 in follower-mode (normal speed) and read ADC4 Ch17

3) Changing OPA6 into high speed mode and read ADC4 Ch17 again.

From my observations, method 1 will give best results, OPA in high speed mode will be out of spec but kind of close and normal mode will be hilariously offset .

In case of PB12 is working one can try to verify OPAMP3 + ADC3 via pin PB0 (ADC3/Ch12 vs OPA3 @ ADC/Ch13)

Unfortunately i am extremely sick currently, so i cant promise much progress. But if i would dig a little deeper this is how i would do it.

Thank you very much, again.

PS: if you are on the specification.. can you please update the datasheet, so that it actually states that the HRTIM DLL does not work below 100MHz input clock?. There is currently no lower bound (not even a TBD mark) and this information is somehwere in a presenation slide set and it took me hours of time to work that one out.

Vincent Onde
ST Employee
October 25, 2019

​Hi,

I hope you did recover and are feeling better now.

Please let us know your new findings on PB12. For us, it is working fine. Probably we are not in the very same conditions. Did you let enough time for the OpAmp to start? Are you going continuous ADC readings, or just a single shot at start-up?

Thanks for the notice on the DLL min operating frequency. This is something that was missed during the publication process. We'll correct it.

Best regards,

Vincent

Albi G.
Albi G.Author
Associate III
October 28, 2019

Hi Vincent,

i have tried the PB12 experiment. I have attached all relevant code concerning the test procedure:

  • maximum sampling time
  • averaging of 64 samples
  • 2ms wait time between sampling:
  • External voltage @ PB12 = 1.329V
  • VREF = 3.300V
  • VDD = around 3.3V
  • Expected ADC-Reading = 1.329/3.3*2^12 = 1650.

0690X00000ArLT5QAN.pngWell...

Eval.Direct = 1688,
Eval.OPAMP_normal = 1721,
Eval.OPAMP_hs = 1609

No where near... :loudly_crying_face:

Reducing the ADC-Frequency from somewhere near 60MHz to 6MHz:

Eval.Direct = 1720,
Eval.OPAMP_normal = 1704,
Eval.OPAMP_hs = 1736

Reduced effect, but still unacceptable. The change in the apparent absolute value is also a little strange..

There is some run-to-run variation of the ADC-readings. There might be some noise on PB12 in my circuit. However, no matter the values, the relative differences between measurement methods stay somewhat constant. Please consider the values above "representative". Especially have a close look at the influence of the OPAMP speed.

As you can see i am using my own C++ framework. It is entirely possible that i am missing an important bit/setting somewhere.. if so, it is practically undocumented or very well hidden.

Edit: i have done some signal analysis via FFT and stuff (using 2048 samples @ 10ksps).

  • My RMS signal noise is 2.3LSBs when i sample directly from the pin
  • 2.1LSB with OPAMP in high speed mode
  • 6.5LSB with OPAMP in normal mode...
Albi G.
Albi G.Author
Associate III
November 5, 2019

HELP :( Anyone there? Shall i try new processors? Can someone from ST please confirm or deny that there was a bad batch or maybe that certain circuit behavior (like an accidentally bad power on sequence?) could damage the chip this way?

Vincent Onde
ST Employee
November 5, 2019

​Hi Albrecht,

Sorry, I was out for a while...

It's difficult to guess if there are issues with ADC configuration using the code attached. If possible, a register dump would be better.

Considering the code is fine, there may be 2 explanations for what you're seeing now:

  • the interval between 2 conversions is too large (there must be no more than 1ms idle time
  • the routing around ADC is not optimum. Eventually some ringing on the analog input(?), a Vref+ and/or Vdda decoupling not sufficient? The capacitor on Vref+ in particular is critical, it must bez located as close as possible from the MCU).

Are you facing the same issues with a Nucleo board?

Best regards,

Vincent