Reply
Posts: 6
Registered: ‎12-30-2015
Accepted Solution

BURTC missing interrupt

Hello,

 

I am working with EFM32LG on a low power project where I use BURTC interrupt as an alarm clock to wake up the MCU for its next action.

My application sometimes freezes (next action never executed), as if the BURTC interrupt did not fire.

 

I converged on a problem with certain values of BURTC->COMP0 which always fail in my tests: I discovered that, when I set BURTC->COMP0 to a value ending with 0xFF (as in 0x1FF, 0x1234FF, ...) and that BURTC->CNT is close enough that value (e.g.: 0x1FD, 0x1234FD), the interrupt never fires.

 

I first suspected a too short delay between interrupt setup and its deadline, but all my tests with short deadlines (even shorter ones) works perfectly except when the deadline value ends up with 0xFF.

 

I attach a sample code based on STK3600 BURTC example which triggers the problem every single try. Simply import attached project in SimplicityStudio.

The test set BURTC->COMP0 to a threshold value and then to the next value ending with 0xFF. In my tests, when threshold is at 0xFC, interrupt at 0x...FF is always missed.

You can play with STARTING_THRESHOLD to see that the test works well up to a given threshold.

If NEXT_ALARM_OFFSET is set to 1, interrupt always fires, even with very short deadline.

The test code prints on Program Output Console (SWO) details about BURTC registers and interrupts after each interrupt.

 

Could you please advice if it is a known problem and how to fix it, or if I do something wrong with BURTC setup.

 

Thanks for your help. See attached code.

Posts: 487
Registered: ‎02-04-2013

Re: BURTC missing interrupt

Hi,

 

There are a couple of errata for the BURTC on LG Rev B, C, and D.  This might be part of the problem.

 

See the EFM32LG990 errata history:

http://www.silabs.com/Support%20Documents/TechnicalDocs/EFM32LG990-errata-history.pdf

 

Specifically note:

 

BURTC_E101 BURTC

LPMODE entry

Entering LPMODE with LPCOMP=7 causes counter error.
Counting error occurs if overflow on 7 LSBs happens when entering LPMODE with LPCOMP=7. This results in the counter value being 256 less than it should be after the error.
The error accumulates. Avoid using LPMODE with LPCOMP=7.

 

BURTC_E102
BURTC_CNT read error

When LPMODE is active (i.e. BURTC_STATUS_LPMODEACT is high), software reads might result in wrong value being read from BURTC_CNT. Before reading BURTC_CNT, disable LPMODE and wait for BURTC_STATUS_LPMODEACT to be cleared before reading BURTC_CNT.

 

Best regards,

Chris

Posts: 6
Registered: ‎12-30-2015

Re: BURTC missing interrupt

Hello ChrisM,

 

Thanks for your answer. I am aware of the errata for BURTC on EFM32LG. I re-checked but I think I am not concerned by those. Specifically:

 

BURTC_E101: BURTC LPMODE entry

As said before, I am using Silicon Labs STK3600_BURTC example. In that example, LPCOMP = 0 (burtcInit.lowPowerComp = 0) as defined by BURTC_INIT_DEFAULT in em_burtc.h. The example code is not changing this default value. See function burtcSetup() in the code posted above.

 

BURTC_E102: BURTC_CNT read error

Same here, in the example code LPMODE = 0 (burtcInit.lowPowerMode = burtcLPDisable) as defined in BURTC_INIT_DEFAULT in em_burtc.h and is not modified either.

 

Please tell me if my interpretation of those values are wrong.

 

I would like to emphasis that I am using the STK3600_burtc example code with very slight modification to focus on the behavior I experiment on my project.

Could you please confirm that you have the same behavior as I have on the example project I posted above? Specifically, the code is printing traces up to a certain moment when it stalls, because it missed a BURTC interrupt.

 

Thank you for your help.

Posts: 487
Registered: ‎02-04-2013

Re: BURTC missing interrupt

[ Edited ]

Hi,

 

Yes, I'm seeing the same behavior as you.  After a few iterations, the code misses the BURTC COMP0 interrupt and keeps going until it rolls over or I manually suspend, which wakes the part from EM1 and sets the new compare value to start again.

 

While debugging your code, I replaced EnterEM2() with EnterEM1() since I want to debug while it's running, and the part won't actually go into EM2 while debug is active.  This shouldn't change the execution that much.

 

BURTC.png

 

I see that CNT and COMP0 are very close to each other or equal sometimes.  I've seen cases where CNT is 1 less than COMP0 and then I don't get the interrupt.

 

I think the reason this is happening is because the write to COMP0 is not synchronous.  You have to wait for the register write to go from the HF clock domain to the LF clock domain through synchronizers.  You don't know exactly when COMP0 is written.  It's possible that the update doesn't happen until CNT ticks 1 more and they are already equal, so you don't get the interrupt.

 

You probably want to ensure a large enough margin of time between CNT and COMP0 to give time for the synchronization process.

 

Best regards,

Chris

Posts: 6
Registered: ‎12-30-2015

Re: BURTC missing interrupt

Hello Chris,

 

Thanks for your detailed answer. I also suspected synchronization delay between clock domains, but in my case, BURTC count every 3.91 ms (Compare match resolution for PRESC=7 / burtcInit.clkDiv = 128) and, as far as I could find, clock domain sync is much faster than that (I found this post which says 3 positive clock edges on the LF clock, around 92µs).

 

EMLIB is taking care of the synchronization with BURTC_Sync() function call in BURTC_CompareSet(). So I tried to read again BURTC->CNT after calling BURTC_CompareSet() and trace it in my example program.

 

What I see is a trace like the following, just before application freezes:

 

threshold=0xfc, CNT=765 (0x2fd), next alarm=767 (0x2ff), currentCNT=765 (0x2fd) -- IRQ: COMP0=5, OF=0

 

