Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #26460

Re: A timer driver for Cortex-M0+... it rarely doesn't work

From pozz <pozzugno@gmail.com>
Newsgroups comp.arch.embedded
Subject Re: A timer driver for Cortex-M0+... it rarely doesn't work
Date 2017-04-27 13:51 +0200
Organization A noiseless patient Spider
Message-ID <odslqt$ef9$1@dont-email.me> (permalink)
References <odr8bp$op3$1@dont-email.me> <ods8a4$3gr$1@dont-email.me> <odse1j$kc4$1@dont-email.me> <odsieb$30k$1@dont-email.me>

Show all headers | View raw


Il 27/04/2017 12:54, David Brown ha scritto:

> You are sure you are not calling ticks() from within another interrupt
> function?

Yes, sure.  I am very careful during writing interrupts code, even if it 
wasn't sufficient in this case.


>> ticks() are called only by TimerSet() and TimerExpired() and those two
>> functions are called only in normal background (not interrupt) code.
>> This means ticks() always runs in a lower priority than TC0 ISR.
>> Moreover, I never disable interrupts in any part of the code.
>>
>> I don't understand what do you mean with "[...] or interrupts take a few
>> cycles to work through the system". Is it possible to read a rolled-over
>> hw counter value (0x00000003) and a not incremented _ticks_high?
>
> There is always a delay between an overflow in the timer, and the actual
> interrupt function being started.  Depending on details of the chip, it
> is possible there will be several cpu cycles of the current instruction
> stream executed before the interrupt function is run.  If I remember
> rightly, the M3/M4 has a 5 stage pipeline.  When the interrupt is
> registered by the core, it effectively means that a "jump to interrupt
> vector" instruction is squeezed into the instruction stream - but these
> current 5 instructions must be completed before the interrupt call takes
> effect.  You will also have a cycle or two delay in the NVIC, and
> perhaps a cycle or two delay getting the signal out of the timer block
> (especially if the timer does not run at full core speed).  Cortex M
> interrupts are handled quickly - but not immediately.

Really?  So, the core is able to read the register of a peripheral 
(TC0->COUNT32.COUNT register, in my case) with a *new* (i.e., rolled) 
value, but the ISR hasn't run yet?  In other words, a bus access can be 
done (TC0 is on the bus) while interrupt request is pending?

If this is the case, it is a mess :-(


>> - many times you need an additional "timer running/active" flag
>>
>> The second point is more important. If you want to switch on a LED after
>> 10 minutes when a button is pressed:
>>
>> bool tmr_led_active;
>> Timer32 tmr_led;
>> void button_pressed_callback(void) {
>>    TimerSet32(&tmr_led, 10 * 60 * 1000);
>>    tmr_led_active = true;
>> }
>> void main_loop(void) {
>>   ...
>>   if (tmr_led_active && TimerExpired32(&tmr_led)) {
>>     switch_on_led();
>>     tmr_led_active = false;
>>   }
>> }
>>
>> If you don't check the timer flag, the led will switch on at random times.
>
> So you have to add a flag - big deal.  You are not using any more
> memory, your code is smaller, and it is simpler to be sure that
> everything is correct.

Yes, of course. My point here is that you have to **remember** that the 
timers you are using can roll-over at any time in the future, so they 
can change from "not expired" to "expired".

After using TimerSet32() and after the timer expires, you could expect 
it stays "expired", until you arm it again with TimerSet32(). This is 
not true, because TimerExpired32() could returns "false" at a certain 
time in the future.

This is not a problem with timers that are repetitive (armed again as 
soon as they expire), but with one-shot timers.


>> Yes, I will try your ideas.
>
> Good luck - it is not an easy task.  Don't forget to let us know the
> source of the problem, and the solution!

I configured TC0 in 16-bis mode, so now TC0_Handler() is fired every 
75ms.  I changed accordingly ticks() to create a 64-bits by shifting 
_ticks_high for 16-bits.

     if (h2 != h1) return ((uint64_t)h2 << 16) + 0;
     else          return ((uint64_t)h1 << 16) + l1;

I was lucky because I can reproduce the problem more often... and 
incredibly the problem is the opposite.

Remember my state-machine in bus.c:

   switch(bus.state) {
     case BUS_IDLE:
       if (TimerExpired(&bus.tmr_answer)) {
         /* Send new request on the bus */
         ...
         TimerSet(&bus.tmr_answer, timeout_answer);
         bus.state = BUS_WAITING_ANSWER;
       }
       break;

     case BUS_WAITING_ANSWER:
       if (TimerExpired(&bus.tmr_answer)) {
         /* No reply */
         bus.state = BUS_IDLE;
         TimerSet(&bus.tmr_answer, 0);
       } else {
         if (reply_received() == true)    {
           /* Analyze the reply */
           bus.state = BUS_IDLE;
           TimerSet(&bus.tmr_answer, 0);    [*]
         }
       }
       break;
   }

When the problem occurs (the bus blocks), bus.state is BUS_IDLE. The 
problem is with instruction [*].  That instructions should arm 
bus.tmr_answer timer such that it expires immediately and a new request 
is send on the bus (in the future, it will be simple to introduce a 
delay between the reply and the next request).

Sometimes the timer doesn't expire immediately, but after the hw counter 
roll-over again.  Indeed I see the bus blocked for 75ms.

We were thinking that _ticks_high hasn't incremented yet in task() when 
reading hw counter value.  But this would have produced an old already 
expired time, not a future not-expired time. Here the problem is with a 
wrong time in the *future*.
In other words, when the problem occurs, ticks() reads a new corrected 
and incremented value for _ticks_high, but an old not-rolled hw counter 
value.

Why this?  I think it's the usual problem with register syncronization 
in Atmel SAM devices?  Before reading or writing certain registers, you 
need to check if the peripheral is in syncing.  I don't know what this 
exactly means, but it relates to the presence of different asyncronous 
clocks.  But I use only one reference clock (an external crystal) that 
is routed to the Cortex-M core and all peripherals... so I thought 
syncronization wasn't necessary.

In this case, it seems syncronization solve my problem, so my ticks() 
function is now:

   static inline uint64_t ticks(void) {
     uint32_t h1 = volatileAccess(_ticks_high);
     TC0->COUNT32.CTRLBSET.reg =
          TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_READSYNC_Val);
     while(TC0->COUNT32.SYNCBUSY) {
     }
     uint32_t l1 = TC0->COUNT32.COUNT.reg;
     uint32_t h2 = volatileAccess(_ticks_high);
     if (h2 != h1) return ((uint64_t)h2 << 32) + 0;
     else          return ((uint64_t)h1 << 32) + l1;
   }

