Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: LPC11xx не стартует код из своего бутлоадера
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2
zuy
Использую LPC1114. По доке вроде все понятно.
Свой бутлоадер код во флеш прописал.
для старта делаю следующее:
1. копирую 48 векторов прерывания основной программы в ОЗУ с адреса 0x10000000
2. Делаю ремап LPC_SYSCON->SYSMEMREMAP = 0x01; т.е. отобрашаем вектора из ОЗУ в начало адресного простанства
3. инициализирую SP и PC данными из первых 8-ми байт моей проги.

Все это выглядит так:
Код
__asm void boot_jump( uint32_t address )
{
   LDR R1, [R0]    ;Load new stack pointer address
   MOV R13, R1
   LDR R1, [R0, #4];Load new program counter address
   BX  R1
}

void run(unsigned long address)
{
  unsigned long          *dst, size;
  const unsigned long    *src;
  
  // Copy vectors table
  src = (unsigned long *)address;
  dst = (unsigned long *)0x10000000;
  size = VECTORS_TABLE_SIZE >> 2;
  do  { *dst++ = *src++; } while (--size);

  LPC_SYSCON->SYSMEMREMAP = 0x01;        /* remap to SRAM */

  boot_jump(address);
}


В результате поведение непредсказуемое. то висит, то попадает куда-то в середину бутлоадера.
Если убрать ремап, то целевая прога запускается, но естественно с векторами из флеша.

Что я забываю в этой схеме?
Alekseeey
У меня та же проблема.

Пробую на отладке LPCXpresso, среда CodeRed. Процессор LPC1114.

Я взял проект blinky из примеров и попытался просто скопировать вектора в ОЗУ и сделать ремап.
(соотв. область озу зарезервирована в скрипте линкера).

Программа вылетает в HardFault exception.

Написал вопрос в LPCXpresso forum.

Жду ответа... У меня самого идеи кончились.

В дебаг режиме, при пошаговом выполнении, вылетает на команде bl.

Вот код функции Reset_Handler из примера с моими изменениями:

Код
void Reset_Handler(void)
{
    unsigned char *pulSrc, *pulDest;

    //
    // Copy the data segment initializers from flash to SRAM.
    //
    pulSrc = &_etext;
    for(pulDest = &_data; pulDest < &_edata; )
    {
        *pulDest++ = *pulSrc++;
    }

    //
    // Zero fill the bss segment.
    //
    for(pulDest = &_bss; pulDest < &_ebss; pulDest++)
      *pulDest = 0;

    // Copying the vector table to sram
    unsigned char i;
    unsigned char * vectDest;
    unsigned char * vectSrc;

    vectDest = (unsigned char *)(0x10000000);
    vectSrc  = (unsigned char *)(0x00000000);
    for (i = 0; i < 0xC0; i++)
    {
        *vectDest++ = *vectSrc++;
    };

    // Remap
    LPC_SYSCON->SYSMEMREMAP = 1;
    __DMB();

#ifdef __USE_CMSIS
    SystemInit();
#endif


    //
    // Call the application's entry point.
    // __main() is the entry point for redlib based applications (which calls main())
    // main() is the entry point for newlib based applications
    //
    if (__main)
        __main();
    else
        main();

    //
    // main() shouldn't return, but if it does, we'll just enter an infinite loop
    //
    while (1) {
    ;
    }
}



DpInRock
Вообще-то так и должно быть.

Как правильно (это мое имхо, но которое работает).

0. Компилируем программу и линковщику показываем реальные адреса.
1. Грузим программу с нужного адреса.
2. Копируем вектора, только если ремаповая область не совпадает с расположением программы (к примеру код находится SDRAM или FLASH). Т.е в этом случае копируем из области своей программы в область которая может содержать вектора и ремапится.
3. Далее запрещаем прерывания и JMP на начало своей программы: либо на реальное начало, либо на начальный адрес зоны векторов, которые ремапили (там одно и тоже будет - goto Startup).

Что характерно, не надо трогать стеки. Ибо стартап их настроит как вы в линкере укажите.

zuy
Цитата(Alekseeey @ Oct 21 2010, 15:12) *
Вот код функции Reset_Handler из примера с моими изменениями:


Если честно не совсем понял зачем это все в Reset_Handler.
Он от бутлоадера или от прошиваемой проги?
Какая у вас логика работы бутлоадера?

Цитата(DpInRock @ Oct 21 2010, 15:38) *
Вообще-то так и должно быть.

Как правильно (это мое имхо, но которое работает).

0. Компилируем программу и линковщику показываем реальные адреса.
1. Грузим программу с нужного адреса.
2. Копируем вектора, только если ремаповая область не совпадает с расположением программы (к примеру код находится SDRAM или FLASH). Т.е в этом случае копируем из области своей программы в область которая может содержать вектора и ремапится.
3. Далее запрещаем прерывания и JMP на начало своей программы: либо на реальное начало, либо на начальный адрес зоны векторов, которые ремапили (там одно и тоже будет - goto Startup).

Что характерно, не надо трогать стеки. Ибо стартап их настроит как вы в линкере укажите.


А почему оно так должно быть и как починить?

0. Основная прога собрана с адреса 0x2000. RAM использует с 0x100000C0.
1. Верно, бутлоадер прощивает основную прогу во флеш по адресу 0x2000.
2. Копируем таблицу векторов. Т.е. 0xC0 байт c адреса 0x2000 в 0x10000000.
3. Ну перед джампом надо же сделать ремап, чтобы вектора из ОЗУ появились с адреса 0x0. И вот тут у меня сразу улетает куда-то в середину бутлоадера.

А как это стеки трогать не надо? У Cortex M0 вершина стека хранится в самом начале векторов и проц после ресета сам ее оттуда берет. А т.к. мы скачем в основную прогу без ресета, а из своего бутлоадера, то надо самому правильно стек настроить. Стартап у кортексов стеки не настраивает.

DpInRock
Reset - это место откуда программа стартует. Как бы по сигналу сброса. Как бы независимо.
Но в реальности по сигналу сброса проц идет на собственный загрузчик и передает управление программе после ремапа. И тут якобы она стартует с 0. Делает вид,что после сброса как бы.

Разумеется, можно написать даже процедуру, которая будет располагаться с 0. Токо следущими идут вектора прерываний. И если прерывания не используются - да ради бога. Можно.

Против кортекса не попрешь. Извиняюсь.
Но логика все равно остается.
Не забывать прерывания убить.
Ну и правильно джамп сделать.
С переключением в режим супервизора. Я делаю
Код
void  JMP(unsigned int addr)
{

//asm ("mov r0, addr \n");
asm ("SWI 0 \n");
}

Вот так примерно.

В том плане, что я стартую с нуля всегда после ремапа. Ибо так универсальнее, разумеется.
(У меня все остальное - типа переключателя задач работает в супервизоре - выбрал за красивое название)
НУ, еще контроллр внешней памяти не забыть настроить... У меня-то вектора в локальной памяти...
Alekseeey
Цитата(zuy @ Oct 21 2010, 16:52) *
Если честно не совсем понял зачем это все в Reset_Handler.
Он от бутлоадера или от прошиваемой проги?
Какая у вас логика работы бутлоадера?


Логику работы бутлоадера я пока оставил в стороне - у меня не работает именно ремап.
Я попытался взять работающий пример и сделать ремап векторов не трогая ничего другого.
По идее все долно было работать без изменений. Но не работает. smile.gif Что-то делаю не правильно, но не знаю что.

А вообще логика бутлоадера будет такой: лоадер в 0-й странице флеш, грузит программу в страницы 1..7,
копирует ее вектора прерываний в ОЗУ и передает управление ей.

zuy
Цитата(DpInRock @ Oct 21 2010, 15:54) *
Reset - это место откуда программа стартует. Как бы по сигналу сброса. Как бы независимо.
Но в реальности по сигналу сброса проц идет на собственный загрузчик и передает управление программе после ремапа. И тут якобы она стартует с 0. Делает вид,что после сброса как бы.


Да, это понятно. Мой бут делает в общем тоже самое, что и встроенный, только ремапит вектора не из флеша, а из ОЗУ.

Вроде нашел косяк. Забыл прерывания отключить до ремапа!

Тогда еще вопрос. А барьеры синхронизации или памяти нужно после ремапа ставить или нет?
DpInRock
Понятия не имею. Но для вас вряд ли имеет значение скорость старта. После ремпапа и перед джампом можно провести вечность. Ибо прерваний нет.
Но вот не скажу за кортекс - мода процессора будет иметь значение. Надо привилегированный режим.
А то ваша прога полезет модифицировать стеки всех режимов, а ей дадут отлуп.
Alekseeey
Цитата(zuy @ Oct 21 2010, 17:01) *
Да, это понятно. Мой бут делает в общем тоже самое, что и встроенный, только ремапит вектора не из флеша, а из ОЗУ.

Вроде нашел косяк. Забыл прерывания отключить до ремапа!

Тогда еще вопрос. А барьеры синхронизации или памяти нужно после ремапа ставить или нет?


Пожалуйста, опубликуйте работающий вариант процедуры ремапа.

В usermanual lpc1114 написано на стр. 317:
Vector table — If the program changes an entry in the vector table, and then enables the
corresponding exception, use a DMB instruction between the operations. This ensures that
if the exception is taken immediately after being enabled the processor uses the new
exception vector.

Судя по всему - надо.
zuy
Цитата(Alekseeey @ Oct 21 2010, 16:41) *
Пожалуйста, опубликуйте работающий вариант процедуры ремапа.

Да вот что-то еще не прокатило, разбираюсь.
Как будет рабочий вариант, отпишу
zuy
Нет, все равно решительно не понимаю, что делает ремап в LPC1114.
Вот простой код, который просто включает светодиод, и через некоторое время выключает:
Код
int main(void )
{
  unsigned long *dst, size, i;
  const unsigned long    *src;

  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16) | (1<<6);  // Enable IOCON GPIO
  LPC_GPIO1->DIR  |= ( 1 << 8 );                  // LED pin direction out
  LPC_GPIO1->DATA &= ~( 1 << 8 );                 // LED ON

  // Copy vectors table from 0x0 to RAM
  src = (unsigned long *)0;
  dst = (unsigned long *)0x10000000;
  size = 0xC0 >> 2;
  do  { *dst++ = *src++; } while (--size);

  // Pause, just to mention the LED state.
  i= 10000000;
  while( --i );

  __disable_irq();
  LPC_SYSCON->SYSMEMREMAP = 0x01;        // remap to SRAM
  __ISB();

  LPC_GPIO1->DATA |= ( 1 << 8 );    // LED OFF

  while( 1 );
}


