简体   繁体   English

为什么嵌入式系统上的寄存器需要读 - 修改 - 写?

[英]Why is Read-Modify-Write necessary for registers on embedded systems?

I was reading http://embeddedgurus.com/embedded-bridge/2010/03/different-bit-types-in-different-registers/ , which said: 我正在阅读http://embeddedgurus.com/embedded-bridge/2010/03/different-bit-types-in-different-registers/ ,其中说:

With read/write bits, firmware sets and clears bits when needed. 通过读/写位,固件可在需要时设置和清除位。 It typically first reads the register, modifies the desired bit, then writes the modified value back out 它通常首先读取寄存器,修改所需的位,然后将修改后的值写回

and I have run into that consrtuct while maintaining some production code coded by old salt embedded guys here. 我已经遇到了这个问题,同时保留了一些由老盐嵌入式人员编写的生产代码。 I don't understand why this is necessary. 我不明白为什么这是必要的。

When I want to set/clear a bit, I always just or/nand with a bitmask. 当我想设置/清除一点时,我总是只是或者使用位掩码。 To my mind, this solves any threadsafe problems, since I assume setting (either by assignment or oring with a mask) a register only takes one cycle. 在我看来,这解决了任何线程安全问题,因为我假设设置(通过赋值或使用掩码进行设置)寄存器只需要一个周期。 On the other hand, if you first read the register, then modify, then write, an interrupt happening between the read and write may result in writing an old value to the register. 另一方面,如果您首先读取寄存器,然后修改,然后写入,读取和写入之间发生的中断可能导致向寄存器写入旧值。

So why read-modify-write? 那么为什么读 - 修改 - 写? Is it still necessary? 还有必要吗?

This depends somewhat on the architecture of your particular embedded device. 这在某种程度上取决于您的特定嵌入式设备的架构。 I'll give three examples that cover the common cases. 我将举出三个涵盖常见案例的例子。 The basic gist of it, however, is that fundamentally the CPU core cannot operate directly on the I/O devices' registers, except to read and write them in a byte- or even word-wise fashion. 然而,它的基本要点是,基本上CPU内核不能直接在I / O设备的寄存器上操作,除非以字节或甚至字方式读取和写入它们。

1) 68HC08 series, an 8-bit self-contained microcontroller. 1)68HC08系列,一个8位独立微控制器。

This includes a "bit set" and a "bit clear" instruction. 这包括“位设置”和“位清除”指令。 These, if you read the manual carefully, actually internally perform a read-modify-write cycle by themselves. 如果您仔细阅读本手册,实际上内部会自行执行读 - 修改 - 写周期。 They do have the advantage of being atomic operations, since as single instructions they cannot be interrupted. 它们确实具有原子操作的优点,因为作为单个指令它们不能被中断。

You will also notice that they take longer than individual read or write instructions, but less time than using three instructions for the job (see below). 您还会注意到它们比单个读取或写入指令花费的时间更长,但是比使用三个指令更少的时间(见下文)。

2) ARM or PowerPC, conventional 32-bit RISC CPUs (often found in high-end microcontrollers too). 2)ARM或PowerPC,传统的32位RISC CPU(通常也存在于高端微控制器中)。

These do not include any instructions which can both access memory and perform a computation (the and/or) at once. 这些不包括任何可以同时访问存储器和执行计算(和/或)的指令。 If you write in C: 如果你用C写:

*register |= 0x40;

it turns into the folowing assembly (for this PowerPC example, r8 contains the register address): 它变成了下面的程序集(对于这个PowerPC示例,r8包含寄存器地址):

LBZ r4,r8
ORI r4,r4,#0x40
STB r4,r8

Because this is multiple instructions, it is NOT atomic, and it can be interrupted. 因为这是多个指令,所以它不是原子的,可以被中断。 Making it atomic or even SMP-safe is beyond the scope of this answer - there are special instructions and techniques for it. 使其成为原子甚至SMP安全超出了这个答案的范围 - 它有特殊的指令和技术。

3) IA32 (x86) and AMD64. 3)IA32(x86)和AMD64。 Why you would use these for "embedded" is beyond me, but they are a half-way house between the other two examples. 为什么你将这些用于“嵌入式”是超出我的,但它们是另外两个例子之间的中途。

