Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Стартовый загрузчик
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
ДЕЙЛ
Хочется понять, как работает эта штука? В моём представлении это маленькая программа, находящаяся где-то в хвосте флеш-памяти. При старте начинается выполнение программы с нулевого адреса, где проверяется выполнение какого-то условия, например, уровень на определённой ножке. Если условие выполняется, то следует инструкция вроде JMP Bootloader, т.е. переход в область флеш, содержащей команды загрузчика, которые выполняет ЦПУ. Загрузчик принимает данные в виде файла *.bin по какому-либо интерфейсу МК, эти данные он раскладывает во флеш-памяти, затирая старую версию прошивки. Причём в новой версии программы должна быть та же самая процедура перехода в область загрузчика на случай следующего обновления. Если в этом месте мысли правильные, то дальше имеются мысли по поводу организации самопального загрузчика:
1. Пишется программа загрузчика - процедуры приёма и раскладки данных в памяти, компилируется в виде файла *.bin.
2. Пишется программа для записи полученного файла куда-нибудь в дальний угол памяти, начиная с определённого адреса, допустим 0х1000000. Всё, загрузчик находится в памяти.
3. Пишем рабочую программу, которая в самом начале содержит условие с переходом вроде JMP 0x1000000. Размер прошивки должен быть всегда такой, чтобы не затёрся код загрузчика.

Примерно такие у меня измышления. Насколько они правильные? Если правильные, то имеются попутные вопросы:
1. Как организовать запись байта по определённому адресу флеш на С в IAR?
2. В MSP430 перед изменением флеш нужно предварительно настроить контроллер флеш-памяти. Как обстоит дело в LPC1778?
3. Если в LPC1778 прошивка записывается через UART0, то там уже есть загрузчик? Если он есть, то можно ли его случайно стереть?

Где можно подробнее почитать на эту тему с самого нуля с пошаговой инструкцией создания загрузчика?

P.S Вынтернете гуглил на эту тему ровно несколько минут, попадались всякие описания загрузчиков планшетов, смартфонов и прочих девайсов.
smalcom
Цитата
находящаяся где-то в хвосте флеш-памяти

нет. может и в начале быть.

Цитата
Где можно подробнее почитать на эту тему с самого нуля с пошаговой инструкцией создания загрузчика?

в примерах от производителя обычно всё есть.
ДЕЙЛ
Цитата(smalcom @ Sep 2 2014, 14:40) *
нет. может и в начале быть.

В начале находится таблица векторов прерываний. Пока нет желания возиться с её переносом. Я так понял, что расположение загрузчика в памяти непринципиально? Главное не затирать код программы.

И ещё попутный вопрос. Как в С сделать условный переход программы на определённый адрес флеш? На ассемблере это JMP adres, JMZ adres, JMN adres и т.п. Как это будет выглядеть на С?
Lagman
А если загрузчик и программа используют одно прерывание, ну например прием и передачу по последовательному интерфейсу, как тогда поступают с таблицей векторов? Загрузчик свою таблицу использует, а когда передает управление программе, то программа должна таблицу векторов переписать на свою?
adnega
Цитата(ДЕЙЛ @ Sep 2 2014, 15:43) *
В начале находится таблица векторов прерываний. Пока нет желания возиться с её переносом. Я так понял, что расположение загрузчика в памяти непринципиально?

Таблицу векторов переносить нужно. В Cortex-M3 это делается легко.
Логика должна быть другой:
- стартует проц в режиме загрузчика;
- загрузчик проверяет условие и если оно не выполнено, то передает управление пользовательской программе;
- если условие выполнено, то ПО обновляется и управление передается пользовательской программе.

Пользовательская программа вправе вообще ничего не знать про загрузчик.
Загрузчик обязан работать даже если пользовательская программа битая/отсутствует.

Загрузчик и пользовательская программа - это две параллельные сущности с точностью до карты памяти.
Т.е. пользовательская программа и загрузчик не должны пересекаться во флеши.
Загрузчик должен знать начало пользовательской программы (т.е. оно должно быть зафиксировано).
AlexandrY
Цитата(ДЕЙЛ @ Sep 2 2014, 12:34) *
3. Если в LPC1778 прошивка записывается через UART0, то там уже есть загрузчик? Если он есть, то можно ли его случайно стереть?

Где можно подробнее почитать на эту тему с самого нуля с пошаговой инструкцией создания загрузчика?


Конечно есть там уже загрузчик. А то откуда там ISP и IAP? Из воздуха возникают?
Программными средствами там и защита делается. Поэтому LPC так легко взламывают.

Прочитать тоже об этом так просто не получится, на форумах про NXP изредка светятся энтузиасты занимающиеся реверсингом встроенных загрузчиков.
Сергей Борщ
Справа вверху кнопка "Поиск", в выпадающем окне есть "расширенный поиск", в нем можно ограничить поиск разделом "ARM". Выдает 16 страниц сообщений, из них примерно четверть как раз на интересующую вас тему, да и в остальных сможете почерпнуть полезную информацию.

Вот тут я описывал, как реализован загрузчик у меня. В последующих сообщениях объясняю, почему сделано именно так. Вот тут более подробно, но для MSP430. Идея та же, отличаются детали реализации - для Cortex-M3 вместо копирования векторов в ОЗУ надо прописать их адрес в VTOR и для Coretx-M0/M3 надо загрузить из таблицы векторов MSP. Вот тут описывал, как это реализовываю в Cortex-M3.
ДЕЙЛ
Цитата(Lagman @ Sep 2 2014, 15:47) *
А если загрузчик и программа используют одно прерывание, ну например прием и передачу по последовательному интерфейсу, как тогда поступают с таблицей векторов? Загрузчик свою таблицу использует, а когда передает управление программе, то программа должна таблицу векторов переписать на свою?

Думаю, что можно пользоваться каким-то флагом, указывающим, что именно сейчас выполняется - загрузчик или программа. Например, при выполнении кода загрузчика в определённой ячейке памяти одно число записано, при работе программы другой и в самом начале обработчика прерываний смотреть на эту ячейку для выбора дальнейшего варианта действий.
adnega
Цитата(ДЕЙЛ @ Sep 3 2014, 10:25) *
Думаю, что можно пользоваться каким-то флагом, указывающим, что именно сейчас выполняется - загрузчик или программа.

В таком случае нужно определится что вы собираетесь сделать:
- загрузчик
- или программу, которая имеет возможность менять часть своего кода.

Второе гораздо сложнее в проектировании и менее востребовано в жизни.
ДЕЙЛ
Цитата(Сергей Борщ @ Sep 2 2014, 17:10) *
Вот тут я описывал, как реализован загрузчик у меня. В последующих сообщениях объясняю, почему сделано именно так.

Пробежался по ссылкам. Я пока хочу понять сами основы, а не тонкости реализации.

Пока понял так, что загрузчик и программе есть абсолютно независимые программы, связь между которыми в самом простом случае на уровне ассемблерной инструкции JMP Program_addres или JMP Bootloader_addres. Так?
Если скомпилировать бинарный файл прошивки и записать его по нестандартному адресу флеш (не 0x0000, а, допустим, по адресу 0x30000, вставив по адресу 0x0000 инструкцию JMP 0x30000), то программа будет работоспособной или компилятор делает привязку к абсолютным адресам? Пока не рассматриваю конкретный МК.
Как реализовать на Си запись данных во флеш по конкретным адресам в LPC1778? Интересно посмотреть пример кода. Можно даташит пожевать или у гугла спросить, но если кто-то может за пять минут сказать то, что я могу полдня искать, то лучше спросить. Иначе на форуме тогда не было бы смысла читать-писать sm.gif


Цитата(adnega @ Sep 3 2014, 10:29) *
В таком случае нужно определится что вы собираетесь сделать:
- загрузчик
- или программу, которая имеет возможность менять часть своего кода.

Второе гораздо сложнее в проектировании и менее востребовано в жизни.

я свои измышления пишу, в этой теме пока на нуле
adnega
Цитата(ДЕЙЛ @ Sep 3 2014, 10:47) *
я свои измышления пишу, в этой теме пока на нуле

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

Цитата
Хочется понять, как работает эта штука?

Это не постановка задачи, т.к. этих штук может быть превеликое множество:
1. Загрузчики по последовательным интерфейсам, поставляемые в микроконтроллере производителем ("железный");
2. Пользовательские загрузчики ожидающие программу извне и прошивающие ее в МК ("с одной копией программы");
3. Загрузчики копирующие образ программы из внутренней или внешней флеш в область программы и стартующие ее.
Образ получает и пишет сама пользовательская программа ("с двумя копиями программы");
4. и т.п. ...

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

По каждому из этих пунктов тоже можно долго дискутировать. Была тут война из-за кнопки, как-то)

UPD. Да, задачу защиты от "кирпича" не упомянул.
Сергей Борщ
Цитата(ДЕЙЛ @ Sep 3 2014, 09:25) *
Думаю, что можно пользоваться каким-то флагом, указывающим, что именно сейчас выполняется - загрузчик или программа.
Не нужно изобретать велосипед. Такой флаг уже заложен производителем. И называется он "ремап" у ARM7, Cortex-M0 и "регистр VTOR" у Cortex-M3/M4. Один раз прописали как надо и дальше проверка делается в железе. Недостатки вашего варианта с программной проверкой флага: а) программа теряет время на проверку. б) вы обязательно забудете такую проверку в каком-нибудь прерывании.
Цитата(ДЕЙЛ @ Sep 3 2014, 09:47) *
Пока понял так, что загрузчик и программе есть абсолютно независимые программы, связь между которыми в самом простом случае на уровне ассемблерной инструкции JMP Program_addres или JMP Bootloader_addres. Так?
Да, так.
Цитата(ДЕЙЛ @ Sep 3 2014, 09:47) *
Если скомпилировать бинарный файл прошивки и записать его по нестандартному адресу флеш
А вот тут не так - вы его должны скомпилить (точнее слинковать) в нестандартный адрес. Тогда он и запишется куда надо без лишних телодвижений и работать будет.
Цитата(ДЕЙЛ @ Sep 3 2014, 09:47) *
или компилятор делает привязку к абсолютным адресам?
Да, делает.
Цитата(ДЕЙЛ @ Sep 3 2014, 09:47) *
Как реализовать на Си запись данных во флеш по конкретным адресам в LPC1778? Интересно посмотреть пример кода. Можно даташит пожевать или у гугла спросить
В user manual целый раздел с примерами. У меня на чистом Си нет, есть на Си++.
ДЕЙЛ
Цитата(adnega @ Sep 3 2014, 11:21) *
Дык, вы не путь решения ищите, а для начала сформулируйте задачу, мол, я хочу чтобы...
Может, вам загрузчик и не нужен.

Мне нужно в перспективе реализовать удалённую смену прошивки, а для этого нужен тот самый пользовательский загрузчик, ожидающий программу извне. Но т.к. в этом деле пока ноль, то хочу сначала освоить основы. Это как с освоением способов обмена данными между программой на ПК и МК: сначала учимся отправлять-принимать байт, потом групповую передачу массива байтов, потом осваиваем алгоритмы обнаружения ошибок и т.д. Мало кто сразу начинает с освоения протоколов, минуя этап лицезрения отправки байта. Вот и я сначала хочу азы освоить.


