Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: LPC4337, свой загрузчик. Инициализация SDRAM
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
haker_fox
Добрый день!
Задаю вопрос по просьбе своего коллеги. Они изготовил бутлоадер, который принимает приложение по каналу связи, и записывает его во внутреннюю флеш микроконтроллера. Само приложение 100% работает. Но вот нюанс. И приложение, и бутлоадер инициализируют внешнюю SDRAM, которую каждый использует для себя. И вот на старте инициализации SDRAM приложением проц то-ли улетает куда-то, то-ли зависает. Вопрос: почему такое может происходить. Ведь при повторной инициализации EMC приложением, данных, используемых кем-либо, нет. Это какая-то особенность контроллера памяти?
scifi
Переход из загрузчика в приложение надо делать так:
1) Записать в ОЗУ метку (magic number) - признак перехода в приложение.
2) Программный сброс.
3) При старте загрузчик первым делом проверяет, был ли программный сброс, и есть ли метка.
4) Если да, запускает приложение. Если нет, затирает метку и продолжаем исполнять загрузчик.
Тогда никаких проблем с повторными инициализациями не будет.
Kabdim
У меня логика бута нацелена на исключение возможных косяков с частичной инициализацией переферии бутлоадером.
1. Если вошли в режим записи/замены прошивки бутлоадер имеет полное право настраивать всё как ему нужно.
2. После делается ресет проца.
3. На старте бутлоадера есть маленький кусочек кода который использует буквально немного стека проверяет наличие прошивки и отсутсвие запросов на обновления.
4. Если есть стартуемая прошивка - запускается она, вместо полноценной инициализации бута.
ЗЫ Сайфай оказался чуть быстрее >_<.
haker_fox
Спасибо, господа!!! Передам эти рекомендации. Сам давно бутами не занимался, и тольком не помню, какие там нюансы.

Спасибо, господа!!! Передам эти рекомендации. Сам давно бутами не занимался, и тольком не помню, какие там нюансы.
jcxz
Цитата(scifi @ May 22 2018, 11:01) *
Переход из загрузчика в приложение надо делать так:
...
4) Если да, запускает приложение. Если нет, затирает метку и продолжаем исполнять загрузчик.
Тогда никаких проблем с повторными инициализациями не будет.

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

А можно просто корректно написать инициализацию EMC-контроллера (и остальной периферии) не рассчитывающую, что в регистрах периферии находятся дефолтные значения. По-крайней мере той периферии, которая используется в бутлоадере и основной программе. laughing.gif
У меня в бутлоадере я знаю какую периферию использовал и просто перед передачей управления основной программе делаю RESET для данных периферийных модулей. Благо что возможность подать RESET на конкретный периферийный блок в моём МК имеется (да и в LPC43xx она есть).
scifi
Цитата(jcxz @ May 22 2018, 11:30) *
Не совсем правильно. При такой последовательности действий, если потом, при работе основного приложения, произошёл сброс МК без потери питания (по WDT например) или с кратковременной потерей, то при старте будет пропуск бутлоадера, чего очевидно не должно быть.
Так что метку надо затирать и перед передачей управления из бута в основное ПО, а в основном ПО не использовать это место в ОЗУ.

Ну да, про это запамятовал. Впрочем, как следует подумать, прежде чем хватать клавиатуру и говнокодить, никто не отменял. В конце концов, они там разработчики или где? laughing.gif
haker_fox
QUOTE (scifi @ May 22 2018, 16:45) *
В конце концов, они там разработчики или где? laughing.gif

Какой смысл в ваших этих эмоциях? Был задан конкретный технический вопрос, но никто не просил поучений.
haker_fox
При сбросе EMC модуля через RGU возникает hardfault. Уже перечитали документацию. Ничего не помогает. Нет ли у кого внятного примера, как использовать RGU?
Arlleex
Хм. А я не использую никаких зарезервированных меток в ОЗУ.
Вот мой какой-то и загрузчиков:
Код
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 загрузчик должен быть пропущен и должно запуститься снова приложение, на мой взгляд.
VladislavS
Мне кажется, правильнее всего "добить" код инициализации, чтобы он работал всегда вне зависимости от того как приложение запущено. Сегодня его бутлоадер запустил, завтра отладчик, после завтра с внешней флэшки...
jcxz
Цитата(haker_fox @ May 23 2018, 04:46) *
При сбросе 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 нет.
CODE
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();
}
сорри: исходник захламлен, так как проект не был закончен, но периферия работала и так.

Цитата(Arlleex @ May 23 2018, 08:01) *
Почему это? Если сработал WDT, о какой прошивке должна идти речь? Как раз-таки после сброса по WDT загрузчик должен быть пропущен и должно запуститься снова приложение, на мой взгляд.

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

