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

 
 
6 страниц V  « < 3 4 5 6 >  
Reply to this topicStart new topic
> Bootloader или загрузчик, расскажите по подробней...
Anatrulij
сообщение Jun 18 2009, 05:47
Сообщение #61


Участник
*

Группа: Новичок
Сообщений: 64
Регистрация: 7-11-08
Пользователь №: 41 437



Цитата(zltigo @ Jun 18 2009, 00:22) *
Зачем? Чем '0x12345678' привлекательнее 'buffer'


Ух ты!!!! Вроде я уследил за ходом выших мыслей...
Я создаю массив buffer[4096], забиваю его данными, а потом в команде 51 просто указываю имя массива buffer(имя является адресом первого элемента). правильно ли я понял????
Go to the top of the page
 
+Quote Post
HARMHARM
сообщение Jun 18 2009, 13:38
Сообщение #62


читатель даташитов
****

Группа: Свой
Сообщений: 853
Регистрация: 5-11-06
Из: Днепропетровск
Пользователь №: 21 999



О, да!
Go to the top of the page
 
+Quote Post
Anatrulij
сообщение Jun 23 2009, 12:51
Сообщение #63


Участник
*

Группа: Новичок
Сообщений: 64
Регистрация: 7-11-08
Пользователь №: 41 437



Ложу я прошивку с помощью бутлоадера(сам он находиться по 0-му адресу) по адресу 0х00004000
проверяю целостность и прочее - все Ок.
передаю управление на прошивку вот так:

typedef void (*pt2Function)(void);

pt2Function BootCall = (pt2Function)0x00004000;

в итоге - ничего. он все равно крутится в бутлоадере.

может я не правельно передаю управление???
Go to the top of the page
 
+Quote Post
Anatrulij
сообщение Jun 24 2009, 06:19
Сообщение #64


Участник
*

Группа: Новичок
Сообщений: 64
Регистрация: 7-11-08
Пользователь №: 41 437



Цитата(Anatrulij @ Jun 23 2009, 15:51) *
может я не правельно передаю управление???


Утро вечера мудренее smile.gif. Разобрался - надо было перед вызовом вышеописанного перехода сбросить все альтернативные функции портов. Но я не знаю почему данная процедура помогла.
Go to the top of the page
 
+Quote Post
Nixon
сообщение Jun 24 2009, 07:29
Сообщение #65


Гуру
******

Группа: Админы
Сообщений: 2 736
Регистрация: 17-06-04
Из: Киев
Пользователь №: 48



Вот только перейдете вы по этому адресу в user-mode процессора и перенастроить стеки для других режимов уже не сможете.
Поэтому лучше вызывать вот такую функцию (для KEIL):
Код
/* Функция передачи управления пользовательской программе по адресу 0x2000 */
void __asm user(void)
{
    ARM
    mov r0, #0x00002000
    swi    0
}

в проект добавить файл swi.s
Код
                PRESERVE8
                AREA    SWI_Area, CODE, READONLY
                ARM

                EXPORT  SWI_Handler
SWI_Handler  
                BX  R0
                END

и конечно в startup.s сделать импорт SWI_Handler
Код
                IMPORT  SWI_Handler
                    
Reset_Addr      DCD     Reset_Handler
Undef_Addr      DCD     Undef_Handler
SWI_Addr        DCD     SWI_Handler
PAbt_Addr       DCD     PAbt_Handler
DAbt_Addr       DCD     DAbt_Handler
                DCD     0                     ; Reserved Address
IRQ_Addr        DCD     IRQ_Handler
FIQ_Addr        DCD     FIQ_Handler

Undef_Handler   B       Undef_Handler
;SWI_Handler     B       SWI_Handler
PAbt_Handler    B       PAbt_Handler
DAbt_Handler    B       DAbt_Handler
IRQ_Handler     B       IRQ_Handler
FIQ_Handler     B       FIQ_Handler


--------------------
Вам помочь или не мешать?
Go to the top of the page
 
+Quote Post
Anatrulij
сообщение Jun 24 2009, 07:53
Сообщение #66


Участник
*

Группа: Новичок
Сообщений: 64
Регистрация: 7-11-08
Пользователь №: 41 437



Цитата(Nixon @ Jun 24 2009, 10:29) *
Вот только перейдете вы по этому адресу в user-mode процессора и перенастроить стеки для других режимов уже не сможете.
Поэтому лучше вызывать вот такую функцию (для KEIL):


Спасибо за совет.
А можно более подробно прокоментировать код?
Go to the top of the page
 
+Quote Post
Nixon
сообщение Jun 24 2009, 09:13
Сообщение #67


Гуру
******

Группа: Админы
Сообщений: 2 736
Регистрация: 17-06-04
Из: Киев
Пользователь №: 48



Что именно тут комментировать? Вы вызываете SWI прерывание, при вызове которого переключается режим cpu на supervisor и на всякий случай в ARM. И уже в этом режиме вы переходите на адрес пользовательской программы (адрес передается в r0).


--------------------
Вам помочь или не мешать?
Go to the top of the page
 
+Quote Post
Anatrulij
сообщение Jun 24 2009, 10:12
Сообщение #68


Участник
*

Группа: Новичок
Сообщений: 64
Регистрация: 7-11-08
Пользователь №: 41 437



Цитата(Nixon @ Jun 24 2009, 12:13) *
Что именно тут комментировать? Вы вызываете SWI прерывание, при вызове которого переключается режим cpu на supervisor и на всякий случай в ARM. И уже в этом режиме вы переходите на адрес пользовательской программы (адрес передается в r0).


а зачем делать импорт SWI_Handler. многого я еще не знаю и что бы не задавать глупых вопросов, ткните где я могу найти описания того как правельно инициализировать контроллер, вектора и прочее.
Go to the top of the page
 
+Quote Post
Yaumen
сообщение Jun 30 2010, 14:32
Сообщение #69


Частый гость
**

Группа: Свой
Сообщений: 187
Регистрация: 22-06-05
Из: Минск, Беларусь
Пользователь №: 6 213



Для организации загрузки программы написал несколько классов, которые должны использоваться в бутлоадере (AES шифрование, подсчет CRC и т.д.). Написал собственно загрузчик, но теперь осталось правильно все это скомпоновать.

Как сделать, чтобы определенные объекты классов компилировались с 0-го сектора (загрузчик), а остальные скажем со 2-го сектора (пользовательская программа)?

Вычитал, что это решается с помощью scatter, нашел где он настраивается в Keil, но там вроде можно всю программу сместить, а как сделать частями, ума не приложу.
Была идея сделать 2 разных проекта, один загрузчик и разместить его с 0-го сектора (и использовать далее во всех остальных проектах его без изменения). А второй проект это пользовательская программа, которая всегда начинается со 2-го сектора. Есть еще плюсы, у этого метода, но главные минусы в том, что:
- всегда надо четко и, самое главное, в ручную оговаривать точки входа в пользовательскую программу (включая вектора прерываний) и загрузчик, потому что при компиляции они не будут знать о существовании друг друга;
- хотелось быть часть однотипного кода по работе с интерфейсами, USB, CAN и UART разместить в загрузчике, чтобы не дублировать их в двух программах. Но тут встает та же проблема, ручного указания адресов методов и самое главное ручное резервирование памяти, которое используют объекты классов интерфейсов, чтобы компилятор пользовательской программы туда ничего не назначил.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jun 30 2010, 15:52
Сообщение #70


Гуру
******

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



Цитата(Yaumen @ Jun 30 2010, 17:32) *
Была идея сделать 2 разных проекта, один загрузчик и разместить его с 0-го сектора (и использовать далее во всех остальных проектах его без изменения). А второй проект это пользовательская программа, которая всегда начинается со 2-го сектора.
Правильное решение. Программы независимы, нет возможности развивая программу случайно сделать ее несовместимой с уже выпущенными и разлетевшимися по стране загрузчиками.
Цитата(Yaumen @ Jun 30 2010, 17:32) *
- всегда надо четко и, самое главное, в ручную оговаривать точки входа в пользовательскую программу (включая вектора прерываний) и загрузчик, потому что при компиляции они не будут знать о существовании друг друга;
Вход в загрузчик - по адресу 0. В приложение (после ремапа) - тоже по адресу 0. Адрес фиксирован, не меняется.
Цитата(Yaumen @ Jun 30 2010, 17:32) *
- хотелось быть часть однотипного кода по работе с интерфейсами, USB, CAN и UART разместить в загрузчике, чтобы не дублировать их в двух программах. Но тут встает та же проблема, ручного указания адресов методов
сделайте таблицу переходов, расположите ее раз и навсегда в фиксированном месте.
Цитата(Yaumen @ Jun 30 2010, 17:32) *
и самое главное ручное резервирование памяти, которое используют объекты классов интерфейсов, чтобы компилятор пользовательской программы туда ничего не назначил.
Расположите их в начале ОЗУ, сразу за отремапленой таблицей векторов. А приложению уменьшите количество ОЗУ на этот размер.

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