Цитата
Если скомпилировать бинарный файл прошивки и записать его по нестандартному адресу флеш

Цитата(Сергей Борщ @ Sep 3 2014, 11:56) *
А вот тут не так - вы его должны скомпилить (точнее слинковать) в нестандартный адрес. Тогда он и запишется куда надо без лишних телодвижений и работать будет.

Уже интереснее. sm.gif Как это сделать в IAR? И можно ли таким способом по одному адресу раздельно записать загрузчик, а по другому программу? Т.е. по очереди два файла загрузить. Флеш полностью не стирается перед записью?

UP1: В IAR нашёл настройку начальных адресов: Options -> Linker -> вкладка Config -> кнопка Edit
ДЕЙЛ
Есть ещё вопрос по безусловному переходу.

если написать такой код:
Код
int i;
void main (void)
{
for (i = 0; i<20; i++)
      {
       asm (" nop ");
      }
asm (" B 0x???? ");
}

то по какому адресу нужно сделать безусловный переход, чтобы программа стала заново выполняться и возможно ли это? МК LPC1778, среда IAR.
menzoda
Цитата
Уже интереснее. Как это сделать в IAR?

Есть скрипты компоновщика (линкера), в которых указывается расположение всех секций с кодом и данными. Мне совершенно не нравится формат этого файла, я в нем никогда не разбирался, так что сейчас ничего не скажу. Советую почитать документацию IAR по формату этого файла, там все написано.

Цитата
И можно ли таким способом по одному адресу раздельно записать загрузчик, а по другому программу? Т.е. по очереди два файла загрузить.

Можно.

Цитата
Флеш полностью не стирается перед записью?

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

Цитата
по какому адресу нужно сделать безусловный переход, чтобы программа стала заново выполняться и возможно ли это?

Смотри, процесс загрузки выглядит следующим образом:
1. Сброс. Вызов соответствующего обработчика прерывания.
2. Если используется стандартный шаблон проекта, то в обработчике прерывания сброса обычно идет вызов процедуры SysInit, которая выполняет инициализацию некоторого оборудования (PLL например).
3. После SysInit вызывается процедура инициализации библиотеки времени исполнения (CRT, C Runtime Library). В Keil она имеет метку __main, в IAR не помню, может так же, может подругому. Эта процедура инициализирует кучу, статические переменные, ну и остальное по мелочи.
4. После инициализации, внутри __main вызывается уже твой main.

Получается, надо переходить на начало обработчика прерывания сброса. Перейти не проблема - нужно только расположить его по фиксированному адресу в скрипте компоновщика, проблема в другом. Смотри, ты поработал, повключал какие-то прерывания, настроил UART, еще что-нибудь, а потом переходишь на процедуру сброса. Как отреагирует твоя программа на "попорченные" настройки оборудования? Ведь обычно, она запускается со сброшенным оборудованием. Что будет, если при инициализации CRT начнут шпарить прерывания UART? Думаю ничего хорошего. Конечно, можно все это предусмотреть, но это сложно и чревато сложно отлавливаемыми ошибками, поэтому лучше сбрасывать контроллер, а не просто куда-то переходить.
ДЕЙЛ
Цитата(ДЕЙЛ @ Sep 3 2014, 14:34) *
Есть ещё вопрос по безусловному переходу.

если написать такой код:
Код
int i;
void main (void)
{
for (i = 0; i<20; i++)
      {
       asm (" nop ");
      }
asm (" B 0x???? ");
}

то по какому адресу нужно сделать безусловный переход, чтобы программа стала заново выполняться и возможно ли это? МК LPC1778, среда IAR.

С этим разобрался, для программного сброса перезапуска программы и соответственно для передачи управления по какому-либо адресу младший бит этого адреса должен быть всегда установлен, если используется Cortex-M3. Т.е. для программного перезапуска нужно написать:
Код
asm (" B 1 ");

Для меня этот эксперимент принципиален для уяснения принципа передачи управления по конкретному адресу флеш, т.к. бутлоадер таким способом запускает программу, как я представляю.
ДЕЙЛ
Цитата(menzoda @ Sep 4 2014, 09:33) *
2. Если используется стандартный шаблон проекта, то в обработчике прерывания сброса обычно идет вызов процедуры SysInit, которая выполняет инициализацию некоторого оборудования (PLL например).

А если у меня в проекте никакие библиотеки не подключены? Просто имеется такой код:
Код
void main (void)
{
  while(1)
  {
  }
}

и ещё вопрос
Если я участок памяти программы, начиная с нулевого адреса, полностью скопирую в ОЗУ по адресу ADRES, затем после процедуры копирования сделаю перескок ассемблерной инструкцией asm (" B ADRES ");, то это будет равносильно выполнению программы заново? Работать будет?

Пока в матчасть вникаю, до железа руки не доходят.

Вот ещё непонятность.
в описании ядра CM3 есть такие слова: "Таблица векторов прерываний может быть перемещена по другому адресу в области кода или в области ОЗУ" и далее имеется описание регистра смещения таблицы векторов VTOR. Как выглядит участок программы, который перемещает эту таблицу векторов? Нужно просто в регистр смещения записать новый адрес? Какие-то дополнительные действия нужны? Хотелось бы посмотреть пример исходника.
menzoda
Цитата(ДЕЙЛ @ Sep 5 2014, 14:08) *
А если у меня в проекте никакие библиотеки не подключены? Просто имеется такой код:
Код
void main (void)
{
  while(1)
  {
  }
}

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