Ничего не мешает. Ровно как и ничего не мешает инициализировать указатель стека и перейти сразу на приложение явно.
jcxz
Цитата(Arlleex @ May 23 2018, 23:00) *
Ничего не мешает. Ровно как и ничего не мешает инициализировать указатель стека и перейти сразу на приложение явно.

Какой из указателей? rolleyes.gif Про переключение режима CPU конечно забыли?
И про сброс периферии тоже. laughing.gif
Arlleex
Цитата(jcxz @ May 24 2018, 02:20) *
Какой из указателей? rolleyes.gif Про переключение режима CPU конечно забыли?
И про сброс периферии тоже. laughing.gif

А при чем тут указатели стека и режим 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.

Внутри целевого прилоежния первым делом сдвигается таблица векторов прерываний (если это не сделал загрузчик) и поехали.
Или Вы о чем?
jcxz
Цитата(Arlleex @ May 24 2018, 07:58) *
1. Проверяется тип сброса - если программный, то (в моем случае) это только означает, что был запрос обновления ПО из приложения, если нет - JumpToApplicatioin().
...

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

Цитата(Arlleex @ May 24 2018, 07:58) *
2. Инициализируется периферия, необходимая для приема прошивки.

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

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

Цитата(Arlleex @ May 24 2018, 07:58) *
О каком переключении CPU идет речь? И о каком стеке? После сброса CPU всегда использует основной стек и находится в режиме потока с привелегированным доступом, поэтому загрузчик инициализирует именно MSP.
...
Или Вы о чем?

О том что Вы передёргиваете. Вы писали о передаче управления бутлоадеру без сброса, только поправив некий SP. На что я Вам написал, что кроме SP надо много чего ещё исправить:
Цитата(Arlleex @ May 23 2018, 23:00) *
Ничего не мешает. Ровно как и ничего не мешает инициализировать указатель стека и перейти сразу на приложение явно.

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

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

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

Видимо Вы меня не совсем поняли. Из пользовательского ПО я перехожу в bootloader вызовом NVIC_SystemReset(). Поэтому мне не интересно, в каком режиме находится сейчас процессор и что он там выполняет.

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

Теперь тоже так считаете? rolleyes.gif

Цитата
Вы писали о передаче управления бутлоадеру без сброса

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

что контекстно подразумевает, что мы находимся в загрузчике. Речь шла о передаче управления из загрузчика в основную программу.

Итого: из приложения в загрузчик - NVIC_SystemReset(), из загрузчика в приложение - как указал выше JumpToApplication().
Это просто как вариант. Я не говорю что у меня все такие загрузчики. Надежные летают щас даже rolleyes.gif
scifi
Цитата(Arlleex @ May 24 2018, 07:58) *
Код
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;
}

Мне больше нравится вот так:
Код
void jump_to_app(void)
{
    static const uint16_t code[] = {
       0x1d01, // adds r1, r0, #4
       0x6800, // ldr r0, [r0, #0]
       0x4685, // mov sp, r0
       0x6809, // ldr r1, [r1, #0]
       0x4708  // bx r1
    };
    ((void (*)(int))code)(APP_BASE);
}
Arlleex
Цитата
Мне больше нравится вот так:

А какие плюшки от этого?
Вообще конечно из Си-кода менять MSP/PSP не есть комильфо, но с другой стороны не вижу криминала, если в функцию перехода на приложение не передавать никаких параметров.
Цитата
0x1d01, // adds r1, r0, #4
0x6800, // ldr r0, [r0, #0]
0x4685, // mov sp, r0
0x6809, // ldr r1, [r1, #0]
0x4708 // bx r1

Страшно biggrin.gif И не будь тут комментариев, показалось бы ужасным костылем rolleyes.gif Ведь функцию по сути можно было написать чисто на ассемблере (хоть в отдельном .s файле, либо внутри Си-функции инлайн-ассемблером).
scifi
Цитата(Arlleex @ May 24 2018, 10:20) *
А какие плюшки от этого?

Работает везде, используется только стандартный Си.
jcxz
Цитата(scifi @ May 24 2018, 10:01) *
Мне больше нравится вот так:

.h:
Код
#define PFLASH_BEGIN_C      0x08000000 //адрес начала внутренней флешь МК (кэшированная область)
#define FW_WORK_BEGIN       0x00020000 //смещение рабочего ПО (FIRMWARE_TARGET_WORK) относительно начала флешь
extern "C" void StartWorkFw();

.cpp (в бутлоадере):
Код
//деинит/reset использованной в буте периферии и старт рабочего ПО
    IntCpuDis();
    FaultCpuDis();
    IntDisAll();
    //отключение тактирования периферии
    SCU.CCU.CGAT[0].SET = B0 | B1 | B2 | B3 | B4 | B7 | B8 | B9 | B10 | B11 | B16;
    SCU.CCU.CGAT[1].SET = B0 | B3 | B4 | B5 | B6 | B7 | B8;
    SCU.CCU.CGAT[2].SET = B1 | B2 | B4 | B5 | B6 | B7 | B10;
    SCU.CCU.CGAT[3].SET = B2;
    MPUoff();
    StartWorkFw();

