Aaron 1 24 августа, 2011 Опубликовано 24 августа, 2011 · Жалоба Доброго времени суток. Так сложилось, что до сих пор я ни разу не работал с загрузчиками - хватало разъёма JTAG. Но времена меняются, - надо делать загрузчик для своих железок (под UART). Естественно, под gcc (раз уж я начал его изучать) - бутлоадер выступает в роли подопытного кролика ;) Накопал кучу бутлоадеров, выбрал наиболее понравившийся - USB-бутлоадер, и на его основе начал параллельно изучать gcc и особенности работы с flash памятью. Внёс в проект кучу своего "мусора", в частности из проекта at91sam7a3-getting-started с сайта Atmel взял startup код. Как-то из разных мест ещё интересные моменты подглядывал-вставлял, разобрался с makefile и в итоге сейчас проект выглядит вот так: AT91_Bootloader.7z Файлы компилируются, проект собирается - но я ничего не вижу ни в терминале, ни в осциллографе (контролирую ножку LED, которая на PA20 находится) - при включении порт встаёт в "1" и ничего не делает. Отладчиком в eclipse ещё не учился пользоваться, чую скоро придётся засесть изучать ;) Для прошивки использую MT-Link, при работе через консоль j-link показывает, что PC крутится в районе адресов 0x0 - 0x300, после jtag-reset выдаёт сообщение: WARNING: PC of target system has unexpected value of 0x0000023C after reset. Адрес в warning тоже постоянно меняется в этом же диапазоне. Чую себя новичком :) Прошиваю at91sam7a3-getting-started - всё работает, прошиваю свой проект - ничего не работает :) Кто поможет разобраться? Исходники рабочего бутлоадера (если и когда он появится) обязательно выложу. Что происходит (в теории)? 1. инициализируем стеки прерываний (flash-reset.s: Reset_Handler) 2. инициализируем раздел bss (flash-reset.s: _init_data, _init_bss) 3. переходим в наш сишный код бутлоадера (bootrom.c: Bootrom) 4. обрабатываем пакеты, пишем во флэш. Я так понимаю, что затык идёт на 2м этапе скорее всего, чую - перемудрил с секциями. Поэтому кто решится помочь - в первую очередь прошу поглядеть, как я объявил секции (AT91-Bootloader\bootrom\prj\ldscript-flash.lds)! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 11 октября, 2011 Опубликовано 11 октября, 2011 (изменено) · Жалоба Хех, никто не отписался +) Я догадываюсь почему. Потихоньку изучаю утилиты дальше, ковыряю проект, сейчас разобрался - была куча косяков в скрипте ldscript-flash.lds и в стартап коде flash-reset.s. Однако есть новый затык :) Перед вызовом main есть код инициализации стека: //- Set up User Mode and set User Mode Stack msr CPSR_c, #ARM_MODE_USR | F_BIT // msr CPSR_c, #ARM_MODE_USR | I_BIT | F_BIT mov sp, r0 sub sl, r0, r6 Пока запрещены IRQ прерывания (закомментированная строка), код работает нормально, естественно без прерываний. По крайней мере сообщения в консоль нормально вывожу. Но если я разрешаю прерывания, то при настройке периферии при попадании в любое прерывание я вываливаюсь в undefined abort :smile3046: Куски кода я брал из рабочих проектов, первичный обработчик IRQ_Handler входа в прерывание заимствован написан правильно, всё обвешал комментами, сишный код тоже заимствованный. В общем, помогите пожалуйста, опытные люди, гляньте asm код инициализации - мне кажется, собака зарылась именно в нём. В архиве 2 файлика - flash-reset.s и ldscript-flash.lds. bl_startup_files.zip Изменено 11 октября, 2011 пользователем Aaron Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 12 октября, 2011 Опубликовано 12 октября, 2011 · Жалоба Блин, всё, разобрался ))) как и говорил, ошибки были в этих двух файлах. Теперь таблицу векторов разместил в области data в RAM, плюс доработал стартап. Дальше уже дело техники - сишный код писать. Исходники проекта прилагаю, кому интересно - ковыряйтесь на здоровье ))) BootLoaderROM_FLASH.7z Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 12 октября, 2011 Опубликовано 12 октября, 2011 · Жалоба Аминь! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 13 февраля, 2012 Опубликовано 13 февраля, 2012 · Жалоба Потихоньку доделываю проект, интересные вещи узнал. Сейчас застрял на таком моменте: передаю данные для записи во флэш (одну страницу), отрабатываю запись, а дальше код зависает. Перезапускаюсь, считываю участок памяти, который записывал - записалось то, что я хотел. Вопрос - почему зависает код? Если моё понимание кода ошибочно в комментариях, прошу поправить. #define BL_DATA_UINT32_LENGTH 64 typedef struct { unsigned int cmd; // основная команда (служебное слово) unsigned int param; // дополнительные параметры команды unsigned int addr; // адрес страницы памяти union { uint8_t asBytes[BL_DATA_UINT32_LENGTH*4]; unsigned int asDwords[BL_DATA_UINT32_LENGTH]; } data; // данные для записи в страницу памяти unsigned int crc; // контрольная сумма } BootloaderPacket; BootloaderPacket inPckt; volatile AT91PS_MC mc = AT91C_BASE_MC; volatile uint32_t *p; ... p = (volatile uint32_t *)(inPckt.addr); // при записи по адресу памяти FLASH данные записываются сначала в буфер записи памяти, и писать // в него надо по 32 бита за 1 раз! поэтому используем asDwords printf("Setup data to write @ flash addr 0x%08X:", inPckt.addr); for (i = 0; i < BL_DATA_UINT32_LENGTH; i++) { p[i] = inPckt.data.asDwords[i]; printf(" 0x%04X", inPckt.data.asDwords[i]); }; printf("\r\n"); interrupt_mask = *AT91C_AIC_IMR; // сохраняем набор включенных прерываний *AT91C_AIC_IDCR = AT91C_ALL_INT; // выключаем все прерывания // буфер для записи в память уже заполнился, и можно писать страницу FLASH памяти mc->MC_FCR = AT91C_MC_WRITE_KEY | AT91C_MC_FCMD_START_PROG | AT91C_MC_PAGEN(inPckt.addr/AT91C_IFLASH_PAGE_SIZE); while (0 == (mc->MC_FSR & AT91C_MC_EOP)) {}; // ждём когда взведётся статус EndOfProgramming // FIXME: запись происходит, но до сюда код не доходит, зависает в цикле *AT91C_AIC_IECR = interrupt_mask; // восстанавливаем прерывания printf("\r\nWrote page #%04u at address 0x%08X.\r\n", inPckt.addr/AT91C_IFLASH_PAGE_SIZE, inPckt.addr); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 134 13 февраля, 2012 Опубликовано 13 февраля, 2012 · Жалоба mc->MC_FCR = AT91C_MC_WRITE_KEY | AT91C_MC_FCMD_START_PROG | AT91C_MC_PAGEN(inPckt.addr/AT91C_IFLASH_PAGE_SIZE); while (0 == (mc->MC_FSR & AT91C_MC_EOP)) {}; // ждём когда взведётся статус EndOfProgramming Этот участок кода должен выполняться из ОЗУ. Флешка блокируется на время записи и читать из нее команды проц не может. class flash_t { public: INLINE static void clear_buffer(); INLINE static void fill(uint32_t const *addr, uint32_t data) { *(uint32_t *)addr = data; } INLINE static void fill(uint32_t const *dst, uint32_t const *src, uint32_t size); INLINE static uint32_t read(uint32_t const *addr) { return *addr; } INLINE static void read(uint32_t *dst, uint32_t const *src, uint32_t size); INLINE static void erase(void const *page_addr); INLINE static void program(void const *page_addr); static uint32_t const PAGE_SIZE = 128; private: static void command(uint32_t command, uint32_t mode); NOINLINE static void command_RAM(uint32_t command) __attribute__((section(".ramfunc"))); static uint32_t const MCK_CYCLES = ((15ULL * MCK + 5000000ULL) / 10000000ULL); public: static uint32_t const FLASH_WAITSTATES = MCK < 30000000ULL ? AT91C_MC_FWS_0FWS : AT91C_MC_FWS_1FWS; } static Flash __attribute__((__unused__)); void flash_t::erase(void const *page_addr) { command( ((AT91C_MC_KEY / 0xFFU) * 0x5A) | (((uint32_t)page_addr >> 7) << 8) | AT91C_MC_FCMD_START_PROG, (0 * AT91C_MC_FRDY) | (0 * AT91C_MC_LOCKE) | ( 0 * AT91C_MC_PROGE) | (0 * AT91C_MC_NEBP) | (FLASH_WAITSTATES) | (AT91C_MC_FMCN / 0xFFU * MCK_CYCLES) ); } void flash_t::program(void const *page_addr) { command( ((AT91C_MC_KEY / 0xFFU) * 0x5A) | (((uint32_t)page_addr >> 7) << 8) | AT91C_MC_FCMD_START_PROG, (0 * AT91C_MC_FRDY) | (0 * AT91C_MC_LOCKE) | ( 0 * AT91C_MC_PROGE) | (1 * AT91C_MC_NEBP) | (FLASH_WAITSTATES) | (AT91C_MC_FMCN / 0xFFU * MCK_CYCLES) ); } void flash_t::command_RAM(uint32_t command) { AT91C_BASE_MC->MC_FCR = command; while(!(AT91C_BASE_MC->MC_FSR & AT91C_MC_FRDY)) ; } void flash_t::command(uint32_t command, uint32_t mode) { TCritSect cs; AT91C_BASE_MC->MC_FMR = mode; command_RAM(command); } /* .data section which is used for initialized data */ .data : /* place init values immediatly after .text section */ { _data = .; *(.ramfunc*) *(.data*) _edata = .; _data_image = LOADADDR(.data); PROVIDE (edata = .); } > RAM AT > ROM Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 13 февраля, 2012 Опубликовано 13 февраля, 2012 · Жалоба М, понятно, спасибо. Попробую перевести функцию в RAM... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 134 13 февраля, 2012 Опубликовано 13 февраля, 2012 · Жалоба М, понятно, спасибо. Попробую перевести функцию в RAM...Поместите ее в отдельную секцию void command_RAM(uint32_t command) __attribute__((section(".ramfunc"))); а секцию разместите в выходную секцию .data (см. кусочек скрипта в предыдущем сообщении). И обратите внимание, что в ОЗУ достаточно разместить очень небольшой кусочек: void command_RAM(uint32_t command) { AT91C_BASE_MC->MC_FCR = command; while(!(AT91C_BASE_MC->MC_FSR & AT91C_MC_FRDY)) ; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 13 февраля, 2012 Опубликовано 13 февраля, 2012 (изменено) · Жалоба Этот участок кода должен выполняться из ОЗУ. Флешка блокируется на время записи и читать из нее команды проц не может. Да, я проверил код - у меня функция уже была определена как static int BL_ParsePacket(void *packet, uint16_t len) __attribute__ ((section(".ramfunc"))); Просто код давно не смотрел уже, даже успел забыть, что к чему :) Так что проблема в другом месте. Вот пока что для себя решил: 1) EFC я настраиваю, AT91C_BASE_MC->MC_FMR = AT91C_MC_FMCN(BOARD_FMCN) | AT91C_MC_FWS_3FWS | FMCN_FOR_LOCK; 2) Разблокирую память в цикле, коротко: EFC_PerformCommand(pStartEfc, AT91C_MC_FCMD_UNLOCK, startPage); 3) по идее надо стирать память (я не выставляю флаг AT91C_MC_NEBP), но почему тогда у меня всё же страница записывается? 4) после записи я жду флаг AT91C_MC_EOP, а у вас в коде ожидание флага AT91C_MC_FRDY. Надо посмотреть, чем они отличаются... И обратите внимание, что в ОЗУ достаточно разместить очень небольшой кусочек... Я просто думал сделать код так, чтобы потом теоретически его можно было полностью из ОЗУ выполнять для обновления и самого бутлоадера. Изменено 13 февраля, 2012 пользователем Aaron Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 14 февраля, 2012 Опубликовано 14 февраля, 2012 (изменено) · Жалоба Не перестаю удивляться тонкостям сборки проекта. Внёс правки в код в соответствии с вашим примером, - не помогло. А проблема оказывается была в ld-скрипте и мейкфайле, - ещё раз внимательно выставил все флаги и области памяти, - всё заработало. Но за подсказки всё равно большое спасибо :) Изменено 14 февраля, 2012 пользователем Aaron Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Aaron 1 20 февраля, 2012 Опубликовано 20 февраля, 2012 (изменено) · Жалоба Доделал проект по большей части, выкладываю для интересующихся. В архиве at91_bootloader_programmer.7z два проекта - под микроконтроллер (at91-bootloader) и приложение для ПК для связи с бутлоадером (isp-bootloader). Сразу предупреждаю, - код не претендует на красоту, изящество и структурную и идеологическую правильность: код писался с большими перерывами в качестве упражнения на изучение eclipse/gcc/make с одной стороны и понимание прямой работы с флэш памятью контроллера с другой стороны; отдельные куски кода взяты готовыми из разных источников, так что присутствует и захламлённость, и разная стилистика. Однако код снабжён исчерпывающими комментариями, и разобраться в нём не составит труда. Остаётся два открытых вопроса, которые я пока не смог решить, и по которым мне нужна помощь: 1. Как правильно из бутлоадера передать управление в пользовательскую прошивку? в проекте два варианта - через указатель: static void (*BL_StartUserApplication)(void); // указатель на старт юзер-проги BL_StartUserApplication = (void(*)(void))FLASH_USER_START; BL_StartUserApplication(); и через ассемблерный код: // we want to set the LSb in order to switch in to Thumb mode // when we bx. // addr = 0x2801 = (0x5 << 11) | 1 asm("mov r3, #5\n"); asm("lsl r3, r3, #11\n"); asm("mov r4, #1\n"); asm("orr r3, r4\n"); asm("bx r3\n"); Через указатель у меня МК просто рестартится и снова входит в бутлоадер, а через ассемблерный код затихает, и я не знаю, что дальше происходит. Может, и работает? Подскажите плз. 2. Как мне правильно изменить существующие проекты, написанные под МК (среда разработки - Keil), чтобы они запускались после передачи им управления от бутлоадера? Вопрос связан с тем, что происходит ведь уже в бутлоадере инициализация стеков, прерываний (но я их отключаю), взводится кварц, карта памяти в конце концов меняется. UPD: всплыл косяк, в функции Parser_BIN::ParserOpen надо раскомментировать флаг O_BINARY: this->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, ... без него символ возврата коретки модифицируется в записываемом бинарном файле :) Изменено 21 февраля, 2012 пользователем Aaron Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба 1. Оба варианта годятся, но вход в прошивку я бы делал только в режиме ARM. 2. Стеки и прерывания в приложении лучше инициализировать заново, ремап и кварц можно и не трогать, если устраивают. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 134 20 февраля, 2012 Опубликовано 20 февраля, 2012 · Жалоба 1. Как правильно из бутлоадера передать управление в пользовательскую прошивку? в проекте два вариантаЯ на SAM7 делаю так: extern "C" void Application() __attribute__ ((__noreturn__)); if (CRC.ok()) // Application Section OK { // copy application INTVEC to remapped INTVEC location (start of RAM) uint32_t const *Src = (uint32_t *)APP_START; uint32_t *Dst = (uint32_t *)0x00200000; //RAMSTART uint_fast8_t Size = 0x40 / sizeof *Dst; do { *Dst++ = *Src++; } while (--Size); AT91C_BASE_MC->MC_RCR = AT91C_MC_RCB; // REMAP DRIVER(MANUAL_BOOT, OUTPUT); Application(); } а сам символ Application определяю в командной строке линкера (gcc, как делать такое в кейле - понятия не имею): LDFLAGS += -Wl,--defsym,Application=0 2. Как мне правильно изменить существующие проекты, написанные под МК (среда разработки - Keil), чтобы они запускались после передачи им управления от бутлоадера? Вопрос связан с тем, что происходит ведь уже в бутлоадере инициализация стеков, прерываний (но я их отключаю), взводится кварц, карта памяти в конце концов меняется.Я просто смещаю начало доступной приложению области флеша на 4К и линкую вектра так, чтобы они работали будучи скопированными в ОЗУ и отраженными на адрес 0 (remap). Загрузчик при старте настраивает PLL, быстро считает CRC приложения и отдает управление. После чего приложение, как и любая обычная программа, настраивает все (включая стеки) под себя сама. А если CRC не совпало - тогда загрузчик настраивает под себя периферию, ОЗУ, стеки и работает. Выход из загрузчика - по собаке, после чего цикл повторяется. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться