When is CLREX actually needed on ARM Cortex M7?
Asked Answered
H

2

9

I found a couple of places online which state that CLREX "must" be called whenever an interrupt routine is entered, which I don't understand. The docs for CLREX state (added the numbering for easier reference):

(1) Clears the local record of the executing processor that an address has had a request for an exclusive access.

(2) Use the CLREX instruction to return a closely-coupled exclusive access monitor to its open-access state. This removes the requirement for a dummy store to memory.

(3) It is implementation-defined whether CLREX also clears the global record of the executing processor that an address has had a request for an exclusive access.

I don't understand pretty much anything here.

I had the impression that writing something along the lines the example in the docs was enough to guarantee atomicity:

    MOV r1, #0x1                ; load the ‘lock taken’ value
try:                                                       <---\
    LDREX r0, [LockAddr]        ; load the lock value          |
    CMP r0, #0                  ; is the lock free?            |
    STREXEQ r0, r1, [LockAddr]  ; try and claim the lock       |
    CMPEQ r0, #0                ; did this succeed?            |
    BNE try                     ; no - try again   ------------/
    ....                        ; yes - we have the lock
  1. Why should the "local record" need to be cleared? I thought that LDREX/STREX are enough to guarantee atomic access to an address from several interrupts? I.e. GCC for ARM compiles all C11 atomic functions using LDREX/STREX and I don't see CLREX being called anywhere.

  2. What "requirement for a dummy store" is the second paragraph referring to?

  3. What is the difference between the global record and a local record? Is global record needed for multi-core scenarios?

Heng answered 3/7, 2018 at 20:23 Comment(14)
3) yes the documentation states that the global record is for multiple PE (cores).Oculo
I suspect the CLREX or a dummy store are for situations where the interrupt/exception occured between the LDREX and the STREX, and perhaps this is a task switch timer interrupt so an LDREX from one pair is now connected to an STREX from another. With these pairs being implemented in infinite loops increases the odds of hitting them, but the odds are still pretty low.Oculo
I suspect it is for cleanliness when an LDREX/STREX pair are broken by an interrupt. Both CLREX and STREX will clear the local but implementation defined for the global. One tries to do a store the other doesnt.Oculo
"Use LDREX and STREX to implement interprocess communication in multiple-processor and shared-memory systems." Do you have a multi-core cortex-m7 or one that shares its memory with another master?Oculo
@old_timer: no, it's a single-core real-time application, but with strict limits no interrupt latencies. ARMv7 places certain guarantees as long as you don't use SWP (and some other restrictions). I suspect it is for cleanliness when an LDREX/STREX pair are broken by an interrupt. - but I don't understand the reason again, the LDREX/STREX are explicitly created to solve the issue of different interrupts breaking in between.Heng
First off, LDREX/STREX are for multi master systems, the swp replacement is not correct, often taken out of context from arm documentation (need to read all the docs). Unimaster SWP may be your only choice. Anyway the answer you accepted was the comment I gave, mixing an ldrex with some other strex (on a uniprocessor/master system, etc, etc).Oculo
You are correct, where LDREX/STREX are useful (multi-core/master systems) that situation is not an issue you cannot mix and match pairs from different masters and have them pass. I also do not yet see a situation where CLREX nor a dummy STREX are required as those situations dont require STREX/LDREX at all. I have yet to find the dummy store requirement outside the text you found.Oculo
The cortex-m7 is an armv7-m not an armv7 BTW. With armv7 multi-core systems are quite common, cortex-m7...would like to see one, I think the chip vendor has to cobble that together. I dont have access to know what is required (include stuff, modification of the source, compile options, to get a width more than zero of the master bits on the exclusive interface). What chip are you using? (this is all very chip/vendor specific anyway)Oculo
Only exclusive instructions to shared memory result in exclusive accesses on the AHBP. Exclusive accesses to non-shared memory are marked as non-exclusive accesses on the bus.Oculo
Software must avoid performing exclusive accesses to shared regions of memory if no global exclusive monitor is implemented that covers the region in question.Oculo
From these docs it seems like global is handled by the chip vendor not ARM certainly true for the big boys (ARMv6, ARMv7).Oculo
My cortex-m7 board hangs on the STREX, have not dug in to see what kind of fault. So so far I cant do an LDREX/STREX pair (against SRAM).Oculo
@old_timer: the swp replacement is not correct, often taken out of context from arm documentation -- you really need to add some references to that -- all the official arm docs state that swp is deprecated and cannot ensure the stated interrupt latency, so I don't see what could be "taken out of context". The accepted answer states that CLREX is required for multithreaded scenarios where threads can be preempted and explains how this can happen even on a single-core system, while your comment said it's for "cleanliness".Heng
For example: "The model for using Load-Exclusives and Store-Exclusives for synchronization is the same for single-core and multi-core systems." "future architectures are not guaranteed to support these instructions (SWP and SWPB).". "processor must complete both the load and the store part (...), increasing interrupt latency".Heng
S
11

