锁定(原子)寄存器读/写


8

我正在使用GPIO的直接控制进行编码,为此有一些不错的资源,例如http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking;该过程涉及open(“ / dev / mem”),然后mmap操作有效地将所需的物理地址映射到您的虚拟地址空间中。然后,阅读本http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf的第6节,以了解有关如何控制I / O的信息。

要更改引脚的功能(输入或输出,或各种特殊功能),请修改GPFSELx I / O寄存器中的这3位字段(000 =输入,001 =输出对象实例)。这些修改操作被编译为具有普通加载和存储的操作(例如,将GPIO0更改为输入:*(regptr)&=〜7;编译为类似

    ldr     r2, [r3, #0]     ; r = *ptr (load r2 from I/O register)
    bic     r2, r2, #7       ; r2 &= ~7
    str     r2, [r3, #0]     ; *ptr = r2 (store r2 to I/O register)

问题是这样的:如果在加载和存储之间发生中断,并且另一个进程或ISR修改了相同的I / O寄存器,则存储操作(基于读入r2的陈旧)将还原该其他操作的效果。因此,更改这些I / O寄存器确实需要使用原子(锁定)读取/修改/写入操作来完成。我看到的示例未使用锁定操作。

由于这些I / O寄存器通常仅在设置某些内容时才更改,因此不太可能出现问题,但是“从不”总是比“不太可能”更好。另外,如果您有一个应用在其中要模拟集电极开路输出,那么(据我所知)这涉及将输出编程为0,然后在输出(低)或输入( (高/低)。因此,在这种情况下,这些I / O寄存器会经常出现Mod,而不安全的修改将更可能引起问题。

因此,可能有一个ARM“比较并设置”或类似的操作可以在这里使用,有人可以指出这一点,以及如何通过C代码实现这一点?

[注意,当您将I / O编程为输出并将其从0更改为1或反之亦然时,不需要任何特殊操作;因为有一个您要写入的I / O寄存器,用于将选定的位设置为1,另一个用于将选定的位清除为0。此操作不需要读/写,因此没有中断的危险]。


也许我不太理解,但是自从打开后/dev/mem,您的代码似乎是用户空间代码。我认为,在任何现代操作系统中,都不必担心中断更改用户空间代码中的寄存器值的情况。我相信即使在内核空间代码中这也不是问题,因为当中断处理程序完成其工作时,Linux会还原所有寄存器。
Krzysztof Adamski

1
我的理解是,加载/存储通过mmap设置的VM映射(一个I / O寄存器,而不是CPU寄存器)进入物理寄存器。在这种情况下,没有理由另一个进程或设备驱动程序不能同时执行相同的操作并修改相同的寄存器。(我假设它正在修改reg中的一组不同的位,或者显然我们有更大的问题)。没有处理器寄存器的IO寄存器保存/恢复。
greggo

我已经编辑位澄清“I / O寄存器”相对于R2等
greggo

我现在可以明白你的意思了。但是,它比中断处理更像是抢占。至少当两个进程试图同时设置不同的位时,使用原子操作将有所帮助。
Krzysztof Adamski

ldrex / strex在未缓存的内存上不起作用。独占监视器依赖于缓存。实际上,例如,当您尝试在Cortex-A9 SMP系统上尝试锁定CPU时,就可以使用硬锁定。
thinkfat

Answers:


3

我调查了一下,ARM有'ldrex和'strex'指令,如果ldrex失去了专有性(或可能已经丢失),则strex将返回失败结果,因为ldrex包括上下文切换(或另一个修改该上下文的处理器)在多处理器环境中注册)。因此可以使用它来完成;如果strex失败,您将循环,然后重新执行操作(使用新的ldrex)。

参考:http : //infocenter.arm.com/help/index.jsp?topic=/ com.arm.doc.dht0008a/ ch01s02s01.html

下面的例程似乎可以在Raspberry Pi上运行(因为它们生成了我所期望的汇编程序;使用它们时对位的影响符合预期。我尚未验证它们可以防止上下文切换问题) 。请注意,这些是内联而不是函数,因此应将它们放在头文件中。

[ 编辑:这并不能正常工作为目的的讨论,似乎它以某种方式不允许。如果我在* addr是普通变量的情况下使用这些例程,则可以正常工作。当我在* addr指向映射的GPIO寄存器的地方使用它时,该过程会出现总线错误。(当我将ldrex / strex更改为ldr / str并禁用do循环时,它将起作用)。因此,似乎ARM独占监视器无法或未设置为在内存映射的I / O regs上运行,并且问题仍然悬而未决。]

//
// Routines to atomically modify 32-bit registers using ldrex and strex.
// 
//
//
//  locked_bic_to_reg( volatile unsigned * addr, unsigned val )
//                 *addr &= ~val
//  locked_or_to_reg( volatile unsigned * addr, unsigned val )
//                 *addr |= val
//   locked_insert_to_reg( volatile unsigned * addr, unsigned val, int width, int pos )
//           insert 'width' lsbs of 'val into *addr, with the lsb at bit 'pos'.
//           Caller must ensure 1 <= width <= 32 and 0 <= pos < 32-width
//
//
static inline void
locked_bic_to_reg( volatile unsigned * addr, unsigned val )
{
    int fail;
    do{
        asm volatile ("ldrex r0,[%1]\n"
           "   bic r0,r0,%2\n"
           "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(val): "r0" );
    }while(fail!=0);
}
static inline void
locked_or_to_reg( volatile unsigned * addr, unsigned val)
{
    int fail;
    do{
        asm volatile ("ldrex r0,[%1]\n"
           "   orr r0,r0,%2\n"
           "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(val): "r0" );
    }while(fail!=0);
}

static inline void
locked_insert_to_reg( volatile unsigned * addr, unsigned val, int width, int pos )
{
    int fail;
    if(width >=32 ) {
        *addr = val;    // assume wid = 32, pos = 0;
    }else{
        unsigned m=(1<<width)-1;
        val = (val&m) << pos;   // mask and position
        m <<= pos;

        do{
            asm volatile ("ldrex r0,[%1]\n"
               "   bic r0,r0,%2\n"   /// bic with mask
               "   orr r0,r0,%3\n"    // or result
               "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(m), "r"(val): "r0" );
        }while(fail!=0);
    }
}

在我看来,这应该是处理器特定的.h文件中的那种,但是/ usr / include或/ usr / lib / gcc / arm-linux-gnueabihf /下的.h文件中不包含字符串'ldrex '。也许是内置的,还是内核头文件之一?
greggo

1
ldrex / strex用于资源的多核共享(共享ram)。传统上,swp用于单核资源的单核锁定。ldrex / strex恰好可以作为单核解决方案(取决于芯片供应商)使用,因此被滥用。它似乎确实可以在树莓派处理器上运行。
old_timer 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.