SAMD20: again on register synchronization and bus stall

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This topic is very difficult to me and everytime I have many doubts.

 

I need to avoid bus stall in every situations. This mainly because during bus stall pending interrupt requests are blocked. In order to avoid bus stall, some registers (the registers that are tagged with Read-Synchronized and Write-Sinchronized) must be read and/or written with great care.

 

I think I understood that the problem is present even if the registers are clocked from the same source as the Main CPU. From datasheet:

This mechanism is implemented in hardware, so the synchronization process takes place even if the peripheral generic clock is running from the same clock source and on the same frequency as the bus interface.

For example, suppose I have this config: XOSC (16MHz crystal) -> Gen0 (without division, Main CPU) -> GCLK TC0 (without prescaler). TC0 and CPU runs at the same frequency.

Now, what happens when I want to read COUNT register from TC0? If I read it without taking into account the synchronization probem, the bus stall happens for a period that is 5×P_GCLK + 2×P_APB < D < 6×P_GCLK + 3×P_APB. P_GCLK and P_APB are the same, so: 7×P_GCLK < D < 9×P_GCLK. In the worst case, I loose 9 clock pulses to read a simple register from a Timer/Counter peripheral. During bus stall interrupts aren't served.

 

I can improve this prepending the reading of COUNT register with a read request and a sync loop:

TC0->COUNT32.READREQ.bit |= TC_READREQ_RREQ;

while (TC0->COUNT32.STATUS.bit.SYNCBUSY)

  ;

counter = TC0->COUNT32.COUNT.reg;

Here I transformed the bus stall period in a delay loop, when the interrupts can be served. However the 9 clock pulses are always there.

 

 

Now imagine the following classical situation:

OSC32K (32.768kHz) -> GCLK Gen 1 (32.768kHz) -> DFLL48M (48MHz) -> GCLK Gen 0 (48MHz, Main CPU)

OSC32K (32.768kHz) -> GCLK Gen 2 div 32 (1.024kHz) -> RTC (prescaler 1024, 1Hz)

RTC is configured as a 32-bits counter with a counting frequency of 1Hz. I use it as the date/time for the application (the number of seconds from an epoch).

 

What happens when the CPU wants to read the current time, so reading RTC->MODE0.COUNT.reg? I need to wait for sync delay that, in the worst case, is 6×P_GCLK + 3×P_APB. P_GCLK=1.024kHz and P_APB=48MHz so sync maximum delay is about 6ms!!!

Of course, with read request and sync loop I could serve interrupts, but I can't block my application for 6ms only to read a simple register!!

 

I read about continuous synchronization and I tried to implement, without success. 

 

static inline void rtc_init(void)

{

  GCLK->CLKCTRL.reg = 

            GCLK_CLKCTRL_ID_RTC | 

            GCLK_CLKCTRL_GEN_GCLK2 | 

            GCLK_CLKCTRL_CLKEN;

  while(RTC->MODE0.STATUS.bit.SYNCBUSY)

    ;

  RTC->MODE0.CTRL.reg =

      RTC_MODE0_CTRL_MODE_COUNT32 |           // Mode 0 = 32-bits counter

      RTC_MODE0_CTRL_PRESCALER_DIV1024;       // Divisor = 1024

  RTC->MODE0.READREQ.reg |=

      RTC_READREQ_RCONT;                 // Enable continuous synch

  while(RTC->MODE0.STATUS.bit.SYNCBUSY)
      ;

  RTC->MODE0.CTRL.reg |=

      RTC_MODE0_CTRL_ENABLE;             // Enable peripheral

}

 

static inline uint32_t rtc_get_clock(void)
{
    bsp_spare_on();
    uint32_t ret = RTC->MODE0.COUNT.reg;
    bsp_spare_off();
    return ret;
}

On the oscilloscope on spare pin, I always see a pulse of about 6ms, even if the RCONT bit is set. An it seems the bust stall happens. Why? I tried with example project from ASF (RTC_QUICK_START_COUNT_POLLED) and it seems the bus stall always happens.

It appears to me that READREQ functionality doesn't work as expected.

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pozzugno wrote:

It appears to me that READREQ functionality doesn't work as expected.

 

I think I've found the solution, after reading the faq "What is the sequence to be followed to use the RCONT bit of RTC in SAM D device with out CPU stalls and how to prevent the CPU stalls?" on Microchip website (I'm sorry, but I can't copy the full link).

 

I missed the read request issue for the first time, after setting RCONT.

 

I also need to write the COUNT register (because the user changes the current date/time), so I think I have to enable again RCONT and issue again READREQ.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have another problem with RTC.

 

When I need to set another COUNT value, I can use RTC->MODE0.COUNT.reg = newvalue. However this instruction could take too much time (I imagine there's a bus stall) and I don't know how to avoid this delay.

 

I can't face a situation where an instruction takes 6ms!!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you considered using a smaller prescaler for the clock and increasing the compare match value, to achieve the same interrupt timing while decreasing the possible sync delay? It seems it is impossible to completely avoid the sync delay due to hardware design and register size, but perhaps at least you can reduce the application downtime.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use the SYNCRDY interrupt:

 

Bit 6 – SYNCRDY Synchronization Ready
This flag is cleared by writing a one to the flag.
This flag is set on a 1-to-0 transition of the Synchronization Busy bit in the Status register
(STATUS.SYNCBUSY), except when caused by enable or software reset, and an interrupt request will be
generated if INTENCLR/SET.SYNCRDY is one.
Writing a zero to this bit has no effect.
Writing a one to this bit clears the Synchronization Ready interrupt flag.

 

 

You can read/write the registers you need in the interrupt handler.

 

Further information to sync can be found in chapter 13 (clock system) of the datasheet.

(You can also find information about continues read request there, see 13.3.1.5)

 

Another way that would work is to use a samd21 that has a dma controller which could do all the stuff for you.

 

 

 

Flo1991