Datasheet says you need to write the CMD bits of TC0->CTRLB register 
with a known value (TC_CTRLBSET_CMD_READSYNC_Val), before reading COUNT 
register.  Why?  I don't know.  TC0->CTRLB is a **Write-Syncronized** 
register, i.e. the value you are writing will be really wrote after sync 
time.  Maybe the sync loop I added waits for time needed to the CTRLB 
command to be executed... otherwise the COUNT value you read immediately 
after could be wrong (IMHO!)


After this... I think my code is always affected by the original problem 
if, as you explained, TC0 interrupt is delayed when I'm reading hw 
counter in ticks().  I don't have the expertise to understand this 
possibility happens really, so it's better to find another method.

I liked the idea to use a 64-bits counter for ticks that will never 
roll-over during the entire lifetime of the device.

Back to comp.arch.embedded | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-27 00:55 +0200
  Re: A timer driver for Cortex-M0+... it rarely doesn't work Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> - 2017-04-26 23:24 +0000
    Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-27 09:32 +0200
      Re: A timer driver for Cortex-M0+... it rarely doesn't work Jack <jack4747@gmail.com> - 2017-04-27 01:15 -0700
  Re: A timer driver for Cortex-M0+... it rarely doesn't work John Speth <johnspeth@yahoo.com> - 2017-04-26 17:17 -0700
    Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-27 09:37 +0200
      Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 10:13 +0200
        Re: A timer driver for Cortex-M0+... it rarely doesn't work John Speth <johnspeth@yahoo.com> - 2017-04-27 10:24 -0700
          Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 22:04 +0200
  Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 10:01 +0200
    Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 10:27 +0200
      Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-27 11:53 +0200
        Re: A timer driver for Cortex-M0+... it rarely doesn't work Stef <stef33d@yahooI-N-V-A-L-I-D.com.invalid> - 2017-05-01 10:09 +0200
          Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-05-02 09:03 +0200
            Re: A timer driver for Cortex-M0+... it rarely doesn't work Stef <stef33d@yahooI-N-V-A-L-I-D.com.invalid> - 2017-05-04 10:06 +0200
    Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-27 11:38 +0200
      Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 12:54 +0200
        Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-27 13:51 +0200
          Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 15:39 +0200
            Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-28 09:24 +0200
              Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-28 11:05 +0200
                Re: A timer driver for Cortex-M0+... it rarely doesn't work pozz <pozzugno@gmail.com> - 2017-04-28 12:18 +0200
                Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-28 13:10 +0200
                ARM developing under Linux [was Re: A timer driver for Cortex-M0+... it rarely doesn't work] pozz <pozzugno@gmail.com> - 2017-05-02 09:14 +0200
                Re: ARM developing under Linux [was Re: A timer driver for Cortex-M0+... it rarely doesn't work] David Brown <david.brown@hesbynett.no> - 2017-05-02 11:34 +0200
                Re: ARM developing under Linux [was Re: A timer driver for Cortex-M0+... it rarely doesn't work] pozz <pozzugno@gmail.com> - 2017-05-03 09:04 +0200
                Re: ARM developing under Linux [was Re: A timer driver for Cortex-M0+... it rarely doesn't work] David Brown <david.brown@hesbynett.no> - 2017-05-03 10:59 +0200
  Re: A timer driver for Cortex-M0+... it rarely doesn't work Dimiter_Popoff <dp@tgi-sci.com> - 2017-04-27 11:02 +0300
    Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 11:06 +0200
      Re: A timer driver for Cortex-M0+... it rarely doesn't work Dimiter_Popoff <dp@tgi-sci.com> - 2017-04-27 14:20 +0300
        Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 16:16 +0200
      Re: A timer driver for Cortex-M0+... it rarely doesn't work Simon Clubley <clubley@remove_me.eisner.decus.org-Earth.UFP> - 2017-04-27 12:03 +0000
        Re: A timer driver for Cortex-M0+... it rarely doesn't work David Brown <david.brown@hesbynett.no> - 2017-04-27 16:18 +0200

csiph-web