哪些变量类型/大小在STM32微控制器上是原子的? [英] Which variable types/sizes are atomic on STM32 microcontrollers?
问题描述
以下是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个字节)呢?
上下文:我正在多个线程之间共享变量(单核,但是多个线程或称为任务"的变量在 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();
相关,但未回答我的问题:
- ARM中的原子操作
- ARM:是从int原子进行写入/读取吗?
- (关于8位AVR [和Arduino]微控制器中原子性的我自己的问题和答案): https://stackoverflow.com/a/39693278/4561887
- https://stm32f4-discovery.net/2015/06/如何正确启用isable-interrupts-in-arm-cortex-m/
对于该问题的最终确定答案,请直接跳至下面标题为"我的问题的最终答案"的部分. ;.
更新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. */
...当阅读"unsigned long"时, STM32上的(4字节)易失变量. 这意味着他至少100%地确定STM32上4字节的读写是原子的.他没有提到较小字节的读取,但是对于4字节的读取,他有把握地确定. .我必须假设4字节变量是本机处理器的宽度,而且以单词对齐,对于确保这一点至关重要.
从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. */
...在此文件的两个不同位置.
我的问题的最终答案:所有< = 4个字节(以下9行列表中的所有 bolded 类型)都是原子的.
此外,如我上面的屏幕截图所示,在仔细检查了p141上的TRM之后,我想指出的关键句子是:
在ARMv7-M中,单拷贝原子处理器访问是:
•所有字节访问.
•所有半字访问半字对齐的位置.
•所有单词都可以访问单词对齐的位置.
并且,通过此链接,以下内容是正确的用于在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日更改的说明:
- 我有以下参考资料: ARMv7 TRM (技术参考手册).但是,这在两种方面是错误的:1)根本不是TRM! TRM是简短的(〜200 pgs)技术参考手册.但是,这是建筑参考手册",而不是TRM.事实证明,它是更长,更通用的文档,因为《体系结构》参考手册约为〜1000〜2000 pgs. 2)这是针对ARMv7-A和ARMv7-R处理器的,但是我所需要的STM32 MCU的手册是针对ARMv7-M处理器的.
- 以下是指向《 ARM Cortex-M7处理器技术参考手册》的正确链接.在线: https://developer.arm.com/docs/ddi0489/latest . PDF: https://static.docs.arm.com/ddi0489/d/DDI0489D_cortex_m7_trm .pdf .
- 上述正确的TRM,在第99页(5-36)中说:了解更多 有关原子性的信息,请参见《ARM®v7-M体系结构参考手册》.因此,这是该手册.在线下载链接: https://static.docs.arm.com/ddi0489/d/DDI0489D_cortex_m7_trm .pdf .讨论了在p79-80(A3-79至A3-80)上的原子性.
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:
volatile bool
: safe--no mutex requiredvolatile uint8_t
: safe--no mutex requiredvolatile uint16_t
: safe--no mutex requiredvolatile uint32_t
: safe--no mutex requiredvolatile uint64_t
: UNSAFE--YOU MUST USE A Critical section or MUTEX!volatile float
: safe--no mutex requiredvolatile 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:
- Atomic operations in ARM
- ARM: Is writing/reading from int atomic?
- (My own question and answer on atomicity in 8-bit AVR [and Arduino] microcontrollers): https://stackoverflow.com/a/39693278/4561887
- 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
...
/* A critical section is not required because the variables are of type BaseType_t. */
...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...
/* A critical section is not required because the variables are of type BaseType_t. */
...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:
In ARMv7-M, the single-copy atomic processor accesses are:
• all byte accesses.
• all halfword accesses to halfword-aligned locations.
• all word accesses to word-aligned locations.
And, per this link, the following is true for "basic data types implemented in ARM C and C++" (ie: on STM32):
bool
/_Bool
is "byte-aligned" (1-byte-aligned)int8_t
/uint8_t
is "byte-aligned" (1-byte-aligned)int16_t
/uint16_t
is "halfword-aligned" (2-byte-aligned)int32_t
/uint32_t
is "word-aligned" (4-byte-aligned)int64_t
/uint64_t
is "doubleword-aligned" (8-byte-aligned) <-- NOT GUARANTEED ATOMICfloat
is "word-aligned" (4-byte-aligned)double
is "doubleword-aligned" (8-byte-aligned) <-- NOT GUARANTEED ATOMIClong double
is "doubleword-aligned" (8-byte-aligned) <-- NOT GUARANTEED ATOMIC- 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:
- ARM: Is writing/reading from int atomic?
- What is the difference between atomic / volatile / synchronized?
- Can variables inside packed structures be read atomically?
Notes about the 30 Oct. 2018 changes:
- I had this reference: ARMv7 TRM (Technical Reference Manual). However, this is wrong in 2 ways: 1) This isn't a TRM at all! The TRM is a short (~200 pgs) Technical Reference Manual. This, however, is the "Architecture Reference Manual", NOT the TRM. It is a much longer and more generic document, as Architecture reference manuals are on the order of ~1000~2000 pgs it turns out. 2) This is for the ARMv7-A and ARMv7-R processors, but the manual I need for the STM32 mcu in question is for the ARMv7-M processor.
- Here is the correct link to the ARM Cortex-M7 Processor Technical Reference Manual. Online: https://developer.arm.com/docs/ddi0489/latest. PDF: https://static.docs.arm.com/ddi0489/d/DDI0489D_cortex_m7_trm.pdf.
- The correct TRM just above, on p99 (5-36) says, "For more information on atomicity, see the ARM®v7-M Architecture Reference Manual." So, here is that manual. Online download link: https://developer.arm.com/products/architecture/cpu-architecture/m-profile/docs/ddi0403/latest/armv7-m-architecture-reference-manual. PDF: https://static.docs.arm.com/ddi0489/d/DDI0489D_cortex_m7_trm.pdf. It discusses atomicity on p79-80 (A3-79 to A3-80).
这篇关于哪些变量类型/大小在STM32微控制器上是原子的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!