Чтобы ремап не мешал работать, я копирую таблицу векторов из флеш в RAM.
Если строка с ремапом присутствует то код не работает,
если ее комментирую то все нормально.
Но я же скопировал таблицу в RAM. Т.е. после ремапа для процессора вообще ничего не поменялось в начале адресного пространства!

Кто-нибудь может объяснить что тут происходит?

Alekseeey
Цитата(zuy @ Oct 21 2010, 23:14) *
Нет, все равно решительно не понимаю, что делает ремап в LPC1114.

Кто-нибудь может объяснить что тут происходит?


Обьяснить, к сожалению не могу, но могу от себя добавить, что когда я
выполняю программу в отладчике по шагам, по ассемблерным коммандам,
то при включенном ремапе происходит HardFault на инструкциях ветвления.

Причем, в тестовой программе blinky это исключение возникает при попытке
перехода на main();, но не происходит при переходе на SystemInit();
В памяти SystemInit расположена вверх по адресам от точки вызова, а main - вниз,
не знаю, важно это или нет...



Вот только что увеличил размер зарез. области до 0x1000 и скопировал туда 0x1000 байт флеш. Программа blinky дошла до main и попыталась начать работать. Прогресс.

Да, проект blinky заработал. При включенном ремапе. После копирования в ОЗУ 0x1000 байт флеша. У меня нехорошие предчувствия по поводу ремапа...



Интересная опечатка (?) есть в старых версиях user manual на lpc1114: на картинке карты памяти размер области векторов указан 0x0200 байт. И если я копирую 0x0200 байт, то программа работает...

zuy
Цитата(Alekseeey @ Oct 21 2010, 23:09) *
Обьяснить, к сожалению не могу, но могу от себя добавить, что когда я
выполняю программу в отладчике по шагам, по ассемблерным коммандам,
то при включенном ремапе происходит HardFault на инструкциях ветвления.

Отладчик Keil ?
Он для LPC1114 не совсем корректно работает. Тот пример что я привел в случае если ремап убрать, то вылетает в отладчике в HardFault, но работает отлично в железе.

Цитата(Alekseeey @ Oct 21 2010, 23:30) *
Интересная опечатка (?) есть в старых версиях user manual на lpc1114: на картинке карты памяти размер области векторов указан 0x0200 байт. И если я копирую 0x0200 байт, то программа работает...

Рано я обрадовался. в 0х200 байт у меня вся тестовая прога. Получается я ее скопировал всю в ОЗУ.
В реальном бутлоадере это не помогло :-(
rezident
Возникло предположение, что ремапу подвергается Flash кратно одному полному сектору размером 512 байт. Посмотрел в других кристаллах Cortex-M0 и Cortex-M3 - аналогично: либо 512 байт, либо 256 слов. Видимо бангалорские писатели даташитов что-то перемудрили laughing.gif
К сожалению, под рукой у меня нету ни LPC1114, ни отладчика, чтобы чем-то помочь в исследовании проблемы sad.gif
zuy
Цитата(rezident @ Oct 21 2010, 23:55) *
Возникло предположение, что ремапу подвергается Flash кратно одному полному сектору размером 512 байт. Посмотрел в других кристаллах Cortex-M0 и Cortex-M3 - аналогично: либо 512 байт, либо 256 слов. Видимо бангалорские писатели даташитов что-то перемудрили laughing.gif
К сожалению, под рукой у меня нету ни LPC1114, ни отладчика, чтобы чем-то помочь в исследовании проблемы sad.gif


Абсолютно верно. Я точно не проверял 512байт или какое другое значение,
но факт в том, что действительно ремап отображает не таблицу векторов в 0хС0 байт, а больше.

Я решил просто. Разместил код запуска основной проги в начале. Глянул какой максимальный адрес он занимает и
копирую в ОЗУ все до этого адреса. После ремапа, получается этот же код и отображается из ОЗУ и проц продолжает работать.
Далее я уже переписываю первые 0xc0 байт векторов из основной проги, и передаю ей управление через загрузку SP и PC.
Все работает.
rezident
Встречный вопрос. А сколько памяти у вас отведено под стек? Не может ли быть так, что переписывая вектора вы затираете стек?
В тестовом проекте Blinky стек начинается с 0x10000800. Но в конфигурации RAM Debug, когда ремап используется-таки, в icf-файле в начале ОЗУ резервируются 0x124 байта, а не 0x200. Такой же диапазон, но уже во Flash резервируется в конфигурации FLASH Debug.
zuy
Цитата(rezident @ Oct 22 2010, 00:48) *
Встречный вопрос. А сколько памяти у вас отведено под стек? Не может ли быть так, что переписывая вектора вы затираете стек?
В тестовом проекте Blinky стек начинается с 0x10000800. Но в конфигурации RAM Debug, когда ремап используется-таки, в icf-файле в начале ОЗУ резервируются 0x124 байта, а не 0x200. Такой же диапазон, но уже во Flash резервируется в конфигурации FLASH Debug.


У меня стек 0х200 и находится в самом конце используемого ОЗУ.
А чтобы вектора ничего не затерли, я в проекте бутлоадера и основной проги разрешаю использовать ОЗУ с отступом на длину векторов.
Alekseeey
Цитата(zuy @ Oct 22 2010, 00:43) *
Отладчик Keil ?
Он для LPC1114 не совсем корректно работает. Тот пример что я привел в случае если ремап убрать, то вылетает в отладчике в HardFault, но работает отлично в железе.


Рано я обрадовался. в 0х200 байт у меня вся тестовая прога. Получается я ее скопировал всю в ОЗУ.
В реальном бутлоадере это не помогло :-(


Отладчик у меня LPCXpresso. Плата + ide.

Вообще, я думаю, именно в данном случае отладчик мешает.

Сложно описать ощущения, когда для отладки, после команды ремапа ставишь подряд десять нопов, и, выполняя программу по ассемблерным шагам, наблюдаешь случайные изменения регистров и вылет в ХардФолт именно, блин, на седьмом нопе.

Пока я для себя решил делать так: первые 512 (0x200) байт загрузчика заняты его векторами и просто нулями. Т. е. код загрузчика будет начинаться, например, с 0x204.
В основной программе будет запрещено использование ОЗУ меньше адреса 0xC0.


Цитата(rezident @ Oct 22 2010, 01:48) *
Встречный вопрос. А сколько памяти у вас отведено под стек? Не может ли быть так, что переписывая вектора вы затираете стек?
В тестовом проекте Blinky стек начинается с 0x10000800. Но в конфигурации RAM Debug, когда ремап используется-таки, в icf-файле в начале ОЗУ резервируются 0x124 байта, а не 0x200. Такой же диапазон, но уже во Flash резервируется в конфигурации FLASH Debug.


По моим сведениям, в LPCXpresso стек начинается с самого старшего адреса ОЗУ и растет в сторону уменьшения адресов. Переменные же располагаются с самого младшего адреса ОЗУ и заполняют память в сторону увеличения адресов.

У lpc1114, соответственно, в lpcxpresso, стек начинается с адреса 0x10002000.

А у вас, я так понял, Keil?

И он резервирует 0x124? (рит) Обдумаю эту информацию на досуге.
rezident
Цитата(Alekseeey @ Oct 22 2010, 13:00) *
По моим сведениям, в LPCXpresso стек начинается с самого старшего адреса ОЗУ и растет в сторону уменьшения адресов. Переменные же располагаются с самого младшего адреса ОЗУ и заполняют память в сторону увеличения адресов.
Направление изменения стека не зависит от вида/типа платы, а только от архитектуры самого МК.
Цитата(Alekseeey @ Oct 22 2010, 13:00) *
А у вас, я так понял, Keil?
IAR EWARM 5.50. Пример проекта Blinky из него же.
Цитата(Alekseeey @ Oct 22 2010, 13:00) *
И он резервирует 0x124? (рит) Обдумаю эту информацию на досуге.
Это установки конкретного проекта, приводимого в примерах. Ни IDE, ни компилятор сами ничего не резервируют. Все настройки можно поменять так, как захочется. В худшем случае компилятор даст предупреждение или ошибку. Но своевольничать он не имеет права.
Использую самодельную плату с LPC1114FBD48,301 и LCD-модулем (TIC234). Отладчик - MT-Link. Так что никаких ограничений, накладываемых LPCXpresso, у меня нет.
rezident
Сегодня проэкспериментировал со своей платой и исходником из сообщения #11. Все работает корректно. Ничего не виснет и не вылетает. Через 9 секунд после ресета светодиод гаснет. Единственное, что я скорректировал, это обращение к порту, т.к. у меня из индикации только подсветка LCD и ее управление к другому пину МК подключено. Настройки icf-файла я взял из проекта Blinky, т.е. настройки стека как на скриншотах выше. Компилятор IAR EWARM 5.50.5. Отладчик MT-Link. Драйвер отладчика J-Link V4.14e. Что я не так делаю? laughing.gif
Alekseeey
Цитата(rezident @ Oct 23 2010, 00:51) *
Что я не так делаю? laughing.gif


Все так. Пожалуйста, укажи в каких адресах расположена функция main. (я думаю, в отладчике это будет видно).

Мне кажется, что IAR учитывает возможность ремапа и кладет исполняемый код в более старшие адреса. И поэтому все работает. В отличие от lpcxpresso, который размещает исполняемый код сразу после таблицы векторов прерываний (адрес 0xC0), и при ремапе эта область памяти начинает ссылаться на ОЗУ, где лежит мусор. Мне интересно узнать, куда кладет исполняемый код IAR, чтобы подправить у себя линкерный скрипт.
DpInRock
За кортекс не скажу, но кроме векторов надо копировать еще и (если есть) таблицу с константами и реальными переходами.В том смысле, что кусок стартапа связанный непосредственно с векторами. Ну либо вектора должны быть с абсолютной адресацией ( а для нее с все равно потребуется табличка с константами...).

Что-то типа того.
zuy
Цитата(rezident @ Oct 22 2010, 23:51) *
Сегодня проэкспериментировал со своей платой и исходником из сообщения #11. Все работает корректно. Ничего не виснет и не вылетает. Через 9 секунд после ресета светодиод гаснет. Единственное, что я скорректировал, это обращение к порту, т.к. у меня из индикации только подсветка LCD и ее управление к другому пину МК подключено. Настройки icf-файла я взял из проекта Blinky, т.е. настройки стека как на скриншотах выше. Компилятор IAR EWARM 5.50.5. Отладчик MT-Link. Драйвер отладчика J-Link V4.14e. Что я не так делаю? laughing.gif

Конфигурацию FLASH запускали? Поставьте начало ROM с 0х0, зачем кусок флеша размером 0х124 резервировать?
А то получается, собранный код располагается после 0х124 во флеш. А длина куска который ремапится вполне может быть и до 0х124.
У меня ф-ция main того примера до 0x124 заканчивается.
rezident
Цитата(zuy @ Oct 23 2010, 15:18) *
Поставьте начало ROM с 0х0, зачем кусок флеша размером 0х124 резервировать?
Поставил. Все равно работает. Вот проект. Специально включил генерацию всевозможных листингов и map-файла.
Только не знаю, зачем вам это (в смысле начало программы с нуля)? Ведь вся эта программа умещается в сегмент, который копируется и потом маппируется. Я попробовал то же самое (разместить с нуля во Flash) сделать с другой своей программой, которая обслуживает 4 кнопки, LCD и по нажатию на кнопку выводит одну из несколько картинок на него. В ней используются прерывания от timer16_b0 и i2c-модуля. В качестве "рыбы" для этой программы я использовал пример проекта I2C из экзамплов IAR. Программа вместе с битмаповскими картинками занимает чуть меньше 12кБ во Flash. Скомпилировал, загрузил. Работает laughing.gif Опять я что-то не так делаю? Или в IAR не кошерный компилятор? cranky.gif

Update. Я установил Keil 4.12. Но с ним я не знаком и не знаю как создать проект и работать с ним. Если вы выложите свой простейший тестовый проект, который у вас не работает с LPCExpresso, то я попробую проверить его работу на своей плате.
rezident
Любопытство победило smile.gif Со второй попытки создал в Keil работоспособный проект с исходником из сообщения #11. Не мудрствуя лукаво, вывел в окно watch значение регистра SYSMEMREMAP. Изменяя его значение с 0x02 на 0x01, наблюдаю, что содержимое памяти меняется. Вместо кода исполняемой из Flash программы появляются значения 0x00000000 вплоть до границы адреса 0x00000200. То бишь подтверждается версия о маппировании всего сегмента Flash размером 512 байт. Как этот глюк обходится в IAR, мне пока непонятно. laughing.gif Тестовый проект (для Keil 4.12) прикладываю.
zuy
Цитата(rezident @ Oct 24 2010, 04:05) *
Любопытство победило smile.gif Со второй попытки создал в Keil работоспособный проект с исходником из сообщения #11. Не мудрствуя лукаво, вывел в окно watch значение регистра SYSMEMREMAP. Изменяя его значение с 0x02 на 0x01, наблюдаю, что содержимое памяти меняется. Вместо кода исполняемой из Flash программы появляются значения 0x00000000 вплоть до границы адреса 0x00000200. То бишь подтверждается версия о маппировании всего сегмента Flash размером 512 байт. Как этот глюк обходится в IAR, мне пока непонятно. laughing.gif Тестовый проект (для Keil 4.12) прикладываю.

Да, похоже в ИАР это дело автоматически обходится.
В общем понятно что произошло. В Rev 0_0 мануала, они ошибочно указали размер таблицы векторов в 0х200 байт и тогда в описании регистра SYSMEMREMAP написали, что он ремапит таблицу векторов. И все вроде сходилось. Но потом видимо заметили косяк, что таблица то длиной 0хС0 байт и исправили это дело, а в описании регистра SYSMEMREMAP забыли указать, что он теперь ремапит не таблицу векторов, но область 0х200.
Т.о. на данный момент в последней версии мануала мы имеем некорректное описание регистра SYSMEMREMAP. Нигде по мануалу не написано, что он ремапит 0х200 байт.

А зачем мне надо настройки ROM ставить с 0-го адреса. Так я считаю, что это не мое дело раскладывать программу по адресам памяти.
Я линкеру указываю только где находятся области куда он может результат ложить, пусть сам и складывает. Мне то зачем над этим думать.
rezident
Цитата(zuy @ Oct 24 2010, 21:20) *
Т.о. на данный момент в последней версии мануала мы имеем некорректное описание регистра SYSMEMREMAP. Нигде по мануалу не написано, что он ремапит 0х200 байт.
Угу. В NXP-ной документации косяков немало sad.gif
Цитата(zuy @ Oct 24 2010, 21:20) *
А зачем мне надо настройки ROM ставить с 0-го адреса. Так я считаю, что это не мое дело раскладывать программу по адресам памяти.
Любая программа не умнее программиста, написавшего ее wink.gif Если вы знаете чуток побольше компилятора, то почему бы это знание не применить для облегчения ему работы? Ведь вас наверняка интересует результат, а не заморочки, которые испытывает компилятор? Да и результат его работы будет вашим общим, не так ли?
zuy
Цитата(rezident @ Oct 24 2010, 19:17) *
Любая программа не умнее программиста, написавшего ее wink.gif Если вы знаете чуток побольше компилятора, то почему бы это знание не применить для облегчения ему работы? Ведь вас наверняка интересует результат, а не заморочки, которые испытывает компилятор? Да и результат его работы будет вашим общим, не так ли?

Не, тут немного другое. Определив ROM с адреса отличного от 0х0 я должен иметь некоторое основание. Иначе, как я это обосную коллегам по работе.
У нас по бутлоадеру жесткие ограничения, каждый байт нужно знать чем занят.
А то, что ремап захватывает больше байт чем таблица векторов лечится 5-ю строками кода, и не нужно линкеру ничего об этом говорить.
KRS
Тоже столкнулся с проблемой. Но при обычной отладке в памяти.
В какой то момент, после загрузки программы, но до любых шагов портится слово по адресу
0x100000C0 = 0xFFFFFFFF
Может быть проблема в Jlink, я так и не разобрался просто сделал дырку на этом месте.

Память мапится скриптом до загрузки проги, все ок! кроме порчи байта.


К ремапу не имеет отношения вообще, JLINK портит слово 0x100000C0 каждый раз если нажать кнопку reset (я в IAR смотрю).
Т.е. заполняю 0, нажимаю reset и слово по этому адресу портится...
Almaz1988
Вот код бутлоадера:
CODE
#include "LPC11xx.h"
#include "rom_drivers.h"
#include "gpio.h"
#include "string.h"
#include "type.h"
#include "core_cm0.h"
#include "system_LPC11xx.h"
#include "application_Flash.h"


__ASM void __copy_MSP_( )
{
ldr r0, =0x10000000
ldr r0, [r0]
mov sp, r0
}

__ASM void __copy_reset_handler_( )
{
ldr r0, =0x10000004
ldr r0, [r0]
bx r0
}

/********************************************************** Main function ********************************/
int main(void)
{
SystemInit();

LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);

uint32_t const * pSrc = (uint32_t const *)0x00001000; //копируем первые 200 байт флеша, начиная с адреса 0х1000 в ОЗУ
uint32_t * pDst = (uint32_t *)0x10000000;
#define VECTORS_COUNT 128
for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i)
*pDst++ = *pSrc++;

__copy_MSP_( ); //загружаем в стек

__disable_irq(); //запрещаем прерывания
LPC_SYSCON->SYSMEMREMAP = 0x01; // remap to ram

__copy_reset_handler_(); //прыгаем на Resrt_handler

while(1);
}

