|
Стартовый загрузчик, с нуля |
|
|
|
Oct 8 2014, 19:17
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530

|
Не надо ничего сдвигать на 7 битов! Прочитай внимательнее про этот регистр: эти биты не используются, потому что таблица прерываний должна быть выровнена по определенному адресу, кратному размеру таблицы, округленному до следующей степени двойки.
Второе. Никаких жестко заданных 0x8105! При следующей компиляции этот адрес может быть совсем другим. Я же сказал, как его достать: читаешь значение из второго вектора в таблице основного ПО, которая всегда находится в известном (определенном тобой) месте.
Надо бы проект посмотреть, да у меня IAR не стоит. Попробуй для начала просто отладить переход: вставь эту инструкцию в стартап файл загрузчика, сразу в ResetHandler, пускай переходит куда-нибудь рядом, через пару адресов. Попробуй другие инструкции вроде BX, которые переходят по адресу из указанного регистра. Только почитай про них внимательнее, при использовании BX и BLX младший бит адреса назначения должен быть установлен.
|
|
|
|
|
Oct 8 2014, 19:42
|
Местный
  
Группа: Участник
Сообщений: 234
Регистрация: 7-11-13
Пользователь №: 79 085

|
Цитата(menzoda @ Oct 8 2014, 23:17)  Не надо ничего сдвигать на 7 битов! Прочитай внимательнее про этот регистр: эти биты не используются, потому что таблица прерываний должна быть выровнена по определенному адресу, кратному размеру таблицы, округленному до следующей степени двойки. В хидере 35 прерываний и добавим 16 исключений, как написано в руководстве на ядро, получили 51, ближайшая степень двойки - 64, умножаем на 4 и получим 0x100. Этому числу должен быть кратен адрес таблицы векторов, т.е. 0x7000 подходит. Далее читаю описание полей регистра VTOR: Бит 29 TBLBASE - расположение таблицы в оперативке(1) или во флеш(0) Биты 28:7 TBLOFF - Смещение таблицы относительно начала области кода или области ОЗУ,т.е. к 0x7000 добавляем справа 7 нулевых битов и получаем 0x380000 и записываем это число в регистр VTOR. Цитата(menzoda @ Oct 8 2014, 23:17)  Второе. Никаких жестко заданных 0x8105! При следующей компиляции этот адрес может быть совсем другим. Я же сказал, как его достать: читаешь значение из второго вектора в таблице основного ПО, которая всегда находится в известном (определенном тобой) месте. Пока не трогаю записанные данные по адресу 0x7000, т.е. никуда ничего не перемещаю, а пытаюсь маленькой программой запустить большую, расположеннуюв другом месте памяти. И адрес 8105 не меняется в моём случае.
Сообщение отредактировал ДЕЙЛ - Oct 8 2014, 19:43
|
|
|
|
|
Oct 9 2014, 04:54
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530

|
Не так считаешь. Векторов прерываний 51, в байтах это 51*4 = 204. Округляем до 256, в двоичном виде это будет 100000000, младшие 7 бит уже равны нулю как ни крути. Выбираем адрес кратный 256, ты выбрал 0x7000, и пишем его прямо туда, не сдвигая, потому что у всех адресов кратных 256 младшие 8 бит будут всегда равны нулю. В документации всё написано, хоть и несколько запутано: Цитата Vector table base offset field. It contains bits[29:7] of the offset of the table base from the bottom of the memory map. Поле смещения таблицы векторов. Оно содержит биты [29:7] смещения таблицы относительно начального адреса. То есть надо взять смещение 0x7000, взять из него биты 29:7, получившееся число 0xE0 записать в биты 29:7 регистра. Это то же самое, что и запись в регистр числа 0x7000. Цитата Почему-то в пошаговом режиме после перехода по адресу программа в другом конце памяти запускается иногда после команды перехода, а после нажатия кнопки сброса никаких движений. Не понял. Кстати, что-то я не вижу в документации примеров использования инструкции B как у тебя, там только говориться про то, что можно указать метку или значение относительно текущего адреса в виде [PC, #imm]. Попробуй использовать:
Код MOV R0, #0x8105 BX R0
Кроме того B может скакать только на +-2Кб, а у тебя тут вроде больше. Странно, почему ассемблер не ругнулся. Пройдись внимательно по шагам по ассемблерному коду (а не по сишному), точно вылетает на инструкции перехода? Покажи дизассемблер участка с этим переходом (что за инструкция там действительно находится).Нашел у себя на работе IAR, в симуляторе всё работает, странно. Но всё равно ты попробуй перейти с помощью другой инструкции.
Сообщение отредактировал menzoda - Oct 9 2014, 06:33
|
|
|
|
|
Oct 9 2014, 06:47
|
Местный
  
Группа: Участник
Сообщений: 234
Регистрация: 7-11-13
Пользователь №: 79 085

|
насчёт перескока на 2кб на какой странице мануала написано? Надо попробовать, а симулирование и реальность не всегда совпадают, как я заметил. Цитата(menzoda @ Oct 9 2014, 08:54)  Поле смещения таблицы векторов. Оно содержит биты [29:7] смещения таблицы относительно начального адреса. То есть надо взять смещение 0x7000, взять из него биты 29:7, получившееся число 0xE0 записать в биты 29:7 регистра. Это то же самое, что и запись в регистр числа 0x7000. пробовал разные варианты - записывал 0x380000 и 0x7000, в отладчике наблюдалось число 0xE0. Цитата Не понял. Имею ввиду, что программа из флеши запускается в следующем шаге после строки B 0x8105 при нажатии кнопки F10 в пошаговом режиме отладки, т.е. переходит в то место и запускает программу, а когда отключаю JTAG и нажимаю на сброс, то никаких переходов нет, контроллер не шевелится.
|
|
|
|
|
Oct 9 2014, 07:30
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530

|
Цитата насчёт перескока на 2кб на какой странице мануала написано? Тут я ошибся, наверное посмотрел на набор инструкций Thumb. У Cortex-M3 Thumb2 и там можно прыгать на 16 Мб, вот дока. Цитата пробовал разные варианты - записывал 0x380000 и 0x7000, в отладчике наблюдалось число 0xE0. 0xE0 это правильное значение: если взять биты 29:7 от 0x7000, то как раз выйдет 0xE0. Почему у тебя при записи 0x380000 получается 0xE0 я не знаю, может перепутал чего? Во всяком случае, 0xE0 это хорошо. Цитата Имею ввиду, что программа из флеши запускается в следующем шаге после строки B 0x8105 при нажатии кнопки F10 в пошаговом режиме отладки, т.е. переходит в то место и запускает программу, а когда отключаю JTAG и нажимаю на сброс, то никаких переходов нет, контроллер не шевелится. То есть в отладчике переход всё-таки работает? Попробуй пошагать дальше, может ошибка вываливается где-нибудь в другом месте, в отладчике ты до него просто не доходишь. Остановись на инструкции перехода, шагни вперед. Перешло к основному ПО по адресу 0x8105? Если всё отлично - шагай дальше, там будет SystemInit, потом __iar_program_start, из него будет вызвана main основного ПО. Вот хотя бы до main дойди. Заметь, при хождении по основному ПО никакие метки не будут подсвечены (не будет написано, что это __iar_program_start или main), будет сплошная простыня с инструкциями, так что тебе придется посмотреть адреса всех этих меток в map-файле основного ПО и сверять всё вручную.
|
|
|
|
|
Oct 9 2014, 08:53
|
Местный
  
Группа: Участник
Сообщений: 234
Регистрация: 7-11-13
Пользователь №: 79 085

|
Цитата(menzoda @ Oct 9 2014, 11:30)  То есть в отладчике переход всё-таки работает? Попробуй пошагать дальше, может ошибка вываливается где-нибудь в другом месте, в отладчике ты до него просто не доходишь. Под отладчиком после ассемблерной инструкции перехода программа работает в реальном режиме времени, но её можно остановить и увидеть, что всё крутится в адресах после 7000. Мошть ещё нужно указатель стека на нужное место поставить, как мне посоветовали на другом форуме?
Сообщение отредактировал ДЕЙЛ - Oct 9 2014, 08:54
|
|
|
|
|
Oct 9 2014, 10:15
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530

|
Цитата Мошть ещё нужно указатель стека на нужное место поставить, как мне посоветовали на другом форуме? Это идея. Ведь адрес вершины стека извлекается из первого вектора после сброса, а мы при переходе к основному ПО ничего не сбрасываем. Формально говоря, надо бы перед переходом самому извлечь это значение и записать в регистр SP, или можно это сделать в основном ПО в стартап-файле. Однако, не факт, что это является причиной ошибки. Сейчас адрес стека достается основному ПО по наследству от загрузчика. Какие могут быть потенциальные проблемы? Во-первых, загрузчик может использовать значительную часть стека и основному ПО достанется урезанный участок памяти, которого может не хватить, но так как твой загрузчик вообще не использует стек этим вариантом можно пренебречь. Во-вторых, проекты загрузчика и основного ПО могут иметь разное расположение стека: стек загрузчика может пересекаться с областью данных основного ПО. Таким образом основное ПО, работая с данными в RAM, может попортить себе этот неправильно заданный стек. Последняя ситуация более вероятна, но если это и она странно, что в отладчике всё работает. Тем не менее попробуй задать правильное значение стека перед переходом и посмотри что будет. Если не поможет то остается трассировать. Расставляешь на разных этапах загрузчика и основного ПО дерганье выводами МК, так же не забываешь про исключения (HardFault и иже с ним). Запускаешь и смотришь: ага, эта дернулась, значит загрузчик запустился, а эта не дернулась, значит в основное ПО не перешел, выходит проблема где-то между этими этапами. Постепенно передвигая дерганье ногами ближе к друг-другу определяешь место поломки, ну а дальше будет видно.
|
|
|
|
|
Oct 9 2014, 19:26
|
Местный
  
Группа: Участник
Сообщений: 234
Регистрация: 7-11-13
Пользователь №: 79 085

|
Железно заработало дело было ещё и в указателе стека. В этом прикреплённом проекте в тестовой программе для вызова главной нужно написать такой код: Код unsigned int *VTOR; int main() { VTOR = (unsigned int*)0xE000ED08; //адрес данного регистра из хидера *VTOR = 0x7000; //смещение таблицы векторов - из настроек линкера asm ("mov r13, #0x2448"); //запись в регистр стека младших байтов значения указателя стека основной программы, взятого из ячейки с адресом 0x7000; asm ("movt r13, #0x1000"); //запись в регистр стека старших байтов значения указателя стека основной программы, взятого из ячейки с адресом 0x7000; asm ("B 0x8105"); //Безусловный переход по адресу обработчика прерывания сброса, прочитанного из ячейки 0x7002; } Можно было бы поумнее и правильнее код сделать, но для понимания принципа этого хватит. теперь попробую назад в загрузчик из главной программы зайти Цитата(toweroff @ Oct 8 2014, 18:52)  ДЕЙЛ, я, конечно, ничего против не имею, но такая боевая расцветка темы оформления каждый день по несколько часов сидения у монитора глаза не заворачивает на затылок?   Зато оперативка экономится, отключил всё ненужное. Ноуту лет 15, в нём есть привод дискет, два разъёма усб, всамделишный LPT и COM-порты  Но зато экран большой и нет отвлекающего выхода в тырнет.
|
|
|
|
|
Oct 10 2014, 04:59
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530

|
Цитата Железно заработало Наконец! Всем миром загрузчик помогали делать! Зато какой опыт! Цитата теперь попробую назад в загрузчик из главной программы зайти Уж это должно быть несложно - просто вызвать программный сброс. Главный вопрос сбросит ли он VTOR, вроде сбрасывает, но лучше уточнить, периферию то он точно сбрасывает. Цитата Зато оперативка экономится, отключил всё ненужное. Цвет то на оперативку не влияет никак, а так я сам с классической темой сижу, только цвета стандартные - серо-мышиные, а не вырвиглазный зеленый.
|
|
|
|
|
Feb 16 2015, 12:28
|
Местный
  
Группа: Участник
Сообщений: 234
Регистрация: 7-11-13
Пользователь №: 79 085

|
Оставлю тут свой код загрузчика на всякий случай, а то на домашнем компе ненадёжно. Программа принимает файл от прикреплённой программы, написанной на DELPHI. Успеть отправить надо в течение минуты после сброса. Код #include "iolpc1778.h" #include "LPC17xx.h"
#define IAP_LOCATION 0x1fff1ff1 //точка входа в IAP (страница 896 мануала) unsigned int command[5]; unsigned int result[2]; typedef void (*IAP)(unsigned int[], unsigned int[]);
unsigned char DATA[30000]; unsigned int command[5]; unsigned int result[2]; unsigned char RX; unsigned int index_RX; unsigned char RX_PAKET[18]; unsigned char CHSUM, FlagRX; unsigned int indexM, pauza, i; unsigned int adr_copy, stek; unsigned int *adres_stek; unsigned char OutPortByte; int sektor;
void Out_UART0(); //otpravka paketa po UART0
void main(void) { { //INIT
{ //init UART0 IOCON_P1_25 = 0; //PIO FIO1DIR = 0x2200000; //Выходы PIN1_25, PIN1_11 PCONP |= 0x8; IOCON_P0_02 = 0x1; IOCON_P0_03 = 0x1; }
FIO0DIR = 0x4+0x8; SCS |= 0x20; //подключение осциллятора while(!(SCS&0x40)){} //ожидание запуска PLL0CON |= 0x01; PLL0CFG |= 0x09; PLL0FEED = 0xAA; PLL0FEED = 0x55; CCLKSEL |= 0x100;
PCLKSEL = 1; CLKSRCSEL |= 1; U0LCR |= 0x83; //razrecchenie dostupa k delitely U0DLL = 0xC8; //0xF0;//0xA0; //0x14; //0x4E; //nastrojka delitelya U0LCR &=~0x80; //zapret dostupa k delitely U0IER |= 0x1+0x4;//Разрешение прерываний UART0
//__enable_irq(); //Разрешение //__set_FAULTMASK(0); //всех прерываний //__set_BASEPRI(0); //Отключение маскирования //__set_CONTROL(0); AIRCR = 0x05FA0200; IP1 = 0x00000800; } NVIC_EnableIRQ(UART0_IRQn); //Enable IRQ UART0 (ISER0=32) indexM = 0; index_RX = 1;
while(1) { if (index_RX>5) { if ((DATA[index_RX-1]==0x11)&(DATA[index_RX-2]==0x22)&(DATA[index_RX-3]==0x33)&(DATA[index_RX-4]==0x44)) самопальный признак конца файла HEX { FlagRX = 0; //COPY_FLASH adr_copy = (unsigned int)&DATA[4096]; IAP iap_entry; iap_entry = (IAP) IAP_LOCATION;
for (sektor=1; sektor<5; sektor++) { //_______ПОДГОТОВИТЬ СЕКТОР К ЗАПИСИ_______// {
command[0] = 50; //код команды command[1] = sektor; //начальный номер сектора command[2] = sektor; //конечный номер сектора iap_entry (command, result); } //_______ПОДГОТОВИЛИ СЕКТОР К ЗАПИСИ______//
//_______СТЕРЕТЬ СЕКТОР_______// {
command[0] = 52; //код команды command[1] = sektor; //начальный номер сектора command[2] = sektor; //конечный номер сектора command[3] = 120000; //системная тактовая частота в кГц iap_entry (command, result); } //_______СТЁРЛИ СЕКТОР______// //_______ПОДГОТОВИТЬ СЕКТОР К ЗАПИСИ_______// {
command[0] = 50; //код команды command[1] = sektor; //начальный номер сектора command[2] = sektor; //конечный номер сектора iap_entry (command, result); } //_______ПОДГОТОВИЛИ СЕКТОР К ЗАПИСИ______//
//_______КОПИРОВАТЬ ОПЕРАТИВНУЮ ПАМЯТЬ ВО ФЛЕШ_______// {
command[0] = 51; //код команды command[1] = 0x1000*sektor; //начальный адрес перезаписываемой флеш command[2] = adr_copy+4096*(sektor-1); //начальный адрес оперативной памяти, откуда нужно копировать command[3] = 4096; //число байт для копирования command[4] = 120000; //системная тактовая частота в кГц iap_entry (command, result);
} //_______СКОПИРОВАЛИ СЕКТОР______//
//END_FLASH }
VTOR = 0x1000; adres_stek = (unsigned int*)0x1000; stek = (unsigned int)*adres_stek; __set_MSP(stek);
asm("mov R1, #0x1004"); asm("ldr R2, [R1]"); asm("BX R2");
} } }
}
void Out_UART0() { while (!(U0LSR&0x20)); U0THR=0xA0; //признак ответа для программы на ПК }
void UART0_IRQHandler(void)
{ DATA[index_RX] = U0RBR; index_RX++; NVIC_ClearPendingIRQ(UART0_IRQn); return; }
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|