This tells me that BURTC counter did not increment after setting COMP0 (and so after sync'ing clock domains), and that counter is 2 ticks behind COMP0. Nevertheless, my application freezes in that situation.

 

It seems clock domain synchronization is not the problem in that case.

Any other ideas?

 

Attached is the project with the re-read of BURTC->CNT.

Thanks again for your help.

 

Posts: 487
Registered: ‎02-04-2013

Re: BURTC missing interrupt

Hi,

 

In Section 5.3.1.1 Writing in the reference manual:

"Delayed synchronization is used for the other
Low Energy Peripherals, and for these peripherals, a write operation requires 3 positive edges on the
clock of the Low Energy Peripheral being accessed."

 

Looking at Figure 22.1 BURTC Overview, the prescaled LFXO is the BURTC peripheral clock.  In this case, I believe the clock is 32768/128.  So three edges would be 3 CNTs, which would explain the problem.

 

COMP0 is marked as an async register, so the 3 clock delay would apply to writes.

 

You should set COMP0 to CNT+3 at the minimum.

 

Best regards,

Chris

Posts: 6
Registered: ‎12-30-2015

Re: BURTC missing interrupt

Hi Chris,

 

Thanks for your answer. I applied your suggestion to my product code and I did not see the problem happening. So I guess it is solved.

 

Nevertheless, it feels like it is a very long delay to wait in my configuration: BURTC counts-up every 3.9ms, so keeping a 3-tick margin means that, if I have two actions planned in less than 12ms, I have to busy-wait and I am not able to go to sleep. This is not satisfying for an Energy Friendly Microcontroller.

 

Moreover, as mentioned in the reference manual section you refer to, 5.3.1.1.1 Delayed synchronization, says:

 

After writing data to a register which value is to be synchronized into the Low Energy Peripheral using
delayed synchronization, a corresponding busy flag in the <module_name>_SYNCBUSY register (e.g.
LEUART_SYNCBUSY) is set. This flag is set as long as synchronization is in progress and is cleared
upon completion.

 

and EMLIB does this, in BURTC_Sync() function (em_burtc.c):

 

  /* Wait for any pending previous write operation to have been completed */
  /* in low frequency domain. This is only required for the Gecko Family */
  while (BURTC->SYNCBUSY & mask)
    ;

And in the second version of my project, I specifically read again BURTC->CNT after the synchronization operation, and the CNT value did not change (and it is still behind COMP0 by 2 ticks).

 

So, I agree with you that COMP0 should be set at least to CNT+3, but in my example code, I take every precaution and check CNT after synchronization, and still miss the interrupt.

 

I think there is another problem elsewhere.

 

 

Posts: 487
Registered: ‎02-04-2013

Re: BURTC missing interrupt

Hi,

 

Did you add code to sync after BURTC_CompareSet() and before checking BURTC_CounterGet()?  Is this the same code you have in the above post?  I'm using em_burtc.c 4.1.0 and don't see the BURTC_Sync() function.  How are you synchronizing?

 

BURTC_CompareSet() calls regSync() and then writes BURTC->COMP0.  This syncs any old async writes to COMP0 before starting a new one.  It doesn't actually wait for the new COMP0 write to complete.

 

I agree that 12 ms in many situations is too large of a delay.  You could change the BURTC clock prescaler to reduce this delay at the cost of slightly increased current consumption.

 

Best regards,

Chris

Posts: 6
Registered: ‎12-30-2015

Re: BURTC missing interrupt

Hello,

 

Thanks for your message. Indeed, I thought regSync() was waiting for the sync to be over, but as you said, it works for the previous sync.

 

In order to test again, I made regSync() available to my program (remove INLINE and declare it as extern in my C file). Then, after setting COMP0, I call again manually regSync(), then I read the BURTC->CNT register. It is still at the same value (2 ticks behind COMP0) when the system freezes.

 

Could you test and tell me if you experience the same behavior? Tell me if I am doing something wrong. I am also open to any other suggestion to understand what happen here.

 

Thanks.

 

Here is the code modification:

Top of burtc_missing_interrupt.c

extern void regSync(uint32_t mask);

em_burtc.c:

/*__STATIC_INLINE */void regSync(uint32_t mask)

In while loop:

BURTC_CompareSet(0, next);

regSync(BURTC_SYNCBUSY_COMP0);

cnt = BURTC_CounterGet();

 

 

Here is the result log:

threshold=0xfa, CNT=0 (0), next alarm=250 (0xfa), currentCNT=0 (0) -- IRQ: COMP0=0, OF=0
threshold=0xfa, CNT=251 (0xfb), next alarm=255 (0xff), currentCNT=251 (0xfb) -- IRQ: COMP0=1, OF=0
threshold=0xfb, CNT=256 (0x100), next alarm=507 (0x1fb), currentCNT=256 (0x100) -- IRQ: COMP0=2, OF=0
threshold=0xfb, CNT=508 (0x1fc), next alarm=511 (0x1ff), currentCNT=508 (0x1fc) -- IRQ: COMP0=3, OF=0
threshold=0xfc, CNT=512 (0x200), next alarm=764 (0x2fc), currentCNT=512 (0x200) -- IRQ: COMP0=4, OF=0
threshold=0xfc, CNT=765 (0x2fd), next alarm=767 (0x2ff), currentCNT=765 (0x2fd) -- IRQ: COMP0=5, OF=0

 

Posts: 487
Registered: ‎02-04-2013

Re: BURTC missing interrupt

So looking at regSync(), the code returns before polling BURTC->SYNCBUSY because of the following if:

 

  if ((BURTC->FREEZE & BURTC_FREEZE_REGFREEZE)
      || ((BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK) != _BURTC_CTRL_CLKSEL_NONE))
  {
    return;
  }

I'm not sure if this is a typo or what, but since BURTC CLKSEL is set, the if returns before any polling occurs.  Try disabling this if in your project and see if you get the same error.

 

-Chris

Posts: 6
Registered: ‎12-30-2015

Re: BURTC missing interrupt

Hello Chris,

 

Thanks for your answer. I tried your suggestion, and indeed, the code behavior is now conform to our discussion. I changed the code snippet by this one, in em_burtc.c regSync() :

 

  /* Avoid deadlock if modifying the same register twice when freeze mode is
     activated, or when no clock is selected for the BURTC. If no clock is
     selected, then the sync is done once the clock source is set. */
  if ((BURTC->FREEZE & BURTC_FREEZE_REGFREEZE)
      || ((BURTC->CTRL & _BURTC_CTRL_CLKSEL_MASK) == _BURTC_CTRL_CLKSEL_NONE))
  {
    return;
  }

 Now, after setting BURTC->COMP0 and calling regSync(), I see that BURTC->CNT has exceeded COMP0 value when my freeze occurs.

 

This leads to two conclusions:

  1. The precaution you suggested above (set COMP0 to CNT+3 at the minimum) is the solution to my issue; having clean access to (bug free) regSync() would be a plus though.
  2. There is a nasty bug in emlib em_burtc.c regSync()

Has the bug been reported? Will it be fixed in next emlib release? When?

 

In the meantime, I will fix my local version. Thanks again for your help.

Highlighted
Posts: 487
Registered: ‎02-04-2013

Re: BURTC missing interrupt

Hi,

 

I have reported this to our software team (reference: EFM32ESS-1905).  It most likely won't get fixed in the next release in the next week or two, but hopefully by the next one.

 

Best regards,

Chris