c - 嵌入式系统上的 C 代码内存对齐问题?
问题描述
在对嵌入式系统(TI CC2530 上的 IAR C)进行了重大重构之后,我遇到了以下情况:
在外围设备的基本初始化和全局中断启用后,执行错误地以与外部硬件通信的中断处理程序结束。由于此硬件尚未准备好(请记住,我们最终错误地进入了 ISR),因此程序冻结触发看门狗复位。
如果我在 main() 中插入 1、2、3、5、6、7 等 NOP,一切正常。但是如果我插入 0、4、8 等 NOP,我会得到错误的行为。
CC2530 在 4 字节边界上从闪存中获取 4 字节指令。
这告诉我在代码内存方面有些东西是错位的,但我根本不知道从哪里开始。当涉及到目标设置 AFAIK 时,什么都没有改变。
以前见过这种情况的任何人,或者可以指出我正确的方向吗?
#include <common.h>
#include <timer.h>
#include <radio.h>
#include <encryption.h>
#include "signals.h"
#include "lock.h"
#include "nfc.h"
#include "uart1_trace.h"
#include "trace.h"
//------------------------------------------------------------------------------
// Public functions
//------------------------------------------------------------------------------
void main(void)
{
setTp;
// Initialize microcontroller and peripherals
ClockSourceInit();
WatchdogEnable();
PortsInit();
TraceInit();
Timer4Init();
SleepInit();
RadioInit();
Uart1Init();
LoadAesKey("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
clrTp;
NfcInit();
__enable_interrupt();
asm("nop");
// Initialize threads
LockInit();
while (true)
{
WDR();
LockRun();
}
}
void NfcInit(void)
{
// Enable wake up interrupt on external RF field present
// The process for enabling interrupts is described in section 2.5.1 in the CC2530 datasheet.
// Configure interrupt source: interrupt on falling edge, Port 0, pin 7:0
PICTL |= BIT(0);
// 1. Clear port 0 individual interrupt flag. Read-modify-write is not allowed.
// Writing 1 to a bit in this register has no effect, so 1 should be written to flags that are not to be cleared.
P0IFG = ~BIT(3);
// Clear port 0 interrupt flag. This register is bit-accessible.
P0IF = 0;
// 2. Set pin 3 interrupt-enable
P0IEN |= BIT(3);
// 3. Set port 0 interrupt-enable
IEN1 |= BIT(5);
// 4. Global interrupt enable is set in main()
}
// Interrupt handler: falling edge on signal Wake.
// This interrupt will only occur when device is powered off and NFC field present.
// When device is powered on, VCORE is always asserted.
#pragma vector = 0x6B
__interrupt static void NFC_WAKE_ISR(void)
{
static uint16 cnt = 0;
TracePutUint16(cnt); TracePuts("\r\n");
if (++cnt > 10)
IEN1 &= ~BIT(5);
P0IFG = ~BIT(3); // Clear port 1 individual interrupt flag. Read-modify-write is not allowed.
P0IF = 0; // Clear port 1 CPU interrupt flag. This register is bit-accessible.
return;
软件初始化的屏幕截图。
CH1 = 外部中断信号,低电平有效(信号唤醒)。
CH2 = main.c 中的 TP (setTp / clrTp)。
CC-debugger上的reset按钮好像没有去抖,所以TP信号在稳定之前开关了几次(应该不是问题)。VCC 在复位之前很长时间是稳定的。当 TP 最后一次变低时,所有外设都被初始化。
当存在 NFC 场时,外部 NFC IC 用于将 MCU 从睡眠模式中唤醒。NFC IC 由 CC2530 I/O 引脚之一供电。通常,IC 会断电以保持电力。在这种状态下,来自 NFC 场的能量足以产生唤醒信号(低电平有效)。当 MCU 检测到此信号时,它会唤醒,为 NFC IC 供电,NFC 通信开始。
NFC IC 在通电或存在 NFC 场时生成信号。
复位后,所有 I/O 引脚都配置为带上拉的输入。这个上拉输入足以为 NFC IC 供电,这就是产生唤醒信号的原因。复位后立即配置 I/O(在函数 PortsInit() 中),并关闭 NFC IC 的电源。这使得唤醒信号变低。缓慢的上升和下降时间可能是由于我现在将移除的电容器造成的。
这就是事情变得奇怪的地方。尽管唤醒信号为低电平,外部中断配置为下降沿,并且挂起的 int 标志在启用全局输入之前被清除,几毫秒后我最终进入 ISR(在屏幕截图中没有看到)。但如上所述,只有正确数量的 NOP。
如果我在全局 int 启用之前添加 > 15 ms 的延迟,一切都很好。这与从 TP 低点到唤醒高点的时间相吻合。
有人可能认为 int 被错误地配置为低电平有效,但在这种情况下,我应该得到多个 int,而我没有。此外,这并不能解释神奇的 NOP ......
编译器生成的 ISR 汇编代码:
// 77 // Interrupt handler: falling edge on signal Wake.
// 78 // This interrupt will only occur when device is powered off and NFC field present.
// 79 // When device is powered on, VCORE is always asserted.
// 80 #pragma vector = 0x6B
RSEG NEAR_CODE:CODE:NOROOT(0)
// 81 __interrupt static void NFC_WAKE_ISR(void)
NFC_WAKE_ISR:
// 82 {
PUSH A
MOV A,#-0xe
LCALL ?INTERRUPT_ENTER_XSP
; Saved register size: 15
; Auto size: 0
// 83 static uint16 cnt = 0;
// 84
// 85 TracePutUint16(cnt); TracePuts("\r\n");
; Setup parameters for call to function PutUint16
MOV R4,#(TPutc & 0xff)
MOV R5,#((TPutc >> 8) & 0xff)
MOV DPTR,#??cnt
MOVX A,@DPTR
MOV R2,A
INC DPTR
MOVX A,@DPTR
MOV R3,A
LCALL PutUint16
; Setup parameters for call to function TPuts
MOV R2,#(`?<Constant "\\r\\n">` & 0xff)
MOV R3,#((`?<Constant "\\r\\n">` >> 8) & 0xff)
LCALL TPuts
// 86
// 87 if (++cnt > 10)
MOV DPTR,#??cnt
MOVX A,@DPTR
ADD A,#0x1
MOV R0,A
INC DPTR
MOVX A,@DPTR
ADDC A,#0x0
MOV R1,A
MOV DPTR,#??cnt
MOV A,R0
MOVX @DPTR,A
INC DPTR
MOV A,R1
MOVX @DPTR,A
CLR C
MOV A,R0
SUBB A,#0xb
MOV A,R1
SUBB A,#0x0
JC ??NFC_WAKE_ISR_0
// 88 IEN1 &= ~BIT(5);
CLR 0xb8.5
// 89
// 90
// 91 P0IFG = ~BIT(3); // Clear port 1 individual interrupt flag. Read-modify-write is not allowed.
??NFC_WAKE_ISR_0:
MOV 0x89,#-0x9
// 92 P0IF = 0; // Clear port 1 CPU interrupt flag. This register is bit-accessible.
CLR 0xc0.5
// 93
// 94 return;
MOV R7,#0x1
LJMP ?INTERRUPT_LEAVE_XSP
REQUIRE _A_P0
REQUIRE P0IFG
REQUIRE _A_P1
REQUIRE _A_IEN1
REQUIRE _A_IRCON
////////////////////////////////////////////////////////////////////////////////
// lnk51ew_CC2530F64.xcl: linker command file for IAR Embedded Workbench IDE
// Generated: Mon May 24 00:00:01 +0200 2010
//
////////////////////////////////////////////////////////////////////////////////
//
// Segment limits
// ==============
//
// IDATA
// -----
-D_IDATA0_START=0x00
-D_IDATA0_END=0xFF
//
// PDATA
// -----
// We select 256 bytes of (I)XDATA memory that can be used as PDATA (see also "PDATA page setup" below)
-D_PDATA0_START=0x1E00
-D_PDATA0_END=0x1EFF
//
//
// IXDATA
// ------
-D_IXDATA0_START=0x0001 // Skip address 0x0000 (to avoid ambiguities with NULL pointer)
-D_IXDATA0_END=0x1EFF // CC2530F64 has 8 kB RAM (NOTE: 256 bytes are used for IDATA)
//
//
// XDATA
// -----
-D_XDATA0_START=_IXDATA0_START
-D_XDATA0_END=_IXDATA0_END
//
// NEAR CODE
// ---------
-D_CODE0_START=0x0000
-D_CODE0_END=0xFFFF // CC2530F64 has 64 kB code (flash)
//
// Special SFRs
// ============
//
// Register bank setup
// -------------------
-D?REGISTER_BANK=0x0 // Sets default register bank (0,1,2,3)
-D_REGISTER_BANK_START=0x0 // Start address for default register bank (0x0, 0x8, 0x10, 0x18)
//
// PDATA page setup
// ----------------
-D?PBANK_NUMBER=0x1E // High byte of 16-bit address to the PDATA area
//
// Virtual register setup
// ----------------------
-D_BREG_START=0x00
-D?VB=0x20
-D?ESP=0x9B //Extended stack pointer register location
////////////////////////////////////////////////////////////////////////////////
//
// IDATA memory
// ============
-Z(BIT)BREG=_BREG_START
-Z(BIT)BIT_N=0-7F
-Z(DATA)REGISTERS+8=_REGISTER_BANK_START
-Z(DATA)BDATA_Z,BDATA_N,BDATA_I=20-2F
-Z(DATA)VREG+_NR_OF_VIRTUAL_REGISTERS=08-7F
-Z(DATA)PSP,XSP=08-7F
-Z(DATA)DOVERLAY=08-7F
-Z(DATA)DATA_I,DATA_Z,DATA_N=08-7F
-U(IDATA)0-7F=(DATA)0-7F
-Z(IDATA)IDATA_I,IDATA_Z,IDATA_N=08-_IDATA0_END
-Z(IDATA)ISTACK+_IDATA_STACK_SIZE#08-_IDATA0_END
-Z(IDATA)IOVERLAY=08-FF
//
// ROM memory
// ==========
//
// Top of memory
// -------------
-Z(CODE)INTVEC=0
-Z(CODE)CSTART=_CODE0_START-_CODE0_END
//
// Initializers
// ------------
-Z(CODE)BIT_ID,BDATA_ID,DATA_ID,IDATA_ID,IXDATA_ID,PDATA_ID,XDATA_ID=_CODE0_START-_CODE0_END
//
// Program memory
// --------------
-Z(CODE)RCODE,DIFUNCT,CODE_C,CODE_N,NEAR_CODE=_CODE0_START-_CODE0_END
//
// Checksum
// --------
-Z(CODE)CHECKSUM#_CODE0_END
//
// XDATA memory
// ============
//
// Stacks located in XDATA
// -----------------------
-Z(XDATA)EXT_STACK+_EXTENDED_STACK_SIZE=_EXTENDED_STACK_START
-Z(XDATA)PSTACK+_PDATA_STACK_SIZE=_PDATA0_START-_PDATA0_END
-Z(XDATA)XSTACK+_XDATA_STACK_SIZE=_XDATA0_START-_XDATA0_END
//
// PDATA - data memory
// -------------------
-Z(XDATA)PDATA_Z,PDATA_I=_PDATA0_START-_PDATA0_END
-P(XDATA)PDATA_N=_PDATA0_START-_PDATA0_END
//
// XDATA - data memory
// -------------------
-Z(XDATA)IXDATA_Z,IXDATA_I=_IXDATA0_START-_IXDATA0_END
-P(XDATA)IXDATA_N=_IXDATA0_START-_IXDATA0_END
-Z(XDATA)XDATA_Z,XDATA_I=_XDATA0_START-_XDATA0_END
-P(XDATA)XDATA_N=_XDATA0_START-_XDATA0_END
-Z(XDATA)XDATA_HEAP+_XDATA_HEAP_SIZE=_XDATA0_START-_XDATA0_END
-Z(CONST)XDATA_ROM_C=_XDATA0_START-_XDATA0_END
//
// Core
// ====
-cx51
////////////////////////////////////////////////////////////////////////////////
//
// Texas Instruments device specific
// =================================
//
// Flash lock bits
// ---------------
//
// The CC2530 has its flash lock bits, one bit for each 2048 B flash page, located in
// the last available flash page, starting 16 bytes from the page end. The number of
// bytes with flash lock bits depends on the flash size configuration of the CC2530
// (maximum 16 bytes, i.e. 128 page lock bits, for the CC2530 with 256 kB flash).
// Note that the bit that controls the debug interface lock is always in the last byte,
// regardless of flash size.
//
-D_FLASH_LOCK_BITS_START=(_CODE0_END-0xF)
-D_FLASH_LOCK_BITS_END=_CODE0_END
//
// Define as segment in case one wants to put something there intentionally (then comment out the trick below)
-Z(CODE)FLASH_LOCK_BITS=_FLASH_LOCK_BITS_START-_FLASH_LOCK_BITS_END
//
// Trick to reserve the FLASH_LOCK_BITS segment from being used as normal CODE, avoiding
// code to be placed on top of the flash lock bits. If code is placed on address 0x0000,
// (INTVEC is by default located at 0x0000) then the flash lock bits will be reserved too.
//
-U(CODE)0x0000=(CODE)_FLASH_LOCK_BITS_START-_FLASH_LOCK_BITS_END
//
////////////////////////////////////////////////////////////////////////////////
解决方案
据 TI 称,该部件有一个 8051 内核。除了是恐龙废话,8051 是 8 位的,所以对齐不适用。
当对代码的随机修改导致完全不相关的错误或代码失控时,通常是由以下原因之一引起的:
- 你有一个堆栈溢出,或者
- 您遇到了未定义的行为错误,例如未初始化的变量、数组越界访问等。
还要确保所有 ISR 都注册在中断向量表中。
在问题更改 6/4 后编辑:
你通常不 return
应该从中断!我不知道您的具体设置是如何工作的,但是对于通用嵌入式系统编译器,非标准中断关键字意味着两件事:
- 确保进入ISR 时的调用约定是正确的,通过堆叠 CPU/ABI 状态不是由硬件而是由软件堆叠的任何寄存器。
- 确保在离开ISR时恢复相同的寄存器并使用正确的返回指令。
在 8051 上,这意味着反汇编的 ISR 绝对必须以RETI
指令而不是RET
指令结尾!很有可能return
会RET
破坏你的筹码。拆开看看是否确实如此。
推荐阅读
- java - 连接多个设备时如何将 Appium 服务器保持在其分配的 AppiumDriver 上
- android - 如何修复错误:不兼容的类型:tab1 无法转换为 Fragment?
- python-3.x - 将列表中的每个项目写入具有自动分配文件名的单独 txt 文件(python = 3.6)
- php - PHP 将变量封装为字符串
- android - androidx.mediarouter 抱怨 MediaBrowserCompat
- mongodb - 尝试连接mongodb单节点副本集超时异常
- postgresql - 优化名称重复数据删除查询
- java - 任何允许 Pivot 中的撇号 SQL 列别名的安全问题?
- javascript - 将参数传递给事件处理程序
- wpf - 在非主 TabItem 内时,绑定在 DataGrid 列标题中不起作用