读取内存映射 IO 是否比微控制器上的常规内存需要更长的时间?
Does it take longer to read memory-mapped IO than regular memory on a microcontroller?
我的具体上下文是STM32 ARM M0,但问题比较笼统。
读取或写入内存映射外设(例如 GPIO 端口或串行端口缓冲区)的内容与物理 RAM 中的位置是否需要相同数量的时钟?这是否因架构而异?
几乎总是这样。 AHB 或 AXI 总线比 APB 总线快得多。不仅时钟变慢了,总线宽度也变慢了。使事情变快需要消耗功率和芯片面积。最大波特率为 115200 的串行端口不需要像 DDR 或串行 SPI 闪存控制器一样快。为了缓解这种情况,一些软件会 RAM shadow peripheral registers 来加速驱动程序。通常供应商不会记录 APB 总线速度,因为他们使用来自 ARM 的 IP。某处的一些 ARM 文档会告诉您。几乎总是,您的核心内存会非常快;特别是皮质-M 上的 TCM。
ARM 是 load/store 架构。这意味着从寄存器到内存有特定的指令load/store。不能直接对内存进行操作。例如,某些 CPU 允许您将常量添加到内存值。因此通常有 'load' 和 'store' 的流水线阶段。在这个阶段,任何内存都可能有 等待状态。您的编译器和 CPU 会知道这一点,并且通常会尝试获得尽可能多的性能。如果您假设设备的内存顺序,这可能是一场灾难。
如果您有驱动程序 read
和 write
例程,通常实现寄存器缓存会更快。最好将寄存器读取和写入内联或定义为总线将来可能会发生变化。包装 read/write 对 ensure the ordering of access to a peripheral 可能是必要的。 volatile
本身可能不足以映射内存 I/O。明天硬件可能会更改为 SPI 或其他方式以节省引脚数。如果包装访问,添加阴影很容易。
从embedds.com处的图表中,您可以看到Flash/RAM在AHB总线和APB上的外围设备上。这意味着外设速度较慢。
可能有帮助:
做一些测试,然后报告你的结果!拿起示波器并执行 "oscilloscope profiling"。这意味着使用示波器来测量操作所花费的时间。通过使用直接寄存器引脚写入(为了速度和一致性)在执行一堆寄存器测试写入之前将引脚写入高电平,然后写入低电平来执行此操作。
如何对嵌入式源代码进行示波器分析
例如:写一个 pin HIGH/LOW
// set pin HIGH (set to 1)
GPIOA_ODR |= 1UL << (uint32_t)pin_index;
// set pin LOW (clear to 0)
GPIOA_ODR &= ~(1UL << (uint32_t)pin_i);
用这些包围你的测试代码:
// set oscilloscope profiling pin HIGH
// do your operations you'd like to time
// set oscilloscope profiling pin LOW
观察示波器上的方波。高脉冲时间减去一个转换时间 = 操作花费的时间!
即:你的方程式如下:
total_time = time_transition_to_LOW - time_transition_to_HIGH - pin_write_time.
要获得 pin_write_time
,这是将引脚写入高电平或低电平需要多长时间(但不是两者合并,只有 1),请快速申请写入高电平然后写入低电平,两者之间没有延迟.注意使用写入技术,使写入 LOW 和 HIGH 需要相同数量的时钟周期(即:通过使用 GPIOA_ODR
寄存器,如上所示,而不是 GPIOA_BSRR
或 GPIOA_BRR
,我最后检查了不同数量的时钟周期,具体取决于您是将引脚写为 HIGH 还是 LOW)。现在,在示波器上测量总时间,对于这个测试:
pin_write_time = time_transition_to_LOW - time_transition_to_HIGH
要写入 RAM 中的特定地址,因为您需要将其与寄存器写入进行比较,请像下面这样进行一些奇特的指针操作。假设您要写入的地址是 0x20000000
。这是一个向其写入 val
的示例。
uint32_t val = 1234567;
*((volatile uint32_t *)0x20000000UL) = val;
注意不要覆盖 RAM 中使用的实际变量。我不会详细介绍,但您可以通过修改链接器脚本以保留某些地址空间来保证这一点,或者您可以只检查并打印代码中使用的一些变量地址,并确保选择远测试地址远离这些,这样你就可以知道你没有覆盖正在使用的真实变量。
请注意,当然,您可以只使用普通代码写入变量,但是执行上述操作很重要,因此您可以测试不同的地址,因为内存是根据地址分段的,不同的内存段有不同的公交车的种类。请参阅芯片数据表中的内存映射。例如:从 DS11243 (DocID028294 Rev 6)、p102、图 22。内存映射(见下文),您可以看到您有各种 RAM 组要测试:
- ITCM 内存
- DTCM 内存
- SRAM1
- SRAM2
注意reading/writingto/from电池备份SRAM(BKPSRAM)和Flash需要特殊的访问程序and/or函数,所以上面的指针操作成功了自己工作。您必须按照芯片参考手册中指定的正确程序进行操作。
不管怎样,做一些测试然后回复我们。我对你的结果很感兴趣。
参考文献:
没有必要。答案的细节将取决于实现(如其他答案中所述)。在 ARM 架构中,内存读取不关心互连另一端的外设 - 无论是外设还是内存控制器。
在低性能部分,两种类型的访问可能都是单周期的。一旦时钟速度开始增加,就需要考虑实现细节——更有可能至少优化一些内存访问(因为取指令是执行性能的主要限制)。
通过高性能的实时优化实现,您可能会发现一些内存访问比延迟优化的外设访问慢得多 - 但仍然会有一些 'tightly coupled memory' 的执行速度与或比这些外围设备更快。
我的具体上下文是STM32 ARM M0,但问题比较笼统。
读取或写入内存映射外设(例如 GPIO 端口或串行端口缓冲区)的内容与物理 RAM 中的位置是否需要相同数量的时钟?这是否因架构而异?
几乎总是这样。 AHB 或 AXI 总线比 APB 总线快得多。不仅时钟变慢了,总线宽度也变慢了。使事情变快需要消耗功率和芯片面积。最大波特率为 115200 的串行端口不需要像 DDR 或串行 SPI 闪存控制器一样快。为了缓解这种情况,一些软件会 RAM shadow peripheral registers 来加速驱动程序。通常供应商不会记录 APB 总线速度,因为他们使用来自 ARM 的 IP。某处的一些 ARM 文档会告诉您。几乎总是,您的核心内存会非常快;特别是皮质-M 上的 TCM。
ARM 是 load/store 架构。这意味着从寄存器到内存有特定的指令load/store。不能直接对内存进行操作。例如,某些 CPU 允许您将常量添加到内存值。因此通常有 'load' 和 'store' 的流水线阶段。在这个阶段,任何内存都可能有 等待状态。您的编译器和 CPU 会知道这一点,并且通常会尝试获得尽可能多的性能。如果您假设设备的内存顺序,这可能是一场灾难。
如果您有驱动程序 read
和 write
例程,通常实现寄存器缓存会更快。最好将寄存器读取和写入内联或定义为总线将来可能会发生变化。包装 read/write 对 ensure the ordering of access to a peripheral 可能是必要的。 volatile
本身可能不足以映射内存 I/O。明天硬件可能会更改为 SPI 或其他方式以节省引脚数。如果包装访问,添加阴影很容易。
从embedds.com处的图表中,您可以看到Flash/RAM在AHB总线和APB上的外围设备上。这意味着外设速度较慢。
可能有帮助:
做一些测试,然后报告你的结果!拿起示波器并执行 "oscilloscope profiling"。这意味着使用示波器来测量操作所花费的时间。通过使用直接寄存器引脚写入(为了速度和一致性)在执行一堆寄存器测试写入之前将引脚写入高电平,然后写入低电平来执行此操作。
如何对嵌入式源代码进行示波器分析
例如:写一个 pin HIGH/LOW
// set pin HIGH (set to 1)
GPIOA_ODR |= 1UL << (uint32_t)pin_index;
// set pin LOW (clear to 0)
GPIOA_ODR &= ~(1UL << (uint32_t)pin_i);
用这些包围你的测试代码:
// set oscilloscope profiling pin HIGH
// do your operations you'd like to time
// set oscilloscope profiling pin LOW
观察示波器上的方波。高脉冲时间减去一个转换时间 = 操作花费的时间!
即:你的方程式如下:
total_time = time_transition_to_LOW - time_transition_to_HIGH - pin_write_time.
要获得 pin_write_time
,这是将引脚写入高电平或低电平需要多长时间(但不是两者合并,只有 1),请快速申请写入高电平然后写入低电平,两者之间没有延迟.注意使用写入技术,使写入 LOW 和 HIGH 需要相同数量的时钟周期(即:通过使用 GPIOA_ODR
寄存器,如上所示,而不是 GPIOA_BSRR
或 GPIOA_BRR
,我最后检查了不同数量的时钟周期,具体取决于您是将引脚写为 HIGH 还是 LOW)。现在,在示波器上测量总时间,对于这个测试:
pin_write_time = time_transition_to_LOW - time_transition_to_HIGH
要写入 RAM 中的特定地址,因为您需要将其与寄存器写入进行比较,请像下面这样进行一些奇特的指针操作。假设您要写入的地址是 0x20000000
。这是一个向其写入 val
的示例。
uint32_t val = 1234567;
*((volatile uint32_t *)0x20000000UL) = val;
注意不要覆盖 RAM 中使用的实际变量。我不会详细介绍,但您可以通过修改链接器脚本以保留某些地址空间来保证这一点,或者您可以只检查并打印代码中使用的一些变量地址,并确保选择远测试地址远离这些,这样你就可以知道你没有覆盖正在使用的真实变量。
请注意,当然,您可以只使用普通代码写入变量,但是执行上述操作很重要,因此您可以测试不同的地址,因为内存是根据地址分段的,不同的内存段有不同的公交车的种类。请参阅芯片数据表中的内存映射。例如:从 DS11243 (DocID028294 Rev 6)、p102、图 22。内存映射(见下文),您可以看到您有各种 RAM 组要测试:
- ITCM 内存
- DTCM 内存
- SRAM1
- SRAM2
注意reading/writingto/from电池备份SRAM(BKPSRAM)和Flash需要特殊的访问程序and/or函数,所以上面的指针操作成功了自己工作。您必须按照芯片参考手册中指定的正确程序进行操作。
不管怎样,做一些测试然后回复我们。我对你的结果很感兴趣。
参考文献:
没有必要。答案的细节将取决于实现(如其他答案中所述)。在 ARM 架构中,内存读取不关心互连另一端的外设 - 无论是外设还是内存控制器。
在低性能部分,两种类型的访问可能都是单周期的。一旦时钟速度开始增加,就需要考虑实现细节——更有可能至少优化一些内存访问(因为取指令是执行性能的主要限制)。
通过高性能的实时优化实现,您可能会发现一些内存访问比延迟优化的外设访问慢得多 - 但仍然会有一些 'tightly coupled memory' 的执行速度与或比这些外围设备更快。