Как исполнить свой код ДО инициализации среды Arduino?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Коллеги, тут как-то обсуждался вопрос, как выполнить некий код сразу же после загрузки процессора (до инициализации среды Arduino). Кажется, вопрос был связан с отключение сработавшего WatchDog'а, который, как известно, должен быть отключён очень быстро (сколько-то микросекунд) после загрузки, а среда инициализируется дольше.

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

В приведённом примере функция preInit – это САМЫЕ ПЕРВЫЕ команды процессора, которые выполняются непосредственно после Reset. До них не выполняется ничего – не инициализирован стек, не инициализирован глобальные переменные, не отработали конструкторы глобальных объектов (привет Serial'у – он ещё не «родился») - ничего!

Имейте в виду – у Вас нет ничего, даже нормального стека!

Чисто показать «первость» не могу (просто верю документации), но косвенно, смотрите:

1. переменная cat2 объявлена «как обычно» (и стало быть она будет проинициализирована нулём при общей инициализации среды), а переменная cat1 объявлена с указанием «не инициализировать».

2. В функции preInit я записываю в обе переменные ненулевые значения.

3. При печати видно, что cat1 сохранила своё значение, а cat2 стала нулём.

Вывод: функция preInit выполнялась, по крайней мере, ДО инициализации глобальных переменных.

//
//	Эксперимент на кошках!
//	Проверка функций инициализации (функций исполняемых до всего остального)
//
//	Глобальная переменная cat1 помечена как "неициализируемая", 
//	чтобы предотвратить присваивание ей нуля в секции .init4
//	cat2 описана стандартно.
//
int cat1 __attribute__ ((section (".noinit")));
int cat2;

//
//	Функция preInit будет выполняться ДО ВСЕГО
//	прямо в момент "сотворения мира" (секция .init0)
//	Однако в этот момент ещё даже не инициализирован стек!
//	Стек инициализируется в секции .init2, потому, если возможно, лучше 
//	нам использовать не .init0 как здесь, а .init3
//	тогда хоть стеком можно пользоваться.
//
void preInit (void) __attribute__ ((naked)) __attribute__ ((section (".init0")));

void preInit (void) {
	cat1 = 321;
	cat2 = 123;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Test on cats");
  Serial.print("Cat #1:");
  Serial.println(cat1);
  Serial.print("Cat #2:");
  Serial.println(cat2);
}

void loop() {}

 

Sindbad
Offline
Зарегистрирован: 08.12.2015

Т.е. если записать

void preInit (void) {
	while (true);
}

Потребуется перепрошивка загрузчика?

Radjah
Offline
Зарегистрирован: 06.08.2014

ЕМНИП загрузчик как раз отработал и передал управление на код программы. Или всё это работает только без загрузчика?

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Radjah пишет:

ЕМНИП загрузчик как раз отработал и передал управление на код программы. Или всё это работает только без загрузчика?

подозреваю, что балалайка работает в загрузчике.

Logik
Offline
Зарегистрирован: 05.08.2014

Клапауций 321 пишет:

Radjah пишет:

ЕМНИП загрузчик как раз отработал и передал управление на код программы. Или всё это работает только без загрузчика?

подозреваю, что балалайка работает в загрузчике.

Не. Работает именно до загрузчика. После ресета происходит команда безусловного перехода именно на этот код. Все остальное потом. В общем прелесно,  можна вирусы писать или блокировать софт у заказчика нерасплатившегося, просто прелесно )))

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Logik пишет:

прелесно, можна вирусы писать или блокировать софт у заказчика нерасплатившегося, просто прелесно )))

*откуда и когда бы это не запускалось - это записывается в секцию прикладной программы.

**проверил - на работу загрузчика пример не влияет.

Logik
Offline
Зарегистрирован: 05.08.2014

Клапауций 321 пишет:

мне это напоминает #крымнаш - кто до того запрещал писать вирусы и блокировать софт?

*откуда и когда бы это не запускалось - это записывается в секцию прикладной программы.

Блокировать после загрузчика не интересно, не мешает перезалить софт заново, залить другой софт. А до загрузчика - без программатора никак. А ведь можна так сделать запрос кода на розблокировку.

Клапауций 321 пишет:

**проверил - на работу загрузчика пример не влияет.

А впишите типа A:goto A;

 

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Sindbad пишет:

Т.е. если записать

void preInit (void) {
	while (true);
}

Потребуется перепрошивка загрузчика?

не знаю, может я что-то не так сделал

// void preInit (void) {
//  cat1 = 321;
//  cat2 = 123;
// }

void preInit (void) {
while (true);
}

почему же у меня работает загрузчик?, если всё должно повиснуть в бесконечном цикле цитирую: прямо в момент "сотворения мира" (секция .init0)