Taking (and paraphrasing) your three questions separately:

1. Why clear the access record?

When strict nesting of code is enforced, such as when you're working with interrupts, then CLREX is not usually required. However, there are cases where it's important. Imagine you're writing a context switch for a preemptive operating system kernel, which can asynchronously suspend a running task and resume another. Now consider the following pathological situation, involving two tasks of equal priority (A and B) manipulating the same shared resource using LDREX and STREX:

Task A      Task B
  ...
 LDREX
-------------------- context switch
             LDREX
             STREX   (succeeds)
              ...
             LDREX
-------------------- context switch
 STREX               (succeeds, and should not)
  ...

Therefore the context switch must issue a CLREX to avoid this.

2. What 'requirement for a dummy store' is avoided?

If there wasn't a CLREX instruction then it would be necessary to use a STREX to relinquish the exclusive-access flag, which involves a memory transaction and is therefore slower than it needs to be if all you want to do is clear the flag.

3. Is the 'global record' for multi-core scenarios?

Yes, if you're using a single-core machine, there's only one record because there's only one CPU.

Singletary answered 4/7, 2018 at 8:59 Comment(2)
There is no need to use CLREX for the reason you describe for (1) on Cortex-M processors, other than compatibility. Please see my answer for more details.Barna
Also, for (2), I can see how CLREX saves a few cycles over STREX. But don't see how that can be useful if the exclusive access flag is cleared automatically on a context switch. If it returned the exclusive access state like STREX I can see cases it could be used, but it doesn't.Barna
B
6

Actually CLREX isn't needed for exceptions/interrupts on the M7, it appears to only be included for compatibility reasons. From the documenation (Version c):

CLREX enables compatibility with other ARM Cortex processors that have to force the failure of the store exclusive if the exception occurs between a load exclusive instruction and the matching store exclusive instruction in a synchronization operation. In Cortex-M processors, the local exclusive access monitor clears automatically on an exception boundary, so exception handlers using CLREX are optional.

So, since Cortex-M processors clear the local exclusive access flag on exception/interrupt entry/exit, this negates most (all?) of the use cases for CLREX.

With regard to your third question, as others have mentioned you are correct in thinking that the global record is used in multi-core scenarios. There may still be use cases for CLREX on multi-core processors depending on the implementation defined effects on local/global flags.

I can see why there is confusion around this, as the initial version of the M7 documentation doesn't include these sentences (not to mention the various other versions of more generic documentation on the ARM website). Even now, I cannot even link to the latest revision. The page displays 'Version a' by default and you have to manually change the version via a drop down box (hopefully this will change in future).

Update

In response to comments, an additional documentation link for this. This is the part of the manual that describes the usage of these instructions outside of the specific instruction documentation (and also has been there since the first revision):

The processor removes its exclusive access tag if:

  • It executes a CLREX instruction.

  • It executes a STREX instruction, regardless of whether the write succeeds.

  • An exception occurs. This means the processor can resolve semaphore conflicts between different threads.

In a multiprocessor implementation:

  • Executing a CLREX instruction removes only the local exclusive access tag for the processor.

  • Executing a STREX instruction, or an exception, removes the local exclusive access tags for the processor.

  • Executing a STREX instruction to a Shareable memory region can also remove the global exclusive access tags for the processor in the system.

Barna answered 21/1, 2019 at 16:52 Comment(5)
That is genuinely very interesting. The fact that this information is only available in one hard-to-find version of the documentation doesn't give me sufficient confidence to remove the CLREX from my context switches though! I wouldn't be surprised to find that it didn't apply to early silicon revisions, for example.Singletary
Personally I'm also surprised by this because automatic clearing of the exclusive access flag on an exception boundary doesn't seem like a very useful feature. It potentially saves a CLREX in a context switch, at the cost of needlessly causing STREX failures whenever exclusive access blocks are interrupted by ISRs that don't even touch the exclusive access mechanism. Not sure of the logic of that.Singletary
@cooperised. I added another link. This is what I was orginally aware of, but I found the other one and decided to use that instead since it mentioned the compatibility. I'm not sure the full reasons for the change, but I guess one argument for this is when it comes to the use of libraries or an RTOS that implement exceptions that you have little control over. Another factor is probably just that is less prone to user errors.Barna
Not sure that argument is valid - if the exception handlers you don't have control over use LDREX/STREX then the flag gets cleared anyway; if they don't, there's no need to clear the flag because there's no race. Literally the only place a CLREX is required (if the flag were not cleared at the exception boundary) is in the context switch. Ah well...Singletary
@cooperised. Yeah, agreed, possible CLREX usage is more limited than I thought. I added the paragraph following my last quote. On the M7, it looks like only STREX can clear the global exclusive access tag. If CLREX was removing the global tag when the exception boundary isn't, then there would be a use case on multiprocessor chips. The way it is, it seems like the other processor can be left hanging by a context switch. I don't get that.Barna

© 2022 - 2024 — McMap. All rights reserved.