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


Groups > comp.arch.embedded > #29293 > unrolled thread

Cortex-M: share an int between two tasks

Started bypozz <pozzugno@gmail.com>
First post2020-03-13 15:38 +0100
Last post2020-03-18 16:24 +0000
Articles 20 on this page of 49 — 12 participants

Back to article view | Back to comp.arch.embedded


Contents

  Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-13 15:38 +0100
    Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-13 17:49 +0100
      Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-18 09:09 +0100
        Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 09:17 +0100
        Re: Cortex-M: share an int between two tasks Hans-Bernhard Bröker <HBBroeker@t-online.de> - 2020-03-18 17:37 +0100
          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 19:23 +0100
      Re: Cortex-M: share an int between two tasks Les Cargill <lcargill99@comcast.com> - 2020-03-22 13:42 -0500
        Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-22 21:45 +0100
        Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-22 20:50 -0400
          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-23 09:09 +0100
            Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-23 08:00 -0400
              Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-23 14:21 +0100
                Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-03-25 09:55 +1100
                  Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-30 16:07 +0200
                    Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-03-31 09:02 +1100
                      Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-31 10:57 +0200
                        Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-04-01 12:05 +1100
                  Re: Cortex-M: share an int between two tasks Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2020-03-30 17:49 +0300
                  Re: Cortex-M: share an int between two tasks kegs@provalid.com (Kent Dickey) - 2020-05-19 00:34 -0500
                    Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-05-19 17:40 +1000
                    Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-05-19 12:00 +0200
                      Re: Cortex-M: share an int between two tasks Tom Gardner <spamjunk@blueyonder.co.uk> - 2020-05-19 13:48 +0100
    Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-18 09:20 +0100
      Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 10:06 +0100
        Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-18 10:59 +0100
          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 11:48 +0100
            Re: Cortex-M: share an int between two tasks upsidedown@downunder.com - 2020-03-18 20:37 +0200
              Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 21:24 +0100
                Re: Cortex-M: share an int between two tasks Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2020-03-19 14:05 +0200
                  Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 13:16 +0100
                    Re: Cortex-M: share an int between two tasks Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2020-03-19 14:27 +0200
                      Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 14:13 +0100
              Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 08:03 +0100
                Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 09:10 +0100
                Re: Cortex-M: share an int between two tasks upsidedown@downunder.com - 2020-03-19 12:51 +0200
            Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 07:40 +0100
              Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 09:38 +0100
                Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 11:26 +0100
                  Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 12:12 +0100
                    Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 15:31 +0100
                      Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-23 09:22 +0100
                      Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-23 08:10 -0400
                        Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-23 13:47 +0100
                          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-23 14:31 +0100
                          Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-23 13:50 -0400
              Re: Cortex-M: share an int between two tasks Niklas Holsti <niklas.holsti@tidorum.invalid> - 2020-03-19 12:27 +0200
                Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 11:45 +0100
                  Re: Cortex-M: share an int between two tasks upsidedown@downunder.com - 2020-03-19 13:27 +0200
      Re: Cortex-M: share an int between two tasks antispam@math.uni.wroc.pl - 2020-03-18 16:24 +0000

Page 1 of 3  [1] 2 3  Next page →


#29293 — Cortex-M: share an int between two tasks

Frompozz <pozzugno@gmail.com>
Date2020-03-13 15:38 +0100
SubjectCortex-M: share an int between two tasks
Message-ID<r4g5sc$8jr$1@dont-email.me>
I'm working on a Cortex-M4 MCU and using FreeRTOS.

One task:

uint32_t wait_period;
while(1) {
   // make some things
   vTaskDelay(pdMS_TO_TICKS(wait_period));
}

The following function can be called from another task:

void set_waiting_period(uint32_t new_period) {
   wait_period = new_period;
}


In this case, is it needed to protect the access of the shared variable 
wait_period with a mutex? I don't think, we are dealing with an integer 
variable that should be accessed (reading and writing) atomically.

[toc] | [next] | [standalone]


#29294

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-13 17:49 +0100
Message-ID<r4gdi2$tn$1@dont-email.me>
In reply to#29293
On 13/03/2020 15:38, pozz wrote:
> I'm working on a Cortex-M4 MCU and using FreeRTOS.
> 
> One task:
> 
> uint32_t wait_period;
> while(1) {
>   // make some things
>   vTaskDelay(pdMS_TO_TICKS(wait_period));
> }
> 
> The following function can be called from another task:
> 
> void set_waiting_period(uint32_t new_period) {
>   wait_period = new_period;
> }
> 
> 
> In this case, is it needed to protect the access of the shared variable
> wait_period with a mutex? I don't think, we are dealing with an integer
> variable that should be accessed (reading and writing) atomically.
> 

You don't need to use a mutex, as you won't have race conditions when
accessing it (race conditions happen when at least one thread can be
writing, and at least one thread reading, and the accesses are not
atomic).  However, you /do/ need to make the accesses volatile, or there
will be no guarantee that the variable will be read or written in the
spots where you have accessed it.

[toc] | [prev] | [next] | [standalone]


#29295

