реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> LPC1768 uart bootloader, не получается
Cosmojam
сообщение Dec 8 2011, 21:00
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182



Всем привет!
Помогите победить проблему.
Пробую сделать загрузчик по юарту. Позже планируется usb, но пока с этим ничего не получается.
За основу взят usb bootloader от NXP AN10866 http://www.nxp.com/documents/other/LPC1700..._bootloader.zip
В аттаче архив с 2 проектами для LPCXpresso. UART_bootloader - собственно бутлодырь, точнее только его скелет с самым самым минимумом. test - светодиодная моргалка, которую надо загружать для теста.
Проблема такова:
Загрузчик работает верно, бинарный файл с прошивкой тестового проекта сохраняет как нужно (файл не выравнен по 512байт, то чего не хватает для записи последнего блока дописываю ручками, временно так). Проверки при записи проходят т.е. неверно залитый файл исключается.
При старте он проверяет контрольную сумму прошивки в секторе 4 (туда по умолчанию загружаем). Сумма есть в бинарнике.
Сумма сошлась, надо переходить к исполнению программы с 4 сектора.
Указываем новое местоположение таблицы векторов прерываний, смещаем программный счётчик и верхушку стека (всё как в NXP-шном примере, только там для Keil, а у меня gcc с другим синтаксисом ассемблера):
Код
void boot(uint32_t a)
{
    asm(
    "LDR SP, [R0]\n"
    "LDR PC, [R0, #4]\n"
    );
}

void execute_user_code(void)
{
    uint32_t addr=(uint32_t)sector_start_adress[USERCODE_SECTOR_START];
    SCB->VTOR = (addr & 0x1FFFFF80);
    boot(addr);
}

Успешно попадаем в ResetISR загруженной программы (смотрю пошагово в отладчике и сверяю адреса инструкций с адресами в .map файле)
Дальше самое интересное:
Тестовая программа:
Код
volatile uint32_t del;
void _delay(uint32_t delay)
{
    uint32_t i;
    for(i = 0; i < delay; i++ )
        del = i; // do this so that the compiler does not optimize away the loop.
}


int main(void)
{
//    SystemInit();

//    init_uart(57600);

    LPC_SC->PCONP |= ( 1 << 15 ); // power up GPIO
    LPC_GPIO0->FIODIR |= 1 << 1;
    LPC_GPIO0->FIOPIN = 1<<1;

//    uart_sendstring("YES\n");

    uint32_t i;
    const uint32_t delay=1<<22;
    while(1)
    {
    //    _delay(1<<22);
        for(i = 0; i < delay; i++ )
            del = i;
        LPC_GPIO0->FIOPIN ^= 1 << 1;
    }
    return 0;
}

В таком виде работает, светодиод на P1.1 моргает. Но стоит раскомментировать задержку в вечном цикле и закомментировать "инлайновую" задержку (так она в теле цикла, а станет функцией) - всё, получаем HardFault Precise Error (скачок на адрес 0x40F6 где лежит обработчик HardFault-ов). Тоже самое и с юартом и системинитом. Можно предположить косяк со стеком при вызове функции, но фактически стек работает верно. Например, если убрать системинит, но оставить юарт, то хардфолтов не будет, он исправно пробежит uart_sendstring("YES\n"); но ничего не отправит. Та же программа зашитая в 0 сектор как обычно (с соответсвующей правкой скрипта линкера) работает абсолютно верно.
Оно мне уже мозг вынесло. Подскажите плз, что я делаю не так?

Прикрепленные файлы
Прикрепленный файл  uart_boot.zip ( 183.5 килобайт ) Кол-во скачиваний: 26
 


--------------------
typedef enum { no, yes, maybe } bool; | блог тут
Go to the top of the page
 
+Quote Post
Dron_Gus
сообщение Dec 9 2011, 07:01
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 9-01-05
Из: Санкт-Петербург
Пользователь №: 1 861



А приложение точно собрано для того адреса, по которомц Вы его грузите? Точно-точно? Уж очень симптомы похожи на выполнение ни с того адреса.


--------------------
Если сверху смотреть, то сбоку кажется, что снизу ничего не видно.
Go to the top of the page
 
+Quote Post
Cosmojam
сообщение Dec 9 2011, 07:30
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182



Точно-точно. По крайней мере скрипты линкера верные, в map-файле адреса тоже верные.
Вчера уже в полу-сонном состоянии решил добавить задержку (for на 10000) перед переходом на адрес программы из загрузчика и оно заработало! юарт начал печатать, функции вызываться, диод моргать.Но радость была не долгой sad.gif Попробовал залить реальную программу где уже 70кб набыдлокодено и в ней получаю похожие симптомы: то Imprecise Error прямо в ResetISR, то после срабатывания 2-го прерывания от таймера, то ещё что-то, глючит по-полной в общем. Опять же адрес начала верный MFlash512 (rx) : ORIGIN = 0x4000, LENGTH = 0x7C000 в map файле адреса верные. Эти симптомы с этой программой похожи на те, с которыми я столкнулся пытаясь разместить загрузчик в коде основной программы, но по специальному адресу. Там тоже сначала ни в какую не получалось, потом помогли на форуме lpcxpresso чтобы заработал тестовый проект, а когда те же скрипты линкера перенёс в реальный проект то получил такие глюки. Вот эта тема http://knowledgebase.nxp.com/showthread.php?t=2670 . Но если проблема в моём быдлокоде, то почему он стабильно работает из 0 сектора?! Продолжаю мучиться...


--------------------
typedef enum { no, yes, maybe } bool; | блог тут
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 9 2011, 10:05
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514



стартовый адрес точно __main ?

упс.. прошу прощения sad.gif

у Вас действительно ResetISR точка входа
Go to the top of the page
 
+Quote Post
Cosmojam
сообщение Dec 9 2011, 13:04
Сообщение #5


Местный
***

Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182



Поступило предложение использовать барьеры памяти и сбросить конвейер перед "прыжком в другую реальность". Почитал что это и как, не особо хорошо понял этот механизм, но всё же добавил:
Код
   __asm (
            "dmb\n"
            "dsb\n"
            "isb\n"
        );

Прямо перед вызовом функции, изменяющей PC в загрузчике. А так же в моей программе в ResetISR прямо в самом начале.
Вот что происходит в этом случае:
Загрузчик всё так же успешно отрабатывает, обновляет SP и PC, прыгаем в ResetISR моей программы и на этапе инициализации data/bss происходит сразу 3 пакости: Access Violation, Stack Error, Precise Error. Но что интересно они происходят не без причины. SP внезапно изменяется на 3FE т.е. какой-то код в загрузчике, поскольку в моей программе эта область не отмечена вообще никак - происходит Access Violation. Но кто мог так надругаться над стеком?? Затрейсить выполнение нечем, но в отладчике отображаются красным цветом с градиентом последние 4 команды и это всё тот же цикл в инициализации секторов data/bss, т.е. дальше этого места она никак не могла выпрыгнуть. Есть вариант что кончается память и "куча" дорастает до стека, но этого не может быть т.к. у меня нет столько глобальных переменных, есть парочка статических буферов, один на 4k, другой на 256 и ещё по мелочи, но это никак не переползёт до 32k если только не произойдёт какой-то бяки и цикл инициализации не "взбесится". Но какого лешего тогда оно работает из 0 сектора?
Вот что интересно, если не добавлять барьеры и сброс конвейера в начале ResetISR, то поведение будет другое. SP уже так не изменяется, остаётся на 10007ED8 (начальная верхушка 10008000) во время хардфолта, и ошибка - Bus Error. Тут не удаётся увидеть что было перед этим (видимо отладчик глючит), но судя по такому отрастанию стека там уже какие-то функции успели вызваться. Если же убрать барьеры и из загрузчика, то будет примерно также, но ошибка Imprecise Error и тот же хардфолт.
Кошмар sm.gif


--------------------
typedef enum { no, yes, maybe } bool; | блог тут
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 9 2011, 13:17
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514



прямо несколько минут назад поднимали одну мою ветку
там упоминалось, что __main, в числе прочего, вызывает еще и __user_initial_stackheap, а потом уже main()
может, тут собака порылась? (хотя там применительно к Keil, возможно, в GCC все по-другому)
Go to the top of the page
 
+Quote Post
Cosmojam
сообщение Dec 9 2011, 20:26
Сообщение #7


Местный
***

Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182



Всё намного проще biggrin.gif
Ну почему люди такие идиоты? Это я про себя. Банальнейшая ошибка в загрузчике из-за которой блоки записывались неверно но проверялись верно, настолько банальная, что признаться стыдно. Вот так ищем ошибку там где её нет, и не замечаем на самом видном месте.
А заметил вот как: в LPCXpresso можно прошивать бинарник со смещением на любой адрес. Ради интереса пошил так свою программу, загрузчик там тоже был и вот оно побежало как положено. Спасибо всем за отклики!
Что примечательно до этого 3 суток загара перед монитором и никакого толку, а тут отвлёкся с друзьями в баре по пиву, вернулся домой и почти сразу "попёрло" biggrin.gif Зато поближе с архитектурой проца познакомился.
Остались несколько белых пятен насчёт задержек перед прыжком, переключения тактовых частот и барьеров, но это уже по ходу дела разберусь.


--------------------
typedef enum { no, yes, maybe } bool; | блог тут
Go to the top of the page
 
+Quote Post
Cosmojam
сообщение Dec 9 2011, 22:05
Сообщение #8


Местный
***

Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182



В догонку ещё вопрос по теме бутлодырей.
Что если мне нужно вызвать загрузчик из основной программы, но при этом передав ему параметр? Т.е. в основной программе видим что с компьютера приходит запрос на соединение от программы для обновления прошивки. На это нужно перепрыгнуть обратно в загрузчик, но при этом знать что сейчас будет обновление прошивки и не нужно проверять суммы и стартовать программу. Обычно это делают с помощью кнопки или небольшой задержки при старте загрузчика чтобы попытки соединения уже шли во время его старта. А можно как-то более изящно? На ум приходит глобальная переменная по жёстко заданному адресу доступному обоим в загрузчике и в программе, но ведь она будет инициализирована в 0 всякий раз при старте загрузчика, а портить стартап код не хочется ради этого. Или в каком-нибудь регистре передать параметр и на самом входе в ResetISR загрузчика забрать этот параметр из регистра? Но не будет ли в регистре произвольный мусор при POR? Подскажите как лучше это сделать?


