映射到外围寄存器的 C++ 结构

Posted

技术标签:

【中文标题】映射到外围寄存器的 C++ 结构【英文标题】:C++ struct mapped onto peripheral registers 【发布时间】:2021-08-13 19:02:17 【问题描述】:

假设我在 Zynq-7000 SoC 上运行以下 C++ 代码

  struct ControlReg
  
    uint32_t reset_bit : 1;
  ;

  struct ConfigReg
  
    uint32_t even_channel_value : 16;
    uint32_t odd_channel_value : 16;
  ;

  struct PeripheralRegs
  
    volatile ControlReg control_reg;
    volatile uint32_t status_reg_01[2];
    volatile uint32_t status_reg_02[2];
    volatile ConfigReg config_reg_01[8];
    volatile ConfigReg config_reg_02[8];
    volatile uint32_t status_reg_03[2];
  ;

  PeripheralRegs *regs;
  regs = new (reinterpret_cast<void *>(0x43C20000)) PeripheralRegs;

  uint16_t value = 6573;

  regs->config_reg_01[0].odd_channel_value = value;
  regs->config_reg_01[0].even_channel_value = value;

我在调试器中单步执行了这段代码,我发现config_reg_01[0] 尽管尝试将6573 值写入其中,但仍包含全零。谁能告诉我为什么会发生这种行为?奇怪的是,如果我将一些值写入status_reg_01[0],我会在调试器的“内存监视器”中的适当地址看到这个值。

编辑

我已经部分找到了这种行为发生的原因。它在某种程度上与代码优化有关。该结论基于以下观察:如果我在 ARM v7 g++ 编译器设置中将代码优化级别从 O2 切换到 O0,问题就会消失。我的第二个观察结果是,如果我使用代码优化级别 O2 并进行了以下修改:

  struct ControlReg
  
    volatile uint32_t reset_bit : 1;
  ;

  struct ConfigReg
  
    volatile uint32_t even_channel_value : 16;
    volatile uint32_t odd_channel_value : 16;
  ;

在优化级别 O0 的情况下会发生相同的正确行为。 谁能向我解释为什么应用于位域项的volatile 关键字对代码优化有如此巨大的影响。

【问题讨论】:

似乎硬件阻止您写入 config_reg_01 寄存器。也许还有另一个寄存器可以让你解锁那个寄存器。 确保相关时钟已启用。详情请查看芯片参考手册。 @doron 你认为这个问题可能是由“PeripheralRegs”结构到内存的错误映射引起的吗?有什么方法可以排除这个假设吗? 没有文档,我不知道 【参考方案1】:

如果您在 RAM 中设置一个位并将其读回,您将获得您设置的值。外设并不总是这样。这就是为什么外围存储器必须声明为易失性的原因,因为不能对类似 RAM 的行为做出假设。

一个典型的例子是确认中断。您可以通过将中断状态位设置为 1 来确认中断。这将清除中断,因此当您随后读取中断状态时,通常会返回为零。

连续读取也可能得到不同的值。

【讨论】:

感谢您的反应。据我了解,您认为这可能是我平台的正常行为。就其他寄存器的行为而言,我已经编辑了我的问题。【参考方案2】:

根据Zynq-7000 参考手册,地址0x43C20000 位于器件的可编程逻辑 地址区域(“通用端口#0 " : 4000_0000 到 7FFF_FFFF)。该位置的行为将取决于(大概 - 我不熟悉该部分)实现的逻辑(如果有)。

此外,可以关闭可编程逻辑 (PL) 模块的电源,在这种情况下它什么也不做(松散的配置)。

因此,目前无法说在这个例子中应该发生什么,但正在发生的事情显然是合理的。除非并且直到您启动 PL 并对其进行配置,否则访问该地址区域没有什么意义。

设备上似乎还有 TrustZone 安全功能,可能会阻止您访问特定区域,更不用说 ARM-A9 的 MMU 可能会将物理地址重新映射到虚拟地址或完全阻止访问某个区域。

您引用的代码是合理的,并且对于可编程逻辑可能有用;但地址和寄存器名称需要与实际的硬件或有效的可编程逻辑配置相关。

【讨论】:

以上是关于映射到外围寄存器的 C++ 结构的主要内容,如果未能解决你的问题,请参考以下文章

Linux内存映射原理

STM32的外设寄存器在哪里?它们是在皮质-m 核心还是在外围单元本身?

干货|STM32寄存器版的基础知识—内存映射

第五章 S5PV210硬件结构

存储器

如何在c ++中获取eax寄存器的结构