arm - 一次只能读取32位的方法,如何准确读取一个64位的寄存器值?

标签 arm embedded microcontroller low-level-io

在过去的采访中遇到过这个问题,但没有得到任何反馈。

由于它是一个寄存器,我是否需要在访问寄存器之前禁用中断以防止数据损坏?考虑使用两个缓冲区,32 位和 64 位,并将 32 位缓冲区发送到 read32() 并将其相应地转移到 64 位缓冲区。让我们假设这是小端架构。

我在 repl.it 上写了一个快速示例代码(输出与寄存器值不匹配)

#include <stdio.h>
#include <string.h>
#include <stdint.h>

void read32(uint64_t *reg, uint32_t *buffer){
  memcpy(buffer, reg, 4);
}

int main(void) {
  
  //register
  uint64_t reg = 0xAAAAAAAAFFFFFFFF;
  
  //buffers
  uint32_t buf_32 = 0;
  uint64_t buf_64 = 0;

  //read LSW
  read32(&reg, &buf_32);
  buf_64 |= buf_32; //add LSW

  //read MSW
  read32(&reg+4, &buf_32);
  buf_64 |= ((uint64_t)buf_32 << 32);
  
  printf("64 bit register value: 0x%lx\n", buf_64);
  return 0;
}

输出:

64 bit register value: 0x1ffffffff

最佳答案

禁用中断不会阻止可能独立​​于代码序列而改变的 I/O 寄存器。

通常需要两个大于架构宽度的硬件寄存器之间的数据一致性,硬件数据表或引用手册将建议如何读取数据 - 通常通过指定必须读取寄存器的顺序来使用使其“安全”的硬件机制。

在其他情况下,该方法可能由寄存器的性质及其功能/行为决定。例如,如果你有两个 32 位定时器/计数器,其中一个溢出触发另一个的递增,形成一个 64 位计数器,那么显然高位计数器只会在低位计数器溢出时发生变化。在这种情况下,您可以简单地读取低点,然后是高点,如果低点已经回绕,则重复:

uint32_t low_word = 0, high_word = 0;

do
{
    low_word = *low_reg_addr ;
    high_word = *high_reg_addr ;

} while( *low_reg_addr > low_word ) ;  // If low has wrapped, re-read

uint64_t full_word = (uint64_t)high_word << 32 | low_word;

所以如果读完高位寄存器后低位寄存器没有回绕,那么数据一定是一致的,退出循环。如果它已经回绕,数据可能不一致,必须重新读取。

关于arm - 一次只能读取32位的方法,如何准确读取一个64位的寄存器值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65689520/

相关文章:

我可以在 Microchip C18 中制作一个接受 ram 和 rom 指针的函数吗?

c - 无需类型转换的二进制 int 到 double

c - qemu-arm 运行已编译的二进制文件

c++ - Protobuf ld undefined symbol ~InternalMetadata()

c - 为微 Controller 编写设备驱动程序,在哪里定义 IO 端口引脚?

c - 如何在 32 位单片机上执行得比 "snprintf(mystr, 22, "{%+0.4f,%+0.4f }", (double)3.14159265, (double) 2.718281828459);"更快

assembly - 如何设置 ARM 堆栈框架以便 GDB 可以遍历它?

linux - 交叉编译到 ARM 的链接问题

c - uint64_t 类型的参数与 void* 类型的参数不兼容

我可以使用逻辑运算符进行十六进制比较吗?