I forget whether there is a single-instruction in-memory bit-set and bit-clear on x86. 我忘记了x86上是否存在单指令内存中位设置和位清除。 If not, then see the RISC section above, it just takes only two instructions instead of three because x86 can load and modify in one instruction. 如果没有,那么请参阅上面的RISC部分,它只需要两个指令而不是三个,因为x86可以在一条指令中加载和修改。

Assuming there are such instructions, they also need to internally load and store the register as well as modifying it. 假设有这样的指令,他们需要在内部加载和存储寄存器以及修改它。 Modern versions will explcitly break the instruction into the three RISC-like operations internally. 现代版本将在内部将指令分解为三个类似RISC的操作。

The oddity is that x86 (unlike the HC08) can be interrupted on the memory bus in mid-transaction by a bus master, not just by a conventional CPU interrupt. 奇怪的是,x86(与HC08不同)可以在总线主控器中间在内存总线上中断,而不仅仅是传统的CPU中断。 So you can manually add a LOCK prefix to an instruction that needs to do multiple memory cycles to complete, as in this case. 因此,您可以手动将LOCK前缀添加到需要执行多个内存周期的指令中,如本例所示。 You won't get this from plain C though. 但是你不会从普通的C中得到这个。

The thing is if you don't want to modify the other bits in the register you have to know what they are before you write something it. 问题是如果你不想修改寄存器中的其他位,你必须在写它之前知道它们是什么。 Hence the read/modiy/write. 因此,读/ modiy /写。 Note that if you use a C statement like: 请注意,如果您使用C语句,如:

*pRegister |= SOME_BIT;

