我的猜测是__no_operation()内部(ARM)指令应该执行1 /(168 MHz),前提是每个NOP指令在一个时钟周期内执行,我想通过文档验证.
是否有关于处理器的指令周期执行时间的信息的标准位置?我试图确定STM32f407IGh6处理器执行运行在168 MHz的NOP指令应该花多长时间.
某些处理器每个指令周期需要多次振荡,有些处理器在将时钟周期与指令周期进行比较时为1比1.
术语"指令周期"甚至不存在于STMicro提供的整个数据表中,也不存在于他们的编程手册中(列出处理器的指令集,顺便说一下).但是,8051文档明确定义了其指令周期执行时间,以及其机器周期特性.
以下是故事:我正在使用AC6 Toolpack为Linux中的ARM Cortex-M0处理器开发C++软件.在我使用Keil(在windows中)(拥有自己的工具链)之前,我已经迁移到GNU-toolchain((GNU Tools for ARM Embedded Processors)5.2.1).我意识到的第一件事是; 二进制文件大小大幅增加.我已经测试了每个编译器优化(链接时间优化除外,它在内联汇编时出错,不是问题的一部分,但可能与答案有关).然后开始使用任何可用工具检查可执行文件(elf文件不是bin,gnu生成两者):objdump,readelf,nm.我发现一些符号导致尺寸增加,重要的是:' d_print_comp_inner',' d_exprlist',' d_template_args'.但不知道是什么导致这些函数出现在二进制文件中.(我使用过最小的库:nano newlib).长话短说我开始逐一消除代码以找到罪魁祸首.最后是抽象方法声明!
将功能定义为
virtual Return_type function_name(...)=0;
Run Code Online (Sandbox Code Playgroud)
代替
virtual Return_type function_name(...);
Run Code Online (Sandbox Code Playgroud)
添加45 KB和我提到的符号.这是源代码中唯一的变化.存在基类中的空定义.请注意:方法仍然是虚拟的,并在子类中被覆盖
没有抽象类的大小输出:
text data bss dec hex filename
15316 24 4764 20104 4e88 temc_discovery.elf
Run Code Online (Sandbox Code Playgroud)
抽象类的大小输出:
text data bss dec hex filename
61484 128 4796 66408 10368 temc_discovery.elf
Run Code Online (Sandbox Code Playgroud)
这里的方法是抽象的符号和他们的大小,消除了两个版本中出现的符号和他们的大小.(使用nm工具.不完整列表,大小> = 0x60的列表)
00002de4 t d_print_comp_inner
00001a34 t d_exprlist
00000ca4 t d_template_args
00000678 t d_type
00000574 t d_print_mod
000003f8 t d_encoding
000003e0 r …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用远程GDB调试嵌入式项目.我的系统:
我的.gdbinit文件中有以下内容:
target remote localhost:2331 #(I remove this line when debugging with CLion)
set verbose on
file "/path_to_output_file/blinky.elf"
monitor reset
break main
Run Code Online (Sandbox Code Playgroud)
几天来困扰我的事情是,如果我直接从终端调试gdb,这可以正常工作,但是当我在CLion中使用调试器时却没有.在CLion我收到错误:
此目标不支持"monitor"命令.
我的理论是终端接受"监视器重置"命令(至少它没有抱怨).另一方面,CLion会打印错误,但之后会显示继续执行而不进行重置.结果似乎是当我在CLion中启动一个新的调试会话时,我不会在main()的开头开始.
CLion是否阻止了监视器命令?如果是这样,那么为什么并且有解决方法?
我觉得我的问题可能与CPP-7322和CPP-7256有关.
我成功地编写了一个在我的 STM32F4 上运行的裸机 C 程序。没什么特别的,只是通常的led-blinky-program。在这个项目中,我编写了初始化例程,用于清除该.bss部分并.data自己初始化该部分。
这并不复杂。在链接器脚本中,我只是指示链接器创建一些标记.dataand.bss部分的开始和结束的符号。
.data 0x20001000 :
ALIGN(4)
{
__etext = LOADADDR(.data);
__data_start__ = ADDR(.data) ;
*(.data*) ;
__data_end__ = ADDR(.data) + SIZEOF(.data) ;
} >RAM AT>ROM
.bss :
ALIGN(4)
{
__bss_start__ = ADDR(.bss) ;
*(.bss*) ;
__bss_end__ = ADDR(.bss) + SIZEOF(.bss) ;
} >RAM
Run Code Online (Sandbox Code Playgroud)
然后我在我的代码中使用了这些符号:
extern unsigned int __etext;
extern unsigned int __data_start__;
extern unsigned int __data_end__;
extern unsigned int __bss_start__;
extern unsigned int __bss_end__;
void Reset_Handler()
{
unsigned …Run Code Online (Sandbox Code Playgroud) 这可能是一个愚蠢的问题,但我对 ARM CMSIS 缓存操作函数的措辞有些困惑。
ARM CMSIS 公开了可以“清除”缓存的函数、可以“使”缓存无效的函数,以及一组同时执行这两种操作的函数。
这些实际上做什么还不清楚,坦率地说,arm 文档让我感到困惑:
- 您可以在一次操作中使整个数据缓存(刷新数据缓存)无效,而无需回写脏数据。
- 您可以在不写回任何脏数据的情况下使单个行无效(刷新数据缓存单个条目)。
- 您可以逐行执行清洁。数据只有在遇到脏行时才通过写缓冲区写回,清除后的行保留在缓存中(clean data cache single entry)。您可以使用它们在数据缓存中的索引或它们在内存中的地址来清理缓存行。
- 您可以在一次操作中清理和刷新单个行,使用它们在数据缓存中的索引或它们在内存中的地址。
我的理解是,缓存中的“刷新”数据将导致其被写回主内存,而缓存中的“无效”数据将导致处理器在下次尝试访问相关数据时重新读取主内存。
然而,ARM 文档似乎暗示刷新操作实际上是一个失效操作,而我一直在想的是刷新操作实际上是一个“干净”操作。
我想我只是严重误解了文档。有人可以指出我正确的方向吗?
如果相关,我专门为皮质 M7 开发。
阅读这个问题的答案时,我注意到它register在 C++17 中不再是有效的存储说明符。一些评论甚至表明编译器已经忽略register了一段时间。
我将 GCC 6.x 与 ARM Cortex-M MCU 一起使用,并且有一些带有内联汇编的代码,这些代码绝对需要在寄存器中有一个变量。以前我认为register关键字会为我做这件事,但显然它没有。
编辑:为什么我需要在寄存器中存储一些东西?
我正在使用 ARM LDREX/STREX指令实现无锁环形缓冲区。我需要将 ARMLDREX指令的结果存储在寄存器中,因为将其存储在内存中会破坏 Cortex-M 上的整个机制。
编辑:示例代码。
这是从环形缓冲区中截取的代码片段,用于说明问题的要点。兴趣点是__LDREXW,__STREXW并且__CLREX都在 中定义cmsis_gcc.h。它们是 ARM 同步原语的内在函数。我使用它们来实现无锁机制。
template<typename T, uint32_t maxCount>
class RingBuffer final {
__attribute__((aligned(8)))
T buffer[maxCount];
uint32_t start;
uint32_t end;
bool pushBack(const T &item) {
register uint32_t exclusiveEnd;
register uint32_t oldEnd; …Run Code Online (Sandbox Code Playgroud) 对于我自己的malloc动态内存分配实现,我想在运行时访问堆基地址和堆限制地址。我知道它们在我的 startup.s 文件中作为全局变量导出。如何访问这些地址?目标平台是带有 GCC 的 ARM Cortex-M4。
用.globl声明的符号对于链接器是已知的,并且应该可以使用c 代码中的extern符号访问。但是使用这种方法,我得到的值与生成的.map文件中的值不同。
以下是 startup.s 文件中定义的堆符号:
#ifdef __HEAP_SIZE
.equ Heap_Size, __HEAP_SIZE
#else
.equ Heap_Size, 0x00000C00
#endif
.globl __HeapBase
.globl __HeapLimit
__HeapBase:
.if Heap_Size
.space Heap_Size
.endif
.size __HeapBase, . - __HeapBase
__HeapLimit:
.size __HeapLimit, . - __HeapLimit
.section .vectors
.align 2
.globl __Vectors
Run Code Online (Sandbox Code Playgroud)
这是我在我的 c 函数中的访问:
#ifdef __HEAP_SIZE
.equ Heap_Size, __HEAP_SIZE
#else
.equ Heap_Size, 0x00000C00
#endif
.globl __HeapBase
.globl __HeapLimit
__HeapBase:
.if Heap_Size
.space Heap_Size
.endif
.size __HeapBase, …Run Code Online (Sandbox Code Playgroud) 我正在阅读 Cortex M4 TRM 以了解指令执行周期。但是,那里有一些令人困惑的描述
STR Rx,[Ry,#imm]是总是一个周期,这是因为地址生成在初始循环中执行,并且所述数据存储在同一时间作为下一个指令正在执行执行。
如果存储到
write buffer,并且write buffer已满或未启用,则下一条指令将延迟,直到存储完成。
如果存储不是到
write buffer,例如代码段,并且该事务停止,则只有在完成之前执行另一个加载或存储操作时才会感受到对计时的影响
LDR可以通过跟随LDR和进行流水线化STR,但STR不能通过跟随指令进行流水线化。其他指令在
STR使用寄存器偏移后无法流水线化。STR 只能在跟随 LDR 时被流水线化,但在存储之后什么都不能流水线化。由于写缓冲区的原因,即使停止STR通常也只需要两个周期
更具体的让我困惑的是:
一季度。1和2似乎相互冲突,STR实际需要多少个周期,1 还是 2?(虽然我的实验显示为 1)
Q2。2表示如果存储通过write buffer并且不可用,它仍然会停止流水线,但是如果存储绕过它,流水线可能只在加载/存储指令跟随时停止。闻起来write buffer只会让事情变得更糟。这是有悖常理的。
Q3。3表示STR不能与后续指令流水线化,但是2 …
ARMv6 之前的 MPU/MCU 有SWP指令(例如良好的 ole 和仍然有效的 ARM7TDMI)。在 ARMv6 架构中LDREX,/STREX对已被引入和SWP删除。然而,除了一个例外 \xe2\x80\x93 ARMv6-M (Cortex-M0/M0+) 既没有LDREX/STREX也没有SWP. 奇怪的。
那么如何在这个平台上实现原子操作/同步原语呢?以老式的方式使用“启用中断”/“禁用中断”?或者我错过了什么?例如ChromiumOS使用老式方法。
\n禁用中断适用于单核 MCU,但多核 MCU 又如何呢?此方法是否适用于例如RP2040 (双核 Cortex-M0+, Raspberry Pi Pico的核心)?
\n我注意到一个无法解释的行为:函数的执行时间似乎取决于它在闪存 ROM 中的位置。我使用的是 STM32F746NGH 微控制器(基于 ARM-cortex M7)和 STM32CubeIDE(适用于 ARM 的 GCC 编译器)。
这是我的测试:
我初始化了 SysTick 计数器以触发固定周期 T = 1ms 的中断。在中断处理程序中,我在两个线程之间切换(像 RTOS):让我们将它们命名为 Thread1 和 Thread2。
每个线程只是简单地递增一个变量。
这是两个线程的代码:
uint32_t ctr1, ctr2;
void thread1(void)
{
while(1)
{
ctr1++;
}
}
void thread2(void)
{
while(1)
{
ctr2++;
}
}
Run Code Online (Sandbox Code Playgroud)
在监视这些变量时,我注意到 ctr2 的增量比 ctr1 快得多。
使用此代码:线程1的地址是0x08000418,线程2的地址是0x0800042C。
然后,我尝试将另一个函数放在 thread1 之前的内存中:我们将其命名为 thread0。
所以我的新代码是:
uint32_t ctr0, ctr1, ctr2;
void thread0(void)
{
while(1)
{
ctr0++;
}
}
void thread1(void)
{
while(1)
{
ctr1++;
}
}
void thread2(void)
{
while(1) …Run Code Online (Sandbox Code Playgroud)