Jump to content

    
Sign in to follow this  
ДЕЙЛ

Стартовый загрузчик

Recommended Posts

Не надо ничего сдвигать на 7 битов! Прочитай внимательнее про этот регистр: эти биты не используются, потому что таблица прерываний должна быть выровнена по определенному адресу, кратному размеру таблицы, округленному до следующей степени двойки.

 

Второе. Никаких жестко заданных 0x8105! При следующей компиляции этот адрес может быть совсем другим. Я же сказал, как его достать: читаешь значение из второго вектора в таблице основного ПО, которая всегда находится в известном (определенном тобой) месте.

 

Надо бы проект посмотреть, да у меня IAR не стоит. Попробуй для начала просто отладить переход: вставь эту инструкцию в стартап файл загрузчика, сразу в ResetHandler, пускай переходит куда-нибудь рядом, через пару адресов. Попробуй другие инструкции вроде BX, которые переходят по адресу из указанного регистра. Только почитай про них внимательнее, при использовании BX и BLX младший бит адреса назначения должен быть установлен.

Share this post


Link to post
Share on other sites
Не надо ничего сдвигать на 7 битов! Прочитай внимательнее про этот регистр: эти биты не используются, потому что таблица прерываний должна быть выровнена по определенному адресу, кратному размеру таблицы, округленному до следующей степени двойки.

В хидере 35 прерываний и добавим 16 исключений, как написано в руководстве на ядро, получили 51, ближайшая степень двойки - 64, умножаем на 4 и получим 0x100. Этому числу должен быть кратен адрес таблицы векторов, т.е. 0x7000 подходит.

Далее читаю описание полей регистра VTOR:

Бит 29 TBLBASE - расположение таблицы в оперативке(1) или во флеш(0)

Биты 28:7 TBLOFF - Смещение таблицы относительно начала области кода или области ОЗУ,т.е. к 0x7000 добавляем справа 7 нулевых битов и получаем 0x380000 и записываем это число в регистр VTOR.

 

Второе. Никаких жестко заданных 0x8105! При следующей компиляции этот адрес может быть совсем другим. Я же сказал, как его достать: читаешь значение из второго вектора в таблице основного ПО, которая всегда находится в известном (определенном тобой) месте.

Пока не трогаю записанные данные по адресу 0x7000, т.е. никуда ничего не перемещаю, а пытаюсь маленькой программой запустить большую, расположеннуюв другом месте памяти. И адрес 8105 не меняется в моём случае.

Edited by ДЕЙЛ

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Не так считаешь. Векторов прерываний 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, в симуляторе всё работает, странно. Но всё равно ты попробуй перейти с помощью другой инструкции.

Edited by menzoda

Share this post


Link to post
Share on other sites

насчёт перескока на 2кб на какой странице мануала написано? Надо попробовать, а симулирование и реальность не всегда совпадают, как я заметил.

 

 

Поле смещения таблицы векторов. Оно содержит биты [29:7] смещения таблицы относительно начального адреса. То есть надо взять смещение 0x7000, взять из него биты 29:7, получившееся число 0xE0 записать в биты 29:7 регистра. Это то же самое, что и запись в регистр числа 0x7000.

пробовал разные варианты - записывал 0x380000 и 0x7000, в отладчике наблюдалось число 0xE0.

Не понял.

Имею ввиду, что программа из флеши запускается в следующем шаге после строки B 0x8105 при нажатии кнопки F10 в пошаговом режиме отладки, т.е. переходит в то место и запускает программу, а когда отключаю JTAG и нажимаю на сброс, то никаких переходов нет, контроллер не шевелится.

Share this post


Link to post
Share on other sites
насчёт перескока на 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-файле основного ПО и сверять всё вручную.

Share this post


Link to post
Share on other sites
То есть в отладчике переход всё-таки работает? Попробуй пошагать дальше, может ошибка вываливается где-нибудь в другом месте, в отладчике ты до него просто не доходишь.

Под отладчиком после ассемблерной инструкции перехода программа работает в реальном режиме времени, но её можно остановить и увидеть, что всё крутится в адресах после 7000. Мошть ещё нужно указатель стека на нужное место поставить, как мне посоветовали на другом форуме?

Edited by ДЕЙЛ

Share this post


Link to post
Share on other sites
Мошть ещё нужно указатель стека на нужное место поставить, как мне посоветовали на другом форуме?

Это идея. Ведь адрес вершины стека извлекается из первого вектора после сброса, а мы при переходе к основному ПО ничего не сбрасываем. Формально говоря, надо бы перед переходом самому извлечь это значение и записать в регистр SP, или можно это сделать в основном ПО в стартап-файле. Однако, не факт, что это является причиной ошибки.

 

Сейчас адрес стека достается основному ПО по наследству от загрузчика. Какие могут быть потенциальные проблемы? Во-первых, загрузчик может использовать значительную часть стека и основному ПО достанется урезанный участок памяти, которого может не хватить, но так как твой загрузчик вообще не использует стек этим вариантом можно пренебречь. Во-вторых, проекты загрузчика и основного ПО могут иметь разное расположение стека: стек загрузчика может пересекаться с областью данных основного ПО. Таким образом основное ПО, работая с данными в RAM, может попортить себе этот неправильно заданный стек.

 

Последняя ситуация более вероятна, но если это и она странно, что в отладчике всё работает. Тем не менее попробуй задать правильное значение стека перед переходом и посмотри что будет. Если не поможет то остается трассировать. Расставляешь на разных этапах загрузчика и основного ПО дерганье выводами МК, так же не забываешь про исключения (HardFault и иже с ним). Запускаешь и смотришь: ага, эта дернулась, значит загрузчик запустился, а эта не дернулась, значит в основное ПО не перешел, выходит проблема где-то между этими этапами. Постепенно передвигая дерганье ногами ближе к друг-другу определяешь место поломки, ну а дальше будет видно.

Share this post


Link to post
Share on other sites

Железно заработало :yeah:

дело было ещё и в указателе стека. В этом прикреплённом проекте в тестовой программе для вызова главной нужно написать такой код:

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;
}

Можно было бы поумнее и правильнее код сделать, но для понимания принципа этого хватит.

 

теперь попробую назад в загрузчик из главной программы зайти

 

:bb-offtopic:

ДЕЙЛ, я, конечно, ничего против не имею, но такая боевая расцветка темы оформления каждый день по несколько часов сидения у монитора глаза не заворачивает на затылок? :)

:bb-offtopic: Зато оперативка экономится, отключил всё ненужное. Ноуту лет 15, в нём есть привод дискет, два разъёма усб, всамделишный LPT и COM-порты ;) Но зато экран большой и нет отвлекающего выхода в тырнет.

Share this post


Link to post
Share on other sites
Железно заработало

Наконец! Всем миром загрузчик помогали делать! Зато какой опыт!

 

теперь попробую назад в загрузчик из главной программы зайти

Уж это должно быть несложно - просто вызвать программный сброс. Главный вопрос сбросит ли он VTOR, вроде сбрасывает, но лучше уточнить, периферию то он точно сбрасывает.

 

Зато оперативка экономится, отключил всё ненужное.

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

Share this post


Link to post
Share on other sites

Оставлю тут свой код загрузчика на всякий случай, а то на домашнем компе ненадёжно. Программа принимает файл от прикреплённой программы, написанной на 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;
}

__________.rar

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this