--------------------
На любой вопрос даю любой ответ
"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
alvy
сообщение Jul 1 2010, 03:21
Сообщение #71


Участник
*

Группа: Участник
Сообщений: 67
Регистрация: 8-05-09
Из: Томск
Пользователь №: 48 809



Цитата(Сергей Борщ @ Jun 30 2010, 22:52) *
сделайте таблицу переходов, расположите ее раз и навсегда в фиксированном месте.

Можно с этого места поподробнее? rolleyes.gif Т.е. саму идею понял, но как это все должно выглядеть - не совсем
Go to the top of the page
 
+Quote Post
Yaumen
сообщение Jul 1 2010, 05:30
Сообщение #72


Частый гость
**

Группа: Свой
Сообщений: 187
Регистрация: 22-06-05
Из: Минск, Беларусь
Пользователь №: 6 213



Цитата(Сергей Борщ @ Jun 30 2010, 18:52) *
Вход в загрузчик - по адресу 0. В приложение (после ремапа) - тоже по адресу 0. Адрес фиксирован, не меняется.


Вот тут немного поподробнее. С загрузчиком все понятно, у него свой main(), который может располагаться где угодно в пределах первых 2-х секторов, а переход на main() действительно будет по 0-му адресу.
Теперь о пользовательской программе. У него также main может располагаться в любом месте оставшейся памяти и об адресе этого main() загрузчик естественно ничего не знает. Значит ему надо это указать, опять же в пользовательской области зафиксировать переход на main(), по этому адресу и будет переходить загрузчик в случае, если с прошивкой все в норме. Возможно есть еще какой-то способ, но похоже я до него еще в голове не дошел.

Есть еще один минус у двух проектов. Я планирую использовать AES шифрование, которое жестко будет зашито в загрузчик, а значит все пользовательские программы необходимо шифровать одним и тем же ключем, это же касается и сигнатуры. Мне кажется это неправильно!!!

Теперь что касается общей части для обоих проектов. Смысл идеи состоит в том, чтобы дать возможность обновлять прошивку удаленно. В моем случае это происходит через штатно установленный отдельный модуль связи, который общается с другими модулями по CAN. Для работы на этой шине, разработан протокол обмена, позволяющий передавать файлы больших размеров, склеивать их на приемном устройстве, вести одновременный прием от нескольких устройств, ну и т.д. Это достаточно большой кусок куда, который не хотелось бы дублировать, чтобы не тратить лишнего, так как я и так уже вышел на использование 75% имеющейся памяти. А в добавок, этот фрагмент кода очень емкий по использованию RAM. Мне самому идея удаленного программирования не нравится, однако начальство настаивает на его присутствии, так как у нас уже были случаи, когда приходилось объезжать множество объектов для перепрошивки, а когда эти объекты находятся в разных городах или даже в странах это еще более утомительно.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jul 1 2010, 09:40
Сообщение #73


Гуру
******

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



Цитата(alvy @ Jul 1 2010, 06:21) *
Можно с этого места поподробнее? rolleyes.gif Т.е. саму идею понял, но как это все должно выглядеть - не совсем

для gcc примерно так:
Код
boot_app_cross_table.h:
struct boot_app_cross_table
{
     uint32_t (*Serial_no)();
     void (*AES_init)(uint32_t *init_vector);
     void (*AES_decode)(uint32_t *block);
     bool (*TCP_send)(void const * from, size_t size);
}extern Bootloader;

//=========
bootloader.cpp:

extern uint32_t Serial_no();
void AES_init(uint32_t * init_vector)
{
     AES.init(init_vector);
}

void AES_decode(uint32_t * block)
{
     AES.decode(block);
}
extern void TCP_send(void const * from, size_t size);

__attribute__((section, ".boot_app_cross"))
Bootloader =
{
    Serial_no,
    AES_init,
    AES_decode,
    TCP_send,
};