Работает только если в "рабочей программе" не используются прерывания. Если прерывания используются, то "Рабоча программа" нивкакую не стартует.
Какие есть догадки, в чем ошибка?
_Артём_
Цитата(Almaz1988 @ Sep 24 2012, 11:46) *
Работает только если в "рабочей программе" не используются прерывания. Если прерывания используются, то "Рабоча программа" нивкакую не стартует.
Какие есть догадки, в чем ошибка?

Попробуйте по-другому.
Например так:
Код
typedef void (*ISRPtr) (void);
    uint32_t *ram_vector_table, *flash_vector_table;
    ram_vector_table=(uint32_t *)0x10000000;
    flash_vector_table=(uint32_t *)0x1000;
    for (uint_fast32_t i=0; i<48; i++)
        *ram_vector_table++=*flash_vector_table++;
    LPC_SYSCON->SYSMEMREMAP=1;
    __set_MSP(*(uint32_t *)0x1000);
    ISRPtr application_reset_handler=(ISRPtr)(*(uint32_t *)0x1004);
    application_reset_handler();
KRS
Т.к. у Cortex-M0 нет VTOR и переместить таблицу прерываний нельзя, а все таки понадобился вторичный бутлоадер, я сделал так:
в бутлоадере прерывания не используются! И все указатели из таблицы прерываний (кроме ресета) указывают на такую функцию:
Код
void handler(void)
{
        asm (
             "mrs r0,IPSR\n"
             "movs r1,#0x1\n"
             "lsls r1,#10\n"
             "add r0, r1\n"
             "lsls r0,#2\n"
             "ldr r0,[r0]\n"
             "mov r15, r0");
        while(1);
}


А старт приложения такой последовательностью:
Код
        asm ("movs r1,#0x1\n"
             "lsls r1,#12\n"
             "ldm r1, {r0,r1}\n"
             "mov r13, r0\n"
             "mov r15, r1");
        while(1);


Все работает!
Приложение ничем не отличается от обычного, только начинается с адреса 0x1000 по которому лежит обычная таблица прерываний.
Сергей Борщ
QUOTE (Almaz1988 @ Sep 24 2012, 11:46) *
Вот код бутлоадера:
Для вставки кода в сообщения есть кнопочка на форме ввода. Используйте ее, пожалуйста. Здесь так принято.
QUOTE (Almaz1988 @ Sep 24 2012, 11:46) *
uint32_t const * pSrc = (uint32_t const *)0x00001000; //копируем первые 200 байт флеша, начиная с адреса 0х1000 в ОЗУ
uint32_t * pDst = (uint32_t *)0x10000000;
#define VECTORS_COUNT 128
for(uint_fast8_t i = 0; i < VECTORS_COUNT; ++i)
*pDst++ = *pSrc++;
А теперь смотрите внимательно: 1) Вы в комментариях пишете, что копируете 200 байт, а на самом деле копируете 128*sizeof(uint32_t), т.е. 512 байт.
2) Та часть ОЗУ, куда вы пишете, у вас зарезервирована? Т.е. не получается ли так, что вы копируя разрушаете данные или стек загрузчика?
_Артём_
Цитата(Almaz1988 @ Sep 24 2012, 11:46) *
Работает только если в "рабочей программе" не используются прерывания. Если прерывания используются, то "Рабоча программа" нивкакую не стартует.
Какие есть догадки, в чем ошибка?

