Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: LPC11xx(C-M0) ремап векторов в RAM
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
_Артём_
Здраствуйте.
Пытаюсь сделать ремап векторов прерываний на ОЗУ.
Последовательность такая.
Создаю секцию в ld-файле:

Код
MEMORY
{
    TEXT (rx)       : ORIGIN = 0x00000000, LENGTH =  32K
    RAMVectorTable (rwx) : ORIGIN = 0x10000000, LENGTH = 256
    RAM (xrw)       : ORIGIN = 0x10000100, LENGTH =  0x1F00
}

    .IntVecTable :
    {
        *(.IntVecTable)
    } > RAMVectorTable


структура таблицы векторов(vector_table.h):
Код
#define LPC11XX_VECTOR_QTY 47
typedef void (*ISRPtr) (void);
struct TLPC11xxVectorTable {
    ISRPtr StackPtr;// уже не нужен
    ISRPtr ResetISR;// уже не нужен
    ISRPtr Reserved0[9];
    ISRPtr SvcISR;
    ISRPtr Reserved1[2];
    ISRPtr PendSvcISR;
    ISRPtr SysTickISR;
    /* Device interrupt vectors */
    ISRPtr WakeUpISR[13];
    ISRPtr Reserved2;
    ISRPtr Ssp1ISR;
    ISRPtr I2cISR;
    ISRPtr Timer16_0_ISR;
    ISRPtr Timer16_1_ISR;
    ISRPtr Timer32_0_ISR;
    ISRPtr Timer32_1_ISR;
    ISRPtr Ssp0ISR;
    ISRPtr UART_ISR;
    ISRPtr Reserved3[2];
    ISRPtr ADC_ISR;
    ISRPtr Wdt_ISR;
    ISRPtr Bod_ISR;
    ISRPtr Reserved4;
    ISRPtr EINT3_ISR;
    ISRPtr EINT2_ISR;
    ISRPtr EINT1_ISR;
    ISRPtr EINT0_ISR;
    void SetDefault(ISRPtr default_handler);
};


main.cpp:
CODE

#include "vector_table.h"
__attribute__ ((__section__(".IntVecTable")))
struct TLPC11xxVectorTable IntVecTable;

extern "C" void SVC_Handler(void);
extern "C" void PendSVC_ISR(void);
extern "C" void Default_Handler(void);

void InitVectorTable()
{
IntVecTable.SvcISR=&SVC_Handler;
IntVecTable.PendSvcISR=&PendSVC_ISR;
IntVecTable.SysTickISR=&OS::SystemTimer_ISR;
IntVecTable.Timer16_0_ISR=&Timer16_0_IRQ_Handler;
IntVecTable.UART_ISR=&UART_IRQ_Handler;
LPC_SYSCON->SYSMEMREMAP=1;
}

void TLPC11xxVectorTable::SetDefault(ISRPtr default_handler) {
ISRPtr *isr_ptr=&ResetISR;
for (uint8_t i=0; i<LPC11XX_VECTOR_QTY; i++)
*isr_ptr++=default_handler;
for (uint8_t i=0; i<9; i++)
Reserved0[i]=0;
Reserved4=
Reserved3[0]=Reserved3[1]=
Reserved2=
Reserved1[0]=Reserved1[1]=0;
}