//=========
bootloader.ld:
MEMORY
{
  .....
  CROSS               (rx)  : ORIGIN = 0x00100040, LENGTH = 4 * 4
  ....
}

SECTIONS
{
  .....
  .cross :
  {
    KEEP(*(.boot_app_cross*))
  } > CROSS

//=========
application.cpp:
#include "boot_app_cross_table"
void Test()
{
   printf("Serial number: %d", Bootloader.Serial_no());
}

//=========
makefile:
LDFLAGS += -Wl,--defsym,Bootloader=0x00100040


Цитата(Yaumen @ Jul 1 2010, 08:30) *
Вот тут немного поподробнее. С загрузчиком все понятно, у него свой main(), который может располагаться где угодно в пределах первых 2-х секторов, а переход на main() действительно будет по 0-му адресу.
До main() еще дойти надо. До вызова main() исполняется startup-код, который готовит переменные, настраивает стеки и кое-какую периферию, вызывает конструкторы глобальных объектов и уже после всего этого вызывает main(). А сам стартап-код вызывается из вектора исключения RESET.
Цитата(Yaumen @ Jul 1 2010, 08:30) *
Теперь о пользовательской программе. У него также main может располагаться в любом месте оставшейся памяти и об адресе этого main() загрузчик естественно ничего не знает.
У приложения точно так-же есть своя таблица векторов. В которой вектор RESET указывает на стартап-код. А стартап-код вызывает main(). Загрузчик копирует эту таблицу в начало ОЗУ и делает remap. Таблица оказывается отражена на адрес 0. По адресу 0 оказывается команда перехода на стартап-код приложения. Осталось перейти на адрес 0.
Цитата(Yaumen @ Jul 1 2010, 08:30) *
Есть еще один минус у двух проектов. Я планирую использовать AES шифрование, которое жестко будет зашито в загрузчик, а значит все пользовательские программы необходимо шифровать одним и тем же ключем, это же касается и сигнатуры. Мне кажется это неправильно!!!
Почему? А если все программы будут с разными ключами - как вы их загрузите в одно устройство?
Цитата(Yaumen @ Jul 1 2010, 08:30) *
разработан протокол обмена, позволяющий передавать файлы больших размеров, склеивать их на приемном устройстве, вести одновременный прием от нескольких устройств, ну и т.д. Это достаточно большой кусок куда, который не хотелось бы дублировать
Нужна ли вся эта функциональность загрузчику? Может ему достаточно сильно урезанной версии? Или может у приложения есть возможность сохранить образ прошивки в какой-то внешней памяти, а загрузчик тогда будет брать шифрованную прошивку из этой памяти? А как аварийный вариант и для первоначальной загрузки предусмотреть что-нибудь типа uart?


--------------------
На любой вопрос даю любой ответ
"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
Yaumen
сообщение Jul 1 2010, 10:56
Сообщение #74


Частый гость
**

Группа: Свой
Сообщений: 187
Регистрация: 22-06-05
Из: Минск, Беларусь
Пользователь №: 6 213



Цитата(Сергей Борщ @ Jul 1 2010, 12:40) *
Почему? А если все программы будут с разными ключами - как вы их загрузите в одно устройство?


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

Цитата
Нужна ли вся эта функциональность загрузчику? Может ему достаточно сильно урезанной версии? Или может у приложения есть возможность сохранить образ прошивки в какой-то внешней памяти, а загрузчик тогда будет брать шифрованную прошивку из этой памяти? А как аварийный вариант и для первоначальной загрузки предусмотреть что-нибудь типа uart?

Если бы в микроконтроллерах RAMа было бы больше ROMa, то можно было бы использовать RAM как временный буфер, но увы это не так. А случись что во время программирования, то и вообще этот механизм накрывается медным тазом.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jul 1 2010, 11:35
Сообщение #75


Гуру
******

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



Цитата(Yaumen @ Jul 1 2010, 13:56) *
Имелось в виду, что создавая загрузчик как отдельный проект я его буду зашивать во все свои устройства, а это могут быть совершенно разные устройства и модули
Так что мешает при компиляции под конкретный модуль подставлять заголовочный файл с ключами шифрования для этого конкретного устройства. Или просто держать копию исходников загрузчика в папке с проектом конкретного модуля (у меня так).


--------------------
На любой вопрос даю любой ответ
"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

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

 


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


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