Такая догадка: в рабочей программе происходит очистка памяти, в которой находятся адреса обработчиков прерываний.
Проверьте на входе в main рабочей программы что у вас по адресам ОЗУ 0x10000000-0x100000C0? Заполнена эта область нулями или там адреса обработчиков?
Эту область нужно как-то выделить в скрипте линкера или указать в скрипте область ОЗУ рабочей программы не 0x10000000-0x100001FF, а от 0x100000C0 до 0x100001FFF.

Цитата(Almaz1988 @ Sep 24 2012, 11:46) *
нивкакую не стартует.
Какие есть догадки, в чем ошибка?

Не стартует - это что значит? А что она делает?
В HardFault не попадает?

P.S. Выложили бы проекты целиком - программа это не только текст.
Almaz1988
Наметился небольшой прогресс))
В "загрузчике" копирую из флеша ячейки 0х0000-0х0200 в ОЗУ по адресам 0х1000 0000 - 0х1000 0200, делаю ремап и затем прыгаю по адресу Reset_handler(0х1000 0004). "Загрузчик" успешно перезапускается.
Если же я копирую в ОЗУ из адресов флеша 0х2000-0х2200. То после ремапа и прыжка "Рабочая программа", которая залита по адресу 0х2000 не запускается.
В том что у меня залита корректная рабочая программа я могу убедиться если не делая ремапа прыгаю:
Код
    void (*fptr)(void);
    fptr = (void (*)(void))0x000002675;
    fptr();

Тогда рабочая программа запускается.
Прикладываю проекты "Загрузчика" и "Рабочей программы".



Еще один вопрос:
Используемый МК - lpc11c24 с "железным" протоколом CAN. В рабочей программе он используется.
В мануале на МК написано:
Цитата
On-chip
RAM from address 0x1000 0050 to 0x1000 00B8 is used by the CAN API. This address
range should not be used by the application. For applications using the on-chip CAN API,
the linker control file should be modified appropriately to prevent usage of this area for
application’s variable storage.

Выходит, что ремап векторов в ОЗУ будет приводить к затиранию CAN функций?
Значит ремап не решение.
Какие есть другие способы решения проблемы, если и загрузчик и рабочая программа активно будут пользоваться прерываниями?
KRS
Цитата(Almaz1988 @ Sep 28 2012, 10:22) *
Еще один вопрос:
Используемый МК - lpc11c24 с "железным" протоколом CAN. В рабочей программе он используется.
В мануале на МК написано:

Выходит, что ремап векторов в ОЗУ будет приводить к затиранию CAN функций?



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

Если вы используете функции CAN, которые в ПЗУ лежат, то они могут перетереть ваши вектора - т.к. используют эту область памяти под свои данные (сами функции стереть нельзя они в ПЗУ)

В любом случае хранить вектора прерываний в ОЗУ для данного контроллера не очень хорошее решение!
Я уже привел метод - он прекрасно работает! http://electronix.ru/forum/index.php?s=&am...t&p=1095595
Можно использовать метод еще тупее - в бутлоадере создать обработчик на каждое прерывание (для M0 их не так много) который просто берет указатель из таблицы прерываний приложения и переходит по нему. (собственно говоря приведенный исходник это и делает, но что бы не плодить функции он узнает номер из регистра).

При этом приложения с бутлоадером и без бутлоадера отличаются только icf файлом! (началом флеша) и никаких ограничений на использования памяти нет.
Сергей Борщ
QUOTE (Almaz1988 @ Sep 28 2012, 09:22) *
и затем прыгаю по адресу Reset_handler(0х1000 0004).
А вот неверно - вы прыгаете на вектор сброса. А надо прыгать на адрес, которых хранится в этом векторе:
CODE
    void (**fptr)(void);
    fptr = (void (**)(void))0x10000004;
    (*fptr)();
Almaz1988
Цитата(KRS @ Sep 28 2012, 11:15) *
Если вы используете функции CAN, которые в ПЗУ лежат, то они могут перетереть ваши вектора - т.к. используют эту область памяти под свои данные (сами функции стереть нельзя они в ПЗУ)

В любом случае хранить вектора прерываний в ОЗУ для данного контроллера не очень хорошее решение!
Я уже привел метод - он прекрасно работает! http://electronix.ru/forum/index.php?s=&am...t&p=1095595
Можно использовать метод еще тупее - в бутлоадере создать обработчик на каждое прерывание (для M0 их не так много) который просто берет указатель из таблицы прерываний приложения и переходит по нему. (собственно говоря приведенный исходник это и делает, но что бы не плодить функции он узнает номер из регистра).

