Перейти к содержанию
    

LPC4337, свой загрузчик. Инициализация SDRAM

Добрый день!

Задаю вопрос по просьбе своего коллеги. Они изготовил бутлоадер, который принимает приложение по каналу связи, и записывает его во внутреннюю флеш микроконтроллера. Само приложение 100% работает. Но вот нюанс. И приложение, и бутлоадер инициализируют внешнюю SDRAM, которую каждый использует для себя. И вот на старте инициализации SDRAM приложением проц то-ли улетает куда-то, то-ли зависает. Вопрос: почему такое может происходить. Ведь при повторной инициализации EMC приложением, данных, используемых кем-либо, нет. Это какая-то особенность контроллера памяти?

Изменено пользователем haker_fox
Добавил теги к популярной теме.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Переход из загрузчика в приложение надо делать так:

1) Записать в ОЗУ метку (magic number) - признак перехода в приложение.

2) Программный сброс.

3) При старте загрузчик первым делом проверяет, был ли программный сброс, и есть ли метка.

4) Если да, запускает приложение. Если нет, затирает метку и продолжаем исполнять загрузчик.

Тогда никаких проблем с повторными инициализациями не будет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У меня логика бута нацелена на исключение возможных косяков с частичной инициализацией переферии бутлоадером.

1. Если вошли в режим записи/замены прошивки бутлоадер имеет полное право настраивать всё как ему нужно.

2. После делается ресет проца.

3. На старте бутлоадера есть маленький кусочек кода который использует буквально немного стека проверяет наличие прошивки и отсутсвие запросов на обновления.

4. Если есть стартуемая прошивка - запускается она, вместо полноценной инициализации бута.

ЗЫ Сайфай оказался чуть быстрее >_<.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, господа!!! Передам эти рекомендации. Сам давно бутами не занимался, и тольком не помню, какие там нюансы.

 

Спасибо, господа!!! Передам эти рекомендации. Сам давно бутами не занимался, и тольком не помню, какие там нюансы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Переход из загрузчика в приложение надо делать так:

...

4) Если да, запускает приложение. Если нет, затирает метку и продолжаем исполнять загрузчик.

Тогда никаких проблем с повторными инициализациями не будет.

Не совсем правильно. При такой последовательности действий, если потом, при работе основного приложения, произошёл сброс МК без потери питания (по WDT например) или с кратковременной потерей, то при старте будет пропуск бутлоадера, чего очевидно не должно быть.

Так что метку надо затирать и перед передачей управления из бута в основное ПО, а в основном ПО не использовать это место в ОЗУ.

 

А можно просто корректно написать инициализацию EMC-контроллера (и остальной периферии) не рассчитывающую, что в регистрах периферии находятся дефолтные значения. По-крайней мере той периферии, которая используется в бутлоадере и основной программе. :laughing:

У меня в бутлоадере я знаю какую периферию использовал и просто перед передачей управления основной программе делаю RESET для данных периферийных модулей. Благо что возможность подать RESET на конкретный периферийный блок в моём МК имеется (да и в LPC43xx она есть).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не совсем правильно. При такой последовательности действий, если потом, при работе основного приложения, произошёл сброс МК без потери питания (по WDT например) или с кратковременной потерей, то при старте будет пропуск бутлоадера, чего очевидно не должно быть.

Так что метку надо затирать и перед передачей управления из бута в основное ПО, а в основном ПО не использовать это место в ОЗУ.

Ну да, про это запамятовал. Впрочем, как следует подумать, прежде чем хватать клавиатуру и говнокодить, никто не отменял. В конце концов, они там разработчики или где? :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В конце концов, они там разработчики или где? :laughing:

Какой смысл в ваших этих эмоциях? Был задан конкретный технический вопрос, но никто не просил поучений.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При сбросе EMC модуля через RGU возникает hardfault. Уже перечитали документацию. Ничего не помогает. Нет ли у кого внятного примера, как использовать RGU?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хм. А я не использую никаких зарезервированных меток в ОЗУ.

Вот мой какой-то и загрузчиков:

unsigned int BootloaderInputStatus = HW_BootloaderInput();
    
if((BootloaderInputStatus == BOOT_INPUT_NONE) || (BootloaderInputStatus == BOOT_INPUT_HARD_RESET))
  HW_JumpToApplication();
    
HW_RCCInit();
HW_TimerInit();
HW_ExchangeUARTInit();
...
// основная программа
...
// перед выходом в приложение деактивирую всю использованную периферию

