【问题标题】:Is there something like ATOMIC_INC in CMSIS for Cortex-M 3 4 7?Cortex-M 3 4 7 的 CMSIS 中是否有类似 ATOMIC_INC 的东西?
【发布时间】:2022-11-08 00:22:07
【问题描述】:

Cortex M 3 4 7 支持 LDREX 和 STREX 汇编器指令,并通过这些 CMSIS 提供例如 ATOMIC_MODIFY_REG 以确保对 (u)int32_t 进行原子修改(即清除一些位并设置一些(可能是其他)位)。 现在我认为也可以等效地使用 ATOMIC_INC 和 ATOMIC_DEC 来原子地增加或减少 (u)int32_t 变量。但是没有。 这个想法有问题吗?我可以轻松地将 ATOMIC_MODIFY_REG 更改为 ATOMIC_INC,但测试这是否真的是原子的并不是那么容易。 我正在使用最新版本的 STMCubeIDE。

谢谢你的帮助

编辑:不再确定 ATOMIC_MODIFY_REG 是否真的是 CMSIS。 这是我在 STM CubeIDE 中的 ATOMIC_MODIFY_REG:

    /* Atomic 32-bit register access macro to clear and set one or several bits */
#define ATOMIC_MODIFY_REG(REG, CLEARMSK, SETMASK)                    \
  do {                                                               \
    uint32_t val;                                                    \
    do {                                                             \
      val = (__LDREXW((__IO uint32_t *)&(REG)) & ~(CLEARMSK)) | (SETMASK); \
    } while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U);         \
  } while(0)

【问题讨论】:

  • ldrex/strex 最初是为多核系统设计的,用于管理对资源的共享访问。我相信对于某些内核/架构(cortex-m)来说,这些并不是排他性的(支持它们有意义吗?)。您将需要不同的进程 ID,您可以将处理器放入其中,以便让一个进程设置独占位,而另一个进程干预以查看它是否有效。如何使用您拥有的核心/芯片做到这一点?
  • 为了共享一个资源,你需要另一个资源来管理这个锁。您要更改的位置以及用于管理该位置的位置。并不是说在宏/库中无法做到这一点。您是否尝试过自己实施?
  • 某些核心上的 wfe/wfi 只是一个 nop。确保它按照您认为它首先为您的核心/芯片所做的工作。如果宏实际上没有执行所暗示的操作,那么创建宏是没有意义的,因此首先它会按照您的想法进行操作,并且必须有一种特定的系统方式来提供该功能。

标签: c arm stm32 atomic cortex-m


【解决方案1】:

CMSIS 5.1 定义了 LDREX/STREX 功能的宏/函数。宏/函数有 'B'yte、'H'alfword 和 'Word' 的变体。即 LDREXH、STREXB。它不使用该功能来实现原子/无锁原语,但您可以使用它们来实现您自己的。

|说明 | CMSIS 功能 |
| ------------ | ---------------------------------- |
| LDREX | uint32_t __LDREXW (uint32_t *addr) |
| LDREXH | uint16_t __LDREXH (uint16_t *addr) |
| LDREXB | uint8_t __LDREXB (uint8_t *addr) |
| STREX | uint32_t __STREXW(uint32_t 值,uint32_t *addr)|
|拉伸 | uint32_t __STREXH(uint16_t 值,uint16_t *addr)|
| STREXB | uint32_t __STREXB(uint8_t 值,uint8_t *addr)|
|克莱克斯 |无效 __CLREX (无效) |

对于单核 CPU 和裸机单线程,没有理由进行原子增量(大多数驱动程序不希望原子增量)。对于与中断共享的位域,以原子方式进行 RMW(读-修改-写)可能很重要。如果您有带有调度功能的 RTOS,调度程序必须在上下文切换上执行CRLEX()。您还可以使用“单读/单写”结构,例如环形缓冲区/fifo,而无需求助于原子。但是,我假设您有知识并且需要拥有这些原语。

我会使用 C++ 或生成的代码来确认“C”等价物。

请参阅:godbolt 处理此 SO example 以获取某些汇编程序,

create_id():
        ldr     r3, =ID
        dmb     ish
.L2:
        ldrex   r0, [r3]
        adds    r2, r0, #1
        strex   r1, r2, [r3]
        cmp     r1, #0
        bne     .L2
        dmb     ish
        bx      lr

这给出了,

void atomic_inc(uint32_t *val)
{
   DMB();
   do {
      uint32_t tmp = LDREX(val);
      tmp++;
   } while(STREX(tmp,val));
   DMB();
}

对于 CMSIS 'C' 实施。我只会使用 C++ 原子并将它们公开为extern "C"。仅使用原子C++ 原语,我会信任(并验证)编译器。许多聪明人都在努力使它正确。

对于非 SMP 系统,您可能不需要 DMB(),但除非性能是必须的,否则它并没有那么有害。如您所见,循环可以永久锁定。您可能希望根据您的应用程序空间和设计标准插入一个重试计数器。

【讨论】:

  • Cortex-M 的保留颗粒通常是整个地址空间,因为它们没有缓存。
  • Stackoverflow 表似乎不起作用。如果您知道如何修复表格,请随时编辑此帖子。
  • 为什么你认为不需要原子公司。除非它是一个只存在于寄存器中的局部变量,否则 inc 也是一个读-修改-写。
  • @Martin_from_K 对于单个任务和驱动程序,通常有可以工作的单读/单写结构。我对系统/架构知之甚少。您很可能需要它们。我要说的是,可以考虑一些替代方案。当有其他结构可以工作时,人们常常会认为他们需要atomic_inc()
  • 例如,大多数寄存器都有一个别名为“write one to clear”(W1C)。 ATOMIC_MODIFY_REG() 有点杂乱无章。你需要你的中断来支持clrex;我认为 Cortex-M 可能会默认执行此操作。但是,可以选择更改 Cortex-M 中断行为。我会比“ldrex/strex”对更信任位带和/或(W1C)寄存器,并且系统将更有效地工作。但是,它可能是唯一可用的机制。它通常比屏蔽中断更好。 (这种情况是不是atomic_inc())。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-25
  • 1970-01-01
  • 2021-09-29
  • 2016-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多