--------------------
typedef enum { no, yes, maybe } bool; | блог тут
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 10 2011, 08:25
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514



Жестко прописанная переменная в Non-ZI сегменте
Go to the top of the page
 
+Quote Post
Dron_Gus
сообщение Dec 10 2011, 09:45
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 9-01-05
Из: Санкт-Петербург
Пользователь №: 1 861



SVC? Можно сдвинуть таблицу векторов обратно на бутлоадер и вызвать. А можно просто взять адрес из таблицы векторов бутлоадера и вызвать напрямую.


--------------------
Если сверху смотреть, то сбоку кажется, что снизу ничего не видно.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 10 2011, 10:45
Сообщение #11


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (Cosmojam @ Dec 10 2011, 00:05) *
На это нужно перепрыгнуть обратно в загрузчик, но при этом знать что сейчас будет обновление прошивки и не нужно проверять суммы и стартовать программу.
То есть нужен ровно 1 бит информации. Делаете программный ресет и в загрузчике проверяете его флаг. Убиваете сразу двух кроликов: 1) попадаете в загрузчик без лишних теодвижений
2) Сбрасываете всю периферию, в том числе и настроенный приложением WDT, в известное и четко определенное состояние.
QUOTE (Cosmojam @ Dec 10 2011, 00:05) *
Обычно это делают с помощью кнопки или небольшой задержки при старте загрузчика чтобы попытки соединения уже шли во время его старта.
Чушь. Так делают студенты. Кнопка - еще куда ни шло как средство аварийного запуска загрузчика, помогает на этапе разработки после заливки мертвой прошивки. А в общем случае устройство должно перешиваться без участия человека, по команде через штатный интерфейс.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Cosmojam
сообщение Dec 10 2011, 17:54
Сообщение #12


Местный
***

Группа: Свой
Сообщений: 311
Регистрация: 12-01-11
Из: Калининград (Koenigsberg)
Пользователь №: 62 182



Цитата(toweroff @ Dec 10 2011, 11:25) *
Жестко прописанная переменная в Non-ZI сегменте

Так ведь глобальные переменные инициируются в 0, т.е. нужно править стартап чтобы эту переменную не потёрли в ResetISR.
Цитата(Dron_Gus @ Dec 10 2011, 12:45) *
SVC? Можно сдвинуть таблицу векторов обратно на бутлоадер и вызвать. А можно просто взять адрес из таблицы векторов бутлоадера и вызвать напрямую.

Да, только бутлодырь должен уметь отличать вызвали его программно, или по POR.
Цитата(Сергей Борщ @ Dec 10 2011, 13:45) *
То есть нужен ровно 1 бит информации. Делаете программный ресет и в загрузчике проверяете его флаг. Убиваете сразу двух кроликов: 1) попадаете в загрузчик без лишних теодвижений
2) Сбрасываете всю периферию, в том числе и настроенный приложением WDT, в известное и четко определенное состояние.
Чушь. Так делают студенты. Кнопка - еще куда ни шло как средство аварийного запуска загрузчика, помогает на этапе разработки после заливки мертвой прошивки. А в общем случае устройство должно перешиваться без участия человека, по команде через штатный интерфейс.

Вы имеете в виду програмный ресет от вочдога и проверку флага WDTR в регистре RSID ?


--------------------
typedef enum { no, yes, maybe } bool; | блог тут
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 10 2011, 23:27
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514



Цитата(Cosmojam @ Dec 10 2011, 21:54) *
Так ведь глобальные переменные инициируются в 0, т.е. нужно править стартап чтобы эту переменную не потёрли в ResetISR.

а Вы читаете, что я пишу? в NON-Zero-Init сегменте
после программного ресета сегмент никто не тронет, проц просто рестартнет
прочитаете магическое слово(байт, бит) - все, меня программно рестартнули, значит надо ждать прошивку
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Dec 11 2011, 00:47
Сообщение #14


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (Cosmojam @ Dec 10 2011, 19:54) *
Вы имеете в виду програмный ресет от вочдога и проверку флага WDTR в регистре RSID ?
Нет, я имел ввиду сброс через SYSRESETREQ. К сожалению,
QUOTE
Note: support for
SYSRESETREQ is not included in LPC17xx devices.
Значит, такой вариант отпадает.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
toweroff
сообщение Dec 11 2011, 07:59
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514



Цитата(Сергей Борщ @ Dec 11 2011, 04:47) *
Нет, я имел ввиду сброс через SYSRESETREQ. К сожалению, Значит, такой вариант отпадает.

а чем плоха перезагрузка вотчдогом?
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 13:02
Рейтинг@Mail.ru


Страница сгенерированна за 0.01526 секунд с 7
ELECTRONIX ©2004-2016