При этом приложения с бутлоадером и без бутлоадера отличаются только icf файлом! (началом флеша) и никаких ограничений на использования памяти нет.


Ваш пример подойдет в случае, если в загрузчике не используются прерывания. В моем же случае, я планирую это делать.
Я думаю написать обработчики для каждого прерывания с двумя вариантами выбора:
Код
__ASM void CAN_IRQapplication(void)
{
    ldr r0, =0x2074
    ldr r0, [r0]
    mov pc, r0
}
__ASM void CAN_IRQbootloader(void)
{
    ldr r0, =0x0074
    ldr r0, [r0]
    mov pc, r0
}
void CAN_IRQ(void)
{
              if ( main < 2000 )
              {
                          CAN_IRQapplication();
              }
              else
              {
                          CAN_IRQbootloader();
              }
}

Такое решение приведено в примере NXP secondary bootloader (без варианта выбора). Сейчас пытаюсь вкрутить их стартап-файл (LPCXpresso) в свой Keil-овский проект.


Сработает?
Сергей Борщ
QUOTE (Almaz1988 @ Sep 28 2012, 11:10) *
Сработает?
Нет. для этой программы main будет константой, она понятия не имеет, что в другой программе есть свой main и никаким образом не может получить его адрес. Можно взводить и анализировать какой-то флаг.
Almaz1988
Цитата(Сергей Борщ @ Sep 28 2012, 12:44) *
Нет. для этой программы main будет константой, она понятия не имеет, что в другой программе есть свой main и никаким образом не может получить его адрес. Можно взводить и анализировать какой-то флаг.

Так нам и не нужен main другой программы.
Запускается загрузчик.
Начинает работать.
При прерываниях прыгает в свою табл векторов ( т.к. адрес его функции main < 0x2000).
Закончил работу, передал управление рабочей программе.
Та начинает работу и при прерываниях прыгает по своим адресам ( т.к. адрес его функции main > 0x2000).

что в keil'e, что в LPCXpresso startup-файлы полны загадочных extern и импорт, например:
в Keil:
Цитата
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
LDR R0, =__main
BX R0
ENDP


в LPCXpresso:
Цитата
extern unsigned long _etext;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;

void ResetISR(void)
{
unsigned char *pulSrc, *pulDest;
pulSrc = (unsigned char *)&_etext;
for(pulDest = (unsigned char *)&_data; pulDest < (unsigned char *)&_edata; )
{
*pulDest++ = *pulSrc++;
}
.........


Откуда в Кейловском варианте берется __main, а в LPC-шном _etext, _data, _edata....?
Их тупо проэкстернили, но они нигде не инициализируются


Сергей Борщ
QUOTE (Almaz1988 @ Sep 28 2012, 12:06) *
Та начинает работу и при прерываниях прыгает по своим адресам ( т.к. адрес его функции main > 0x2000).
Прыгает в каком месте? В тех функциях, которые принадлежат загрузчику и знают только о существовании своего main. Либо приложению надо переписать эти функции поверх функций загрузчика, тогда зачем в них ветвление?
Almaz1988
Цитата(Сергей Борщ @ Sep 28 2012, 13:26) *
Прыгает в каком месте? В тех функциях, которые принадлежат загрузчику и знают только о существовании своего main. Либо приложению надо переписать эти функции поверх функций загрузчика, тогда зачем в них ветвление?

В каждый проект(загрузчик и рабочая программа) добавляет один и тот же файл (Handlers.h), в котором написаны обработчики прерываний которые отправляют по адресам 0х0000 - 0х00С0, если они вызваны из загрузчика и по адресам 0х2000 - 0х20С), если они вызваны из рабочей программы. По идее нужно просто переделать стартап файл, чтобы из него при всех прерываниях прыгать в мои самописные обработчики прерываний.
Но он меня пока вводит в тупик - своими экстернами и импортами нигде не объявленных переменных
KRS
Цитата(Almaz1988 @ Sep 28 2012, 12:10) *
Я думаю написать обработчики для каждого прерывания с двумя вариантами выбора:

там функции на асме не нужны! Проще сразу из С вызвать нужную функцию из таблиц указателей!

тут весь вопрос в условии - как определить что бутлоадер еще работает!

А можно и один код для всех обработчиков оставить - только базовый адрес таблицы вычислять в зависимости от условия

Код
void handler(void)
{
        asm (
             // вычислить адрес таблицы в R1

             "mrs r0,IPSR\n"
             "add r0, r1\n"
             "lsls r0,#2\n"
             "ldr r0,[r0]\n"
             "mov r15, r0");
        while(1);
}

_Артём_
Цитата(Almaz1988 @ Sep 28 2012, 09:22) *
Используемый МК - lpc11c24 с "железным" протоколом CAN. В рабочей программе он используется.
В мануале на МК написано:

Выходит, что ремап векторов в ОЗУ будет приводить к затиранию CAN функций?
Значит ремап не решение.
Какие есть другие способы решения проблемы, если и загрузчик и рабочая программа активно будут пользоваться прерываниями?

Almaz1988, а в буте CAN используется?
Если не используется, то может можно сделать наоборот:
бут располагается где нибудь в конце памяти, и при старте ремапит вектора в ОЗУ, а основная программа работает с векторами расположенными во flash.
Almaz1988
Продолжаю штурм))
и в загрузчике и в рабочей программе будут использоваться прерывания: SysTick, SVC, PendSV, CAN.
Пишу обработчики для этих прерываний.

