Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Определение размера 0-го сектора в LPC2000
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Ykidia
Здравствуйте!

У меня вопрос: есть ли какие-нибудь трюки определения размера 0-го сектора в Philips LPC2000 без вызова IAP?
Например, в LPC2138 размер равен 4k, а в LPC2214 - 8k. Собственно, варианта на данной архитектуре всего два.

Зачем это нужно. Есть большой проект под IAR EWARM 4.41. Там свой cstartup, который располагался, как ему и положено, в 0-м секторе, так что любые изменения обработчиков исключений в cstartup'е соответственно и адекватно отражались на работе всего проекта.
Теперь решили сделать свой загрузчик. Вообще-то, сделан он уже давно, но сейчас решили поместить его в 0-й сектор, а основной проект "подвинуть" на сектор вверх - расположить его с 1-го сектора. Это для того, чтобы при перепрошивке LPC2000 данным загрузчиком не было сбоев, связанных с перепрошивкой 0-го сектора.
Т.е., основной проект получает по Ethernet новую firmware самого себя (простой бинарник, включающий в себя также и загрузчик в 0-м секторе), записывает ее в Data Flash AT45DB161x, взводит сигнатуру в Data Flash и перезапускает контроллер. При перезапуске первым получает управление (после Philips'овского bootloader'а) загрузчик в 0-м секторе, который читает и проверяет сигнатуру из Data Flash на предмет перепрошивки. Если надо перепрошиваться, то проверяется другая сигнатура на предмет использования внешнего прошивальщика. Если нужно использовать внешний, то он загружается из Data Flash во внутренний RAM LPC2000, ему передается управление, далее он перепрошивает контроллер полностью, включая 0-й сектор, обновляет сигнатуру (мол, прошито) и перезапускает контроллер.
Если же внешний прошивальщик использовать не нужно, то загрузчик сам перепрошивает контроллер, но исключая 0-й сектор. Затем он обновляет сигнатуру и передает управление на 1-й сектор, где располагается основной проект.

Такая схема позволяет обойтись без сбоев в режиме перепрошивки без 0-го сектора. Загрузчик запустился, определил ID контроллера, сравнил новую прошивку с текущим содержимым Flash контроллера, составил битовое поле различных секторов, перепрошил только те сектора, которые нуждаются в обновлении, и передал управление дальше, главному проекту. Однако это накладывает некоторые ограничения. Например, обработчики исключений/прерываний теперь находятся в загрузчике, а не в основном проекте (и поэтому его cstartup уже "не катит"). Чтобы передать управление на обработчики основного проекта, нужно знать размер 0-го сектора: 4k или 8k.
Я упомянул, что по старту загрузчика определяется ID контроллера, чтобы знать карту секторов - для перепрошивки и просто для того, чтобы корректно передать управление основному проекту. В конце концов, нужно где-то хранить всего 1 бит, хотя желательно байт (для уверенности, что ID был прочитан), однако неизвестно, как распределяет память основной проект и, соответственно, нет гарантии, что нужный нам байт/бит не будет затерт.
Если делать каждый раз при входе в исключение/прерывание вызов IAP - это долго, да и потом полученный ID надо еще дешифровать, чтобы узнать размер сектора.

Отсюда вопрос - возможно ли как-то сразу при входе в исключение/прерывание узнать, какой размер сектора? Может, где-то есть какой-нибудь битик, либо может его можно где-то хранить?

P.S. Сейчас основной проект использует работу с исключениями при MEMMAP = 2, что освобождает от данного ограничения. Но хотелось бы сделать максимально совместимо (типа задела на будущее).
Alex03
1. А чем MEMMAP = 2 не нравится?

2. А в загрузчике выделить этот байтик и менять его при перепрошивке загрузчика один раз вызвав ИАП? А то и не байтик а сразу слово - смещение основной проги.

