STM32微控制器上哪些变量类型/大小是原子的?

Gab*_*les 7 c arm atomic stm32 freertos

以下是STM32微控制器上的数据类型:http : //www.keil.com/support/man/docs/armcc/armcc_chr1359125009502.htm

这些微控制器使用32位ARM核心处理器。

哪些数据类型具有自动原子读取和原子写入访问权限?

我很确定所有32位数据类型都可以(因为处理器是32位),而所有64位数据类型都不能(因为要读或写一个64位字至少需要2个处理器操作),但是bool(1个字节)和uint16_t/ int16_t(2个字节)呢?

上下文:我正在STM32上的多个线程(在FreeRTOS中称为单核,但有多个线程或称为“任务”)之间共享变量,并且需要知道是否需要通过使用中断关闭中断来强制进行原子访问互斥锁等

更新:

参考此示例代码:

volatile bool shared_bool;
volatile uint8_t shared u8;
volatile uint16_t shared_u16;
volatile uint32_t shared_u32;
volatile uint64_t shared_u64;
volatile float shared_f; // 32-bits
volatile double shared_d; // 64-bits

// Task (thread) 1
while (1)
{
    // Write to the values in this thread.
    // What I write to each variable will vary. Since other threads
    // are reading these values, I need to ensure my *writes* are atomic, or else
    // I must use a mutex to prevent another thread from reading a variable in the middle
    // of this thread's writing.
    shared_bool = true;
    shared_u8 = 129;
    shared_u16 = 10108;
    shared_u32 = 130890;
    shared_f = 1083.108;
    shared_d = 382.10830;
}

// Task (thread) 2
while (1)
{
    // Read from the values in this thread.
    // What thread 1 writes into these values can change at any time, so I need to ensure
    // my *reads* are atomic, or else I'll need to use a mutex to prevent the other 
    // thread from writing to a variable in the midst of reading
    // it in this thread.
    if (shared_bool == whatever)
    {
        // do something
    }
    if (shared_u8 == whatever)
    {
        // do something
    }
    if (shared_u16 == whatever)
    {
        // do something
    }
    if (shared_u32 == whatever)
    {
        // do something
    }
    if (shared_u64 == whatever)
    {
        // do something
    }
    if (shared_f == whatever)
    {
        // do something
    }
    if (shared_d == whatever)
    {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,可以在不使用互斥锁的情况下针对哪些变量执行此操作?我的怀疑如下:

  1. volatile bool:安全-不需要互斥
  2. volatile uint8_t:安全-不需要互斥
  3. volatile uint16_t:安全-不需要互斥
  4. volatile uint32_t:安全-不需要互斥
  5. volatile uint64_t:不安全-您必须使用关键部分或MUTEX!
  6. volatile float:安全-不需要互斥
  7. volatile double:不安全-您必须使用关键部分或MUTEX!

实施例与FreeRTOS的关键部分:
- https://www.freertos.org/taskENTER_CRITICAL_taskEXIT_CRITICAL.html

// Force atomic access with these critical section atomic access guards.
taskENTER_CRITICAL();
// do the (now guaranteed to be safe) read or write here
taskEXIT_CRITICAL();
Run Code Online (Sandbox Code Playgroud)

相关,但未回答我的问题:

Gab*_*les 11

对于该问题的最终确定答案,请直接跳至下面标题为“ 我的问题的最终答案 ”的部分。

2018年10月30日更新:我不小心引用了(略有)错误的文档(但是说的完全相同),因此我在这里将其固定为答案。有关详细信息,请参阅此答案底部的“有关2018年10月30日更改的注意事项”。

我绝对不了解这里的每个词,但是ARM v7-M体系结构参考手册在线资源PDF文件直接下载)(不是技术参考手册[TRM],因为它没有讨论原子性)证实了我的假设:

在此处输入图片说明

所以...我认为我在问题底部的7个假设都是正确的。[2018年10月30日:是的,这是正确的。有关详情,请参见下文。


2018年10月29日更新:

另一个小花絮:

FreeRTOS的创始人,专家和核心开发人员Richard Barry在tasks.c...

/ *不需要关键部分,因为变量的类型为BaseType_t。* /

...在STM32上读取“无符号长”(4字节)易失性变量时。这意味着他至少可以100%确保STM32上4字节的读写是原子的。他没有提到较小字节的读取,但是对于4字节的读取,他有绝对的把握。我必须假定4字节变量是本机处理器的宽度,并且也是字对齐的,这对于实现这一点至关重要。

例如,来自tasks.cFreeRTOS v9.0.0中的第2173-2178行:

UBaseType_t uxTaskGetNumberOfTasks( void )
{
    /* A critical section is not required because the variables are of type
    BaseType_t. */
    return uxCurrentNumberOfTasks;
}
Run Code Online (Sandbox Code Playgroud)

他使用了这个确切的短语...

/ *不需要关键部分,因为变量的类型为BaseType_t。* /

...在此文件的两个不同位置。

我的问题的最终答案:所有小于等于 4个字节的类型(下面9行的列表中所有加粗的类型)都是原子的。

此外,如上截图所示,在仔细检查了p141上的TRM之后,我想指出的关键句子是:

在ARMv7-M中,单副本原子处理器访问为:
•所有字节访问。
•所有半字访问半字对齐的位置。
•所有单词都可以访问单词对齐的位置。

并且,通过此链接,“ ARM C和C ++中实现的基本数据类型”(即:在STM32上)满足以下条件:

  1. bool/ _Bool是“字节对齐”(1字节对齐)
  2. int8_t/ uint8_t是“字节对齐”(1字节对齐)
  3. int16_t/ uint16_t是“半字对齐”(2字节对齐)
  4. int32_t/ uint32_t是“字对齐”(4字节对齐)
  5. int64_t/ uint64_t是“双字对齐”(8字节对齐)<-不能保证原子
  6. float 是“字对齐”(4字节对齐)
  7. double 是“双字对齐”(8字节对齐)<-不保证原子
  8. long double 是“双字对齐”(8字节对齐)<-不保证原子
  9. 所有指针都是“字对齐的”(4字节对齐的)

这意味着我现在已经拥有并理解了我需要得出的结论,即上面所有加粗的行都具有自动的原子读写访问权限(但是当然不能进行增/减,这是多项操作)。这是我问题的最终答案。 我认为这种原子性的唯一例外可能是打包结构,在这种情况下,这些原本自然对齐的数据类型可能不会自然对齐。

另请注意,在阅读《技术参考手册》时,“单拷贝原子性”显然仅表示“单核CPU原子性”或“单CPU核体系结构上的原子性”。这与“多副本原子性”相反,后者是指“多处理系统”或多核CPU体系结构。维基百科指出“多处理是在单个计算机系统中使用两个或多个中央处理器(CPU)”(https://en.wikipedia.org/wiki/Multiprocessing)。

我所讨论的体系结构STM32F767ZI(具有ARM Cortex-M7内核)是单核体系结构,因此显然适用于“单拷贝原子性”(如我上面从TRM引述的那样)。

进一步阅读:

关于2018年10月30日更改的注意事项:

  • 但是,仍然存在C操作,即使它们操作32位或更少位(例如,使用`int a`的`a + = 1`),它们也可以编译为多个指令,并且您需要小心使用这些指令。一个不太明显的示例是,如果您使用的字段具有未对齐的结构:编译器将需要至少生成两个加载和两个存储来处理它们的读/写操作。在某些优化级别上,复制适合32位的结构也可能会使用多个指令。但是,对于数值变量,通常都不是问题。 (2认同)