Цитата(ДЕЙЛ @ Sep 5 2014, 14:08) *
Если я участок памяти программы, начиная с нулевого адреса, полностью скопирую в ОЗУ по адресу ADRES, затем после процедуры копирования сделаю перескок ассемблерной инструкцией asm (" B ADRES ");, то это будет равносильно выполнению программы заново? Работать будет?

В общем случае нет. На этапе компоновки вместо вызовов функций и обращений к переменным подставляются абсолютные адреса, то есть необходимо заранее знать где будет исполняться программа. Если ты переместишь программу в другое место, то все сломается. Однако, компоновщик может поддерживать генерацию кода, инвариантного к расположению в памяти (position independent). По крайней мере в тулчейне от ARM я видео такую опцию, но подробно не интересовался.

Кроме того, как я уже говорил - кто будет сбрасывать состояние оборудование перед началом выполнения программы? Все это реализуемо, но требует понимания проблемы и дополнительных телодвижение. Поэтому, подытожив, еще раз отвечу на твой вопрос:
Цитата
будет равносильно выполнению программы заново? Работать будет?

В общем случае нет.

Цитата
Пока в матчасть вникаю, до железа руки не доходят.

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

Может лучше исходить из конечной цели? Например, сделать загрузчик для такого-то МК, который должен то-то и то-то, а не "что будет, если я вызову инструкцию перехода". Так сказать от общего к частному, сначала по советам сделаешь работающий загрузчик, а потом будешь разбираться почему именно так.

Цитата
Вот ещё непонятность.
в описании ядра CM3 есть такие слова: "Таблица векторов прерываний может быть перемещена по другому адресу в области кода или в области ОЗУ" и далее имеется описание регистра смещения таблицы векторов VTOR. Как выглядит участок программы, который перемещает эту таблицу векторов? Нужно просто в регистр смещения записать новый адрес? Какие-то дополнительные действия нужны? Хотелось бы посмотреть пример исходника.

Сначала скопировать таблицу векторов в нужное место, а потом в регистр смещения записать новый адрес. Скопировать можно как угодно, таблица векторов прерываний это просто числа в памяти. Никаких дополнительных действий не нужно.
ДЕЙЛ
Цитата(menzoda @ Sep 5 2014, 14:45) *
Может лучше исходить из конечной цели?

конечная цель такая: имеется LPC1778, имеется скомпилированный файл proga.bin.
Нужно сделать так, чтобы я мог по какому-нибудь интерфейсу с ПК этот файл побайтно отправить своей программой, а в контроллере эти байты встретит чуть ранее загруженный самопальный бутлоадер, который всё принятое разложит в памяти и запустит на выполнение. С написанием программы для ПК проблем не вижу, больше вопросов по принимающей стороне, т.е. с организацией записи прошивки в МК самопальным загрузчиком. Далее предполагаю передавать прошивку по радиоканалу и тем самым иметь возможность удалённо перепрошивать девайс.
menzoda
В кратце опишу, как я это вижу. Подробности поведения загрузчика опускаю, это уже на твоей совести.

- Думаем сколько секторов памяти (начиная с первого) выделить под загрузчик.
- Настраиваем проект1 загрузчика так, чтобы его таблица векторов прерываний располагалась по нулевому адресу2, а остальной код не выходил за рамки отведенной памяти.
- Настраиваем проект основного ПО так, чтобы его образ не выходил за рамки отведенной области, а таблица векторов прерываний находилась в начале области.
- Загрузчик может работать с файлами формата Intel HEX. Файл состоит из простых текстовых строк с данными и адресом, куда эти данные записать. Это стандарт де-факто для прошивки различных устройств. Генерируется всеми средствами разработки. Можно использовать и другие форматы.
- Для простоты передача управления от загрузчика к основному ПО будет происходить через безусловный переход. При необходимости этот механизм можно заменить чем-нибудь другим.
- Цель безусловного перехода - обработчик прерывания сброса основного ПО. Адрес обработчика находится в известном векторе прерывания. Таблица векторов прерываний находится в начале области, отведенной под основное ПО.
- Для перехода к основному ПО необходимо (именно в такой последовательности):
1. запретить все прерывания
2. привести состояние использованной периферии к начальному
3. задать в регистре смещения новое положение таблицы векторов прерываний основного ПО
4. совершить переход
- Загрузчик может получать файл прошивки по любому интерфейсу. При получении файла он разбирает его формат и записывает содержимое в соответствующую область памяти. Если используется формат Intel HEX, то мы имеем явное указание куда записывать данные3. Если используется простой бинарный образ, то записываем его последовательно в начало области, отведенной под основное ПО.
- Перед переходом к основному ПО загрузчик должен убедиться, что оно существует. Для этого при записи ПО подсчитываем его контрольную сумму, результат записываем в заранее определенное место. Перед переходом пробегаемся по области основного ПО и сверяем контрольную сумму. Если все хорошо - переходим, если не сошлось - остаемся в загрузчике4.
- Чтобы перейти из основного ПО в загрузчик - программно сбрасываем контроллер.

Вроде все верно. Если что - другие поправят.

1 Настраивать придется скрипт компоновщика (линкера).
2 Имеется ввиду адрес, к которому обращается ядро после сброса. Он обычно (но необязательно) численно равен нулю.
3 При этом все-равно нужно проверять, что мы попадаем в область основного ПО, чтобы при получении неправильного файла случайно не перетереть загрузчик.
4 Можно придумать какой-нибудь другой алгоритм определения наличия ПО.
andrewlekar
Вот исходники загрузчика для LPC178X. Практически один в один по описанию из первого поста:

https://github.com/blackyblack/LPC-DfuSe-Bootloader
esaulenka
Цитата(AlexandrY @ Sep 2 2014, 16:37) *
LPC так легко взламывают.


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

Не корысти ради, а из любопытства, когда нам клиенты наши собственные прошивки принесут. Исправленные и дополненные.
ДЕЙЛ
Пока в исходник загрузчика глубоко не влазил. Хочу попробовать что-либо записать во флеш из программы.

т.е. если написать вот так:
Код
adres = (unsigned int*)0x20000200; //область оперативной памяти
*adres = 0x12345678;

то всё нормально - в ячейку с указанным адресом записывается число, а если написать вот так
Код
adres = (unsigned int*)0x1000; //область флеш-памяти
*adres = 0x12345678;

то ничего не записывается. Тут нужно предварительно какие-то настройки делать. В мануале что-то написано, но нет примеров.
Хотелось бы увидеть самый простой пример включения возможности записи в ячейку флеш. МК LPC1778
Сергей Борщ
Цитата(ДЕЙЛ @ Sep 30 2014, 13:35) *
В мануале что-то написано, но нет примеров.
Хотелось бы увидеть самый простой пример включения возможности записи в ячейку флеш. МК LPC1778
У них нет возможности записи в ячейку. Вы можете записать только блок. Пример там писать не стали - вся запись сводится к загрузке 4 регистров и вызвове одной функции IAP. Читайте раздел Flash Memory до просветления.
ДЕЙЛ
Цитата(Сергей Борщ @ Sep 30 2014, 15:36) *
У них нет возможности записи в ячейку. Вы можете записать только блок. Пример там писать не стали - вся запись сводится к загрузке 4 регистров и вызвове одной функции IAP. Читайте раздел Flash Memory до просветления.

Но ведь хочется спросить и тут разжёваный ответ получить rolleyes.gif Спасибо за направляющий ответ, буду копать.

Как в общих чертах происходит запись блока? Какие шаги нужно проделать?
Lagman
Цитата(ДЕЙЛ @ Sep 30 2014, 16:50) *
Но ведь хочется спросить и тут разжёваный ответ получить rolleyes.gif Спасибо за направляющий ответ, буду копать.

Как в общих чертах происходит запись блока? Какие шаги нужно проделать?

Прочитать сраницу из flash, изменить байтик, (возможно очистить flash), записать станицу во flash. Все подробности в application note (а такой есть 99%) на ваш процессор.
ДЕЙЛ
Покопался в настройках линкера, нашёл настройки линкера:
Config->Edit->Linker configuration file editor->Vector Table-> .intvec start = 0x7000
Config->Edit->Linker configuration file editor->Memory Regions-> ROM = 0x7000 - 0x7FFFF
После компилирования и запуска в пошаговом режиме видно, что программа раположена по адресу, начиная с 0x7000, на этом этапе всё работает нормально - данные бегут из UART0.
Получается, что программа уже нормально записана в память и работоспособна.
Далее пробую написать маленькую программу, расположенную в обычном месте, т.е. в начальном секторе флеш.
Пишу такой текст:
Код
main()
{
asm ("B 0x7000");
}

Перебрал несколько возможных адресов, но всегда зависает в Hard Fault.


Цитата(menzoda @ Sep 5 2014, 17:25) *
- Для простоты передача управления от загрузчика к основному ПО будет происходить через безусловный переход.
......
- Цель безусловного перехода - обработчик прерывания сброса основного ПО. Адрес обработчика находится в известном векторе прерывания. Таблица векторов прерываний находится в начале области, отведенной под основное ПО.

В каком известном векторе прерывания находится адрес обработчика? Как узнать этот адрес?


Цитата(menzoda @ Sep 5 2014, 17:25) *
- Для перехода к основному ПО необходимо (именно в такой последовательности):
1. запретить все прерывания
2. привести состояние использованной периферии к начальному
3. задать в регистре смещения новое положение таблицы векторов прерываний основного ПО
4. совершить переход

Написал маленькую тестовую программу без подключения библиотек и лишних файлов
Код
unsigned int *VTOR
int main()
{
VTOR = (unsigned int*)0xE000ED08; //адрес регистра смещения таблицы
*VTOR = 0x7000;  //смещение адреса таблицы веторов прерываний
asm ("B 0x7000"); //адрес перехода
}

Т.е.
1. Прерывания не разрешал, близко к ним не подходил в этом коде.
2. Переферию не трогал
3. С этим вопрос - как узнать адрес?
4. По какому адресу переходить? Как его узнать?

UP1: в прикреплённом архиве проект основного ПО и второй проект тестовой программы, которая должна запускть основное ПО
menzoda
Цитата
В каком известном векторе прерывания находится адрес обработчика? Как узнать этот адрес?

Первый вектор таблицы содержит адрес вершины стека, второй - адрес обработчика прерываний сброса, подробнее смотри в документации ARM на своё ядро (искать по словам vector table).

Цитата
Написал маленькую тестовую программу без подключения библиотек и лишних файлов

Во-первых, ты переходишь по адресу в первом векторе таблицы, а это адрес вершины стека, так что надо переходить по второму. Во-вторых, ты переходишь в этот вектор, а надо прочитать значение из этого вектора и перейти уже по нему.

Исходя из твоего третьего скриншота, тебе надо прочитать содержимое по адресу 0x7004, судя по скриншоту оно было равно 0x8105, и перейти в этот самый 0x8105.
toweroff
bb-offtopic.gif
ДЕЙЛ, я, конечно, ничего против не имею, но такая боевая расцветка темы оформления каждый день по несколько часов сидения у монитора глаза не заворачивает на затылок? sm.gif
ДЕЙЛ
Цитата(menzoda @ Oct 8 2014, 09:17) *
Во-первых, ты переходишь по адресу в первом векторе таблицы, а это адрес вершины стека, так что надо переходить по второму. Во-вторых, ты переходишь в этот вектор, а надо прочитать значение из этого вектора и перейти уже по нему.