.asm:
Код
               SECTION  .text:CODE:NOROOT(2)
               THUMB
               PUBLIC   StartWorkFw
StartWorkFw:   LDR      R0, =PFLASH_BEGIN_C + FW_WORK_BEGIN
               LDRD     R1, R2, [R0]
               MOV      SP, R1
               BX       R2
               LTORG
Arlleex
Цитата(jcxz @ May 24 2018, 12:04) *
.asm:
Код
               SECTION  .text:CODE:NOROOT(2)
               THUMB
               PUBLIC   StartWorkFw
StartWorkFw:   LDR      R0, =PFLASH_BEGIN_C + FW_WORK_BEGIN
               LDRD     R1, R2, [R0]
               MOV      SP, R1
               BX       R2
               LTORG

Ну в общем на вкус и цвет, как говорится. А таблицу векторов где смещаете, кстати? Тоже где-то в boot-е или в основном приложении в начале?
scifi
Цитата(jcxz @ May 24 2018, 11:04) *
Код
               SECTION  .text:CODE:NOROOT(2)
               THUMB
               PUBLIC   StartWorkFw
StartWorkFw:   LDR      R0, =PFLASH_BEGIN_C + FW_WORK_BEGIN
               LDRD     R1, R2, [R0]
               MOV      SP, R1
               BX       R2
               LTORG

Точно!
Код
void jump_to_app(void)
{
    static const uint16_t code[] = {
       0xc806, // ldmia r0!, {r1, r2}
       0x468d, // mov sp, r1
       0x4710  // bx r2
    };
    ((void (*)(int))code)(APP_BASE);
}

Этот код будет работать и на Cortex-M0, в отличие от.
jcxz
Цитата(Arlleex @ May 24 2018, 11:13) *
Ну в общем на вкус и цвет, как говорится. А таблицу векторов где смещаете, кстати? Тоже где-то в boot-е или в основном приложении в начале?

само запускаемое приложение программирует всю периферию как ему надо. Бутлоадер только обеспечивает чтобы после него регистры периферии были в изначальном состоянии.
scifi
Цитата(scifi @ May 24 2018, 11:36) *
Точно!
...
Этот код будет работать и на Cortex-M0, в отличие от.

Поправлю сам себя. Последний штрих:
Код
void jump_to_app(void)
{
    static const uint16_t code[] = {
        0xc806, // ldmia r0!, {r1, r2}
        0x468d, // mov sp, r1
        0x4710  // bx r2
    };
    ((void (*)(int))(1 + (int)code))(APP_BASE);
}

Arlleex
Цитата(scifi @ May 28 2018, 15:05) *
Поправлю сам себя. Последний штрих:
Код
void jump_to_app(void)
{
    static const uint16_t code[] = {
        0xc806, // ldmia r0!, {r1, r2}
        0x468d, // mov sp, r1
        0x4710  // bx r2
    };
    ((void (*)(int))(1 + (int)code))(APP_BASE);
}

ИМХО, лишнее.
Компилятор вроде сам это контролирует (я про режим Thumb).
На ассемблере да, надо следить.

P.S. Может, ошибаюсь.
scifi
Цитата(Arlleex @ May 28 2018, 15:22) *
ИМХО, лишнее.
Компилятор вроде сам это контролирует (я про режим Thumb).
На ассемблере да, надо следить.

P.S. Может, ошибаюсь.

Не лишнее. При тестировании вскрылось.
Arlleex
Цитата(scifi @ May 28 2018, 15:46) *
Не лишнее. При тестировании вскрылось.

Пардон. Я вычитывал слово по нужному адресу, а там было слово из таблицы векторов прерываний (вектор сброса) - а там уже в младшем бите установлена единица.
Интересно, а считается ли Ваш способ наглядным примером самомодифицирующегося кода?
scifi
Цитата(Arlleex @ May 28 2018, 15:52) *
Пардон. Я вычитывал слово по нужному адресу, а там было слово из таблицы векторов прерываний (вектор сброса) - а там уже в младшем бите установлена единица.
Интересно, а считается ли Ваш способ наглядным примером самомодифицирующегося кода?

Мне кажется, вы запутались wacko.gif
Arlleex
Да нет, не запутался.
Говорю же что ошибся насчёт лишней установки младшего бита. Признаю, что устанавливать нужно его.
А насчёт СМК - слышал, но не встречался, поэтому и подумал, а почему бы коду, копирующего самого себя и передающего управление функции в ОЗУ не называться СМК. Вот и все. Поправьте, если не прав.
А, у вас же static const... Все во Flash разместится...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.