HW_ExchangeUARTStop();
HW_TimerStop();
HW_RCCStop();

HW_JumpToApplication();

 

HW_BootloaderInput() - считывает значение специального регистра, в котором есть статусные биты, какой вид сброса произошел и от кого.

 

...то при старте будет пропуск бутлоадера, чего очевидно не должно быть.

Почему это? Если сработал WDT, о какой прошивке должна идти речь? Как раз-таки после сброса по WDT загрузчик должен быть пропущен и должно запуститься снова приложение, на мой взгляд.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мне кажется, правильнее всего "добить" код инициализации, чтобы он работал всегда вне зависимости от того как приложение запущено. Сегодня его бутлоадер запустил, завтра отладчик, после завтра с внешней флэшки...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При сбросе EMC модуля через RGU возникает hardfault. Уже перечитали документацию. Ничего не помогает. Нет ли у кого внятного примера, как использовать RGU?

Так расшифруйте причину HF. Все возможности для этого есть.

С внешней ОЗУ на LPC43xx не работал, но для SPIFI делал так:

  CGU.IDIV.C = PCLK_DIVC - 1 << 2 | B11 | CLK_SRC_ID_PLL1 << 24;
  CGU.BASE.SPIFI = B11 | CLK_SRC_ID_IDIVC << 24;
  PeripheralOn(BRANCH_CLK_M4_SPIFI);
  PeripheralOn(BRANCH_CLK_SPIFI, RGU_RST_SPIFI);

Для USB:

  PeripheralOn(BRANCH_CLK_M4_USB0);
  PeripheralOn(BRANCH_CLK_USB0, RGU_RST_USB0);

И для всех остальных драйверов - аналогично. HF нет.

enum RGU_RST {
 RGU_RST_CORE    = 0,
 RGU_RST_PERIPH  = 1,
 RGU_RST_MASTER  = 2,
 RGU_RST_none    = 3,
 RGU_RST_WWDT    = 4,
 RGU_RST_CREG    = 5,
 RGU_RST_BUS     = 8,
 RGU_RST_SCU     = 9,
 RGU_RST_M0SUB   = 12,
 RGU_RST_M4      = 13,
 RGU_RST_LCD     = 16,
 RGU_RST_USB0    = 17,
 RGU_RST_USB1    = 18,
 RGU_RST_DMA     = 19,
 RGU_RST_SDIO    = 20,
 RGU_RST_EMC     = 21,
 RGU_RST_ENET    = 22,
 RGU_RST_FLASHA  = 25,
 RGU_RST_EEPROM  = 27,
 RGU_RST_GPIO    = 28,
 RGU_RST_FLASHB  = 29,
 RGU_RST_TIMER0  = 32,
 RGU_RST_TIMER1  = 33,
 RGU_RST_TIMER2  = 34,
 RGU_RST_TIMER3  = 35,
 RGU_RST_RITIMER = 36,
 RGU_RST_SCT     = 37,
 RGU_RST_MCPWM   = 38,
 RGU_RST_QEI     = 39,
 RGU_RST_ADC0    = 40,
 RGU_RST_ADC1    = 41,
 RGU_RST_DAC     = 42,
 RGU_RST_UART0   = 44,
 RGU_RST_UART1   = 45,
 RGU_RST_UART2   = 46,
 RGU_RST_UART3   = 47,
 RGU_RST_I2C0    = 48,
 RGU_RST_I2C1    = 49,
 RGU_RST_SSP0    = 50,
 RGU_RST_SSP1    = 51,
 RGU_RST_I2S     = 52,
 RGU_RST_SPIFI   = 53,
 RGU_RST_CAN1    = 54,
 RGU_RST_CAN0    = 55,
 RGU_RST_M0APP   = 56,
 RGU_RST_SGPIO   = 57,
 RGU_RST_SPI     = 58,
 RGU_RST_ADCHS   = 60
};