Если в обработчике для каждого прерывания делаю прыжок, например в SysTick_Handler:
Код
__ASM void SysTick_Handler(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

То Загрузчик передает управление Рабочей программе, в которой все прерывания обрабатываются корректно.

Но, стоит написать вот так (через функцию посредник):
Код
__ASM void SysTick_Handler_of_application(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

void SysTick_Handler(void)
{
              SysTick_Handler_of_application( );
}

И три из четырех обработчика прерывания работать уже не хотят!
Обрабатываются только CAN-прерывания!

Функции посредники нужны мне для того, чтобы производить проверку, из какой программы вызвано прерывание:
Код
void SysTick_Handler(void)
{
    if ( *(uint32_t *)0x100001F0 == 0x67 ) //сли обработчик прерывания вызван из рабочей программы, то прыгаем в таблицу векторов рабочей программы
    SysTick_Handler_of_application( );
              else
              SysTick_Handler_of_bootloader();//иначе обработчик прерывания вызван из загрузчика
}
Almaz1988
Продолжаю штурм))
и в загрузчике и в рабочей программе будут использоваться прерывания: SysTick, SVC, PendSV, CAN.
Пишу обработчики для этих прерываний.

Если в обработчике для каждого прерывания делаю прыжок, например в SysTick_Handler:
Код
__ASM void SysTick_Handler(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

То Загрузчик передает управление Рабочей программе, в которой все прерывания обрабатываются корректно.

Но, стоит написать вот так (через функцию посредник):
Код
__ASM void SysTick_Handler_of_application(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

void SysTick_Handler(void)
{
              SysTick_Handler_of_application( );
}

И три из четырех обработчика прерывания работать уже не хотят!
Обрабатываются только CAN-прерывания!

Функции посредники нужны мне для того, чтобы производить проверку, из какой программы вызвано прерывание:
Код
void SysTick_Handler(void)
{
    if ( *(uint32_t *)0x100001F0 == 0x67 ) //сли обработчик прерывания вызван из рабочей программы, то прыгаем в таблицу векторов рабочей программы
    SysTick_Handler_of_application( );
              else
              SysTick_Handler_of_bootloader();//иначе обработчик прерывания вызван из загрузчика
}
Almaz1988
Продолжаю штурм))
и в загрузчике и в рабочей программе будут использоваться прерывания: SysTick, SVC, PendSV, CAN.
Пишу обработчики для этих прерываний.

Если в обработчике для каждого прерывания делаю прыжок, например в SysTick_Handler:
Код
__ASM void SysTick_Handler(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

То Загрузчик передает управление Рабочей программе, в которой все прерывания обрабатываются корректно.

Но, стоит написать вот так (через функцию посредник):
Код
__ASM void SysTick_Handler_of_application(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

void SysTick_Handler(void)
{
              SysTick_Handler_of_application( );
}

И три из четырех обработчика прерывания работать уже не хотят!
Обрабатываются только CAN-прерывания!

Функции посредники нужны мне для того, чтобы производить проверку, из какой программы вызвано прерывание:
Код
void SysTick_Handler(void)
{
    if ( *(uint32_t *)0x100001F0 == 0x67 ) //сли обработчик прерывания вызван из рабочей программы, то прыгаем в таблицу векторов рабочей программы
    SysTick_Handler_of_application( );
              else
              SysTick_Handler_of_bootloader();//иначе обработчик прерывания вызван из загрузчика
}
Almaz1988
Продолжаю штурм))
и в загрузчике и в рабочей программе будут использоваться прерывания: SysTick, SVC, PendSV, CAN.
Пишу обработчики для этих прерываний.

Если в обработчике для каждого прерывания делаю прыжок, например в SysTick_Handler:
Код
__ASM void SysTick_Handler(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

То Загрузчик передает управление Рабочей программе, в которой все прерывания обрабатываются корректно.

Но, стоит написать вот так (через функцию посредник):
Код
__ASM void SysTick_Handler_of_application(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

void SysTick_Handler(void)
{
              SysTick_Handler_of_application( );
}

И три из четырех обработчика прерывания работать уже не хотят!
Обрабатываются только CAN-прерывания!

Функции посредники нужны мне для того, чтобы производить проверку, из какой программы вызвано прерывание:
Код
void SysTick_Handler(void)
{
    if ( *(uint32_t *)0x100001F0 == 0x67 ) //сли обработчик прерывания вызван из рабочей программы, то прыгаем в таблицу векторов рабочей программы
    SysTick_Handler_of_application( );
              else
              SysTick_Handler_of_bootloader();//иначе обработчик прерывания вызван из загрузчика
}
Almaz1988
Продолжаю штурм))
и в загрузчике и в рабочей программе будут использоваться прерывания: SysTick, SVC, PendSV, CAN.
Пишу обработчики для этих прерываний.

Если в обработчике для каждого прерывания делаю прыжок, например в SysTick_Handler:
Код
__ASM void SysTick_Handler(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

То Загрузчик передает управление Рабочей программе, в которой все прерывания обрабатываются корректно.

Но, стоит написать вот так (через функцию посредник):
Код
__ASM void SysTick_Handler_of_application(void)
{
    ldr r0, =0x203C
    ldr r0, [r0]
    mov pc, r0
}

void SysTick_Handler(void)
{
              SysTick_Handler_of_application( );
}

И три из четырех обработчика прерывания работать уже не хотят!
Обрабатываются только CAN-прерывания!

Функции посредники нужны мне для того, чтобы производить проверку, из какой программы вызвано прерывание:
Код
void SysTick_Handler(void)
{
    if ( *(uint32_t *)0x100001F0 == 0x67 ) //сли обработчик прерывания вызван из рабочей программы, то прыгаем в таблицу векторов рабочей программы
    SysTick_Handler_of_application( );
              else
              SysTick_Handler_of_bootloader();//иначе обработчик прерывания вызван из загрузчика
}
Almaz1988
Добил бутлоадер)))
В бутлоадере использую только CAN-прерывания:
Код
__ASM void CAN_IRQHandler_of_application(void)
{
    ldr r0, =0x2074
    ldr r0, [r0]
    bx r0
}

void CAN_IRQHandler(void)
{
    if ( *(uint32_t *)0x100001F0 == 0x67 )
    {
        CAN_IRQHandler_of_application( );
    }
    else
    {
        (*rom)->pCAND->isr();
    }
}


Значение 0х67 в ячейку 0x100001F0 записывает рабочая программа при запуске (в своем main()):
Код
    uint32_t *ram_vector_table;
    ram_vector_table=(uint32_t *)0x100001F0;
    *ram_vector_table = 0x67;


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