int main()
{
IntVecTable.SetDefault(&Default_Handler);
InitVectorTable();
//....... код




Вроде работает. Но может это только пока, а потом перестанет?
Может есть какие-то способы получше?
Кто как делает подобные действия?

Спасибо.
Сергей Борщ
Да вроде бы все. Обратите внимание на тонкость: после ремапа ваша новая таблица закроет часть флеша и если эта часть случайно в целях экономии используется не как остаток оригинальной таблицы, а для кода исполняемого после ремапа - будет ой. В этом случае надо вручную разместить туда код, выполняющийся только до ремапа.

Не ясна лишь цель всего этого. Я использую ремап для запуска приложения из загрузчика, и просто копирую таблицу. У вас же, очевидно, какая-то другая цель. Какая? Подмена на лету каких-то отдельных векторов? Просто любопытно. Ну и, может быть, зная цель придет в голову какое-то альтернативное решение.

Для удобства в вашем случае можно вектора периферии оформить в виде массива и заполнять, используя в качестве индекса xxx_IRQn.
_Артём_
Цитата(Сергей Борщ @ Mar 27 2012, 00:58) *
Не ясна лишь цель всего этого.

Цель простая: стандартная задача. Видимо я что-то не так понял и мне первым на ум пришёл какой-то альтернативный вариант и всё делается проще. Наверное надо ещё разок заглянуть в appn по secondary bootloader.
Так вот цель: в начале флэш от 0 до N - bootloader (сколько это N пока не знаю). От N до конца - приложение. Соответственно вектора находятся по адресам от 0 до 0xC0 и прошиты раз и навсегда (переписывать boot мне как-то не хочется).
Можно конечно располагать в application-области обработчики по фиксированным адресам (кстати ещё не пробовал), а в boot вообще не использовать прерывания (что кстати и напрашивается). Хм.
Получается, что ремап даёт полную отвязку от прошитых векторов, что может и не нужно, если вектора указывают на область приложения.

А вы как делали?

Сергей Борщ
QUOTE (_Артём_ @ Mar 27 2012, 01:28) *
А вы как делали?
Несколько проще. Загрузчик занимает место с 0 до 0x1000. Со своими векторами, прибитыми гвоздями. Приложение линкуется как обычно, но начиная не с 0, а с 0x1000, в остальном это обычная программа:
CODE
MEMORY
{
    REMAPED(r)      : ORIGIN = 0x00000000, LENGTH =  192                 /* 192 = vectors table size */
    TEXT (rx)       : ORIGIN = 0x00001000, LENGTH =  32K - 4K - 4K       /* 4K - boot, 4K - config */
    CONFIG(r)       : ORIGIN = 32K - 4K,   LENGTH =  4K

    RAM (rw)        : ORIGIN = 0x10000000 + 192, LENGTH =  8K - 192 - 32 /* 192 - remaped vectors, 32 - iap working area */
}


Для загрузчика карта памяти будет, соответственно:
CODE
MEMORY
{
    TEXT (rx)       : ORIGIN = 0x00000000, LENGTH = 4K

    APPLICATION(rx) : ORIGIN = 4K,         LENGTH = 32K - 4K - 4K        /* 4K - boot, 4K - config */
    CONFIG(r)       : ORIGIN = 32K - 4K,   LENGTH = 4K

    RAM (rw)        : ORIGIN = 0x10000000 + 512, LENGTH =  8K - 512 - 32 /* 32 - IAP reserved space */
    REMAPPED (xrw)  : ORIGIN = 0x10000000, LENGTH =  512                 /* 512 = remappable area size */
}
SECTIONS
{
    .remapped (NOLOAD) :
    {
        _remap_area = .;
    } > REMAPPED
     .......
    .application (NOLOAD):
    {
        Application = .;
    } > APPLICATION
}

И загрузчик просто копирует всю таблицу векторов приложения начиная с метки Application в _remap_area:
CODE
struct application
{
    struct vectors
    {
        typedef void( *handler )( void );
        uint32_t    MSP_init;
        handler     Reset_vector;
        handler     Core_handler[14];
        handler     MCU_handler[32];
    }       Vectors;
};

extern const application Application;
extern application::vectors _remap_area;

....
            if (CRC.ok())                   // Application Section OK
            {
                // copy application vectors table to remap area
//                memcpy(&_remap_area, &Application.Vectors, sizeof(Application.Vectors));
                uint32_t const * pSrc = (uint32_t const *)&Application.Vectors;
                uint32_t * pDst = (uint32_t *)&_remap_area;
                do
                {
                    *pDst++ = *pSrc++;
                }
                while(pSrc < (uint32_t const *)(&Application.Vectors + 1));

                asm volatile
                (
                    "   MSR   MSP, %0\n" // store App stack init value to MSP
                    :
                    : "r" (Application.Vectors.MSP_init)
                );
                LPC_SYSCON->SYSMEMREMAP = LPC_MAP_RAM;  // remap to ram
                Application.Vectors.Reset_vector();
            }
        }