//---------------------------------------------------------------------------
//CCU1 branch clocks
enum BRANCH_CLK {
 BRANCH_CLK_APB3         =0x000, //APB3 bus clock.
 BRANCH_CLK_I2C1         =0x001, //clock to the I2C1 register interface and I2C1 peripheral clock.
 BRANCH_CLK_DAC          =0x002, //clock to the DAC register interface.
 BRANCH_CLK_ADC0         =0x003, //clock to the ADC0 register interface and ADC0 peripheral clock.
 BRANCH_CLK_ADC1         =0x004, //clock to the ADC1 register interface and ADC1 peripheral clock.
 BRANCH_CLK_CAN0         =0x005, //clock to the C_CAN0 register interface and C_CAN0 peripheral clock.
 BRANCH_CLK_APB1         =0x020, //APB1 bus clock.
 BRANCH_CLK_MCPWM        =0x021, //clock to the PWM Motor control block and PWM Motor control peripheral clock.
 BRANCH_CLK_I2C0         =0x022, //clock to the I2C0 register interface and I2C0 peripheral clock.
 BRANCH_CLK_I2S          =0x023, //clock to the I2S0 and I2S1 register interfaces and I2S0 and I2S1 peripheral clock.
 BRANCH_CLK_CAN1         =0x024, //clock to the C_CAN1 register interface and C_CAN1 peripheral clock.
 BRANCH_CLK_SPIFI        =0x040, //clock for the SPIFI SCKI clock input.
 BRANCH_CLK_M4           =0x060, //M4 bus clock.
 BRANCH_CLK_M4_SPIFI     =0x061, //clock to the SPIFI register interface.
 BRANCH_CLK_M4_GPIO      =0x062, //clock to the GPIO register interface
 BRANCH_CLK_M4_LCD       =0x063, //clock to the LCD register interface.
 BRANCH_CLK_M4_ENET      =0x064, //clock to the Ethernet register interface.
 BRANCH_CLK_M4_USB0      =0x065, //clock to the USB0 register interface.
 BRANCH_CLK_M4_EMC       =0x066, //clock to the External memory controller.
 BRANCH_CLK_M4_SDIO      =0x067, //clock to the SD/MMC register interface.
 BRANCH_CLK_M4_DMA       =0x068, //clock to the DMA register interface.
 BRANCH_CLK_M4_M4CORE    =0x069, //clock to the Cortex-M4 core
 BRANCH_CLK_M4_SCT       =0x06D, //clock to the SCT register interface.
 BRANCH_CLK_M4_USB1      =0x06E, //clock to the USB1 register interface.
 BRANCH_CLK_M4_EMCDIV    =0x06F, //clock to the EMC with clock divider.
 BRANCH_CLK_M4_FLASHA    =0x070, //clock to the flash bank A
 BRANCH_CLK_M4_FLASHB    =0x071, //clock to the flash bank B
 BRANCH_CLK_M4_M0APP     =0x072, //clock to the M0APP coprocessor.
 BRANCH_CLK_M4_ADCHS     =0x073, //clock to the ADCHS.
 BRANCH_CLK_M4_EEPROM    =0x074, //clock to the EEPROM
 BRANCH_CLK_M4_WWDT      =0x080, //clock to the WWDT register interface.
 BRANCH_CLK_M4_UART0     =0x081, //clock to the USART0 register interface.
 BRANCH_CLK_M4_UART1     =0x082, //clock to the UART1 register interface.
 BRANCH_CLK_M4_SSP0      =0x083, //clock to the SSP0 register interface.
 BRANCH_CLK_M4_TIMER0    =0x084, //clock to the timer0 register interface and timer0 peripheral clock.
 BRANCH_CLK_M4_TIMER1    =0x085, //clock to the timer1 register interface and timer1 peripheral clock.
 BRANCH_CLK_M4_SCU       =0x086, //clock to the System control unit register interface.
 BRANCH_CLK_M4_CREG      =0x087, //clock to the CREG register interface.
 BRANCH_CLK_M4_RITIMER   =0x0A0, //clock to the RI timer register interface and RI timer peripheral clock.
 BRANCH_CLK_M4_UART2     =0x0A1, //clock to the UART2 register interface.
 BRANCH_CLK_M4_UART3     =0x0A2, //clock to the UART3 register interface.
 BRANCH_CLK_M4_TIMER2    =0x0A3, //clock to the timer2 register interface and timer2 peripheral clock.
 BRANCH_CLK_M4_TIMER3    =0x0A4, //clock to the timer3 register interface and timer3 peripheral clock.
 BRANCH_CLK_M4_SSP1      =0x0A5, //clock to the SSP1 register interface.
 BRANCH_CLK_M4_QEI       =0x0A6, //clock to the QEI register interface and QEI peripheral clock.
 BRANCH_CLK_PERIPH       =0x0C0, //clock to the peripheral bus and the Cortex-M0 subsystem AHB multilayer matrix.
 BRANCH_CLK_PERIPH_CORE  =0x0C2, //clock to the Cortex-M0 subsystem core.
 BRANCH_CLK_PERIPH_SGPIO =0x0C3, //clock to the SGPIO interface.
 BRANCH_CLK_USB0         =0x0E0, //USB0 peripheral clock.
 BRANCH_CLK_USB1         =0x100, //USB1 peripheral clock.
 BRANCH_CLK_SPI          =0x120, //clock to the SPI interface.
 BRANCH_CLK_ADCHS        =0x140, //ADCHS clock.
//CCU2 branch clocks
 BRANCH_CLK_AUDIO        =0x200, //audio system (I2S) clock.
 BRANCH_CLK_UART3        =0x220, //USART3 peripheral clock.
 BRANCH_CLK_UART2        =0x240, //USART2 peripheral clock.
 BRANCH_CLK_UART1        =0x260, //UART1 peripheral clock.
 BRANCH_CLK_UART0        =0x280, //USART0 peripheral clock.
 BRANCH_CLK_SSP1         =0x2A0, //SSP1 peripheral clock.
 BRANCH_CLK_SSP0         =0x2C0, //SSP0 peripheral clock.
 BRANCH_CLK_SDIO         =0x2E0  //SD/MMC peripheral clock.
};

//return: 1 - если соотв.периферия доступна (включена, затактирована).
int PeripheralReady(BRANCH_CLK branchClk)
{
 CCU_CFG_STAT volatile *p = &CCU1.BRANCH[0];
 if (branchClk >> 9) p = &CCU2.BRANCH[0];
 return p[branchClk & B9 - 1].STAT & B0;
}

void PeripheralResetOn(RGU_RST periph)
{
 if (periph == RGU_RST_none) return;
 __DMB();
///  *BITBAND_IO(&RGU.CTRL[periph >> 5], periph & B5 - 1) = 1;

 ///
 u64 q = 1ull << periph | 1ull << RGU_RST_M0APP | 1ull << RGU_RST_M0SUB;
 if (periph >> 5) q >>= 32;
 RGU.CTRL[periph >> 5] = q;

}

void PeripheralResetOff(uint periph)
{
 if (periph == RGU_RST_none) return;
 *BITBAND_IO(&RGU.CTRL[periph >> 5], periph & B5 - 1) = 0;
 __DMB();
}

//Подаёт импульс RESET (или снимает постоянный RESET) на указанную периферию.
//И включает её ветвь тактирования. Не конфигурит базовую частоту!
void PeripheralOn(BRANCH_CLK branchClk, RGU_RST periphRst)
{
 CCU_CFG_STAT volatile *p = &CCU1.BRANCH[0];
 if (branchClk >> 9) p = &CCU2.BRANCH[0];
 p += branchClk & B9 - 1;
 p->CFG = B0;
 while (!(p->STAT & B0));
 if (periphRst == RGU_RST_none) return;
 PeripheralResetOn(periphRst); ///
///  int i = 1;
///  if (periphRst == RGU_RST_M0SUB || periphRst == RGU_RST_M0APP) i = 0;
///  *BITBAND_IO(&RGU.CTRL[periphRst >> 5], periphRst & B5 - 1) = i;
 while (!*BITBAND_IO(&RGU.ACTIVE[periphRst >> 5], periphRst & B5 - 1));
 __DMB();
}

сорри: исходник захламлен, так как проект не был закончен, но периферия работала и так.

 

Почему это? Если сработал WDT, о какой прошивке должна идти речь? Как раз-таки после сброса по WDT загрузчик должен быть пропущен и должно запуститься снова приложение, на мой взгляд.

Что мешает делать после приёма прошивки рестарт по WDT?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что мешает делать после приёма прошивки рестарт по WDT?

Ничего не мешает. Ровно как и ничего не мешает инициализировать указатель стека и перейти сразу на приложение явно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ничего не мешает. Ровно как и ничего не мешает инициализировать указатель стека и перейти сразу на приложение явно.

Какой из указателей? :rolleyes: Про переключение режима CPU конечно забыли?

И про сброс периферии тоже. :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какой из указателей? :rolleyes: Про переключение режима CPU конечно забыли?

И про сброс периферии тоже. :laughing:

А при чем тут указатели стека и режим CPU? Загрузчик обязан обеспечить обновление ПО и (если необходимо) переход на приложение. Приложение не должно знать о существовании загрузчика, поэтому компилируется ровно так же, как и без загрузчика (только поправить ld-скрипт и вначале приложения сместить таблицу векторов. Ну или перед переходом на приложение в загрузчике ее сместить). При старте МК с загрузчиком:

1. Проверяется тип сброса - если программный, то (в моем случае) это только означает, что был запрос обновления ПО из приложения, если нет - JumpToApplicatioin().

2. Инициализируется периферия, необходимая для приема прошивки.

3. Принимается прошивка, сравниваются соответствующие контрольные суммы и т.д.

4. Деинициализируется периферия в случае успешного обновления ПО.

5. JumpToApplication().

 

void HW_JumpToApplication(void)
{
unsigned int pFunction = *((volatile unsigned int *)(APPLICATION_BASE_ADDRESS + 4));
void (*UserApplication)(void) = (void (*)())pFunction;
__set_MSP(*(volatile unsigned int *)APPLICATION_BASE_ADDRESS);
UserApplication();
    
return;
}

О каком переключении CPU идет речь? И о каком стеке? После сброса CPU всегда использует основной стек и находится в режиме потока с привелегированным доступом, поэтому загрузчик инициализирует именно MSP.

 

Внутри целевого прилоежния первым делом сдвигается таблица векторов прерываний (если это не сделал загрузчик) и поехали.

Или Вы о чем?

Изменено пользователем Arlleex

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1. Проверяется тип сброса - если программный, то (в моем случае) это только означает, что был запрос обновления ПО из приложения, если нет - JumpToApplicatioin().

...

Видимо Вы на практике никогда не писали реально работающий в боевых условиях бутлоадер, умеющий обновлять прошивку... :laughing:

Внимание вопрос:

Если после приёма прошивки (и записи её во флешь с установленным флажком "имеется новая прошивка") выполняется Ваш программный сброс, загрузчик делает как Вы написали и переходит к пункту 2 вашего списка.... и в этот момент происходит случайный сброс (например - сработал супервизор питания из-за помехи по питанию). Что получится? Да - прошивка не обновится уже никогда. :laughing:

А если сброс произошёл во время прошивки? А если 100 сбросов произошло во время прошивки? ...подумайте.

Нормально написанный бутлоадер должен после любого типа сброса проверять флаг наличия новой прошивки и начинать прошивку (или продолжать её с момента обрыва) и проводить до тех пор, пока новая прошивка не будет полностью прошита и снят флаг "имеется новая прошивка". И только тогда может выполнять переход на рабочее ПО.

И новая прошивка и флаг "имеется новая прошивка" должны находиться в энергонезависимой памяти.

 

2. Инициализируется периферия, необходимая для приема прошивки.

пункт 2 замечаний:

При таком алгоритме загрузчик должен быть рассчитан на старт как после обычного аппаратного сброса так и после передачи управления из ПО с произвольным состояние регистров/режима процессора и периферии. Т.е. - инициализация его должна быть более сложной. А на кой ляд?? Почему не привести всё к начальному состоянию и упростить инициализацию при старте загрзучика? Так будет гораздо меньше кода (что для загрузчика очень важно, так как он:

а) часто должен быть как можно меньше;

б) должен быть (обязательно!) как можно более простым, чтобы исключить возможность бага в нём, который потом уже невозможно будет устранить удалённой перепрошивкой.

А в вашем случае получается, что стартовое состояние загрузчика постоянно меняется при изменении основного ПО. Сейчас вы загрузчик отладили и он работает (вроде как стабильно), отправили устройства заказчику, а потом сделали новую версию основной прошивки, которая стала инитить периферию немного по-другому или задействовала новую периферию и ... привет! - загрузчик перестал работать, так как заранее не предусмотрели и не отладили его работу со всеми возможными состояниями периферии/регистров на старте. :smile3009:

 

Так что такой метод передачи управления загрузчику допустим разве что в настольно-наколенных поделках. :laughing:

 

О каком переключении CPU идет речь? И о каком стеке? После сброса CPU всегда использует основной стек и находится в режиме потока с привелегированным доступом, поэтому загрузчик инициализирует именно MSP.

...

Или Вы о чем?

О том что Вы передёргиваете. Вы писали о передаче управления бутлоадеру без сброса, только поправив некий SP. На что я Вам написал, что кроме SP надо много чего ещё исправить:

Ничего не мешает. Ровно как и ничего не мешает инициализировать указатель стека и перейти сразу на приложение явно.

А после сброса да - состояние МК и стек известны - кто-ж спорит?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...