Event though that might look like a simple write operation at first glace, the compiler must perform a read first in order to preserve the other bits in the value (this is generally true, even if you're not talking about hardware registers, unless the compiler is able to use other knowledge about the value to optimize the read away). 事件虽然在第一次看起来像一个简单的写操作,但编译器必须首先执行读操作以保留值中的其他位(这通常是正确的,即使您不是在讨论硬件寄存器,除非编译器能够使用有关该值的其他知识来优化读取)。

Note that memory-mapped hardware registers are generally marked volatile specifically so that these optimizations cannot take place (otherwise many hardware register routines wouldn't work properly). 请注意,内存映射硬件寄存器通常标记为volatile特定,因此无法进行这些优化(否则许多硬件寄存器例程将无法正常工作)。

Finally, sometimes there is hardware support for registers that specifically set or clear bits in the hardware without requiring a read/modify/write sequence. 最后,有时硬件支持寄存器,专门设置或清除硬件中的位而无需读/修改/写序列。 Some Atmel ARM microcontrollers I've worked with have this with specific registers that clear or set bits in hardware only those bits that are set when you write to the register (leaving any unset bit alone). 我使用的一些Atmel ARM微控制器具有特定寄存器,可以清除或设置硬件中的位,只读取写入寄存器时设置的位(仅保留任何未设置的位)。 Also the Cortex M3 ARM CPU supports accessing a single bit (for read or write) in memory or in hardware registers this through accessing a specific address space with a technique they call 'bit-banding' . 此外,Cortex M3 ARM CPU支持通过使用他们称之为“位带”的技术访问特定地址空间来访问存储器或硬件寄存器中的单个位(用于读取或写入)。 The bit-banding algorithm looks complex at first glance, but it's really just some simple arithmetic to map the offset of a bit in one address to another 'bit-specific' address. 位带算法乍一看看起来很复杂,但实际上只是将一个地址中的位偏移映射到另一个“位特定”地址的简单算法。

Anyway, the bottom line is that there are some processors where you can get away without a read/modify/write series, but that's by no means universally true. 无论如何,最重要的是,有一些处理器,你可以在没有读/修改/写入系列的情况下离开,但这绝不是普遍真实的。

If you have to modify a subset of the bits in a word, and the architecture only supports word level read/write, you have to read the bits that must not change to know what to write back so that they are not modified. 如果必须修改字中的一部分位,并且该体系结构仅支持字级读/写,则必须读取不得更改的位以了解要写回的内容,以便不修改它们。

Some architectures support bit level memory access either globally or for specific regions of memory. 某些体系结构支持全局或特定内存区域的位级内存访问。 But even then when modifying multiple bits, read-modify-write many result in fewer instructions. 但即便如此,当修改多个位时,read-modify-write很多会导致更少的指令。 In multi-threaded systems care must be taken to ensure that two threads cannot perform this non-atomic action on the same word concurrently. 在多线程系统中,必须注意确保两个线程不能同时对同一个字执行此非原子操作。

Modern processors can either set or clear bits with single instruction. 现代处理器可以使用单指令设置或清除位。 However, these instructions can not both set and clear at the same time. 但是,这些说明不能同时设置和清除。 There are instances when some of bits of an IO port must all change together and not affect other bits. 有些情况下,IO端口的某些位必须一起更改而不会影响其他位。 As long as the sequence of read-modify-write can not be broken, there is no problem. 只要读取 - 修改 - 写入的顺序不能被破坏,就没有问题。

The situation where the rmw can become a problem requires three conditions. rmw可能成为问题的情况需要三个条件。

  1. The variable must be globally accessible such as an IO port or special function register or globally defined variable. 变量必须是全局可访问的,例如IO端口或特殊功能寄存器或全局定义的变量。

  2. The global variable can be modified in a function that can be preempted. 可以在可以被抢占的函数中修改全局变量。

  3. The same global variable is modified while servicing a preemption. 在为抢占提供服务时修改了相同的全局变量。

The only way to resolve multiple bit modifications using a rmw non-atomic sequence is to protect the sequence of instructions by disabling the interrupt for the interrupt service routine that can also modify the variable or register. 使用rmw非原子序列解决多位修改的唯一方法是通过禁用中断服务程序的中断来保护指令序列,中断服务程序也可以修改变量或寄存器。 This is similar to digine exclusive access to resources such as LCD or serial ports. 这类似于digine对LCD或串行端口等资源的独占访问。

When I want to set/clear a bit, I always just or/nand with a bitmask. 当我想设置/清除一点时,我总是只是或者使用位掩码。

For some registers that's good enough. 对于一些足够好的寄存器。 In such a case the CPU's firmware is still going to do a read-modify-write. 在这种情况下,CPU的固件仍将进行读 - 修改 - 写。

To my mind, this solves any threadsafe problems, since I assume setting (either by assignment or oring with a mask) a register only takes one cycle. 在我看来,这解决了任何线程安全问题,因为我假设设置(通过赋值或使用掩码进行设置)寄存器只需要一个周期。

If you let the CPU's firmware do the read-modify-write for you, obviously it's going to include at least a read cycle and a write cycle. 如果你让CPU的固件为你做了读 - 修改 - 写,显然它至少会包括一个读周期和一个写周期。 Now, most CPUs won't interrupt this instruction in the middle, so your thread will execute this entire instruction before your thread's CPU checks for interrupts, but if you haven't locked the bus then other CPUs can modify the same register. 现在,大多数CPU不会在中间中断该指令,因此您的线程将在线程的CPU检查中断之前执行整个指令,但如果您没有锁定总线,则其他CPU可以修改相同的寄存器。 Your thread and other threads can still walk all over each other. 你的线程和其他线程仍然可以遍布彼此。

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

相关问题 什么时候可以将原子读-修改-写操作分解为组成松散操作+障碍? - When is ok to break up atomic Read-Modify-Write operations into constituent relaxed operations + barrier? GCC可以使用读 - 修改 - 写指令来更新volatile变量吗? - Can GCC use read-modify-write instructions to update volatile variables? 是否可以为多平台(嵌入式或所有操作系统)编写C代码和库? - is it possible to write c code and library, for multiplatform: embedded or all operating systems? 具有并发读写的嵌入式数据库 - Embedded database with concurrent read and write STM32 - 代码为什么不像它应该修改寄存器? - STM32 - why the code does not modify the registers like it is supposed to? C原子读取修改写入 - C atomic read modify write 如何使用关键字volatile读写内存映射的寄存器? - How to read and write memory mapped registers using keyword volatile? 在 ARM 上的 Linux 中写入和读取内存映射设备寄存器 - WRITE and READ memory mapped device registers in Linux on ARM 如何从CSV文件(在C中)读写寄存器? - How to read and write registers from a CSV file (in C)? 控制C中存储器映射寄存器的读写访问宽度 - Controlling read and write access width to memory mapped registers in C
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM