繁体   English   中英

外设读取 vs RAM 读取 + RAM 写入

[英]Peripheral read vs RAM read + RAM write

我正在使用 IOs 调试关键代码,我遇到了一个难题:

这两个功能之间最快的是什么?
我的 CPU 在哪个 function 花费的时间会更少?

A:CPU读取一个外设寄存器,写入外设寄存器

void d_toggle_pin(void)
{ 
  NRF_P1->OUT ^= 1 << Debug_Pin; 
}

B:CPU读一个RAM变量并写外设寄存器+写一个RAM变量

void d_toggle_pin(void)
{ 
  static byte pin_state = 0;
  if(pin_state) 
  { 
    NRF_P1->OUTCLR = 1U << Debug_Pin;  
    pin_state = 0;
  }
  else
  { 
    NRF_P1->OUTSET = 1U << Debug_Pin;  
    pin_state = 1;
  }
}

我正在使用 nrf52840(皮质 M4 CPU),但无论实现如何,答案都是一样的

TL;DR:第一个版本表现更好。


性能方面的差异是微不足道的。 Cortex M3 及更高版本具有简单的分支预测和流水线,但这不会对这里的这个简单的小代码产生太大影响。 当然,第二个版本在分支预测器上可能会稍微粗糙一些,因为它们是两个独立的内存映射寄存器,但差异可以忽略不计。

如果您坚持要比较它们,那么这里有一个 gcc ARM non-eabi -O3的小基准,我在其中替换了寄存器名称并将“调试引脚”设为硬编码常量: https://godbolt.org/z/88vn1EqKj 该分支已被优化掉,但第一个版本的性能仍然稍好一些。


然而,您在这里的首要任务应该是功能性和可读性。 这两个函数都可以,但如果我要剖析它们......

  • XOR 版本的优点是 XOR 是一种切换位的惯用方式,因此它是可读的。 您还可以保证代码始终与实际寄存器值同步,以防万一。

  • XOR 版本的缺点是,对硬件寄存器进行读-修改-写访问有时会出现问题,因为它会引入副作用,并且在某些情况下还会导致重入问题。 因此,与其将寄存器值用作 XOR 的占位符,我认为您的其他版本单独跟踪端口并仅执行写访问就可以了。


其他注意事项:

1 <<...在 C 中总是错误的。您几乎可以肯定永远不要移动 signed int ,它是 integer 常量1的类型。 例如1 << 31调用未定义的行为。 始终使用1u

为诸如设置/清除/切换 GPIO 引脚之类的非常基本的事情编写包装函数已经完成了数百次……并且没有人设法编写比这更容易阅读的 function 包装函数:

  • reg |= mask (设置)
  • reg &= ~mask (清除)
  • reg ^= mask; (切换)

这是惯用的、超快的、超可读的 C 代码,所有 C 程序员 100% 都可以轻松理解。 在查看了数百个失败的、臃肿的 GPIO HAL 之后,我可以自信地说简单 GPIO 的抽象可以而且只会导致臃肿。 我自己写了很多这样的东西,但总是出错。

(对于带有一堆路由寄存器、中断处理、奇怪的状态标志等的更复杂的 GPIO,那么一定要编写一个 HAL 和一个驱动程序。但不是为了只做简单的端口 I/O。)

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM