我正在使用带有 Cortex M4 的 STM32F407,并且通过在DWT_CYCCNT调用我在汇编中实现的函数(C 语言)之前和之后直接读取来测量函数的周期计数。我想了解我得到的结果。
08000610 <my_function>:
8000610: f04f 20ff mov.w r0, #4278255360 ; 0xff00ff00
8000614: f04f 11ff mov.w r1, #16711935 ; 0xff00ff
8000618: ea81 0100 eor.w r1, r1, r0
800061c: ea81 0100 eor.w r1, r1, r0
8000620: ea81 0100 eor.w r1, r1, r0
8000624: ea81 0100 eor.w r1, r1, r0
8000628: 4770 bx lr
800062a: bf00 nop
Run Code Online (Sandbox Code Playgroud)
执行上述(包括函数调用)需要21个周期。当我添加一条eor指令时:
08000610 <my_function>:
8000610: f04f 20ff mov.w r0, #4278255360 ; 0xff00ff00
8000614: f04f 11ff mov.w r1, #16711935 …Run Code Online (Sandbox Code Playgroud) ARM 文档对 PUSH 和 POP 做了以下说明
PUSH 将寄存器存储在堆栈上,编号最低的寄存器使用最低的内存地址,编号最高的寄存器使用最高的内存地址。
POP 从堆栈中加载寄存器,编号最低的寄存器使用最低的内存地址,编号最高的寄存器使用最高的内存地址。
我发现的一个教程是这么说的
...{} 中的寄存器可以按任意顺序指定,但它们在堆栈中出现的顺序是固定的...
因此,根据上述解释,一个 PUSH 括号中的寄存器顺序并不重要。即PUSH {R0,R1,R2},PUSH {R2,R1,R0}, 和PUSH {R1,R2,R0}所有都会导致堆栈中的某种排序,因为“...最低/最高编号的寄存器(R0/R2)使用最低/最高(堆栈)内存地址...”。
这是否意味着如果单个 PUSH 指令在括号中具有多个寄存器,汇编器会自动在目标代码中对推送操作进行排序,其中PUSH R2首先进入堆栈以获取最高地址,然后以PUSH R1获取PUSH R0最低地址结束?
因此,如果我想保证 R2 在 LIFO 堆栈中最后压入并首先弹出(即 SP 指向 R2 或让 R2 获取最低堆栈地址),我不能在一个 PUSH 括号语句中执行此操作,而只能单独使用PUSH R0; PUSH R1; PUSH R2?
我住在一个流行的微控制器不易获得的国家.我能找到的唯一基于arm的是STM32F4 Discovery和STM32VL Discovery.后者更便宜,因此这是优选的.
所以问题是,我正朝着正确的方向前进吗?这些微控制器是否专门用于嵌入式编程?(我是初学者)如果有人能让我朝着正确的方向前进,那将会非常有帮助.
谢谢!
我有一些ARM代码,我试图在Cortex M3上运行.我用Thumb编写的大部分代码都是用C语言编写的 - 但对于某些函数,我希望能够运行普通的ARM代码(据我所知,这在M3上是可行的吗?).
所以...
原始C代码和汇编:
int donothing(int a) {
return a;
}
00000068 <donothing>:
68: e52db004 push {fp} ; (str fp, [sp, #-4]!)
6c: e28db000 add fp, sp, #0
70: e24dd00c sub sp, sp, #12
74: e50b0008 str r0, [fp, #-8]
78: e51b3008 ldr r3, [fp, #-8]
7c: e1a00003 mov r0, r3
80: e28bd000 add sp, fp, #0
84: e8bd0800 ldmfd sp!, {fp}
88: e12fff1e bx lr
Run Code Online (Sandbox Code Playgroud)
编译使用 arm-none-eabi-gcc -mfloat-abi=soft -nostdinc -nostdlib
我转而使用blx r4- 如果基地址&3为0,它应该交换.
在GDB中逐步执行此操作,即使地址包含正确的数据,它也会一旦到达显示的行,就会进入HardFaults.
(gdb) …Run Code Online (Sandbox Code Playgroud) 有一种RAII风格的C++模式,它通过创建一个没有成员并依赖于类的构造函数和析构函数的类来实现基于作用域的所有权(以及在函数返回时自动调用析构函数的事实).例如,标准std::lock_guard实现了这种模式.
我正在编程一个EFM32 ARM Cortex-M微控制器,并提出了这个使用类似风格的类:
#include <em_int.h>
class InterruptGuard final {
public:
explicit inline InterruptGuard() {
INT_Disable();
}
InterruptGuard(const InterruptGuard &other) = delete;
InterruptGuard(const InterruptGuard &&other) = delete;
inline ~InterruptGuard() {
INT_Enable();
}
InterruptGuard &operator=(const InterruptGuard &other) = delete;
InterruptGuard &operator=(const InterruptGuard &&other) = delete;
};
Run Code Online (Sandbox Code Playgroud)
因此,如果我想在具有多个return语句的函数内禁用中断,我可以确保它们将重新启用,而不必担心在每个return语句中显式重新启用它们.
注意:INT_Enable和INT_Disable函数实现一个计数器,因此它INT_Enable会做正确的事情,只有在真正需要启用时才启用中断.所以这个类应该是可以正确嵌套的.
void func() {
InterruptGuard guard;
// ...
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:
当我使用这个模式,是编译器会做"正确的事"在这里,优化了对象(所以没有内存实际上是由这个类消费),只是内联INT_Enable和INT_Disable调用到使用功能InterruptGuard类?
我有一个简单的C程序:
int main(){
unsigned int counter = 0;
++counter;
++counter;
++counter;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用以下编译标志:
arm-none-eabi-gcc -c -mcpu=cortex-m4 -march=armv7e-m -mthumb
-mfloat-abi=hard -mfpu=fpv4-sp-d16 -DPART_TM4C123GH6PM -O0
-ffunction-sections -fdata-sections -g -gdwarf-3 -gstrict-dwarf
-Wall -MD -std=c99 -c -MMD -MP -MF"main.d" -MT"main.o" -o"main.o" "../main.c"
Run Code Online (Sandbox Code Playgroud)
(为简洁起见,删除了一些-I指令)
请注意,我故意使用-O0禁用优化,因为我有兴趣了解编译器要优化的内容.
这将编译为ARM Cortex-M4的以下程序集:
6 unsigned int counter = 0;
00000396: 2300 movs r3, #0
00000398: 607B str r3, [r7, #4]
7 ++counter;
0000039a: 687B ldr r3, [r7, #4]
0000039c: 3301 adds r3, #1
0000039e: 607B str r3, [r7, …Run Code Online (Sandbox Code Playgroud) 我在拇指模式下使用STM32L486ZG板.我正在运行一个没有任何RTOS的简单裸机应用程序.我使用FSM将外部SRAM连接到电路板.外部SRAM位于地址0x60000000.系统初始化并以72MHz运行(我已尝试过这个问题,频率从18-80 MHz)现在在我的主要功能中,我有以下代码:
int main(){
asm volatile (
"push {r0}\n"
"mov r0, #0x60000000\n"
"add r0, #0x400\n"
"stmdb r0!, {r1-r12}\n"
"ldmia r0!, {r1-r12}\n"
"pop {r0}\n"
);
}
Run Code Online (Sandbox Code Playgroud)
根据此代码,在执行此main函数后不应更改寄存器,但在以下指令之后不是这种情况
ldmia r0!, {r1-r12}
Run Code Online (Sandbox Code Playgroud)
即r9执行后不正确.stmdb指令工作正常但未正确ldmia加载数据.我通过查看内存中的内容来验证这一点.
这个问题在ldmia指令中的任何参数都是持久的:第9个寄存器总是受到影响.
说明: 假设我正在调试此代码,下一条要执行的指令是:
stmdb r0!, {r1-r12}
Run Code Online (Sandbox Code Playgroud)
加强所有这些寄存器后,已保存在内存和价值r0是0x600003d0
记忆的内容:
0x600003D0 00000000 40021008 0000000C .......@....
0x600003DC 40000000 00000000 00000000 ...@........
0x600003E8 20017FEC 00000000 00000000 ì.. ........
0x600003F4 00000000 00000000 00000000 ............
Run Code Online (Sandbox Code Playgroud)
登记册内容:
r0 0x600003d0
r1 0x00000000
r2 0x40021008
r3 0x0000000c
r4 …Run Code Online (Sandbox Code Playgroud) 在M0 +设备上是否存在除零除外?
我知道Cortex M3和M4设备有此功能.
当下面的代码针对像MSP430微控制器这样的16位整数机运行时,s32得到65446.
#include <stdint.h>
uint16_t u16c;
int32_t s32;
int main()
{
u16c = 100U;
s32 = 10 - u16c;
}
Run Code Online (Sandbox Code Playgroud)
我的理解是10 - u16c获取unsigned int的隐式类型提升.数学上10 - u16c等于-90.但是如何将负数表示为unsigned int呢?当-90被提升为unsigned int时,是否意味着忽略了数字的符号?
让我们假设,数字的符号被忽略.
90的二进制表示是00000000 01011010.当这被分配给s3232位宽的有符号整数变量时,转换是如何发生的?
为了s32等于65446,90必须采用2的补码.那就是00000000 10100110.
我不了解s32成为65446 的过程.
在像ARM这样的32位宽整数机器中,s32是-90,这是正确的.
要修复在16位整型机这种情况下,需要的类型转换(int16_t)为u16c.这是如何解决这个问题的?
添加s32了IAR Workbench(右下角)所示的hexa数据表示.它显示s32成为0x0000FFA6.因此,对于MSP430,从无符号16位转换为带符号32位的机器实现,它只是预先设置16 0位.
我在cortex-m0上有一个svc异常处理程序的以下实现:
int __attribute__((naked))
sv_call_handler(uint32_t n, uint32_t arg1, uint32_t arg2, uint32_t arg3,
uint32_t arg4, uint32_t arg5)
{
irq_off();
Run Code Online (Sandbox Code Playgroud)
当我为cortex-m0构建它时,它看起来像这样:
0x7a50 <sv_call_handler> movs r4, r0
0x7a52 <sv_call_handler+2> str r1, [r7, #12]
0x7a54 <sv_call_handler+4> str r2, [r7, #8]
0x7a56 <sv_call_handler+6> str r3, [r7, #4]
0x7a58 <sv_call_handler+8> bl 0x3194 <irq_off>
Run Code Online (Sandbox Code Playgroud)
当然,导致硬故障,R7中的值是"未定义的",并且它很可能包含不在地址范围内的值.
当我删除裸属性时,程序集更有意义:
0x7a50 <sv_call_handler> push {r4, r5, r7, lr}
0x7a52 <sv_call_handler+2> sub sp, #32
0x7a54 <sv_call_handler+4> add r7, sp, #8
0x7a56 <sv_call_handler+6> str r0, [r7, #12]
Run Code Online (Sandbox Code Playgroud)
我之前没有使用过裸属性,为什么现在会发生这种情况呢?与svc异常处理程序是一个特殊情况这一事实有什么关系吗?