Исходя из твоего третьего скриншота, тебе надо прочитать содержимое по адресу 0x7004, судя по скриншоту оно было равно 0x8105, и перейти в этот самый 0x8105.

Написал две строки
Код
*VTOR = 0x380000; //смещение таблицы относительно начала области кода (0x0000) 0x7000 передвинуты на 7 бит влево, т.к. смещение записывается в битах 28:7
asm ("B 0x8105");

Нивкакую не работает. Что ещё упустил и как правильно написать для перехода к программе, начинающейся с адреса 0x7000?
menzoda
Не надо ничего сдвигать на 7 битов! Прочитай внимательнее про этот регистр: эти биты не используются, потому что таблица прерываний должна быть выровнена по определенному адресу, кратному размеру таблицы, округленному до следующей степени двойки.

Второе. Никаких жестко заданных 0x8105! При следующей компиляции этот адрес может быть совсем другим. Я же сказал, как его достать: читаешь значение из второго вектора в таблице основного ПО, которая всегда находится в известном (определенном тобой) месте.

Надо бы проект посмотреть, да у меня IAR не стоит. Попробуй для начала просто отладить переход: вставь эту инструкцию в стартап файл загрузчика, сразу в ResetHandler, пускай переходит куда-нибудь рядом, через пару адресов. Попробуй другие инструкции вроде BX, которые переходят по адресу из указанного регистра. Только почитай про них внимательнее, при использовании BX и BLX младший бит адреса назначения должен быть установлен.
ДЕЙЛ
Цитата(menzoda @ Oct 8 2014, 23:17) *
Не надо ничего сдвигать на 7 битов! Прочитай внимательнее про этот регистр: эти биты не используются, потому что таблица прерываний должна быть выровнена по определенному адресу, кратному размеру таблицы, округленному до следующей степени двойки.

В хидере 35 прерываний и добавим 16 исключений, как написано в руководстве на ядро, получили 51, ближайшая степень двойки - 64, умножаем на 4 и получим 0x100. Этому числу должен быть кратен адрес таблицы векторов, т.е. 0x7000 подходит.
Далее читаю описание полей регистра VTOR:
Бит 29 TBLBASE - расположение таблицы в оперативке(1) или во флеш(0)
Биты 28:7 TBLOFF - Смещение таблицы относительно начала области кода или области ОЗУ,т.е. к 0x7000 добавляем справа 7 нулевых битов и получаем 0x380000 и записываем это число в регистр VTOR.

Цитата(menzoda @ Oct 8 2014, 23:17) *
Второе. Никаких жестко заданных 0x8105! При следующей компиляции этот адрес может быть совсем другим. Я же сказал, как его достать: читаешь значение из второго вектора в таблице основного ПО, которая всегда находится в известном (определенном тобой) месте.

Пока не трогаю записанные данные по адресу 0x7000, т.е. никуда ничего не перемещаю, а пытаюсь маленькой программой запустить большую, расположеннуюв другом месте памяти. И адрес 8105 не меняется в моём случае.
ДЕЙЛ
Почему-то в пашаговом режиме после перехода по адресу программа в другом конце памяти запускается иногда после команды перехода, а после нажатия кнопки сброса никаких движений.
menzoda
Не так считаешь. Векторов прерываний 51, в байтах это 51*4 = 204. Округляем до 256, в двоичном виде это будет 100000000, младшие 7 бит уже равны нулю как ни крути. Выбираем адрес кратный 256, ты выбрал 0x7000, и пишем его прямо туда, не сдвигая, потому что у всех адресов кратных 256 младшие 8 бит будут всегда равны нулю.

В документации всё написано, хоть и несколько запутано:
Цитата
Vector table base offset field. It contains bits[29:7] of the offset of the table base from the bottom of the memory map.

Поле смещения таблицы векторов. Оно содержит биты [29:7] смещения таблицы относительно начального адреса. То есть надо взять смещение 0x7000, взять из него биты 29:7, получившееся число 0xE0 записать в биты 29:7 регистра. Это то же самое, что и запись в регистр числа 0x7000.

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

Не понял.

