|
|
  |
как узнать в программе последний адрес этой программы (WinAVR) |
|
|
|
Apr 8 2008, 18:46
|

Частый гость
 
Группа: Свой
Сообщений: 91
Регистрация: 10-10-07
Из: Воронежа
Пользователь №: 31 250

|
Пользую WinAVR-20071221. Возникла необходимость подсчитывать контрольную сумму программы, но не получается из самой программы узнать до куда, собственно, считать. Думаю, нужно посчитать до адреса _еtext + длинна(.data), но реализовать это чет пока никак не получается. Должно же быть простое решение? Поиск пока ничего вразумительного не дал. Наставьте, пожалуста, на путь истиный. ЗЫ: на асме это делалось элементарно...
|
|
|
|
|
Apr 8 2008, 21:36
|

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

|
Цитата(injen-d @ Apr 8 2008, 21:46)  Пользую WinAVR-20071221. Возникла необходимость подсчитывать контрольную сумму программы, но не получается из самой программы узнать до куда, собственно, считать. Как советует zltigo, храните в заранее оговоренном месте в начале программы (например, сразу после векторов) адрес, по которому расположена контрольная сумма. Цитата(injen-d @ Apr 8 2008, 21:46)  Думаю, нужно посчитать до адреса _еtext + длинна(.data), но реализовать это чет пока никак не получается. Должно же быть простое решение? объявляете в скрипте линкера, в самом конце раздела SECTIONS фиктивный кусочек выходной секции .text (чтобы он разместился после уже заполненной .text и инициализирующих значений .data), объявляете в нем символ и присваиваете ему значение текущего адреса: Код SECTIONS { ........ .text : { _app_end = .; } > text } а в программе объявляете фиктивную внешнюю переменную с таким именем и берете ее адрес. Цитата(injen-d @ Apr 8 2008, 21:46)  ЗЫ: на асме это делалось элементарно...  Можно пример?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 8 2008, 23:15
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
А чем плохо CRC всего набортного флеша (либо до boot - секции) ? На асме ставится метка в главном файле в самом конце Код ................ ZZLast_Code: nop а хранить CRC имхо лучше в последних двух байтах (опять же, либо перед boot либо там где FLASHEND) название метки содержит достаточное кол-во ZZZ, чтобы имя было в конце таблицы символов
|
|
|
|
|
Apr 9 2008, 09:29
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(Сергей Борщ @ Apr 9 2008, 00:36)  Как советует zltigo, храните в заранее оговоренном месте в начале программы (например, сразу после векторов) адрес, по которому расположена контрольная сумма. объявляете в скрипте линкера, в самом конце раздела SECTIONS фиктивный кусочек выходной секции .text (чтобы он разместился после уже заполненной .text и инициализирующих значений .data), объявляете в нем символ и присваиваете ему значение текущего адреса: Код SECTIONS { ........ .text : { _app_end = .; } > text } а в программе объявляете фиктивную внешнюю переменную с таким именем и берете ее адрес. Можно пример? +1
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 9 2008, 16:43
|

Частый гость
 
Группа: Свой
Сообщений: 91
Регистрация: 10-10-07
Из: Воронежа
Пользователь №: 31 250

|
Цитата Как советует zltigo, храните в заранее оговоренном месте в начале программы (например, сразу после векторов) адрес, по которому расположена контрольная сумма. Не, контрольную сумму (КС) храню в EEPROM, а адрес КС в EEPROM -- простая константа. Цитата объявляете в скрипте линкера, в самом конце раздела SECTIONS фиктивный кусочек выходной секции .text (чтобы он разместился после уже заполненной .text и инициализирующих значений .data), объявляете в нем символ и присваиваете ему значение текущего адреса Попробовал, не подходит. В этом случае _app_end у меня всегда равно уже существующей _etext. Цитата а в программе объявляете фиктивную внешнюю переменную с таким именем и берете ее адрес. А как правильно объявить? Какого она должна быть типа? У меня в итоге получилось, но как-то "через одно место" это работает, но работает правильно. Цитата (injen-d @ Apr 8 2008, 21:46) ЗЫ: на асме это делалось элементарно... Можно пример? _Pasha Вам ответил, это банальная метка в конце программы. Цитата А чем плохо CRC всего набортного флеша (либо до boot - секции) ? Пока хватает простого побайтового суммирования по модулю 256 или 65536, по вкусу. И зачем контролировать неиспользуемые байты? При порче хоть одного из них, программа начнет бить ложную тревогу, оставаясь при этом полностью работоспособной. А вдруг это (порча) случится "на самом интересном" Как я уже сказал, _app_end у меня всегда равно уже существующей _etext, а _edata = (0x100 + количество инициализируемых переменных (при выравнивании, еще +1)) девайс - ATmega645; По hex-файлу убедился, что _etext меньше адреса последнего записываемого во флеш байта как раз на количество инициализируемых переменных(ес-но "байтовость" переменных тоже учел), таким образом объявляем в скрипте линкера в самом конце: Код ...... _size_prog = _etext +(_edata -0x100); } далее используем в функции подсчета. Привожу свою функцию, может кому будет интересно. В железе пока не проверял, за его отсутствием, но данный алгоритм (реализованный на асме) успешно работает в других готовых девайсах. Как надо объявлять в функции переменную _size_prog, на сколько я убедился - без разницы, но есть важная особенность: переменная _size_prog не содержит ничего, а вот ее адрес и есть адрес последнего байта, записываемого во флеш. При попытке использовать _size_prog как есть, компилятор генерит код, пытающийся загрузить значение из ОЗУ с адреса последнего используемого байта во флеш! Код //=================================================================== //Функция считает побайтовою сумму flash-памяти с 0 по последний используемый байт; //Контрольная сумма (КС) хранится в EEPROM; //Если в EEPROM имеется достоверная КС (байт в EEPROM по адресу f_ee_summ равен 0xAE), то сравнение, //если нет (первое включение МК после программирования), то запись в EEPROM подсчитанной суммы и //значения 0xAE по адресу f_ee_summ, затем рекурсивный вызов самой себя; //Возвращаемое значение: 0 - норма, 0x12 - код ошибки "Ошибка контрольной суммы памяти программ"; #define ee_summ 0x40 //адрес расположения в EEPROM контрольной суммы #define f_ee_summ 0x30 //адрес в EEPROM флага наличия посчитанной контрольной суммы extern const uint8_t _size_prog; //переменная _size_prog должна быть определена в скрипте линкера, //в конце SECTIONS, следующим образом: _size_prog =_etext +(_edata-0x100); uint8_t summ_flash(void) { uint16_t summ = 0; uint16_t adr = 0; uint8_t f_err; do { summ += (uint16_t)pgm_read_byte(adr); //побайтовое суммирование; сумма 16-битовая adr++; } while (adr < (const uint16_t)(&_size_prog)+1); //пока адрес меньше, чем (последний байт во Flash)+1 if (eeprom_read_byte((uint8_t*)f_ee_summ)==0xAE) //если имеется посчитанная сумма { if (eeprom_read_word((uint16_t*)ee_summ) != summ) //если контрольная сумма не совпала- f_err = 0x12; // - вернуть код ошибки, else f_err = 0; // иначе - 0; } else //если посчитанной суммы нет (первое включение МК после программирования) { eeprom_write_word((uint16_t*)ee_summ, summ); //запись подсчитанной суммы eeprom_write_byte((uint8_t*)f_ee_summ, 0xAE); //запись флага наличия достоверной суммы while(EECR & (1<<EEWE)); //ждать окончания записи в EEPROM f_err = summ_flash(); } return f_err; }
|
|
|
|
|
Apr 9 2008, 18:20
|

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

|
Цитата(injen-d @ Apr 9 2008, 19:43)  Не, контрольную сумму (КС) храню в EEPROM, а адрес КС в EEPROM -- простая константа. А смысл? Почему не хранить ее прямо во флеше, в конце прошивки? Тогда вероятность ее порчи будет такая же, как и для остальных байтов прошивки. Цитата(injen-d @ Apr 9 2008, 19:43)  Попробовал, не подходит. В этом случае _app_end у меня всегда равно уже существующей _etext. Возможно, вы вставили ее не в самый конец, а до объявления секции .data? Я перед написанием того ответа специально поробовал, проходит (правда пробовал на arm-elf-gcc). И у меня скрипт несколько отличается от скриптов из комплекта avr-libc. Вместо ручного подсчета размеров и принудительного задания адресов Код .data : AT (ADDR (.text) + SIZEOF (.text)) { ...... } > data .bss SIZEOF(.data) + ADDR(.data) : { ....... } > data я даю возможность линкеру самому все это считать: Код .data : { ....... } > data AT > text .bss : { ....... } > data Цитата(injen-d @ Apr 9 2008, 19:43)  А как правильно объявить? Какого она должна быть типа? У меня в итоге получилось, но как-то "через одно место" это работает, но работает правильно. да любого типа. Вас же интересует ее адрес. Если вы сумируете память побайтно, то и ее разумно объявить байтом, чтобы получился указатель на байт. Цитата(injen-d @ Apr 9 2008, 19:43)  _Pasha Вам ответил, это банальная метка в конце программы. У меня тоже получилась банальная метка _app_end в конце программы. А если программа состоит из нескольких отдельно компилируемых файлов, то без скрипта линкера не обойтись, будь она на ассемблере или С.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Apr 9 2008, 19:26
|

Частый гость
 
Группа: Свой
Сообщений: 91
Регистрация: 10-10-07
Из: Воронежа
Пользователь №: 31 250

|
Цитата А смысл? Почему не хранить ее прямо во флеше, в конце прошивки? Тогда вероятность ее порчи будет такая же, как и для остальных байтов прошивки. Что-то в этом есть... Будет свободное время - сделаю вариант с хранением суммы во flash, не будет - оставлю как есть. Цитата Возможно, вы вставили ее не в самый конец, а до объявления секции .data? Нет. Сделал в точности как Вы указывали: в самом конце SECTIONS { }. Только что перепроверил: _app_end == _etext -- значения для инициализации переменных расположены в последующих байтах. Использую скопированный из winavr соответствующий файл скрипта (avr5.x). Попробовал переделать как у Вас Код я даю возможность линкеру самому все это считать: .data : { ....... } > data AT > text .bss : { ....... } > data (injen-d @ Apr 9 2008, 19:43) тоже работает, но результат тот же: _app_end == _etext Пожалуй лучше использовать приведенный мной вариант: Код SECTIONS { ...... _size_prog = _etext +(_edata -0x100); }
--------------------
- Бендер, ты же робот, зачем тебе пить пиво? - Незачем! Я могу бросить в любой момент!
|
|
|
|
|
Apr 10 2008, 17:23
|
Знающий
   
Группа: Свой
Сообщений: 601
Регистрация: 22-09-05
Из: Kharkov
Пользователь №: 8 847

|
Цитата(Сергей Борщ @ Apr 10 2008, 17:22)  Вы знаете, я проверил. Да, получаю такой же результат. И в avr-gcc и в arm-elf-gcc. Но ведь перед написанием того ответа я легко получил то, что нужно! Пойду домой, попробую повторить фокус. Если получится - буду искать причину такого странного поведения. Просто поставте последней секцию типа такой. Код .finish : { _app_end = .; } > FLASH
--------------------
- А мораль отсюда такова: всякому овощу свое время. Или, хочешь, я это сформулирую попроще: никогда не думай, что ты иная, чем могла бы быть иначе, чем будучи иной в тех случаях, когда иначе нельзя не быть. © Lewis Carroll. Alice's adventures in wonderland.
|
|
|
|
|
Apr 10 2008, 18:11
|

Частый гость
 
Группа: Свой
Сообщений: 91
Регистрация: 10-10-07
Из: Воронежа
Пользователь №: 31 250

|
Цитата Просто поставте последней секцию типа такой. Проверил, работает правильно. Код .finish : { _size_prog = .; } > text } Спасибо за подсказки, пожалуй, буду использовать этот вариант. Добавлю еще, что если использовать файл скрипта из WinAVR и просто добавить в конце: Код .finish : { _size_prog = .; } > text } то заначение _size_prog все равно будет неверным, чтобы это исправить необходимо изменить описание секции .data как указал Сергей Борщ: вместо Код .data : AT (ADDR (.text) + SIZEOF (.text)) { PROVIDE (__data_start = .); *(.data) *(.data*) *(.rodata) /* We need to include .rodata here if gcc is used */ *(.rodata*) /* with -fdata-sections. */ *(.gnu.linkonce.d*) . = ALIGN(2); _edata = .; PROVIDE (__data_end = .); } > data нужно сделать: Код .data : { PROVIDE (__data_start = .); *(.data) *(.data*) *(.rodata) /* We need to include .rodata here if gcc is used */ *(.rodata*) /* with -fdata-sections. */ *(.gnu.linkonce.d*) . = ALIGN(2); _edata = .; PROVIDE (__data_end = .); } > data AT > text
--------------------
- Бендер, ты же робот, зачем тебе пить пиво? - Незачем! Я могу бросить в любой момент!
|
|
|
|
|
May 25 2009, 08:07
|
Участник

Группа: Новичок
Сообщений: 22
Регистрация: 20-05-09
Пользователь №: 49 303

|
AT (ADDR (.text) + SIZEOF (.text)) Народ, разясните смысл этих строк, SIZEOF вроде понятно-это размер секции, тогда ADDR-это начало? и что это за оператор AT?. И еще, в WinAvr есть папка с man, как вот эти man прочесть? Я думаю, что масса вопросов сразу бы отпала.
|
|
|
|
|
May 25 2009, 08:39
|

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

|
Цитата(antiwin @ May 25 2009, 11:07)  Народ, разясните смысл этих строк, SIZEOF вроде понятно-это размер секции, тогда ADDR-это начало? и что это за оператор AT?. GNU ld: Output Section Description Цитата(antiwin @ May 25 2009, 11:07)  И еще, в WinAvr есть папка с man, как вот эти man прочесть? Хороший вопрос. в *никсах для этого есть утилита man. В msys/mingw я такой не нашел.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|