Jump to content

    

Размещение инициализации стека в указанной области памяти

Доброго дня.

Делаю bootloader. PIC24F. XC16. MPLAB 5.00.

Увертюра: Архитектура такая, что в одном проекте размещаю bootloader и application. В линкере секции разбиты так, что стирается только определенная область памяти. В эту область записывается приложение, которое разрабатывается в этом же проекте, а не отдельно от загрузчика.

Все, как мне казалось сделал, но не работает. Если залить это же приложение, то работает. Если добавить в приложение хотя бы один Nop(), то перестает работать.

При запуске программы с 0x00 адреса программа переходит не в функцию main(), а инициализирует стек, переменные, возможно, еще что-то. Эти команды формируются автоматически (компилятором) в стираемой мною области программы (во время перепрошивки).

MOV #0x1BBA, W15
MOV #0x6FF0, W14
MOV W14, SPLIM
NOP
MOV #0x0, W0
CP0 W0
BRA Z, CORCON_RESET
MOV #0x10, W0
MOV W0, CORCON
RCALL __psv_init
RCALL __crt_start_mode, __crt_start_mode_normal
CP0 W0
BRA NZ, 0x4072
MOV #0x6F6C, W0
MOV #0x0, W1
BRA 0x4076
MOV #0x0, W0
MOV #0x0, W1
IOR W0, W1, [W15]
BRA Z, 0x407C
RCALL __data_init, __data_init_da
MOV #0x0, W0
CP0 W0
BRA Z, 0x4086
CALL 0x0
NOP
CALL main
NOP

Мне нужно разместить эту часть программы (автоматически формируемую инициализацию) по жестко указанному адресу, т.к. в эту область переходит контроллер из адреса 0x0 при запуске, а код по адресу 0x0 я не меняю при перепрошивке.

Задача: "прибить гвоздями" эту часть кода. Что и куда приписать в линкере?

 

Share this post


Link to post
Share on other sites

RCALL __psv_init
RCALL __crt_start_mode, __crt_start_mode_normal

RCALL __data_init, __data_init_da

и ещё сам код верхнего уровня - тоже название функции надо знать - типа reset_handler, __reset, __crt_reset - map-файл посмотреть.

Про синтаксис компилятора не подскажу, но судя по всему вы знаете что и как делать.

Первый момент. Вам надо в линковщике все эти функции явно объявить внутри секции, относящейся к бутлодеру.

Второй момент. Если вы всё делаете в рамках одного проекта, то у вас секция .bss и .data будут содержать сведения как о загрузкике, так и о приложении. Переписывая приложение, размер занимаемого места секциями может измениться - вы всё равно влиять будете на загрузчик. Чтобы это исключить, вам надо в явном виде все константы и переменные, относящиеся к приложению, определять в коде в секцию приложения. Имхо это слишком большой гемор, лучше сделать два независимых проекта bootloader и app.

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

Других причин создавать загрузчик и приложение в одном проекте я не вижу.

 

 

Share this post


Link to post
Share on other sites
В 18.01.2019 в 13:24, I_am_Lexx сказал:

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

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

Share this post


Link to post
Share on other sites

В целом у меня получился бутлодер. Но не все.

Структуру бутлодера я не выбирал. Делал, как коллега по работе. Но он на Ассемблере пишет с пустого листа. Стек сам инициализирует:

 

mov
mov
mov
#__SP_init,w15
#__SPLIM_init,w0
w0,_SPLIM
; initialize w15
;
; initialize SPLIM

 

У меня на Си стек инициализируется сам. Точно механизм я не знаю. Код приведенный ниже формируется сам с адреса 0x00100. Первые 3 строки - это инициализация стека. И только с строки 12 переход в main().

 

1   00100    226B6F        MOV #0x26B6, W15
2   00102    26FF0E        MOV #0x6FF0, W14
3   00104    88010E        MOV W14, SPLIM
4   00106    200301        MOV #0x30, W1
5   00108    E00001        CP0 W1
6   0010A    320002        BRA Z, 0x110
7   0010C    202001        MOV #0x200, W1
8   0010E    880191        MOV W1, DSRPAG
9   00110    200000        MOV #0x0, W0
10 00112    E00000        CP0 W0
11 00114    320002        BRA Z, 0x11A
12 00116    020000        CALL 0x0
13 00118    000000        NOP
14 0011A    023000        CALL main
15 0011C    000000        NOP
16 0011E    DA4000        BREAK
17 00120    FE0000        RESET
18 00122    FFFFFF         NOPR
 

Как я понял это дело находится по адресу C:\...\pic30\crt0_extended24.s. Кусок кода ниже.

 

;; Initialize stack and PSV window
;; 
;; registers used:  w1
;;  (w0 is cleared by device reset, so ARGC = 0)
;;
;; Inputs (defined by user or linker):
;;  __SP_init         = initial value of stack pointer
;;  __SPLIM_init      = initial value of stack limit register

;; Inputs (defined by linker):
;;  __const_length    = length of section .const
;;  __const_psvpage   = PSVPAG setting for section .const
;; 
;; Outputs:
;;  (does not return - resets the processor)
;; 
;; Calls:
;;  _main
;; 
        .weak    __user_init, __has_user_init

        mov      #__SP_init,w15    ; initialize w15
        mov      #__SPLIM_init,w14

 

Значения __SP_init и __SPLIM_init вычисляются автоматически при компиляции. Системные константы. Это вершина стек и размер стека. При изменении программы они меняются. А мой бутлодер не меняет эту область. Чтобы обойти эту проблему я хочу явно указать начало и размер стека и не менять их от версии к версии.

 

На Ассемблере это можно сделать так.

.section my_stack, stack, address(0x1800) .space 0x100
 

Прошу объяснить, 1) как это можно сделать в Си, учитывая, что код формируется до вызова main() или

2) как указать в линкере, чтобы стек был в нужном месте, нужного размера.

Share this post


Link to post
Share on other sites

Точнее не файл  "C:\...\pic30\crt0_extended24.s"

а исходя из кода линкера "crt1_extended24.o". Т.е. исходники уже скомпилированы.

 

OUTPUT_ARCH("24FJ128GA606")
#if __XC16_VERSION__ > 1027
CRT0_STARTUP(crt0_extended24.o)
CRT1_STARTUP(crt1_extended24.o)
#else
CRT0_STARTUP(crt0_extended.o)
CRT1_STARTUP(crt1_extended.o)
#endif
#if __XC16_VERSION__ > 1027
/*
 * Define how to startup, by default we initialize
 * everything as normal; change to crt_start_mode to
 * preserve preserved data on a restart
 *
 * Or define your own __crt_start_mode fucntion
 */
CRT_STARTMODE(crt_start_mode_normal)
#endif

Share this post


Link to post
Share on other sites

> 1) как это можно сделать в Си, учитывая, что код формируется до вызова main()

Вам надо написать функцию reset handler (точное имя определено в crt..s). Вы её можете написать на Си. Или возьмите исходник crt ассемблерный и под себя его перепишите.

> 2) как указать в линкере, чтобы стек был в нужном месте, нужного размера.

Вы же уже ответили сами строчкой выше: .section my_stack, stack, address(0x1800) .space 0x100

Определите глобальные константы исходя из этой секции, аналогичные __SP_init и __SPLIM_init - и используйте свои константы в своём файле инициализации.

в gcc это всё можно задать .ld файлом, в keil - scatter. Для pic надо мануалы курить, наверняка тоже есть нечто аналогичное.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now