Frompozz <pozzugno@gmail.com>
Date2020-03-18 09:09 +0100
Message-ID<r4skvb$tkq$1@dont-email.me>
In reply to#29294
Il 13/03/2020 17:49, David Brown ha scritto:
> On 13/03/2020 15:38, pozz wrote:
>> I'm working on a Cortex-M4 MCU and using FreeRTOS.
>>
>> One task:
>>
>> uint32_t wait_period;
>> while(1) {
>>    // make some things
>>    vTaskDelay(pdMS_TO_TICKS(wait_period));
>> }
>>
>> The following function can be called from another task:
>>
>> void set_waiting_period(uint32_t new_period) {
>>    wait_period = new_period;
>> }
>>
>>
>> In this case, is it needed to protect the access of the shared variable
>> wait_period with a mutex? I don't think, we are dealing with an integer
>> variable that should be accessed (reading and writing) atomically.
>>
> 
> You don't need to use a mutex, as you won't have race conditions when
> accessing it (race conditions happen when at least one thread can be
> writing, and at least one thread reading, and the accesses are not
> atomic).  

Ok, I got the point.


> However, you /do/ need to make the accesses volatile, or there
> will be no guarantee that the variable will be read or written in the
> spots where you have accessed it.

Let me explain just to be sure I got it.

If wait_period is not volatile, in the task endless loop, the compiler 
could make the assumption the variable doesn't ever change, so for 
example it could "cache" its value in a register. In this case, when 
set_waiting_period() is called from another task, the variable in RAM is 
changed, but the value in the register doesn't change, so the task will 
continue using the old value.

I think the access could be volatile only in the loop and not in 
set_waiting_period:

uint32_t wait_period;
while(1) {
     // make some things
     volatile uint32_t *vwait_period = (volatile uint32_t *)&wait_period;
     vTaskDelay(pdMS_TO_TICKS(*vwait_period));
  }

void set_waiting_period(uint32_t new_period) {
    wait_period = new_period;
}

[toc] | [prev] | [next] | [standalone]


#29296

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-18 09:17 +0100
Message-ID<r4slfn$i3$1@dont-email.me>
In reply to#29295
On 18/03/2020 09:09, pozz wrote:
> Il 13/03/2020 17:49, David Brown ha scritto:
>> On 13/03/2020 15:38, pozz wrote:
>>> I'm working on a Cortex-M4 MCU and using FreeRTOS.
>>>
>>> One task:
>>>
>>> uint32_t wait_period;
>>> while(1) {
>>>    // make some things
>>>    vTaskDelay(pdMS_TO_TICKS(wait_period));
>>> }
>>>
>>> The following function can be called from another task:
>>>
>>> void set_waiting_period(uint32_t new_period) {
>>>    wait_period = new_period;
>>> }
>>>
>>>
>>> In this case, is it needed to protect the access of the shared variable
>>> wait_period with a mutex? I don't think, we are dealing with an integer
>>> variable that should be accessed (reading and writing) atomically.
>>>
>>
>> You don't need to use a mutex, as you won't have race conditions when
>> accessing it (race conditions happen when at least one thread can be
>> writing, and at least one thread reading, and the accesses are not
>> atomic). 
> 
> Ok, I got the point.
> 
> 
>> However, you /do/ need to make the accesses volatile, or there
>> will be no guarantee that the variable will be read or written in the
>> spots where you have accessed it.
> 
> Let me explain just to be sure I got it.
> 
> If wait_period is not volatile, in the task endless loop, the compiler 
> could make the assumption the variable doesn't ever change, so for 
> example it could "cache" its value in a register. In this case, when 
> set_waiting_period() is called from another task, the variable in RAM is 
> changed, but the value in the register doesn't change, so the task will 
> continue using the old value.

Exactly right.

(The same can work when writing variables in a loop - the compiler knows 
that it has full control of any normal variables it is using, so there 
is no need to write out the variable to ram until later, if that would 
give more efficient code.)

> 
> I think the access could be volatile only in the loop and not in 
> set_waiting_period:
> 
> uint32_t wait_period;
> while(1) {
>      // make some things
>      volatile uint32_t *vwait_period = (volatile uint32_t *)&wait_period;
>      vTaskDelay(pdMS_TO_TICKS(*vwait_period));
>   }
> 
> void set_waiting_period(uint32_t new_period) {
>     wait_period = new_period;
> }

Bad idea - get in the habit of making such writes volatile too.  If the 
"set_waiting_period" function is compiled and linked separately, then 
the compiler will have to generate the write instruction before the 
return.  But if it has access to the definition (because it is in the 
same file as the one using it, or you have link-time optimisation), and 
you write something like:

	set_waiting_period(100);
	// more code
	set_waiting_period(1000);

then the compiler can skip the first write to "wait_period" if it knows 
"more code" does not use it.