3. А какие из векторов прерываний используются?
Если, например, только irq то
Код
  ldr pc, [pc, #-0xFF0]                         /* irq handler */

по адресу вектора irq сразу загружает VICVectAddr и усё.

4. Подозреваю что сам загрузчик прерывания может и не пользовать? В этом случае можно при перепрошивке основной проги менять вектора в загрузчике, чтобы смотрели на основную прогу. Ну или чтобы загрузчик пользовал меммап=2 если ему нужны прерывания. Всё это кроме ресета понятно.
Ykidia
Спасибо за ответ!
1. Да в принципе нравится, только отлаживать неудобно - кроме сброса нужно еще MEMMAP = 1 каждый раз прописывать в отладчике, мелочь, а достает. В программе приходится каждый раз инициализировать вектора, а вдруг понадобится их намертво зашить?
2. Это надо будет обдумать, если других вариантов не предвидится... Т.е., например так: изобразить переменную, естественно, со значением 0xFFFF, внутри 0-го сектора, а при запуске загрузчика и получении ID прописать при помощи IAP в эту переменную размер сектора - записать 512 байт, внутри к-рых лежит эта переменная, без стирания (хотя данный режим не рекомендуется). А потом брать оттуда значение и проверять его... или же сразу прошивать все вектора, и тогда ничего даже проверять не надо. Было бы неплохо, однако это, повторюсь, не рекомендуется... Чем чревато НЕ следование данной рекомендации (сначала обязательно стирать, а потом прошивать)?
3. В главном проекте пока используются IRQ и FIQ. Используется uC/OS-II, так что крайне нежелательно принимать решения по обработке конкретного прерывания прямо в загрузчике wink.gif
4. Да, Вы правы, загрузчик прерывания не пользует. Насчет перепрошивки векторов надо подумать, не опасно ли это, как в п.2.

добавлено

Нет, прошивка векторов напрямую не катит: там изначально должна быть контрольная сумма (а не 0xFFFFFFFF), которая изменится при прошивке векторов, и возможно для правильного ее обновления придется прежде стирать весь сектор... Остается вариант с "однократно прошиваемой" переменной в области 0-го сектора, а также остается вопрос: чем чревато прошивание без предварительного стирания (т.е. использовать прошивание лишь для сброса нужных бит в OTP-переменной)?
Ну и также приветствуются другие принципы быстрого получения информации о размере сектора.
Сергей Борщ
Цитата(Ykidia @ Apr 4 2007, 14:29) *
1. Да в принципе нравится, только отлаживать неудобно - кроме сброса нужно еще MEMMAP = 1 каждый раз прописывать в отладчике, мелочь, а достает. В программе приходится каждый раз инициализировать вектора, а вдруг понадобится их намертво зашить?
Тогда добавьте в .mac такую конструкцию:
Код
Remap_RAM()
{
    __var i;
    __var tmp;


    for( i = 0; i < 0x40; i += 4 )                      // copy vectors from app
    {
        tmp = __readMemory32(0x00002000 + i, "Memory");     // read from flash
        __writeMemory32(tmp,0x40000000 + i, "Memory");     // write to RAM
    }

    // remap throw JTAG
    __writeMemory8(0x02,0xE01FC040,"Memory");           // MEMMAP, remap vectors to RAM
    __message " remaped to RAM ";

}
execUserReset()
{
    Remap_RAM();
}
и все будет происходить автоматически.
Ykidia
Спасибо. Но мне нужно было немного другое. Чтобы автор главного проекта, забыв (или не зная) о том, что его cstartup находится не в 0-м секторе, и захотев сделать таблицу векторов во Flash (MEMMAP = 1), не бежал сразу ко мне, мол, с твоим загрузчиком мои прерывания не работают, а без него - работают.
Пока сделал так:

Код
        org        0x00
__program_start
        b       __first_init

        org     0x04
__undefined_instruction
        b       __undef_handler

        org     0x08
__software_interrupt
        b       __soft_handler

        org     0x0C
__prefetch_abort
        b       __pref_handler

        org     0x10
__data_abort
        b       __data_handler

        org     0x14
__checksum
        dc32    0x99FFFFCE

        org     0x18
__irq
        b       __irq_handler

        org     0x1C
__fiq
        b       __fiq_handler

;---------------------------------------------------------------
; Exception handlers routers
__reset_handler
        dc32    0xFFFFFFFF
__undef_handler
        dc32    0xFFFFFFFF
__soft_handler
        dc32    0xFFFFFFFF
__pref_handler
        dc32    0xFFFFFFFF
__data_handler
        dc32    0xFFFFFFFF
__rsrv_handler
        dc32    0xFFFFFFFF
__irq_handler
        dc32    0xFFFFFFFF
__fiq_handler
        dc32    0xFFFFFFFF

При первом старте загрузчика все __*_handler прошиваются (при помощи IAP, без стирания сектора) соответствующей командой перехода на адреса 0x10XX или 0x20XX.
Срабатывает железно, при старте отладки через J-link/J-Trace данные адреса уже прошиты, даже при задержке после сброса 0 мс (а вот RDI в отладке не дает прошивать, вернее, он сам перепрошивает сектор для работы с точками останова, поэтому старые значения dc32 0xFFFFFFFF возвращаются на место).
Однако мне этот метод немного непривычен. Может, у кого-то все же найдутся еще какие-нибудь соображения, как это (определение размера сектора без вызова IAP) можно сделать?
Сергей Борщ
Извиняюсь за задержку с ответом. Выходные. Теща-блины, тесть-водка smile.gif
Цитата(Ykidia @ Apr 6 2007, 16:02) *
Пока сделал так:
Мне кажется вы делаете все слишком сложно. Ваш загрузчик лежит в нулевом секторе и честно стартует при включении питания. Главный проект ("приложение") линкуется с первого сектора. В начале первого сектора располагается копия векторов приложения, которая будет скопирована в адреса 0x40000000 - 0x40000040 а после MEMMAP = 2 окажется и в адресах 0x00 - 0x3F , причем линкуется она так, чтобы работала после копирования (точно также как работают __ramfunc - функции). Пример кусочка .xcl.
Код
-DROMSTART=00002000  // application ROM starts at 2000. 0-1FFF reserved for bootloader
-DROMEND=00003ffff

-Z(CODE)INTVEC=00000000-0000003F
-Z(CODE)INTVEC_F=ROMSTART-(ROMSTART+3F)
-QINTVEC=INTVEC_F
Ваш загрузчик просто делает копирование векторов, remap и передает управление на адрес RESET-вектора. Разработчику основной программы надо помнить только три вещи - линковать вектора указанным выше способом, не трогать memmap, настраивать всю периферию - ибо что-то может быть уже настроено загрузчиком не так как надо приложению. В остальном все работает как будто загрузчика и нет, что вам и нужно, при этом процесс перегрузки приложения не трогает вектора загрузчика и при любом сбое не запорет загрузчик. Вот старт приложения из загрузчика:
Код
#define APP_START 0x00002000
extern "C" __arm void __program_start();

void StartApplication()
{
    __disable_interrupt();
    MEMMAP = 0x02;                // REMAP
                                // copy application INTVEC_ID to remapped INTVEC
    uint32_t const *Src = (uint32_t *)APP_START;
    uint32_t *Dst = (uint32_t *)0;
    uint_fast8_t Size = 0x40 / sizeof *Dst;
    do    { *Dst++ = *Src++; } while (--Size);

    __program_start();
}
А размер нулевого сектора вы задаете при компиляции загрузчика (#define APP_START 0x00002000). Вы ведь точно знаете, в какой процессор вы его будете заливать.
Если интересно, я могу сделать вам "рыбу" загрузчика и приложения из своего проекта.
Ykidia
Здравствуйте! Спасибо за подробный ответ.

Цитата
В начале первого сектора располагается копия векторов приложения, которая будет скопирована в адреса 0x40000000 - 0x40000040 а после MEMMAP = 2 окажется и в адресах 0x00 - 0x3F , причем линкуется она так, чтобы работала после копирования

В том-то и дело, что не хотелось бы привязываться к MEMMAP = 2. Нужно чтобы работало при MEMMAP = 1.

Цитата
Разработчику основной программы надо помнить только три вещи - линковать вектора указанным выше способом, не трогать memmap, настраивать всю периферию

Обычно умудрялись не запомнить простые вещи - например, что для внешнего прошивальщика необходимо также прописывать св-ва watchdog'а (в зависимости от платы могли быть 3 разных варианта watchdog'а).
Ради того же я потратил немало времени, чтобы используемую в загрузчике периферию перед передачей управления дальше восстанавливать в состояние как после сброса. Особенно проблемы были с выключением PLL и т.п. Оказалось, там есть тонкости, описания которых я нигде не видел...
Цитата
А размер нулевого сектора вы задаете при компиляции загрузчика (#define APP_START 0x00002000). Вы ведь точно знаете, в какой процессор вы его будете заливать.

А вот и нет! smile.gif Мне достаточно того, что в файле линкера иногда можно указать границу для уверенности в том, что код загрузчика ее не достигает.

Если хотите, могу скинуть Вам весь проект куда-нибудь. Вдруг пригодится. А то я столько с этим намучился wink.gif
Ykidia
Да, и идеи насчет определения размера 0-го сектора без вызова IAP продолжают приниматься. Интересно стало.
Вот по этой статье я попытался прочитать регистр с Part ID, по которому можно определить размер сектора, но этот регистр оказался только на запись, при его чтении я получал все время 0...
Сергей Борщ
Цитата(Ykidia @ Apr 12 2007, 08:47) *
В том-то и дело, что не хотелось бы привязываться к MEMMAP = 2. Нужно чтобы работало при MEMMAP = 1.
Но зачем??? Хотя вопрос риторический, но думаю вы создаете себе лишние трудности.
Цитата(Ykidia @ Apr 12 2007, 08:47) *
Обычно умудрялись не запомнить простые вещи - например, что для внешнего прошивальщика необходимо также прописывать св-ва watchdog'а (в зависимости от платы могли быть 3 разных варианта watchdog'а).
А что, прошивка не проходит какое-то тестирование перед отправкой в производство?
Цитата(Ykidia @ Apr 12 2007, 08:47) *
Ради того же я потратил немало времени, чтобы используемую в загрузчике периферию перед передачей управления дальше восстанавливать в состояние как после сброса. Особенно проблемы были с выключением PLL и т.п. Оказалось, там есть тонкости, описания которых я нигде не видел...
Вот это было бы интересно - я аналогичное делаю в .mac-файле отладчика по команде reset, но пока не достиг совершенства.
Цитата(Ykidia @ Apr 12 2007, 08:47) *
А вот и нет! smile.gif Мне достаточно того, что в файле линкера иногда можно указать границу для уверенности в том, что код загрузчика ее не достигает.
А что мешает сделать несколько файлов линкера для разных кристаллов?
Цитата(Ykidia @ Apr 12 2007, 08:47) *
Если хотите, могу скинуть Вам весь проект куда-нибудь. Вдруг пригодится. А то я столько с этим намучился wink.gif
Киньте в почту, вдруг найду что-то полезное для себя. Но меня настораживает сама идея, что загрузчик может переписывать области, от которых зависит его собственная работа. Как к гадалке не ходите - у удаленного заказчика возникнет сбой питания в момент перезаписи и придется возвращать прибор к производителю вместо того, чтобы просто повторить апгрейд прошивки.

Цитата(Ykidia @ Apr 12 2007, 12:15) *
Да, и идеи насчет определения размера 0-го сектора без вызова IAP продолжают приниматься. Интересно стало.
Сотрите первый сектор и посмотрите что стерлость... biggrin.gif хотя это тоже через IAP. А почему без IAP? По первому сообщению я понял, что вы не хотите каждый раз в исключении вызывать IAP. Но что мешает один раз вызвать IAP, сохранить полученное значение и дальше пользовать его? В документации иного способа кроме IAP не описано, поэтому если вы и найдете какое-либо "левое" решение, никаких гарантий что оно будет работать на другом типе кристалла или даже на этом же процессоре другой партии.
Ykidia
Цитата
вы создаете себе лишние трудности.

Есть такое. Но это в целях самообразования, хотя я стараюсь обычно далеко не соваться в "недокументированные особенности".

Цитата
А что, прошивка не проходит какое-то тестирование перед отправкой в производство?

Проходит, еще как, вот оттуда и выяснилось, хотя когда Вы посмотрите исходники, то увидите, что это не мешает загрузчику доделать свое дело до конца (при условии, что период watchdog'а находится в пределах нескольких секунд).

Цитата
А что мешает сделать несколько файлов линкера для разных кристаллов?

Они есть. Только неизвестно, что и как повернется потом.

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

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

Цитата
Но что мешает один раз вызвать IAP, сохранить полученное значение и дальше пользовать его

Сохранить куда? Только во flash, в известное место в своем секторе, а это по сути то же самое. Уславливаться с разработчиком о выделенном месте в ОЗУ? Тогда мне проще оставить все как есть.

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

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