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

时间:2018-10-12 17:43:15

标签: c arm atomic stm32 freertos

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

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

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

我很确定所有32位数据类型都可以(因为处理器是32位),而所有64位数据类型都不能(因为至少需要2个处理器操作才能读取或写入64位数据)位字),但是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
    }
}

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

  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();

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

3 个答案:

答案 0 :(得分:4)

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

更新2018年10月30日:我不小心引用了(略有)错误的文档(但说的一模一样),因此在此处将其修复。有关详细信息,请参见此答案底部的“有关2018年10月30日更改的注意事项”。

我绝对听不懂这里的每个词,但 ARM v7-M体系结构参考手册Online source; PDF file direct download)(不是技术参考手册[TRM ],因为它没有讨论原子性)验证了我的假设:

enter image description here

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


更新2018年10月29日:

又一个小花絮:

FreeRTOS创始人,专家和核心开发人员理查德·巴里(Richard Barry)在tasks.c中表示...

  

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

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

例如,在tasks.c中,位于FreeRTOS 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;
}

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

  

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

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

我的问题的最终答案:

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

  

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

per this link对“在ARM C和C ++中实现的基本数据类型”(即:在STM32上)满足以下条件:

  • bool / _Bool是“字节对齐”(1字节对齐)
  • int8_t / uint8_t是“字节对齐”(1字节对齐)
  • int16_t / uint16_t是“半字对齐”(2字节对齐)
  • int32_t / uint32_t是“单词对齐”(4字节对齐)
  • int64_t / uint64_t是“双字对齐”(8字节对齐)<-不能保证原子
  • float是“字对齐”(4字节对齐)
  • double是“双字对齐”(8字节对齐)<-无法保证原子
  • long double是“双字对齐”(8字节对齐)<-无法保证原子
  • 所有指针均为“字对齐”(4字节对齐)

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

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

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

进一步阅读:

关于2018年10月30日更改的说明:

答案 1 :(得分:1)

取决于原子的含义。

如果不是简单的加载或存储操作

a += 1;

然后所有类型都不是原子的。

如果是32位的简单存储或装入不透明性,则16位和8位数据类型是原子的。如果必须将寄存器中的值归一化,则8位和16位存储和加载可能不是原子的。

如果您的硬件支持位带,则如果使用位带,则位操作(设置和重置)意味着支持位带的存储区域是原子的

注意。

如果您的代码不允许未对齐的操作,则8位和16位操作可能不是原子操作。

答案 2 :(得分:0)

原子“算术”可以由CPU内核寄存器处理!

它可以是任何类型一个或四个字节取决于架构和指令集

对位于内存中的任何变量进行BUT修改至少需要3个系统步骤: RMW =读取要注册的存储器,修改寄存器和将寄存器写入存储器。

因此,仅当您控制使用CPU寄存器时,才可以进行原子修改,这确实意味着需要使用纯汇编程序,而不使用C或Cpp编译器。

使用C \ Cpp编译器时,它会将全局或全局静态变量放置在内存中,因此 C \ Cpp不提供任何原子操作和类型

注意:例如,您可以使用“ FPU寄存器”进行原子修改(如果确实需要),但是必须对具有FPU的体系结构的编译器和RTOS隐藏。