我刚刚发现ARM我正在编写代码(Cortex M0),不支持未对齐的内存访问.
现在在我的代码中我使用了很多打包结构,而且我从来没有得到任何警告或硬错误,那么当它不允许未对齐访问时,Cortex如何访问这些结构的成员?
我正在尝试一个用于 ARM-C 互操作的简单程序。这是代码:
#include<stdio.h>
#include<stdlib.h>
int Double(int a);
extern int Start(void);
int main(){
int result=0;
printf("in C main\n");
result=Start();
printf("result=%d\n",result);
return 0;
}
int Double(int a)
{
printf("inside double func_argument_value=%d\n",a);
return (a*2);
}
Run Code Online (Sandbox Code Playgroud)
汇编文件如下:
.syntax unified
.cpu cortex-m3
.thumb
.align
.global Start
.global Double
.thumb_func
Start:
mov r10,lr
mov r0,#42
bl Double
mov lr,r10
mov r2,r0
mov pc,lr
Run Code Online (Sandbox Code Playgroud)
在 LPC1769(嵌入式艺术家板)上调试期间,我在指令“ result=Start() ”上收到硬故障错误。我正在尝试在这里进行arm-C网络互连。上述语句(result=Start())执行过程中lr值为0x0000029F,即错误指令所在,pc值为0x0000029E。这就是我在 r1 中得到错误指令的方式
__asm("mrs r0,MSP\n"
"isb\n"
"ldr r1,[r0,#24]\n");
Run Code Online (Sandbox Code Playgroud)
有人可以解释一下我哪里出错了吗?任何解决方案表示赞赏。先感谢您。
我是 cortex-m3 的初学者,正在使用由 Code_Red 提供支持的 NXP LPCXpresso IDE。这是我的代码的反汇编。
IntDefaultHandler:
00000269: …Run Code Online (Sandbox Code Playgroud) 据我所知,ARM Cortex-M CPU始终处于Thumb状态,这意味着:
程序计数器指示的拇指状态为奇数(LSB = 1).分支到偶数地址将导致异常,因为不允许切换回ARM状态.
但是,当我使用CortexM0和M4 CPU时,PC始终是均匀的.每次分支时,LR记录PC + 1,每次返回时,PC都给出LR-1.
例如,如果lr = 0x0000_01D5,
执行
BX lr
然后PC应该是0x0000_01D5,而它给出0x0000_01D4.
这不可能吗?
任何评论将不胜感激.
我正在尝试学习使用 cortex m0 处理器。我有一个 stm32f0 开发板,它可以让我查看每个地址的每一位并轻松上传新的二进制文件。我一直在阅读大量手册,了解许多规则和功能,但仍然不知道程序计数器在重置时从哪里开始,它期望什么类型的参数,我什至不知道如何编写诸如 add 之类的东西或二进制形式的 str/ldr。我读过的手册中是否遗漏了这些基本知识?
它说 m0 有一个完整的降序堆栈,但它似乎表明起点在另一端(0x00000000)。如果向量表也可以用外行的术语来解释,那就太好了。
我试图在我的代码中检索IRQ处理程序的返回地址.我的目标是在看门狗定时器到期之前和复位之前使用WDT_IRQHandler()保存PC的值以进行调试.我也在用其他IRQ测试这种方法来检查我是否掌握了这个想法.但似乎我没有.
我已经阅读了可用的文档.我知道当异常发生时,8个寄存器被推送到堆栈:R0,R1,R2,R3,R12,LR,PC和XPSR.
我还读过堆栈自动双字对齐.所以在我看来,检索返回地址就像这样简单:
检查附加的调试器,似乎不是这种情况,该内存地址的内容并不总是指向闪存区域,甚至指向有效区域,并且在任何情况下,它都不是PC在POP指令.
代码工作正常,所以我认为这是一个问题,我理解它是如何工作的.
如果我检查反汇编,在某些IRQ中,在POPping(?)之前向sp添加一个常量
00001924: 0x000009b0 ...TE_IRQHandler+280 add sp, #36 ; 0x24
00001926: 0x0000f0bd ...TE_IRQHandler+282 pop {r4, r5, r6, r7, pc}
Run Code Online (Sandbox Code Playgroud)
在其他IRQ中,这不会发生.
我知道可能会发生更多的寄存器被推送到堆栈,所以我如何确定在哪个偏移量来检索PC?
如果我在代码仍在IRQ处理程序中时检查SP周围的内存转储,我可以发现返回地址,但它始终位于一个奇怪的位置,与SP相比具有负偏移.我无法理解如何获得正确的地址.
ARM Cortex-M内核文档说,在执行异常条目堆栈框架时.这导致寄存器R0,R1,R2,R3,R12,LR,PC,xPSR被推到当前堆栈上.我的问题是为什么这种方式只推动那些寄存器而不是所有的上下文?例如,如果某些数据位于R5寄存器中,则异常处理程序使用该寄存器时将覆盖该数据.
异常处理程序本身的编译函数会推送一些寄存器(以及所有其他常规函数,因为异常处理函数没有区别),但经过大量调试后我发现事实并非总是如此,因为不同的变体寄存器被推送然后恢复.
我正在使用以下 CFLAGS 为 Cortex-M4 (STM32F4) 编译我的 FreeRTOS 应用程序:
-fpic -msingle-pic-base -mpic-data-is-text-relative -mpic-register=r10
Run Code Online (Sandbox Code Playgroud)
并-fpic为链接器设置了标志。
矢量表已正确复制到 RAM,并更改了当前正在启动的特定固件插槽设备的偏移值。
然而,从开始时隙固件主应用程序后,在设备挂起HardFault与IMPRECISERR集CFSR寄存器。执行以下行时,它发生在vPortSVCHandler 中:
ldmia r0!, {r4-r11, r14}
Run Code Online (Sandbox Code Playgroud)
此操作后,链接寄存器值等于0000 0000。
我还应该做些什么来为 STM32 正确创建位置无关的应用程序?重新定位全局偏移表?如果是,在内存中的哪个位置?
编辑
根据以下问题,我的目标是使用引导加载程序和 2 个固件插槽创建应用程序。我需要 FLASH 中的固件插槽,以便我的设备能够在运行时更新。为什么我需要用 PIC 标志编译固件?每个插槽在内存中都有自己的空间。默认情况下,它是为第一个插槽地址编译的。从第二个插槽运行这样的二进制文件,为第一个插槽准备,是不可能的,因为数据访问等(老实说,这是我第一次使用 GOT 和 PIC 编译)是基于程序计数器寄存器值(当前位置在程序存储空间)。固件更新使用当前未使用的下一个空闲插槽。因为我不知道当前正在使用哪个,所以我需要构建可以在所有插槽(在这种情况下为 2)上正常工作的二进制文件。这就是为什么在阅读了许多网站、主题等之后。
我从引导加载程序切换到主应用程序的流程如下:
在 old_timer 发布答案后编辑
因此,我尝试为两个插槽编译代码。下面是 GOT 反汇编的结果:
Disassembly of section …Run Code Online (Sandbox Code Playgroud) 我正在研究运行 Cortex-M4 处理器和 ARM/Thumb 指令集的 STM32l475 微控制器。我看(从objdump),有beq.n和bne.n在一个ARM程序的二进制生成的指令(I添加-mthumb标志编译该程序时)。但是,我在最新的ARMv7-M手册中没有找到这些分支指令。
谁能告诉我原因?以及手册中与这两条分支指令等效的指令有哪些?
我正在努力将 linux 引入具有 16 Mb SDRAM 和 64 Mb 闪存的定制 Cortex-M7 板。该平台没有 MMU,没有共享库,没有 FLAT 可执行文件。
我在使用非常简单的 init.d shell 脚本启动 Busybox 系统时遇到问题。通过执行诸如“[”或“printf”之类的简单shell命令,系统就会耗尽内存。事实证明,每次执行这些命令之一时,系统都需要加载 FULL、一个且唯一的 busybox 可执行文件(在我的系统上为 650 Kb)。
所以问题是:如果系统总是要为在busybox 中执行的每个命令都在内存中加载一个巨大的可执行文件,那么这有多方便?我不明白在极快地退出内存的同时节省几兆字节的廉价和丰富的存储空间,但也许我忽略了一些东西。
我的平台是 Busybox 的用例吗?如果没有,是否有任何东西可以方便地在自己的可执行文件上构建 linux 系统实用程序?
提前致谢!
编辑:
根据他们自己的说法,Busybox “在编写时考虑了大小优化和有限的资源”,因此成为嵌入式系统中一种毋庸置疑的事实上的标准。但是他们的声明与 RAM(而非存储)受限系统上的上述问题有何关联?我相信这值得一些澄清。
跟进,系统详情:
内核已经为 XIP 编译,从 64 Mb 外部闪存执行。整个读/写 ext3 根文件系统(包括 busybox 二进制文件)现在驻留在微型 SD 卡上。Busybox 可执行文件使用 FLAT 格式(“bFLT”)并启用加载到 RAM 位,该位似乎在每次运行并发命令时在不同的内存块上导致新的加载,直到它耗尽拟合块。将busybox(整个/bin、/sbin)放在XIP 文件系统上的建议非常棒,它肯定会提高执行速度(当然,这个新文件系统需要驻留在64 Mb 外部闪存上)。我从未尝试过在这样的文件系统上执行“bFLT”(也不知道它是否有效),但我会对此进行研究/测试。
根据关于并发的Rust Embedded Book,在上下文之间共享一些数据的更好方法之一是使用带有 refcells 的互斥锁。我理解它们是如何工作的以及为什么这是必要的。但在某些情况下,间接成本似乎很高。
cortex_m crate 的互斥锁是这样工作的:
cortex_m::interrupt::free(|cs| {
let my_value = my_mutex.borrow(cs).borrow();
// Do something with the value
});
Run Code Online (Sandbox Code Playgroud)
互斥体cs在提供访问权限之前需要(CriticalSection) 令牌。在临界区,不会发生中断,所以我们知道我们是唯一可以更改和读取值的人。这很好用。
但是,我现在所处的场景是将变量写入一次以进行初始化(在运行时),然后始终将其视为只读值。就我而言,它是 MCU 的时钟速度。这不能是编译时常量。一个为什么从深度睡眠中醒来的例子:根据硬件的状态,可能会选择使用较慢的时钟速度来节省一些能量。因此,在启动时(或者更确切地说是所有 RAM 都消失的唤醒),每次都可以选择不同的时钟速度。
如果我只是想读取值,完成整个临界区设置似乎很浪费。如果这个变量可能再次改变,那么,是的,这是必要的。但这里的情况并非如此。它只会被读取。
有没有更好的方法来以更少的开销读取共享变量而不使用不安全的 Rust?