Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.arch.embedded > #29293 > unrolled thread
| Started by | pozz <pozzugno@gmail.com> |
|---|---|
| First post | 2020-03-13 15:38 +0100 |
| Last post | 2020-03-18 16:24 +0000 |
| Articles | 20 on this page of 49 — 12 participants |
Back to article view | Back to comp.arch.embedded
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 →
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2020-03-13 15:38 +0100 |
| Subject | Cortex-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | pozz <pozzugno@gmail.com> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Hans-Bernhard Bröker <HBBroeker@t-online.de> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Les Cargill <lcargill99@comcast.com> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Clifford Heath <no.spam@please.net> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Clifford Heath <no.spam@please.net> |
|---|---|
| Date | 2020-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]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2020-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]
| From | Clifford Heath <no.spam@please.net> |
|---|---|
| Date | 2020-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]
| From | Tauno Voipio <tauno.voipio@notused.fi.invalid> |
|---|---|
| Date | 2020-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]
| From | kegs@provalid.com (Kent Dickey) |
|---|---|
| Date | 2020-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]
| From | Clifford Heath <no.spam@please.net> |
|---|---|
| Date | 2020-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