*оно виснет - дигиспарк не распознаётся системой, т.к. не грузится библиотека и не происходит инициализация в сетапе, но... прекрасно перепрошивается.

значит - волшебство происходит после загрузчика, а не до.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Sindbad пишет:

Т.е. если записать

void preInit (void) {
	while (true);
}

Потребуется перепрошивка загрузчика?

Не знаю, я ж написал, что сейчас проверить не могу, приходится просто доверять документации (сижу на вокзале с ноутбуком на коленях, нанка есть, а вот программатора - нету :( )

Атмеловская документация говорит, что что код .init0 "will be jumped into immediately after a reset".

Да и знаете, для моих нужд необходимо было кое-что сделать до запуска конструкторов глобальных объектов (мне и .init3 выше головы хватило). А делать что-то до загрузчика мне не надо. Проверьте сами и скажите нам, что получилось, ладно?

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

ЕвгенийП пишет:

Атмеловская документация говорит, что что код .init0 "will be jumped into immediately after a reset".

ясно - "will be jumped into immediately after a reset" прикладной программы делает загрузчик.

*расходимся - нас, в очередной раз, наебали.

Beginner100500
Offline
Зарегистрирован: 24.07.2015

По ресету происходит прыг на адрес 0, который в зависимости от настройки фьюзов, находится или в самом начале флеша ИЛИ в начале  загрузчика. Я так перевел, с гугло-переводчиком. И где-то ещё читал про то, что загрузчик первым делом смотрит на USB и если там нет активности, то отдает управление на 0 адрес флеша.

Согласно документации на glibc (тоже поверил гуглю) в начало программы, с 0 адреса флеша, линкуются вектора прерываний и какие-то точки init.0 .. init.9, которые отвечают за начальную настройку памяти, пересылку в неё предустановленных значений глобалов, настройку классов объектов и ещё что-то, каждая за свое дело. Что именно так и не разобрался, плюнул.

Logik
Offline
Зарегистрирован: 05.08.2014

Beginner100500 пишет:

По ресету происходит прыг на адрес 0, который в зависимости от настройки фьюзов, находится или в самом начале флеша ИЛИ в начале  загрузчика. 

Да. В этом вероятно и фишка. В начале флеша стоит прыг на код ТС. Надо фюзы смотреть. 

Посмотрел. Действительно фюзы настроены на загрузчик (по крайней мере для наны и мини 328), соответственно код ТС будет после загрузчика. Жаль. Скайнет опечален )))

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Logik пишет:

Посмотрел. Действительно фюзы настроены на загрузчик (по крайней мере для наны и мини 328), соответственно код ТС будет после загрузчика. Жаль. Скайнет опечален )))

Вообще-то, мне непонятна Ваша печаль.

Никто не заявлял, про "ДО или ПОСЛЕ" загрузчика. Заявлялось, что это выпоняется "в момент сотворения мира", т.е. первым делом после Reset - когда можно, например, поуправлять созданием глобальных объектов и т.п.

Некоторые читатели вбили себе в голову (неясно с чего), что это будет до загрузчика, а когда выяснилось, что не так - посчитали. что их обманули. А обманули-то сами себя. Нехрен было вбвать себе в голову то, что никто не писал.

Logik
Offline
Зарегистрирован: 05.08.2014

Не моя печаль, скайнета ;)

Вся беда пошла от фразы:

ЕвгенийП пишет:

 функция preInit – это САМЫЕ ПЕРВЫЕ команды процессора, которые выполняются непосредственно после Reset. До них не выполняется ничего – не инициализирован стек, не инициализирован глобальные переменные, не отработали конструкторы глобальных объектов (привет Serial'у – он ещё не «родился») - ничего!

Её надо читать так:

функция preInit – это САМЫЕ ПЕРВЫЕ команды процессора, которые выполняются непосредственно после Reset и загрузчика. До них не выполняется ничего (кроме загрузчика) – не инициализирован стек, не инициализирован глобальные переменные, не отработали конструкторы глобальных объектов (привет Serial'у – он ещё не «родился») - ничего (загрузчик это не делает, а может и делает, но не так как нам надо, потому надо считать что это не сделано)!

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

ЕвгенийП пишет:

Никто не заявлял, про "ДО или ПОСЛЕ" загрузчика. Заявлялось, что это выпоняется "в момент сотворения мира", т.е. первым делом после Reset - когда можно, например, поуправлять созданием глобальных объектов и т.п.

Некоторые читатели вбили себе в голову (неясно с чего), что это будет до загрузчика, а когда выяснилось, что не так - посчитали. что их обманули. А обманули-то сами себя. Нехрен было вбвать себе в голову то, что никто не писал.

какой-то противоречивый набор заявлений:
1. ТС заявляет, что у него есть чюдо-средство для платы дуино запуска кода сразу после Reset.
2. ТС-у на практике объяснили, что первым делом после Reset у платы дуино начинает работать код загрузчика.
3. ТС начинает путаться и давать взаимоисключающие показания: см. цитату выше.
*. вангую уход ТС в полный отказ.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Logik пишет:

Не моя печаль, скайнета ;)

Вся беда пошла от фразы:

ЕвгенийП пишет:

 функция preInit – это САМЫЕ ПЕРВЫЕ команды процессора, которые выполняются непосредственно после Reset. До них не выполняется ничего – не инициализирован стек, не инициализирован глобальные переменные, не отработали конструкторы глобальных объектов (привет Serial'у – он ещё не «родился») - ничего!

Её надо читать так:

функция preInit – это САМЫЕ ПЕРВЫЕ команды процессора, которые выполняются непосредственно после Reset и загрузчика

Нет, так её нельзя читать.Она опять будет некорректной, только с другого конца.
 
Эта фраза была переводом документации производителя, которая писалась в расчёте на то, что читатель знает матчасть и понимает о чём речь.
 
А матчасть такова: логически есть два ресета - "Application reset" и "Boot Loader Reset" (см. даташит на стр. 281). Кто из них получит управление при включении питания, зависит от состояния фьюза "Boot Reset Fuse". 
 
В данном месте документации имелся в виду именно "Application Reset" и авторы считали, что читатель это понимает. Те же кто этого не понял, стали ожидать чудес, а когда не дождались, привычно наехали на ТС.
 
Поэтому, если и нужно поменять мою фразу, то добавляя лишь одно слово, вот так: "... это САМЫЕ ПЕРВЫЕ команды процессора, которые выполняются непосредственно после Application Reset ..."
 
Вот так будет строго и абсолютно корректно для любого случая.
Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вопрос, можно? А зачем это вообще нужно? Есть задачи, когда требуется лезть в код этих init.0 .. init9, какие? Интересно просто, особенно в применении к Wiring, поскольку у вас упор сделан "до инициализации среды Wiring" :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Arhat109-2 пишет:

Вопрос, можно? А зачем это вообще нужно? Есть задачи, когда требуется лезть в код этих init.0 .. init9, какие? 

Первый абзац первого поста читали? Или "чукча - не читатель ..."?

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Arhat109-2 пишет:

Вопрос, можно? А зачем это вообще нужно? Есть задачи, когда требуется лезть в код этих init.0 .. init9, какие? Интересно просто, особенно в применении к Wiring, поскольку у вас упор сделан "до инициализации среды Wiring" :)

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

*не знаю для кого он это пишет - для себя, наверное.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

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

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

Logik
Offline
Зарегистрирован: 05.08.2014

Возможно таким путем можно получить информацию была ли попытка обновления прошивки и  успешна ли она. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Arhat109-2 пишет:

Читаю темы, просматриваю .. не попадалось.

Тем и вопросов о том, что watchdog необходимо выключить при загрузке, а в setup - уже поздно, было много. Например:

Из того, Вам они не попадались, вовсе не следует, что их не было. 

Там предлагалось лечение путём установки нестандартного загрузчика. Здесь другой рецепт.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Logik пишет:

Возможно таким путем можно получить информацию была ли попытка обновления прошивки и  успешна ли она. 

Как? Есть идеи как это сделать?

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Arhat109-2 пишет:

Да, читал .. "вот тут спрашивали" .. 

здесь спрашивали, но походу у хабражителя ничего не вышло

http://geektimes.ru/post/255800/

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Судя по статье вопрос гарантированно решается перекомпиляцией и перепрошивкой загрузчика, а вовсе не предложенным способом. Я не работал ещё с watchdog .. хотя, возможно в ближайшем будущем и понадобится. У нас идет активная подготовка к февральским соревнованиям, хотим таки туда попасть .. а там потребуется переводить контроллер в спящий режим, хотя бы на время его стояния в карантинах, дабы не жрать батарейки. Разберусь - отпишусь, если интересно. Но вряд ли потребуется лезть в init секции.

Клапауций 321
Offline
Зарегистрирован: 17.12.2015

Arhat109-2 пишет:

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

в той статье из интересного этот код

uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));
void get_mcusr(void){
  mcusr_mirror = MCUSR;
  MCUSR = 0;
  wdt_disable();
}

Arhat109-2 пишет:

Я не работал ещё с watchdog .. хотя, возможно в ближайшем будущем и понадобится. У нас идет активная подготовка к февральским соревнованиям, хотим таки туда попасть .. а там потребуется переводить контроллер в спящий режим, хотя бы на время его стояния в карантинах, дабы не жрать батарейки. Разберусь - отпишусь, если интересно. Но вряд ли потребуется лезть в init секции.

если будет интересно, то лучше посмотри и напиши компилляцию на тему "Application reset, который делает bootloader" или, подозреваю, не делает.