Кстати, что-то я не вижу в документации примеров использования инструкции B как у тебя, там только говориться про то, что можно указать метку или значение относительно текущего адреса в виде [PC, #imm]. Попробуй использовать:
Код
MOV R0, #0x8105
BX R0


Кроме того B может скакать только на +-2Кб, а у тебя тут вроде больше. Странно, почему ассемблер не ругнулся. Пройдись внимательно по шагам по ассемблерному коду (а не по сишному), точно вылетает на инструкции перехода? Покажи дизассемблер участка с этим переходом (что за инструкция там действительно находится).


Нашел у себя на работе IAR, в симуляторе всё работает, странно. Но всё равно ты попробуй перейти с помощью другой инструкции.
ДЕЙЛ
насчёт перескока на 2кб на какой странице мануала написано? Надо попробовать, а симулирование и реальность не всегда совпадают, как я заметил.


Цитата(menzoda @ Oct 9 2014, 08:54) *
Поле смещения таблицы векторов. Оно содержит биты [29:7] смещения таблицы относительно начального адреса. То есть надо взять смещение 0x7000, взять из него биты 29:7, получившееся число 0xE0 записать в биты 29:7 регистра. Это то же самое, что и запись в регистр числа 0x7000.

пробовал разные варианты - записывал 0x380000 и 0x7000, в отладчике наблюдалось число 0xE0.
Цитата
Не понял.

Имею ввиду, что программа из флеши запускается в следующем шаге после строки B 0x8105 при нажатии кнопки F10 в пошаговом режиме отладки, т.е. переходит в то место и запускает программу, а когда отключаю JTAG и нажимаю на сброс, то никаких переходов нет, контроллер не шевелится.
menzoda
Цитата
насчёт перескока на 2кб на какой странице мануала написано?

Тут я ошибся, наверное посмотрел на набор инструкций Thumb. У Cortex-M3 Thumb2 и там можно прыгать на 16 Мб, вот дока.

Цитата
пробовал разные варианты - записывал 0x380000 и 0x7000, в отладчике наблюдалось число 0xE0.

0xE0 это правильное значение: если взять биты 29:7 от 0x7000, то как раз выйдет 0xE0. Почему у тебя при записи 0x380000 получается 0xE0 я не знаю, может перепутал чего? Во всяком случае, 0xE0 это хорошо.

Цитата
Имею ввиду, что программа из флеши запускается в следующем шаге после строки B 0x8105 при нажатии кнопки F10 в пошаговом режиме отладки, т.е. переходит в то место и запускает программу, а когда отключаю JTAG и нажимаю на сброс, то никаких переходов нет, контроллер не шевелится.

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

Остановись на инструкции перехода, шагни вперед. Перешло к основному ПО по адресу 0x8105? Если всё отлично - шагай дальше, там будет SystemInit, потом __iar_program_start, из него будет вызвана main основного ПО. Вот хотя бы до main дойди. Заметь, при хождении по основному ПО никакие метки не будут подсвечены (не будет написано, что это __iar_program_start или main), будет сплошная простыня с инструкциями, так что тебе придется посмотреть адреса всех этих меток в map-файле основного ПО и сверять всё вручную.
ДЕЙЛ
Цитата(menzoda @ Oct 9 2014, 11:30) *
То есть в отладчике переход всё-таки работает? Попробуй пошагать дальше, может ошибка вываливается где-нибудь в другом месте, в отладчике ты до него просто не доходишь.

Под отладчиком после ассемблерной инструкции перехода программа работает в реальном режиме времени, но её можно остановить и увидеть, что всё крутится в адресах после 7000. Мошть ещё нужно указатель стека на нужное место поставить, как мне посоветовали на другом форуме?
menzoda
Цитата
Мошть ещё нужно указатель стека на нужное место поставить, как мне посоветовали на другом форуме?

Это идея. Ведь адрес вершины стека извлекается из первого вектора после сброса, а мы при переходе к основному ПО ничего не сбрасываем. Формально говоря, надо бы перед переходом самому извлечь это значение и записать в регистр SP, или можно это сделать в основном ПО в стартап-файле. Однако, не факт, что это является причиной ошибки.

Сейчас адрес стека достается основному ПО по наследству от загрузчика. Какие могут быть потенциальные проблемы? Во-первых, загрузчик может использовать значительную часть стека и основному ПО достанется урезанный участок памяти, которого может не хватить, но так как твой загрузчик вообще не использует стек этим вариантом можно пренебречь. Во-вторых, проекты загрузчика и основного ПО могут иметь разное расположение стека: стек загрузчика может пересекаться с областью данных основного ПО. Таким образом основное ПО, работая с данными в RAM, может попортить себе этот неправильно заданный стек.

Последняя ситуация более вероятна, но если это и она странно, что в отладчике всё работает. Тем не менее попробуй задать правильное значение стека перед переходом и посмотри что будет. Если не поможет то остается трассировать. Расставляешь на разных этапах загрузчика и основного ПО дерганье выводами МК, так же не забываешь про исключения (HardFault и иже с ним). Запускаешь и смотришь: ага, эта дернулась, значит загрузчик запустился, а эта не дернулась, значит в основное ПО не перешел, выходит проблема где-то между этими этапами. Постепенно передвигая дерганье ногами ближе к друг-другу определяешь место поломки, ну а дальше будет видно.
ДЕЙЛ
Железно заработало yeah.gif
дело было ещё и в указателе стека. В этом прикреплённом проекте в тестовой программе для вызова главной нужно написать такой код:
Код
unsigned int *VTOR;
int main()
{
VTOR = (unsigned int*)0xE000ED08; //адрес данного регистра из хидера
*VTOR = 0x7000; //смещение таблицы векторов - из настроек линкера
asm ("mov r13, #0x2448"); //запись в регистр стека младших байтов значения указателя стека основной программы, взятого из ячейки с адресом 0x7000;
asm ("movt r13, #0x1000"); //запись в регистр стека старших байтов значения указателя стека основной программы, взятого из ячейки с адресом 0x7000;
asm ("B 0x8105"); //Безусловный переход по адресу обработчика прерывания сброса, прочитанного из ячейки 0x7002;
}

Можно было бы поумнее и правильнее код сделать, но для понимания принципа этого хватит.

теперь попробую назад в загрузчик из главной программы зайти

Цитата(toweroff @ Oct 8 2014, 18:52) *
bb-offtopic.gif
ДЕЙЛ, я, конечно, ничего против не имею, но такая боевая расцветка темы оформления каждый день по несколько часов сидения у монитора глаза не заворачивает на затылок? sm.gif

bb-offtopic.gif Зато оперативка экономится, отключил всё ненужное. Ноуту лет 15, в нём есть привод дискет, два разъёма усб, всамделишный LPT и COM-порты wink.gif Но зато экран большой и нет отвлекающего выхода в тырнет.
menzoda
Цитата
Железно заработало

Наконец! Всем миром загрузчик помогали делать! Зато какой опыт!

Цитата
теперь попробую назад в загрузчик из главной программы зайти

Уж это должно быть несложно - просто вызвать программный сброс. Главный вопрос сбросит ли он VTOR, вроде сбрасывает, но лучше уточнить, периферию то он точно сбрасывает.

Цитата
Зато оперативка экономится, отключил всё ненужное.

Цвет то на оперативку не влияет никак, а так я сам с классической темой сижу, только цвета стандартные - серо-мышиные, а не вырвиглазный зеленый. biggrin.gif
ДЕЙЛ
Оставлю тут свой код загрузчика на всякий случай, а то на домашнем компе ненадёжно. Программа принимает файл от прикреплённой программы, написанной на DELPHI. Успеть отправить надо в течение минуты после сброса.

Код
#include "iolpc1778.h"
#include "LPC17xx.h"

#define IAP_LOCATION 0x1fff1ff1 //точка входа в IAP (страница 896 мануала)  
unsigned int command[5];
unsigned int result[2];
typedef void (*IAP)(unsigned int[], unsigned int[]);  


unsigned char DATA[30000];
unsigned int command[5];
unsigned int result[2];
unsigned char RX;
unsigned int index_RX;
unsigned char RX_PAKET[18];
unsigned char CHSUM, FlagRX;
unsigned int indexM, pauza, i;
unsigned int adr_copy, stek;
unsigned int *adres_stek;
unsigned char OutPortByte;
int sektor;

void Out_UART0(); //otpravka paketa po UART0


void main(void)
{  
   { //INIT

   { //init UART0  
IOCON_P1_25 = 0; //PIO
FIO1DIR     = 0x2200000; //Выходы PIN1_25, PIN1_11
PCONP      |= 0x8;
IOCON_P0_02 = 0x1;
IOCON_P0_03 = 0x1;
   }


FIO0DIR     = 0x4+0x8;


SCS        |= 0x20; //подключение осциллятора
while(!(SCS&0x40)){} //ожидание запуска

PLL0CON |= 0x01;
PLL0CFG |= 0x09;
PLL0FEED = 0xAA;  
PLL0FEED = 0x55;    
CCLKSEL |= 0x100;


PCLKSEL    = 1;
CLKSRCSEL |= 1;
U0LCR     |= 0x83;   //razrecchenie dostupa k delitely
U0DLL      = 0xC8;   //0xF0;//0xA0; //0x14;  //0x4E;  //nastrojka delitelya
U0LCR     &=~0x80;   //zapret dostupa k delitely
U0IER     |= 0x1+0x4;//Разрешение прерываний UART0

//__enable_irq();              //Разрешение
//__set_FAULTMASK(0);            //всех прерываний
//__set_BASEPRI(0);              //Отключение маскирования
//__set_CONTROL(0);

AIRCR = 0x05FA0200;
IP1   = 0x00000800;
}
  NVIC_EnableIRQ(UART0_IRQn);    //Enable IRQ UART0 (ISER0=32)
  indexM = 0;
  index_RX = 1;
  

while(1)
{
    
  
  if (index_RX>5)
  {
   if ((DATA[index_RX-1]==0x11)&(DATA[index_RX-2]==0x22)&(DATA[index_RX-3]==0x33)&(DATA[index_RX-4]==0x44)) самопальный признак конца файла HEX
  {  
    FlagRX = 0;
  

//COPY_FLASH
      
adr_copy = (unsigned int)&DATA[4096];
IAP iap_entry;
iap_entry = (IAP) IAP_LOCATION;


for (sektor=1; sektor<5; sektor++)
{
//_______ПОДГОТОВИТЬ СЕКТОР К ЗАПИСИ_______//
{

command[0] = 50; //код команды
command[1] = sektor;  //начальный номер сектора
command[2] = sektor;  //конечный номер сектора

iap_entry (command, result);
}
//_______ПОДГОТОВИЛИ СЕКТОР К ЗАПИСИ______//




//_______СТЕРЕТЬ СЕКТОР_______//
{

command[0] = 52;     //код команды
command[1] = sektor;      //начальный номер сектора
command[2] = sektor;      //конечный номер сектора
command[3] = 120000; //системная тактовая частота в кГц

iap_entry (command, result);
}
//_______СТЁРЛИ СЕКТОР______//

//_______ПОДГОТОВИТЬ СЕКТОР К ЗАПИСИ_______//
{

command[0] = 50; //код команды
command[1] = sektor;  //начальный номер сектора
command[2] = sektor;  //конечный номер сектора

iap_entry (command, result);
}
//_______ПОДГОТОВИЛИ СЕКТОР К ЗАПИСИ______//




//_______КОПИРОВАТЬ ОПЕРАТИВНУЮ ПАМЯТЬ ВО ФЛЕШ_______//
{

command[0] = 51;         //код команды
command[1] = 0x1000*sektor;     //начальный адрес перезаписываемой флеш
command[2] = adr_copy+4096*(sektor-1); //начальный адрес оперативной памяти, откуда нужно копировать
command[3] = 4096;        //число байт для копирования
command[4] = 120000;     //системная тактовая частота в кГц
  
iap_entry (command, result);

}
//_______СКОПИРОВАЛИ СЕКТОР______//
      




//END_FLASH
}

VTOR = 0x1000;
adres_stek = (unsigned int*)0x1000;
stek = (unsigned int)*adres_stek;
__set_MSP(stek);

asm("mov R1, #0x1004");
asm("ldr R2, [R1]");
asm("BX R2");


    
  }
  }  
}

}





void Out_UART0()
{
while (!(U0LSR&0x20));
U0THR=0xA0;                  //признак ответа  для программы на ПК
}

void UART0_IRQHandler(void)

{
  DATA[index_RX] = U0RBR;
  index_RX++;
  NVIC_ClearPendingIRQ(UART0_IRQn);
  return;
}

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