简体   繁体   English

如何从C或C ++语言级别安全地访问内存映射硬件寄存器?

[英]How to safely access memory mapped hardware register from C or C++ language level?

In C and C++ I usually access memory mapped hardware registers with the well known pattern: 在C和C ++中,我通常使用众所周知的模式访问内存映射的硬件寄存器:

typedef unsigned int uint32_t;
*((volatile uint32_t*)0xABCDEDCB) = value;

As far as I know, the only thing guaranteed by the C or C++ standard is that accesses to volatile variables are evaluated strictly according to the rules of the abstract machine. 据我所知,C或C ++标准唯一保证的是对volatile变量的访问严格根据抽象机器的规则进行评估。

  1. How can I be sure that the compiler will not generate torn stores for the access for a 32-bit processor? 如何确保编译器不会为32位处理器的访问生成破坏的存储? For example the compiler is allowed to emit two 16-bit stores instead of a one 32-bit store, isn't it? 例如,允许编译器发出两个16位存储而不是一个32位存储,不是吗?
  2. Are there any guarantees in this area made by gcc? 这个区域有什么保证由gcc制作吗?

When speeking about MCUs, as far as I know there are no such guarantees. 在谈论MCU时,据我所知,没有这样的保证。 Even more, each case of accessing HW registers may be device specific and often may have its own sequence, rules and/or set of assembler instructions. 甚至,访问HW寄存器的每种情况可以是特定于设备的,并且通常可以具有其自己的序列,规则和/或汇编指令集。 And it depends on compiler implementation, too. 它也取决于编译器的实现。 The only thing here that works for me is reading datasheets concering concrete devices/compilers and follow the examples. 这里唯一对我有用的是阅读有关具体设备/编译器的数据表,并按照示例进行操作。

Microsoft comment about ISO compliant usage of volatile 微软评论关于ISO兼容的volatile使用情况

"The volatile keyword in C++11 ISO Standard code is to be used only for hardware access" “C ++ 11 ISO标准代码中的volatile关键字仅用于硬件访问”

http://msdn.microsoft.com/en-us/library/12a04hfd.aspx http://msdn.microsoft.com/en-us/library/12a04hfd.aspx

At least in the case of Microsoft C++ (going back to Visual Studio 2005), an example of a pointer to volatile type is shown: 至少在Microsoft C ++的情况下(返回到Visual Studio 2005),显示了指向volatile类型的指针的示例:

http://msdn.microsoft.com/en-us/library/145yc477.aspx http://msdn.microsoft.com/en-us/library/145yc477.aspx

Another reference, in this case C, which also includes examples of pointers to volatile types. 另一个引用,在本例中为C,它还包括指向volatile类型的指针的示例。

"static volatile objects model memory-mapped I/O ports, and static const volatile objects model memory-mapped input ports" “静态易失性对象模型内存映射I / O端口,静态const volatile对象模型内存映射输入端口”

http://en.cppreference.com/w/c/language/volatile http://en.cppreference.com/w/c/language/volatile

Operations on volatile types are not allowed to be reordered by compiler or hardware, a requirement for hardware memory mapped access. 对于易失性类型的操作不允许由编译器或硬件重新排序,这是硬件存储器映射访问的要求。 However operations to a combination of volatile and non-volatile types may end up with reordered operations on the non-volatile types, making them non thread safe (all inter thread sharing of variables would require all of them to be volatile to be thread safe). 但是,对易失性和非易失性类型的组合的操作最终可能会对非易失性类型进行重新排序操作,从而使它们非线程安全(变量的所有线程间共享将要求所有变量都是易失性的,以保证线程安全) 。 Even if two threads only share volatile types, there's still a data race issue (one thread reads just before the other thread writes). 即使两个线程只共享volatile类型,仍然存在数据争用问题(一个线程在其他线程写入之前读取)。

Microsoft compilers have a non-portable (to other compilers) extension to volatile, that makes them thread safe (/volatile:ms - Microsoft specific, used by default except for ARM processors). Microsoft编译器对volatile的非可移植(对其他编译器)扩展,这使得它们是线程安全的(/ volatile:ms - 特定于Microsoft,默认情况下使用除ARM处理器之外)。

Back to the original question, in the case of GCC, you can have the compiler generate assembly code to verify the operation is safe. 回到最初的问题,在GCC的情况下,您可以让编译器生成汇编代码以验证操作是否安全。

If you are really worried use inline assembler. 如果你真的很担心使用内联汇编程序。 A single assembler instruction will not return until completed. 单个汇编器指令在完成之前不会返回。

Also you must ensure that the memory page you are writing to is not cached otherwise the write may not be all the way through. 此外,您必须确保您正在写入的内存页面不会被缓存,否则写入可能无法完成。 On ARM memory barriers may be necessary as well. 在ARM上,内存障碍也是必要的。

Volatile is just an instruction which tells the compiler to make no assuptions about the content of the memory since the value may be changed outside one's program but has no effect or read write ordering. 易失性只是一条指令,它告诉编译器不要对内存的内容进行任何分析,因为这个值可能会在一个程序之外被改变但是没有效果或者读写顺序。 Use memory barriers or atomics if this is an issue. 如果这是一个问题,请使用内存屏障或原子。

How can I be sure that the compiler will not generate torn stores for the access for a 32-bit processor? 如何确保编译器不会为32位处理器的访问生成破坏的存储? For example the compiler is allowed to emit two 16-bit stores instead of a one 32-bit store, isn't it? 例如,允许编译器发出两个16位存储而不是一个32位存储,不是吗?

Normally, the compiler can combine or split memory accesses under the as-if rule, as long as the observable behavior of the program is unchanged, since the observable behavior of access to ordinary objects is the effect on the object's value, and not the memory access itself. 通常,编译器可以在as-if规则下组合或拆分内存访问,只要程序的可观察行为不变,因为访问普通对象的可观察行为是对对象值的影响,而不是对内存的影响访问自己。

However, accesses to volatile objects are part of the observable behavior of a program. 但是,对volatile对象的访问是程序可观察行为的一部分。 Therefore the compiler can no longer combine or split memory transactions. 因此,编译器不能再组合或拆分内存事务。 In the section where the C++ Standard defines "observable behavior" it specifically says that " Access to volatile objects are evaluated strictly according to the rules of the abstract machine. " 在C ++标准定义“可观察行为”的部分中,它特别指出“ 严格根据抽象机器的规则来评估对易失性对象的访问 ”。

Please note that the code shown is still non-portable C++, because the C++ Standard only cares about whether the object accessed is volatile , and not about modifiers on the pointer used to form an lvalue for said access. 请注意,显示的代码仍然是非可移植的C ++,因为C ++标准只关心访问的对象是否是volatile ,而不是关于用于形成所述访问的左值的指针上的修饰符。 You'd need to do something crazy like this example of placement-new, to force the existence of a volatile object: 你需要做一些疯狂的事情,就像这个placement-new的例子一样,强制存在一个易变的对象:

 *(new volatile uint32 ((uint32*)0xABCDEDCB)) = value;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM