本文介绍了STM32 微控制器上哪些变量类型/大小是原子的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是 STM32 微控制器上的数据类型:

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


2018 年 10 月 29 日更新:

还有一点花絮:

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

/* 不需要临界区,因为变量是 BaseType_t 类型.*/

...读取unsigned long"时STM32 上的(4 字节)易失性变量.这意味着他至少 100% 确定 4 字节读取和写入在 STM32 上是原子的. 他没有提到小字节读取,但对于 4 字节读取,他确实确定.我必须假设 4 字节变量是本机处理器宽度,而且 字对齐,对于这一点至关重要.

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

UBaseType_t uxTaskGetNumberOfTasks( void ){/* 不需要临界区,因为变量是类型BaseType_t.*/返回 uxCurrentNumberOfTasks;}

他准确地使用了...

/* 不需要临界区,因为变量是 BaseType_t 类型.*/

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

我的问题的最终答案:所有类型 粗体类型)都是原子的.

此外,如上面截图所示,仔细检查 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 日变更的说明:

Here are the data types on STM32 microcontrollers: http://www.keil.com/support/man/docs/armcc/armcc_chr1359125009502.htm.

These microcontrollers use 32-bit ARM core processors.

Which data types have automatic atomic read and atomic write access?

I'm pretty sure all 32-bit data types do (since the processor is 32-bits), and all 64-bit data types do NOT (since it would take at least 2 processor operations to read or write a 64-bit word), but what about bool (1 byte), and uint16_t/int16_t (2 bytes)?

Context: I'm sharing variables between multiple threads (single core, but multiple threads, or "tasks" as they are called, in FreeRTOS) on the STM32 and need to know if I need to enforce atomic access by turning off interrupts, using mutexes, etc.

UPDATE:

Refering to this sample code:

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
    }
}

In the code above, which variables can I do this for without using a mutex? My suspicion is as follows:

  1. volatile bool: safe--no mutex required
  2. volatile uint8_t: safe--no mutex required
  3. volatile uint16_t: safe--no mutex required
  4. volatile uint32_t: safe--no mutex required
  5. volatile uint64_t: UNSAFE--YOU MUST USE A Critical section or MUTEX!
  6. volatile float: safe--no mutex required
  7. volatile double: UNSAFE--YOU MUST USE A Critical section or MUTEX!

Example critical section with 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();

Related, but not answering my question:

  1. Atomic operations in ARM
  2. ARM: Is writing/reading from int atomic?
  3. (My own question and answer on atomicity in 8-bit AVR [and Arduino] microcontrollers): https://stackoverflow.com/a/39693278/4561887
  4. https://stm32f4-discovery.net/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/
解决方案

For the final, definitive answer to this question, jump straight down to the section below titled "Final answer to my question".

UPDATE 30 Oct. 2018: I was accidentally referencing the (slightly) wrong documents (but which said the exact same thing), so I've fixed them in my answer here. See "Notes about the 30 Oct. 2018 changes" at bottom of this answer for details.

I definitely don't understand every word here, but the ARM v7-M Architecture Reference Manual (Online source; PDF file direct download) (NOT the Technical Reference Manual [TRM], since it doesn't discuss atomicity) validates my assumptions:

So...I think my 7 assumptions at the bottom of my question are all correct. [30 Oct. 2018: Yes, that is correct. See below for details.]


UPDATE 29 Oct. 2018:

One more little tidbit:

Richard Barry, FreeRTOS founder, expert, and core developer, states in tasks.c...

...when reading an "unsigned long" (4-byte) volatile variable on STM32. This means that he, at least, is 100% sure 4-byte reads and writes are atomic on STM32. He doesn't mention smaller-byte reads, but for 4-byte reads he is conclusively sure. I have to assume that 4-byte variables being the native processor width, and also, word-aligned, is critical to this being true.

From tasks.c, lines 2173-2178 in FreeRTOS v9.0.0, for instance:

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

He uses this exact phrase of...

...in two different locations in this file.

Final answer to my question: all types <= 4 bytes (all bolded types in the list of 9 rows below) are atomic.

Furthermore, upon closer inspection of the TRM on p141 as shown in my screenshot above, the key sentences I'd like to point out are:

And, per this link, the following is true for "basic data types implemented in ARM C and C++" (ie: on STM32):

  1. bool/_Bool is "byte-aligned" (1-byte-aligned)
  2. int8_t/uint8_t is "byte-aligned" (1-byte-aligned)
  3. int16_t/uint16_t is "halfword-aligned" (2-byte-aligned)
  4. int32_t/uint32_t is "word-aligned" (4-byte-aligned)
  5. int64_t/uint64_t is "doubleword-aligned" (8-byte-aligned) <-- NOT GUARANTEED ATOMIC
  6. float is "word-aligned" (4-byte-aligned)
  7. double is "doubleword-aligned" (8-byte-aligned) <-- NOT GUARANTEED ATOMIC
  8. long double is "doubleword-aligned" (8-byte-aligned) <-- NOT GUARANTEED ATOMIC
  9. all pointers are "word-aligned" (4-byte-aligned)

This means that I now have and understand the evidence I need to conclusively state that all bolded rows just above have automatic atomic read and write access (but NOT increment/decrement of course, which is multiple operations). This is the final answer to my question. The only exception to this atomicity might be in packed structs I think, in which case these otherwise-naturally-aligned data types may not be naturally aligned.

Also note that when reading the Technical Reference Manual, "single-copy atomicity" apparently just means "single-core-CPU atomicity", or "atomicity on a single-CPU-core architecture." This is in contrast to "multi-copy atomicity", which refers to a "mutliprocessing system", or multi-core-CPU architecture. Wikipedia states "multiprocessing is the use of two or more central processing units (CPUs) within a single computer system" (https://en.wikipedia.org/wiki/Multiprocessing).

My architecture in question, STM32F767ZI (with ARM Cortex-M7 core), is a single-core architecture, so apparently "single-copy atomicity", as I've quoted above from the TRM, applies.

Further Reading:

Notes about the 30 Oct. 2018 changes:

这篇关于STM32 微控制器上哪些变量类型/大小是原子的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-28 15:41