{"id":11905,"date":"2021-03-04T14:10:14","date_gmt":"2021-03-04T14:10:14","guid":{"rendered":"http:\/\/blog.bachi.net\/?p=11905"},"modified":"2021-04-07T15:38:48","modified_gmt":"2021-04-07T15:38:48","slug":"arm-cortex-m4-atomic-mutex-read-modify-write","status":"publish","type":"post","link":"https:\/\/blog.bachi.net\/?p=11905","title":{"rendered":"ARM Cortex-M4 Atomic \/ Mutex \/ Read-Modify-Write"},"content":{"rendered":"<hr>\n<p><!-- ----------------------------------------------------------------------------------------------- --><\/p>\n<h3>Wikipedia<\/h3>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Test-and-set\">Test-and-set<\/a><br \/>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Load-link\/store-conditional\">Load-link\/store-conditional<\/a> (LL\/SC)<\/p>\n<ul>\n<li><strong>Load-link<\/strong> returns the current value of a memory location&#8230;<\/li>\n<li>&#8230;while a subsequent <strong>store-conditional<\/strong> to the same memory location will store a new value only if no updates have occurred to that location since the <strong>load-link<\/strong>.<\/li>\n<li>Together, this implements a <strong>lock-free atomic read-modify-write operation<\/strong>.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Read%E2%80%93modify%E2%80%93write\">Read\u2013modify\u2013write<\/a><br \/>\n<a href=\"https:\/\/de.wikipedia.org\/wiki\/RMW-Befehl\">RMW-Befehl<\/a><\/p>\n<hr>\n<p><!-- ----------------------------------------------------------------------------------------------- --><\/p>\n<h3>ARM Assembly<\/h3>\n<p><a href=\"https:\/\/ece353.engr.wisc.edu\/arm-assembly\/loadstore\/\">LDR\/STR &#8211; Load\/Store<\/a><br \/>\n<a href=\"https:\/\/ece353.engr.wisc.edu\/arm-assembly\/loadstore-multiple\/\">LDM\/STM &#8211; Load\/Store Multiple<\/a><br \/>\n<a href=\"https:\/\/stackoverflow.com\/questions\/20187093\/the-speed-of-arm-assembly-ldm-and-ldr\">the speed of arm assembly ldm and ldr<\/a><\/p>\n<p><a href=\"https:\/\/www.keil.com\/support\/man\/docs\/armasm\/armasm_dom1361289875835.htm\">LDREX &#8211; Load Register Exclusive<\/a><\/p>\n<h4>Cortex-M0+<\/h4>\n<p><a href=\"https:\/\/developer.arm.com\/documentation\/dui0497\/a\/the-cortex-m0-instruction-set\/miscellaneous-instructions\/cps\">CPS &#8211; Change Processor State<\/a><\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nCPSID i ; Disable all interrupts except NMI (set PRIMASK)\r\nCPSIE i ; Enable interrupts (clear PRIMASK)\r\n<\/pre>\n<hr>\n<p><!-- ----------------------------------------------------------------------------------------------- --><\/p>\n<h3>StackOverflow<\/h3>\n<p><a href=\"https:\/\/stackoverflow.com\/questions\/52784613\/which-variable-types-sizes-are-atomic-on-stm32-microcontrollers\">Which variable types\/sizes are atomic on STM32 microcontrollers?<\/a><\/p>\n<p>And, per this link, the following is true for &#8220;basic data types implemented in ARM C and C++&#8221; (ie: on STM32):<\/p>\n<ol>\n<li><code>bool\/_Bool<\/code> is &#8220;byte-aligned&#8221; (1-byte-aligned)<\/li>\n<li><code>int8_t\/uint8_t<\/code> is &#8220;byte-aligned&#8221; (1-byte-aligned)<\/li>\n<li><code>int16_t\/uint16_t<\/code> is &#8220;halfword-aligned&#8221; (2-byte-aligned)<\/li>\n<li><code>int32_t\/uint32_t<\/code> is &#8220;word-aligned&#8221; (4-byte-aligned)<\/li>\n<li><code>int64_t\/uint64_t<\/code> is &#8220;doubleword-aligned&#8221; (8-byte-aligned) <strong><-- NOT GUARANTEED ATOMIC<\/strong><\/li>\n<li><code>float <\/code>is &#8220;word-aligned&#8221; (4-byte-aligned)<\/li>\n<li><code>double <\/code>is &#8220;doubleword-aligned&#8221; (8-byte-aligned) <strong><-- NOT GUARANTEED ATOMIC<\/strong><\/li>\n<li><code>long double<\/code> is &#8220;doubleword-aligned&#8221; (8-byte-aligned) <strong><-- NOT GUARANTEED ATOMIC<\/strong><\/li>\n<li>all pointers are &#8220;word-aligned&#8221; (4-byte-aligned)<\/li>\n<\/ol>\n<hr>\n<p><!-- ----------------------------------------------------------------------------------------------- --><\/p>\n<h3>ARM Community<\/h3>\n<p><a href=\"https:\/\/community.arm.com\/developer\/ip-products\/processors\/f\/cortex-a-forum\/4201\/c-c-atomic-operation-on-arm9-and-arm-cortex-m4\">C\/C++ atomic operation on ARM9 and ARM Cortex-M4<\/a><br \/>\nAccess of <strong>64-bit data<\/strong> can be interrupted on Cortex-M3\/M4:<\/p>\n<ul>\n<li>If a 64-bit data is accessed using <code>LDM\/STM<\/code> instructions, as Jens said, the instruction can get interrupted in the middle, the processor execute the ISR and then resume the <code>LDM\/STM<\/code> from where it was interrupted.<\/li>\n<li>If the 64-bit data is accessed using LDRD\/STRD instructions, the instruction can get abandoned and restart after the ISR.<\/li>\n<li>A compiler could also use multiple <code>LDR\/STR<\/code> to access a 64-bit data.<\/li>\n<\/ul>\n<p>For <strong>8-bit\/16-bit\/32-bit data<\/strong>, provided that the memory instruction generated is a <strong>single <\/strong><code>LDR\/STR<\/code> instruction, <strong>interrupt cannot happen in between<\/strong>. The external memory controller might convert 16-bit \/ 32-bit access into multiple transfers on the memory bus, but the processor doesn&#8217;t know this and will wait until the transfer is done before taking the interrupt.<\/p>\n<p>One more thing to add: there is no 64-bit exclusive access instructions for Cortex-M4.<\/p>\n<p><a href=\"https:\/\/community.arm.com\/developer\/ip-products\/processors\/f\/cortex-m-forum\/10361\/ldrex-strex-on-the-m3-m4-m7\">LDREX\/STREX on the M3,M4,M7<\/a><\/p>\n<p>Each exclusive access sequence (typically contains a read-modify-write) of a semaphore variable is very short. So you can have as many mutex as you like, it is just not all of them being updated at the same time.<\/p>\n<hr>\n<p><!-- ----------------------------------------------------------------------------------------------- --><\/p>\n<h3>mikrocontroller.net<\/h3>\n<p><a href=\"https:\/\/www.mikrocontroller.net\/topic\/358456\">STM32: LDREX\/STREX vs Interruptsperre<\/a><\/p>\n<p>Wenn ich also LDREX ausf\u00fchre, wird der execlusive access monitor gesetzt. wenn nun ein Interrupt (Exception) kommt, wird er wieder gel\u00f6scht und STREX schl\u00e4gt fehl.<\/p>\n<p>The Cortex-M3 includes an exclusive access monitor, that tags the fact that the processor has executed a Load-Exclusive instruction.<br \/>\nThe processor removes its exclusive access tag if:<\/p>\n<ul>\n<li>It executes a CLREX instruction<\/li>\n<li>It executes a Store-Exclusive instruction, regardless of whether the write succeeds.<\/li>\n<li>An exception occurs. This means the processor can resolve semaphore conflicts between different threads.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.mikrocontroller.net\/topic\/312246\">atomic-lib f\u00fcr stm32<\/a><br \/>\n<a href=\"https:\/\/www.mikrocontroller.net\/topic\/123533\">Interrupts ein-\/ausschalten beim ARM cortex-M3<\/a><\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nsomeNonAtomicCode();\r\nATOMIC_BLOCK(...)\r\n{\r\n    doSomeThing();\r\n    \/\/ some comment\r\n    doAtomicStuff();\r\n    \/\/ another comment\r\n    lastAtomicOperation();\r\n}\r\nsomeNonAtomicCode();\r\n<\/pre>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ntemplate &lt;typename F&gt; inline void doAtomic (F f)  {\r\n  __disable_irq ();\r\n  f ();\r\n  __enable_irq ();\r\n}\r\nint main () {\r\n  doAtomic (&#x5B;] () {\r\n    \/\/ ... gesch\u00fctzte Operationen ...\r\n  });\r\n}\r\n<\/pre>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n for( __disable_irq(), uint8_t cont = 1; cont == 1; __enable_irq(), cont = 0 )\r\n  {\r\n    Anweisungen ....\r\n  }\r\n\r\n#define ATOMIC_BLOCK() for( __disable_irq(), uint8_t __cont = 1; __cont == 1; __enable_irq(), __cont = 0 )\r\n<\/pre>\n<p>Die korrekte Vorgehensweise ist:<\/p>\n<ol>\n<li>Zustand der Interrupts in einer Variablen sichern<\/li>\n<li>Interrupt deaktivieren (einen oder alle\u2026)<\/li>\n<li>Atomarer Block\u2026<\/li>\n<li>Urspr\u00fcnglichen Zustand der Interrupts wieder herstellen!<\/li>\n<\/ol>\n<p><a href=\"https:\/\/developer.arm.com\/documentation\/dui0491\/h\/Compiler-specific-Features\/--disable-irq-intrinsic\">__disable_irq intrinsic<\/a><br \/>\n<a href=\"https:\/\/devzone.nordicsemi.com\/f\/nordic-q-a\/8572\/disable-interrupts-and-enable-interrupts-if-they-where-enabled\">disable interrupts and enable interrupts if they where enabled<\/a><\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n__istate_t  interruptstate = __get_interrupt_state();\r\n__disable_interrupt();\r\n\/\/ Atomarer Block\u2026\r\n__set_interrupt_state(interruptstate);\r\n<\/pre>\n<p>Variante mit extra Klasse<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nclass IRQLocker {\r\n  private:\r\n    uint32_t m;\r\n  public:\r\n    inline IRQLocker () { m = __get_PRIMASK (); __disable_irq (); }\r\n    inline ~IRQLocker () { if (m == 0) __enable_irq (); }\r\n};\r\ntemplate &lt;typename F&gt;\r\ninline void doAtomic (F f) {\r\n  IRQLocker l;\r\n  f ();\r\n}\r\nvoid test1 () {\r\n  doAtomic (&#x5B;] () {\r\n    \/* tu was atomisches *\/\r\n  });\r\n}\r\nvoid test2 () {\r\n  {\r\n    IRQLocker l;\r\n    \/* tu was atomisches *\/\r\n  } \/\/ bei der Schlie\u00dfenden Klammer erfolgt das entsperren\r\n}\r\n<\/pre>\n<p>Da ist ein  __disable_interrupt()&#8230;. deterministischer. Was ist eure Erfahrung?<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nbool ATOMIC_int32Add(int32_t *var, int32_t off)\r\n{\r\n  int32_t tempVal;\r\n  int32_t lockedOut;\r\n  do\r\n  {\r\n    tempVal = (int32_t) __LDREX((unsigned long *)var); \r\n    tempVal += off;\r\n    lockedOut = __STREX(tempVal,(unsigned long *)var); \r\n  }\r\n  while ( lockedOut ); \/\/ lockedOut: 0==Success, 1==instruction is locked out\r\n  __DMB(10);  \/\/ recommended by arm -&gt; make sure every memory access is done\r\n  return true;\r\n}\r\n<\/pre>\n<p>Sehr deterministisch und im Fall von DMA auch sehr sinnlos. Gegen DMA hilft die Abschaltung von Interrupts \u00fcberhaupt nicht. Bei mehreren Cores sieht es \u00e4hnlich aus.<\/p>\n<p>Wenn man die Sorge hat, in diese Falle laufen zu k\u00f6nnen, dann kann man solche Spinlocks auch ent-determinisieren, indem das Verhalten (Timing) abh\u00e4ngig von einem Durchlaufz\u00e4hler variiert.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wikipedia Test-and-set Load-link\/store-conditional (LL\/SC) Load-link returns the current value of a memory location&#8230; &#8230;while a subsequent store-conditional to the same memory location will store a new value only if no updates have occurred to that location since the load-link. Together, this implements a lock-free atomic read-modify-write operation. Read\u2013modify\u2013write RMW-Befehl ARM Assembly LDR\/STR &#8211; Load\/Store LDM\/STM [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-11905","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.bachi.net\/index.php?rest_route=\/wp\/v2\/posts\/11905","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bachi.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bachi.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bachi.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bachi.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11905"}],"version-history":[{"count":7,"href":"https:\/\/blog.bachi.net\/index.php?rest_route=\/wp\/v2\/posts\/11905\/revisions"}],"predecessor-version":[{"id":12016,"href":"https:\/\/blog.bachi.net\/index.php?rest_route=\/wp\/v2\/posts\/11905\/revisions\/12016"}],"wp:attachment":[{"href":"https:\/\/blog.bachi.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11905"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bachi.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11905"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bachi.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11905"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}