【问题标题】:How to accurately read a 64 bit register value using a method that can only read 32 bits at a time?如何使用一次只能读取 32 位的方法准确读取 64 位寄存器值?
【发布时间】:2021-01-12 18:05:14
【问题描述】:

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

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

我在 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

【问题讨论】:

  • 我认为您应该省略示例代码,因为您有一个问题,并且代码没有达到您的预期。为什么该代码不起作用的问题是另一个问题。也不清楚该代码如何回答这个问题。如果您不知道只提出问题而不提出自己的解决方案,通常最好,以免它只是关于您的建议的讨论而不是问题的答案。
  • 寄存器这个词是什么意思?可以看到面试问题会如何问,但您的代码不同。

标签: arm embedded microcontroller low-level-io


【解决方案1】:

禁用中断不会阻止可能独立​​于代码序列而改变的 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;

所以如果高位寄存器被读取后低位寄存器没有被包裹,那么数据必须是一致的,并且循环退出。如果已包装,则数据可能不一致,必须重新读取。

【讨论】:

  • 感谢您的清晰解释。另外,do while 条件,不应该是 low_word != *low_reg_addr 吗?
  • 如果它是一个循环计数器(或更快),这将失败。 (你不能两次读取相同的值,永远)。阅读low1highlow2 是安全的,如果low2 low1high 中减去1
  • @RussSchultz :好点,但是还有更多。 high_word 可能会在它被读取之后但在重新读取低之前增加,在这种情况下它应该被递减。正在努力 - 感谢您发现缺陷。
  • 我认为问题在于你不能通用。然后询问有关寄存器的更多细节。在这里,您已经回答了计时器计数器寄存器的问题,这是实际编程的常用习语。但是,它可能是位字段或随机数生成器(在这种情况下,您可能不关心误读)。如果它是一个随机更改的地址,那么示例代码至少需要一些东西来让编译器重新读取。在随机改变指针/地址的情况下,高/低都可以改变;导致是否适合使用陈旧的值?
  • 另一个例子是 FIFO 寄存器。每次读取实际获得下一个值的位置。如果“寄存器”的行为类似于 RAM,那么事情可能是安全的。因此,如果您不了解寄存器的工作原理,就无法编写代码。我想这就是面试问题的目的吗?
【解决方案2】:

我在您的代码中看到的一个错误是 &reg+4 会将指针增加到 x + 32 字节。这不是您想要的,因为它应该只是 x + 4 个字节。您可能需要考虑对 uint32_t 进行类型转换。 ((uint32_t *)&reg + 1) 应该增加 4 个字节。但是,您也必须相应地更改您的读取功能。

【讨论】:

  • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 2012-04-11
  • 2022-07-18
  • 2010-09-15
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多