Вообще-то у меня еще в начале приложения хранится его размер, чтобы считать CRC только занятой памяти:
CODE
struct application
{
    uint32_t Size;

    struct vectors
    {
        typedef void( *handler )( void );
        uint32_t    MSP_init;
        handler     Reset_vector;
        handler     Core_handler[14];
        handler     MCU_handler[32];
    }       Vectors;
};
_Артём_
Спасибо за пример.
Ещё такой вопрос: как отлаживать такую программу? Создавать два ld-файла - Debug (заполняет флеш от 0) и Release (заполняет флеш начиная от 0x1000)? Или есть более другие способы?
Сергей Борщ
QUOTE (_Артём_ @ Mar 27 2012, 18:30) *
Ещё такой вопрос: как отлаживать такую программу? Создавать два ld-файла - Debug (заполняет флеш от 0) и Release (заполняет флеш начиная от 0x1000)? Или есть более другие способы?
Смотря чем отлаживать. Для OpenOCD в аналогичном случае с SAM7 писал скрипт, который после загрузки и ресета делает то же, что и загрузчик - копирует вектора, делает remap, передает управление на нулевой вектор. Здесь надо будет сделать то же самое, плюс загрузить указатель стека из начала таблицы и вместо передачи управления грузить PC из вектора. Еще не помешает добавить ORIGIN() в скрипт.

А как такое сделать с LPCXpresso - не знаю, не разбирался. Я сначала написал загрузчик, а перед отладкой приложения закомментировал в нем проверку правильности контрольной суммы и он всегда запускал свежепрошитое отладчиком приложение.
_Артём_
Цитата(Сергей Борщ @ Mar 27 2012, 21:59) *
Смотря чем отлаживать. Для OpenOCD в аналогичном случае с SAM7 писал скрипт, который после загрузки и ресета делает то же, что и загрузчик - копирует вектора, делает remap, передает управление на нулевой вектор. Здесь надо будет сделать то же самое, плюс загрузить указатель стека из начала таблицы и вместо передачи управления грузить PC из вектора. Еще не помешает добавить ORIGIN() в скрипт.

А как такое сделать с LPCXpresso - не знаю, не разбирался. Я сначала написал загрузчик, а перед отладкой приложения закомментировал в нем проверку правильности контрольной суммы и он всегда запускал свежепрошитое отладчиком приложение.


Да, не АВР. Буду разбираться.
Спасибо.

Еще момент:
Из карты для загрузчика:
Код
REMAPPED (xrw)  : ORIGIN = 0x10000000, LENGTH =  512


Почему 512? С чем это связано?
Сергей Борщ
QUOTE (_Артём_ @ Mar 29 2012, 00:54) *
Почему 512? С чем это связано?
С документацией:
QUOTE
3.5.1 System memory remap register
The system memory remap register selects whether the ARM interrupt vectors are read
from the boot ROM, the flash, or the SRAM. By default, the flash memory is mapped to
address 0x0000 0000. When the MAP bits in the SYSMEMREMAP register are set to 0x0
or 0x1, the boot ROM or RAM respectively are mapped to the bottom 512 bytes of the
memory map (addresses 0x0000 0000 to 0x0000 0200).

_Артём_
Цитата(Сергей Борщ @ Mar 29 2012, 10:17) *
С документацией:

Да, дело было в документации: у меня мануал был версии годовой давности и нём от вашей цитаты только первое предложение, про 512 байт - ничего. Обновил - увидел.

Спасибо.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.