Most problems with missing volatiles are seen from a failure to do 
volatile reads in a loop.  The second most common failure (IME) is 
assuming that volatile accesses place an order on non-volatile accesses 
(they don't).  But failure to use volatile on writes occurs too, and is 
often more subtle, with code working fine until you change something 
else that appears independent.

[toc] | [prev] | [next] | [standalone]


#29302

FromHans-Bernhard Bröker <HBBroeker@t-online.de>
Date2020-03-18 17:37 +0100
Message-ID<hdf12vFmjf4U1@mid.dfncis.de>
In reply to#29295
Am 18.03.2020 um 09:09 schrieb pozz:

> I think the access could be volatile only in the loop and not in 
> set_waiting_period:

You could do that, but you absolutely should not.

You make the _variable_ volatile, and that's that.

>      volatile uint32_t *vwait_period = (volatile uint32_t *)&wait_period;

Just looking at that line should tell you that this cannot possibly be 
the right way to do anything.

The primary rule about explicitly casting pointers in C code is: don't 
do it.  All the evidently safe casts are already performed implicitly by 
the compiler.  The perceived need for less safe ones almost invariably 
results from an incorrect design decision a few steps earlier.

[toc] | [prev] | [next] | [standalone]


#29303

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-18 19:23 +0100
Message-ID<r4tova$fp9$1@dont-email.me>
In reply to#29302
On 18/03/2020 17:37, Hans-Bernhard Bröker wrote:
> Am 18.03.2020 um 09:09 schrieb pozz:
> 
>> I think the access could be volatile only in the loop and not in
>> set_waiting_period:
> 
> You could do that, but you absolutely should not.

I disagree.  I think it is vital to remember that it is /accesses/ that
are volatile, not variables.  Declaring a variable volatile simply means
that all accesses to it must be volatile (and don't need anything extra
to make them volatile).

Often, you /do/ want all accesses to such a variable to be volatile, and
thus declaring it as volatile is useful.  And you can't make correct
code wrong by having an extra volatile - at worst, you might loose a
little efficiency.

> 
> You make the _variable_ volatile, and that's that.
> 
>>      volatile uint32_t *vwait_period = (volatile uint32_t *)&wait_period;
> 
> Just looking at that line should tell you that this cannot possibly be
> the right way to do anything.

True - the cast should be hidden inside a macro like "ACCESS_ONCE".

> 
> The primary rule about explicitly casting pointers in C code is: don't
> do it.  All the evidently safe casts are already performed implicitly by
> the compiler.  The perceived need for less safe ones almost invariably
> results from an incorrect design decision a few steps earlier.

Not entirely true.

Certainly quite a few casts that people use are not safe.  In
particular, it is, in general, /not/ safe to access objects as a
different type just using a cast.  But there are exceptions - and adding
a "volatile" in a cast is one of them.  Casts of pointer types should
definitely be treated with caution, but that does not mean they are
always wrong.

(The C standard has been unclear about the meaning of using a
cast-to-volatile like this.  However, all known C implementations, and
all members of the C standards committee, have been happy that a
cast-to-volatile-pointer does what you expect, at least for simple read
or write accesses.  And this is now codified in C18.)

[toc] | [prev] | [next] | [standalone]


#29330

FromLes Cargill <lcargill99@comcast.com>
Date2020-03-22 13:42 -0500
Message-ID<r58be8$nhk$1@dont-email.me>
In reply to#29294
David Brown wrote:
> On 13/03/2020 15:38, pozz wrote:
>> I'm working on a Cortex-M4 MCU and using FreeRTOS.
>>
>> One task:
>>
>> uint32_t wait_period;
>> while(1) {
>>    // make some things
>>    vTaskDelay(pdMS_TO_TICKS(wait_period));
>> }
>>
>> The following function can be called from another task:
>>
>> void set_waiting_period(uint32_t new_period) {
>>    wait_period = new_period;
>> }
>>
>>
>> In this case, is it needed to protect the access of the shared variable
>> wait_period with a mutex? I don't think, we are dealing with an integer
>> variable that should be accessed (reading and writing) atomically.
>>
> 
> You don't need to use a mutex, as you won't have race conditions when
> accessing it (race conditions happen when at least one thread can be
> writing, and at least one thread reading, and the accesses are not
> atomic).  However, you /do/ need to make the accesses volatile, or there
> will be no guarantee that the variable will be read or written in the
> spots where you have accessed it.
> 

Small digression: ( out of ignorance of the M3 ) - There is nothing you
have to do with an MMU or other caching furniture to guarantee
serialization of access? So "volatile" is known to be sufficient?

I'd be tempted to put a mutex on an measure the cost because race
conditions are quite ... challenging to test for.

-- 
Les Cargill

[toc] | [prev] | [next] | [standalone]


#29331

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-22 21:45 +0100
Message-ID<r58iok$ee8$1@dont-email.me>
In reply to#29330
On 22/03/2020 19:42, Les Cargill wrote:
> David Brown wrote:
>> On 13/03/2020 15:38, pozz wrote:
>>> I'm working on a Cortex-M4 MCU and using FreeRTOS.
>>>
>>> One task:
>>>
>>> uint32_t wait_period;
>>> while(1) {
>>>    // make some things
>>>    vTaskDelay(pdMS_TO_TICKS(wait_period));
>>> }
>>>
>>> The following function can be called from another task:
>>>
>>> void set_waiting_period(uint32_t new_period) {
>>>    wait_period = new_period;
>>> }
>>>
>>>
>>> In this case, is it needed to protect the access of the shared variable
>>> wait_period with a mutex? I don't think, we are dealing with an integer
>>> variable that should be accessed (reading and writing) atomically.
>>>
>>
>> You don't need to use a mutex, as you won't have race conditions when
>> accessing it (race conditions happen when at least one thread can be
>> writing, and at least one thread reading, and the accesses are not
>> atomic).  However, you /do/ need to make the accesses volatile, or there
>> will be no guarantee that the variable will be read or written in the
>> spots where you have accessed it.
>>
> 
> Small digression: ( out of ignorance of the M3 ) - There is nothing you
> have to do with an MMU or other caching furniture to guarantee
> serialization of access? So "volatile" is known to be sufficient?

Yes, volatile is sufficient here.  Almost all processors have a serial 
processing model - that is, no matter how much super-scaling and 
out-of-order execution you have (the M3 does not have either, but the M7 
has super-scaling and can often do two instructions per cycle), the 
result is as though instructions execute fully in program order.  Writes 
can end up re-ordered before they hit memory, due to caches or write 
buffers, but this is invisible to the code.

You need data ordering instructions (like "dsb" or "dmb" on the 
Cortex-M) or MPU memory regions (such as non-cacheable areas) when the 
data is accessed by something other than the processor that reads and 
writes it.  So if you are writing data to a buffer that will be read by 
DMA, or you have a dual processor system (as found in some Cortex-M 
devices), or an SMP system (like Cortex-A devices) it's a different 
matter - you need data synchronisation.

Also remember that volatile accesses do not synchronise with 
non-volatile accesses.  You can't do some non-volatile writes to a 
buffer, then set a volatile flag and think the writes will always come 
before the flag.

> 
> I'd be tempted to put a mutex on an measure the cost because race
> conditions are quite ... challenging to test for.
> 

You avoid race conditions by design, not testing!

[toc] | [prev] | [next] | [standalone]


#29332

FromRichard Damon <Richard@Damon-Family.org>
Date2020-03-22 20:50 -0400
Message-ID<_ITdG.1110033$Gh7.201179@fx45.iad>
In reply to#29330
On 3/22/20 2:42 PM, Les Cargill wrote:
> 
> Small digression: ( out of ignorance of the M3 ) - There is nothing you
> have to do with an MMU or other caching furniture to guarantee
> serialization of access? So "volatile" is known to be sufficient?
> 
> I'd be tempted to put a mutex on an measure the cost because race
> conditions are quite ... challenging to test for.

The key point is that things are different for a
single-processor/single-master system, then when you have multiple
processors/bus masters. A single processor system that didn't keep
accesses in order at least to its own view would be seriously broken. At
worse there could be a few cycles of delay slots that the compiler would
keep track of that needed to be observed between a write and a read.

The one catch with a single processor system is if it has something like
DMA, then the memory accessed by the DMA needs the protections like a
multiprocessor system.

The volatile system was invented back at a time when for most machines,
it was sufficient for most types of access.

[toc] | [prev] | [next] | [standalone]


#29333

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-23 09:09 +0100
Message-ID<r59qsb$ll2$1@dont-email.me>
In reply to#29332
On 23/03/2020 01:50, Richard Damon wrote:
> On 3/22/20 2:42 PM, Les Cargill wrote:
>>
>> Small digression: ( out of ignorance of the M3 ) - There is nothing you
>> have to do with an MMU or other caching furniture to guarantee
>> serialization of access? So "volatile" is known to be sufficient?
>>
>> I'd be tempted to put a mutex on an measure the cost because race
>> conditions are quite ... challenging to test for.
> 
> The key point is that things are different for a
> single-processor/single-master system, then when you have multiple
> processors/bus masters. A single processor system that didn't keep
> accesses in order at least to its own view would be seriously broken. At
> worse there could be a few cycles of delay slots that the compiler would
> keep track of that needed to be observed between a write and a read.
> 
> The one catch with a single processor system is if it has something like
> DMA, then the memory accessed by the DMA needs the protections like a
> multiprocessor system.
> 
> The volatile system was invented back at a time when for most machines,
> it was sufficient for most types of access.
> 

It's also worth noting that even though "volatile" is not sufficient for 
bigger systems (multiple bus masters, like multiple cores or DMA), it is 
generally still /necessary/ along with the memory barriers, cache 
flushes, synchronisation instructions, locks, etc.

Sometimes with modern C or C++ you can use atomic types rather than 
volatile, since the atomic types are effectively volatile in themselves 
(as well as having other properties and synchronisation features).

And of course compiler-specific features can replace the need of 
volatile (such as using a gcc asm memory clobber).

[toc] | [prev] | [next] | [standalone]


#29335

FromRichard Damon <Richard@Damon-Family.org>
Date2020-03-23 08:00 -0400
Message-ID<Ax1eG.523780$ls7.59106@fx40.iad>
In reply to#29333
On 3/23/20 4:09 AM, David Brown wrote:
> On 23/03/2020 01:50, Richard Damon wrote:
>> On 3/22/20 2:42 PM, Les Cargill wrote:
>>>
>>> Small digression: ( out of ignorance of the M3 ) - There is nothing you
>>> have to do with an MMU or other caching furniture to guarantee
>>> serialization of access? So "volatile" is known to be sufficient?
>>>
>>> I'd be tempted to put a mutex on an measure the cost because race
>>> conditions are quite ... challenging to test for.
>>
>> The key point is that things are different for a
>> single-processor/single-master system, then when you have multiple
>> processors/bus masters. A single processor system that didn't keep
>> accesses in order at least to its own view would be seriously broken. At
>> worse there could be a few cycles of delay slots that the compiler would
>> keep track of that needed to be observed between a write and a read.
>>
>> The one catch with a single processor system is if it has something like
>> DMA, then the memory accessed by the DMA needs the protections like a
>> multiprocessor system.
>>
>> The volatile system was invented back at a time when for most machines,
>> it was sufficient for most types of access.
>>
> 
> It's also worth noting that even though "volatile" is not sufficient for
> bigger systems (multiple bus masters, like multiple cores or DMA), it is
> generally still /necessary/ along with the memory barriers, cache
> flushes, synchronisation instructions, locks, etc.
> 
> Sometimes with modern C or C++ you can use atomic types rather than
> volatile, since the atomic types are effectively volatile in themselves
> (as well as having other properties and synchronisation features).
> 
> And of course compiler-specific features can replace the need of
> volatile (such as using a gcc asm memory clobber).
> 

I will admit that it isn't my area of expertise, as most of my
programming is done on machines where volatile is sufficient for most of
the needs, but I remember arguments that for the big machines volatile
wasn't needed at all, as you needed a barrier that said every thing that
happened before this will be done before anything that happens after
this, and with such a barrier volatile isn't needed.

It may well be, and I think it is, that the Standards adopted weaker
barriers (that are perhaps significantly cheaper to perform) and with
those weaker barriers we still have a need for volatile.

The one key thing that I remember is that the compiler needs to
understand these barriers, as it needs to avoid moving accesses accross
them.

[toc] | [prev] | [next] | [standalone]


#29338

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-23 14:21 +0100
Message-ID<r5ad5k$574$1@dont-email.me>
In reply to#29335
On 23/03/2020 13:00, Richard Damon wrote:
> On 3/23/20 4:09 AM, David Brown wrote:
>> On 23/03/2020 01:50, Richard Damon wrote:
>>> On 3/22/20 2:42 PM, Les Cargill wrote:
>>>>
>>>> Small digression: ( out of ignorance of the M3 ) - There is nothing you
>>>> have to do with an MMU or other caching furniture to guarantee
>>>> serialization of access? So "volatile" is known to be sufficient?
>>>>
>>>> I'd be tempted to put a mutex on an measure the cost because race
>>>> conditions are quite ... challenging to test for.
>>>
>>> The key point is that things are different for a
>>> single-processor/single-master system, then when you have multiple
>>> processors/bus masters. A single processor system that didn't keep
>>> accesses in order at least to its own view would be seriously broken. At
>>> worse there could be a few cycles of delay slots that the compiler would
>>> keep track of that needed to be observed between a write and a read.
>>>
>>> The one catch with a single processor system is if it has something like
>>> DMA, then the memory accessed by the DMA needs the protections like a
>>> multiprocessor system.
>>>
>>> The volatile system was invented back at a time when for most machines,
>>> it was sufficient for most types of access.
>>>
>>
>> It's also worth noting that even though "volatile" is not sufficient for
>> bigger systems (multiple bus masters, like multiple cores or DMA), it is
>> generally still /necessary/ along with the memory barriers, cache
>> flushes, synchronisation instructions, locks, etc.
>>
>> Sometimes with modern C or C++ you can use atomic types rather than
>> volatile, since the atomic types are effectively volatile in themselves
>> (as well as having other properties and synchronisation features).
>>
>> And of course compiler-specific features can replace the need of
>> volatile (such as using a gcc asm memory clobber).
>>
> 
> I will admit that it isn't my area of expertise, as most of my
> programming is done on machines where volatile is sufficient for most of
> the needs, but I remember arguments that for the big machines volatile
> wasn't needed at all, as you needed a barrier that said every thing that
> happened before this will be done before anything that happens after
> this, and with such a barrier volatile isn't needed.

Yes - if you have such a barrier, volatile is generally not needed (that 
was one of the exceptions I gave).  But the barrier needs to be defined 
correctly.

If you write:
	
	uint64_t data;
	bool ready_flag;

	data = 123;
	asm ("dmb");
	ready_flag = true;

then the "dmb" instruction will form a memory barrier - all previously 
encountered memory operations are completed before the "dmb" happens, 
and none of the following memory operations are started.  But there is 
nothing stopping the compiler re-arranging the code like:

	ready_flag = true;
	asm ("dmb");
	data = 123;

Instructions like "dmb" force an order on the cpu operations, not the 
compiler - while "volatile" enforces a partial order on the compiler, 
but not the hardware.

The standard solution would be asm("dmb" ::: "memory"), where the memory 
clobber forces an ordering on the compiler - and thus you can (usually) 
omit the "volatile".


> 
> It may well be, and I think it is, that the Standards adopted weaker
> barriers (that are perhaps significantly cheaper to perform) and with
> those weaker barriers we still have a need for volatile.

The C11 and C++11 atomics have a number of barriers - some stronger, 
some weaker in various senses.  Prior to C11 and C++11, there were no 
standard barriers at all.

> 
> The one key thing that I remember is that the compiler needs to
> understand these barriers, as it needs to avoid moving accesses accross
> them.
> 

Yes.

[toc] | [prev] | [next] | [standalone]


#29342

FromClifford Heath <no.spam@please.net>
Date2020-03-25 09:55 +1100
Message-ID<IdweG.1169345$Gh7.768707@fx45.iad>
In reply to#29338
On 24/3/20 12:21 am, David Brown wrote:
> Instructions like "dmb" force an order on the cpu operations, not the 
> compiler - while "volatile" enforces a partial order on the compiler, 
> but not the hardware.
> 
> The standard solution would be asm("dmb" ::: "memory"), where the memory 
> clobber forces an ordering on the compiler - and thus you can (usually) 
> omit the "volatile".
David, if you have time, many of us would appreciate a quick summary of 
the situations where the different barriers should be used.

"dmb" can be a full read/write barrier, or just a write barrier. When to 
use each?

"dsb" is different from "dmb" because it limits instruction ordering, 
but when is that useful? When to use it as a write-only barrier?

"isb" is intended to precede a context switch, i.e. task switching in an 
RTOS. Is it sufficient for that, and is that the only time to use it?

If you know a good article that gives practical guidelines, post a link, 
otherwise I'd really like to hear your thoughts.

Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#29347

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-30 16:07 +0200
Message-ID<r5suft$3e9$1@dont-email.me>
In reply to#29342
On 24/03/2020 23:55, Clifford Heath wrote:
> On 24/3/20 12:21 am, David Brown wrote:
>> Instructions like "dmb" force an order on the cpu operations, not the 
>> compiler - while "volatile" enforces a partial order on the compiler, 
>> but not the hardware.
>>
>> The standard solution would be asm("dmb" ::: "memory"), where the 
>> memory clobber forces an ordering on the compiler - and thus you can 
>> (usually) omit the "volatile".
> David, if you have time, many of us would appreciate a quick summary of 
> the situations where the different barriers should be used.
> 
> "dmb" can be a full read/write barrier, or just a write barrier. When to 
> use each?
> 
> "dsb" is different from "dmb" because it limits instruction ordering, 
> but when is that useful? When to use it as a write-only barrier?
> 
> "isb" is intended to precede a context switch, i.e. task switching in an 
> RTOS. Is it sufficient for that, and is that the only time to use it?
> 
> If you know a good article that gives practical guidelines, post a link, 
> otherwise I'd really like to hear your thoughts.
> 
> Clifford Heath.

[toc] | [prev] | [next] | [standalone]


#29351

FromClifford Heath <no.spam@please.net>
Date2020-03-31 09:02 +1100
Message-ID<u%tgG.312649$kM2.20698@fx02.iad>
In reply to#29347
On 31/3/20 1:07 am, David Brown wrote:
> On 24/03/2020 23:55, Clifford Heath wrote:
>> On 24/3/20 12:21 am, David Brown wrote:
>>> Instructions like "dmb" force an order on the cpu operations, not the 
>>> compiler - while "volatile" enforces a partial order on the compiler, 
>>> but not the hardware.
>>>
>>> The standard solution would be asm("dmb" ::: "memory"), where the 
>>> memory clobber forces an ordering on the compiler - and thus you can 
>>> (usually) omit the "volatile".
>> David, if you have time, many of us would appreciate a quick summary 
>> of the situations where the different barriers should be used.
>>
>> "dmb" can be a full read/write barrier, or just a write barrier. When 
>> to use each?
>>
>> "dsb" is different from "dmb" because it limits instruction ordering, 
>> but when is that useful? When to use it as a write-only barrier?
>>
>> "isb" is intended to precede a context switch, i.e. task switching in 
>> an RTOS. Is it sufficient for that, and is that the only time to use it?
>>
>> If you know a good article that gives practical guidelines, post a 
>> link, otherwise I'd really like to hear your thoughts.
>>
>> Clifford Heath.
> 

I was excited to open your response, then disappointed.
Forget to hit Save?

CH

[toc] | [prev] | [next] | [standalone]


#29353

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-31 10:57 +0200
Message-ID<r5v0lj$mhg$2@dont-email.me>
In reply to#29351
On 31/03/2020 00:02, Clifford Heath wrote:
> On 31/3/20 1:07 am, David Brown wrote:
>> On 24/03/2020 23:55, Clifford Heath wrote:
>>> On 24/3/20 12:21 am, David Brown wrote:
>>>> Instructions like "dmb" force an order on the cpu operations, not 
>>>> the compiler - while "volatile" enforces a partial order on the 
>>>> compiler, but not the hardware.
>>>>
>>>> The standard solution would be asm("dmb" ::: "memory"), where the 
>>>> memory clobber forces an ordering on the compiler - and thus you can 
>>>> (usually) omit the "volatile".
>>> David, if you have time, many of us would appreciate a quick summary 
>>> of the situations where the different barriers should be used.
>>>
>>> "dmb" can be a full read/write barrier, or just a write barrier. When 
>>> to use each?
>>>
>>> "dsb" is different from "dmb" because it limits instruction ordering, 
>>> but when is that useful? When to use it as a write-only barrier?
>>>
>>> "isb" is intended to precede a context switch, i.e. task switching in 
>>> an RTOS. Is it sufficient for that, and is that the only time to use it?
>>>
>>> If you know a good article that gives practical guidelines, post a 
>>> link, otherwise I'd really like to hear your thoughts.
>>>
>>> Clifford Heath.
>>
> 
> I was excited to open your response, then disappointed.
> Forget to hit Save?
> 

No, I just haven't had the time to do a decent reply yet.  It is still 
an open draft on my desktop.  I haven't forgotten about it - I've just 
had too many other things to do.

[toc] | [prev] | [next] | [standalone]


#29356

FromClifford Heath <no.spam@please.net>
Date2020-04-01 12:05 +1100
Message-ID<cNRgG.135300$MZ3.78753@fx22.iad>
In reply to#29353
On 31/3/20 7:57 pm, David Brown wrote:
> On 31/03/2020 00:02, Clifford Heath wrote:
>> On 31/3/20 1:07 am, David Brown wrote:
>>> On 24/03/2020 23:55, Clifford Heath wrote:
>>>> David, if you have time, many of us would appreciate a quick summary 
>>>> of the situations where the different barriers should be used.
>>>>
>>>> "dmb" can be a full read/write barrier, or just a write barrier. 
>>>> When to use each?
>>>>
>>>> "dsb" is different from "dmb" because it limits instruction 
>>>> ordering, but when is that useful? When to use it as a write-only 
>>>> barrier?
>>>>
>>>> "isb" is intended to precede a context switch, i.e. task switching 
>>>> in an RTOS. Is it sufficient for that, and is that the only time to 
>>>> use it?
>>>>
>>>> If you know a good article that gives practical guidelines, post a 
>>>> link, otherwise I'd really like to hear your thoughts.
>> I was excited to open your response, then disappointed.
>> Forget to hit Save?
> 
> No, I just haven't had the time to do a decent reply yet.  It is still 
> an open draft on my desktop.  I haven't forgotten about it - I've just 
> had too many other things to do.

No worries. I look forward to it.

CH

[toc] | [prev] | [next] | [standalone]


#29348

FromTauno Voipio <tauno.voipio@notused.fi.invalid>
Date2020-03-30 17:49 +0300
Message-ID<r5t0tf$kae$1@dont-email.me>
In reply to#29342
On 25.3.20 00:55, Clifford Heath wrote:
> On 24/3/20 12:21 am, David Brown wrote:
>> Instructions like "dmb" force an order on the cpu operations, not the 
>> compiler - while "volatile" enforces a partial order on the compiler, 
>> but not the hardware.
>>
>> The standard solution would be asm("dmb" ::: "memory"), where the 
>> memory clobber forces an ordering on the compiler - and thus you can 
>> (usually) omit the "volatile".
> David, if you have time, many of us would appreciate a quick summary of 
> the situations where the different barriers should be used.
> 
> "dmb" can be a full read/write barrier, or just a write barrier. When to 
> use each?
> 
> "dsb" is different from "dmb" because it limits instruction ordering, 
> but when is that useful? When to use it as a write-only barrier?
> 
> "isb" is intended to precede a context switch, i.e. task switching in an 
> RTOS. Is it sufficient for that, and is that the only time to use it?
> 
> If you know a good article that gives practical guidelines, post a link, 
> otherwise I'd really like to hear your thoughts.
> 
> Clifford Heath.


ARM has an application note (DAI-0321A) explaining the barriers,
see 
<https://developer.arm.com/docs/103490176/10/does-the-cortex-m3-or-cortex-m4-processor-need-memory-barrier-instructions>.

-- 

-TV

[toc] | [prev] | [next] | [standalone]


#29671

Fromkegs@provalid.com (Kent Dickey)
Date2020-05-19 00:34 -0500
Message-ID<VKOdnZq5x7VM8l7DnZ2dnUU7-c_NnZ2d@giganews.com>
In reply to#29342
In article <IdweG.1169345$Gh7.768707@fx45.iad>,
Clifford Heath  <no.spam@please.net> wrote:
>On 24/3/20 12:21 am, David Brown wrote:
>> Instructions like "dmb" force an order on the cpu operations, not the 
>> compiler - while "volatile" enforces a partial order on the compiler, 
>> but not the hardware.
>> 
>> The standard solution would be asm("dmb" ::: "memory"), where the memory 
>> clobber forces an ordering on the compiler - and thus you can (usually) 
>> omit the "volatile".
>David, if you have time, many of us would appreciate a quick summary of 
>the situations where the different barriers should be used.
>
>"dmb" can be a full read/write barrier, or just a write barrier. When to 
>use each?
>
>"dsb" is different from "dmb" because it limits instruction ordering, 
>but when is that useful? When to use it as a write-only barrier?
>
>"isb" is intended to precede a context switch, i.e. task switching in an 
>RTOS. Is it sufficient for that, and is that the only time to use it?
>
>If you know a good article that gives practical guidelines, post a link, 
>otherwise I'd really like to hear your thoughts.
>
>Clifford Heath.

Barriers are very difficult to get right for a programmer.  In my opinion,
it is also an CPU architectural mistake to need barriers for user-level code.
(It's OK for system software to need some barriers like ISB).  Many people
disagree with me on this point, and we don't need to argue it here.

The basic problem is there's a mismatch between what the programmer is
thinking about, and what the compiler/CPU are requiring.  This is one reason
why multithreading is more difficult than it should be.

ARM let's you choose whether the barrier is a read or write or both barrier.
I suggest you always do "both", which ARM calls SYSTEM, and is the
default if you just say "DMB" or "DSB".  I suspect there's almost no
performance difference, and it's one less thing you have to worry about.
It makes sense for macros for Linux to try to be more aggressive, since they
need to show off, and they care more about performance and have the time to
test and debug their logic across a variety of systems.  This stuff is easy to
make a mistake on, and very hard to debug.

For application programming, you should generally only need DMB for
ordering of volatile accesses.

DSB is for ordering other traffic with data accesses--things like cache
invalidates, icache fetches, or TLB shoot downs, etc.  If you have anything
like that which needs to be ordered, you want DSB.  Again, ordering accesses
to variables in normal memory don't need DSB, but you can use it if you want
much lower performance (DSB is a super-set of DMB, so you can use DSB anyplace
you would use DMB).  Generally, user code doesn't need DSB unless you're
doing self-modifying code.

ISB is for ordering special system register accesses, or odering data accesses
with other CPU actions.  If you want to read the TLB using an AT instruction,
you must do an ISB before reading the PAR register.  I cannot think of a
user-level code sequence case that needs ISB off the top of my head now.
ISB does nothing to order data operations, and it's not what you want.
There's hidden magic in the combination: "DSB; ISB".
This waits for most (but not quite all) previous bus traffic to complete
before executing any new instructions, including instruction fetches, data
fetches, etc.  User code generally never needs to do this, but OS code
sometimes does.

In terms of "heaviness", DMB is the lightest--it will slow the pipeline
a few clocks to get the data accesses right.  DSB is actually the slowest--it
generally causes a bus transaction, which is sent to other agents, and
a response is sent back (CPU optimizations can avoid this traffic sometimes,
especially if another DSB was recently done).  You don't want to use DSB
unless you really need it.  And ISB is in the middle--also a few cycles, but
likely a little more than DMB.  And "DSB; ISB" basically brings the CPU
to temporary halt--waits for (almost) every current fetch to finish, then
restarts.

To make things worse, the way to actually insert the barrier is another
level of complexity, and which sadly seems to be compiler dependent.

It's almost as if this whole area is a big giant mess.

Kent

[toc] | [prev] | [next] | [standalone]


#29672

FromClifford Heath <no.spam@please.net>
Date2020-05-19 17:40 +1000
Message-ID<b4MwG.203180$872.69637@fx38.iad>
In reply to#29671
On 19/5/20 3:34 pm, Kent Dickey wrote:
> In article <IdweG.1169345$Gh7.768707@fx45.iad>,
> Clifford Heath  <no.spam@please.net> wrote:
>> On 24/3/20 12:21 am, David Brown wrote:
>>> Instructions like "dmb" force an order on the cpu operations, not the
>>> compiler - while "volatile" enforces a partial order on the compiler,
>>> but not the hardware.
>>>
>>> The standard solution would be asm("dmb" ::: "memory"), where the memory
>>> clobber forces an ordering on the compiler - and thus you can (usually)
>>> omit the "volatile".
>> David, if you have time, many of us would appreciate a quick summary of
>> the situations where the different barriers should be used.
>>
>> "dmb" can be a full read/write barrier, or just a write barrier. When to
>> use each?
>>
>> "dsb" is different from "dmb" because it limits instruction ordering,
>> but when is that useful? When to use it as a write-only barrier?
>>
>> "isb" is intended to precede a context switch, i.e. task switching in an
>> RTOS. Is it sufficient for that, and is that the only time to use it?
>>
>> If you know a good article that gives practical guidelines, post a link,
>> otherwise I'd really like to hear your thoughts.
>>
>> Clifford Heath.
> 
> Barriers are very difficult to get right for a programmer.  In my opinion,
> it is also an CPU architectural mistake to need barriers for user-level code.
> (It's OK for system software to need some barriers like ISB).  Many people
> disagree with me on this point, and we don't need to argue it here.
> 
> The basic problem is there's a mismatch between what the programmer is
> thinking about, and what the compiler/CPU are requiring.  This is one reason
> why multithreading is more difficult than it should be.
> 
> ARM let's you choose whether the barrier is a read or write or both barrier.
> I suggest you always do "both", which ARM calls SYSTEM, and is the
> default if you just say "DMB" or "DSB".  I suspect there's almost no
> performance difference, and it's one less thing you have to worry about.
> It makes sense for macros for Linux to try to be more aggressive, since they
> need to show off, and they care more about performance and have the time to
> test and debug their logic across a variety of systems.  This stuff is easy to
> make a mistake on, and very hard to debug.
> 
> For application programming, you should generally only need DMB for
> ordering of volatile accesses.
> 
> DSB is for ordering other traffic with data accesses--things like cache
> invalidates, icache fetches, or TLB shoot downs, etc.  If you have anything
> like that which needs to be ordered, you want DSB.  Again, ordering accesses
> to variables in normal memory don't need DSB, but you can use it if you want
> much lower performance (DSB is a super-set of DMB, so you can use DSB anyplace
> you would use DMB).  Generally, user code doesn't need DSB unless you're
> doing self-modifying code.
> 
> ISB is for ordering special system register accesses, or odering data accesses
> with other CPU actions.  If you want to read the TLB using an AT instruction,
> you must do an ISB before reading the PAR register.  I cannot think of a
> user-level code sequence case that needs ISB off the top of my head now.
> ISB does nothing to order data operations, and it's not what you want.
> There's hidden magic in the combination: "DSB; ISB".
> This waits for most (but not quite all) previous bus traffic to complete
> before executing any new instructions, including instruction fetches, data
> fetches, etc.  User code generally never needs to do this, but OS code
> sometimes does.
> 
> In terms of "heaviness", DMB is the lightest--it will slow the pipeline
> a few clocks to get the data accesses right.  DSB is actually the slowest--it
> generally causes a bus transaction, which is sent to other agents, and
> a response is sent back (CPU optimizations can avoid this traffic sometimes,
> especially if another DSB was recently done).  You don't want to use DSB
> unless you really need it.  And ISB is in the middle--also a few cycles, but
> likely a little more than DMB.  And "DSB; ISB" basically brings the CPU
> to temporary halt--waits for (almost) every current fetch to finish, then
> restarts.
> 
> To make things worse, the way to actually insert the barrier is another
> level of complexity, and which sadly seems to be compiler dependent.
> 
> It's almost as if this whole area is a big giant mess.
> 
> Kent
> 

Many thanks Kent, that's very useful and to-the-point.

Clifford Heath

[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | comp.arch.embedded


csiph-web