C проблема выравнивания памяти кода во встроенной системе? - PullRequest
1 голос
/ 03 апреля 2020

После серьезного рефакторинга встроенной системы (IAR C на TI CC2530) я попал в следующую ситуацию:

После базовой c инициализации периферийных устройств и разрешения глобальных прерываний, выполнение неправильно заканчивается обработчиком прерываний, который связывается с внешним оборудованием. Поскольку это оборудование не готово (помните, что мы попали в ISR неправильно), программа зависает, вызывая сброс сторожевого таймера.

Если я вставляю 1, 2, 3, 5, 6, 7 и т. Д. c NOPs в main (), все работает нормально. Но если я вставлю 0, 4, 8 и c NOP, я получу некорректное поведение.

CC2530 извлекает 4 байта инструкций из памяти fla sh в 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;

Снимок экрана: программное обеспечение init.

Oscilloscope screenshot

CH1 = Внешний сигнал прерывания, активный низкий уровень (сигнал Wake).

CH2 = TP в главном . c (setTp / clrTp).

Кнопка сброса на отладчике CC, похоже, не деблокирована, поэтому сигнал TP включается и выключается несколько раз перед стабилизацией (не должно быть проблемой ). V CC стабильно задолго до сброса. Когда TP снижается в последний раз, все периферийные устройства инициализируются.

Внешний NF C I C используется для выхода MCU из режима ожидания, когда присутствует поле NF C. Питание NF C I C осуществляется от одного из выводов ввода / вывода CC2530. Обычно I C отключается для сохранения энергии. В этом состоянии энергии от поля NF C достаточно для генерации сигнала пробуждения (активный низкий уровень). Когда MCU обнаруживает этот сигнал, он просыпается, подает питание на NF C I C и начинается связь NF C.

Генерирует NF C I C сигнал либо при включении питания, либо при наличии поля NF C.

После сброса все выводы ввода / вывода конфигурируются как входы с подтягиванием. Этого подтянутого входа достаточно для питания NF C I C, поэтому генерируется сигнал пробуждения. Сразу после сброса конфигурируется вход / выход (в функции PortsInit ()), и питание NF C I C отключается. Это делает сигнал пробуждения go низким. Медленное время нарастания и спада, вероятно, связано с конденсатором, который я сейчас удалю.

Здесь все становится странным. Несмотря на низкий уровень сигнала пробуждения, внешнее прерывание настроено на падающий фронт, и флаг ожидания в ожидании сбрасывается непосредственно перед включенным глобальным включением, я в конечном итоге в ISR через несколько мс (не видно на снимке экрана). Но только с нужным количеством NOP, как описано выше.

Если я добавлю задержку> 15 мс перед включением глобального int, все в порядке. Это совпадает со временем, измеренным от минимума TP до максимума пробуждения.

Можно подумать, что int неправильно настроен на активный низкий уровень, но в этом случае я должен получить несколько целых чисел, а я нет. Кроме того, это не объясняет волшебные c NOPs ...

Сгенерированный компилятором код сборки 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
//
////////////////////////////////////////////////////////////////////////////////

Ответы [ 3 ]

5 голосов
/ 03 апреля 2020

Согласно TI, эта часть имеет ядро ​​8051. Помимо того, что это чушь динозавров, 8051 является 8-битным, поэтому выравнивание не применяется.

Когда случайные изменения в коде приводят к совершенно несвязанным ошибкам или неиспользуемому коду, это чаще всего вызывается одним из этих вещи:

  • У вас переполнение стека, или
  • У вас есть неопределенные ошибки поведения, такие как неинициализированные переменные, доступ к массиву вне границ и т. д. c.

Также убедитесь, что все ISR зарегистрированы в таблице векторов прерываний.


РЕДАКТИРОВАТЬ после изменения вопроса 6/4:

Обычно вы должны не return от прерываний! Я не знаю, как работает ваша специфицированная c установка, но с общим компилятором встроенных систем нестандартное ключевое слово прерывания означает две вещи:

  • Убедитесь, что соглашение о вызовах при ввод правильного ISR, путем суммирования всех регистров состояние CPU / ABI не аппаратно, а программно.
  • Убедитесь, что те же регистры восстанавливаются при , оставляя ISR и , что используется правильная инструкция возврата.

На 8051 это означает, что разобранный ISR обязательно должен заканчиваться инструкцией RETI, а не RET! Высоки шансы, что return приведет к RET, что саботирует ваш стек. Разберите, чтобы увидеть, так ли это на самом деле.

0 голосов
/ 07 апреля 2020

Я начинаю полагать, что это аппаратная проблема, связанная с соединением между CC2530 и NF C I C.

Питание и сброс на NF C I C, который отправляет запрос внешнего прерывания, управляется выводом ввода-вывода CC2530 с текущей емкостью привода 20 мА. При сбросе до начала выполнения программы все выводы ввода / вывода по умолчанию устанавливаются на входы с внутренним слабым подтягиванием. Кажется, что тока через подтягивающий резистор достаточно, чтобы включить NF C I C. Сигнал прерывания от NF C I C имеет высокий уровень, когда на NF C подается питание или присутствует поле NF C, и инвертируется транзистором FET до достижения CC2530. Следовательно, ISR запускается падающим фронтом на входе.

Итак, при запуске происходит то, что NF C I C неправильно включается (и позже выключается, когда порты инициализируются) и сигнал WAKE падает и растет очень медленно из-за плохой емкости привода подтягивания (что еще хуже, большой конденсатор 1 мкФ подключен параллельно с затвором полевого транзистора, а другой 1 мкФ фильтрует NF C I C штырь питания).

WAKE должен вызывать прерывание только по падающему фронту, но пребывание в области перехода в течение до 10 мс, как видно на скриншоте осциллографа выше, кажется, вызывает CC2530 для запуска прерывания, даже когда WAKE поднимается. ISR начинает обмениваться данными с NF C I C через SPI, но в это время кажется, что NF C I C не работает из-за ложных переходов на V CC и сбрасывается. Он отказывается отвечать, выполнение останавливается в ISR и укусы сторожа. И процесс начинается снова, навсегда.

Когда я вставляю задержку, которая обеспечивает стабильно высокий уровень WAKE перед включением прерывания, все хорошо. Если я снимаю крышку 1 мкФ на затворе FET, WAKE поднимается очень быстро, и больше нет необходимости в задержке. И когда я добавляю питание 4k7 к источнику питания NF C, он больше не включается при сбросе.

Проблема, похоже, решена. Рефакторинг реорганизовал код и изменил последовательность запуска, что привело к другой задержке, которая выявила проблему. При правильном обновлении аппаратного обеспечения задержка не потребуется.

Но меня по-прежнему беспокоит то, что я не понимаю магические c NOP. Когда CC2530 включил прерывание и столкнулся с медленно растущим WAKE, он не всегда будет неправильно отображаться в ISR. И когда это произошло, я всегда мог запустить его, добавив 1..3 NOP. Естественно, всякий раз, когда я добавлял или удалял строку кода, количество требуемых NOP менялось, что, как вы можете себе представить, сводило меня с ума.

Мне потребовалось некоторое время, чтобы сузить круг вопросов, и я очень благодарен на все ваши комментарии и предлагаемые решения, особенно Клиффорд, который заставил меня выпустить осциллограф.

0 голосов
/ 06 апреля 2020

Руководство пользователя для CC2530 гласит:

Инструкция для установки бита PCON.IDLE должна быть определенным образом выровнена для правильной работы. Первый байт инструкции по сборке, следующий сразу за этой инструкцией, не должен быть размещен на 4-байтовой границе.

Вероятно, поэтому система дает сбой при кратности NOP, равной четырем.

Чуть ниже предупреждения есть реализация для исправления этого выравнивания, специально предназначенная для компилятора IAR.

...