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

подключение разных модулей к основной программе в CCS3.3

Проблема такая. Имеется мое устройство и к нему может подключаться большое число других приборов. Процедура обработки и режимы работы при подключении разных приборов различаются. Соответственно требуется и разные программа для них. Программа у меня делится на общую часть характерную для всех приборов и часть для работы с конкретным прибором. Я хочу общую часть программы загружать из флешки которая на плате, а дополнительную часть программы загружать уже после через USB. Вопрос как прикрутить эту дополнительную часть программы к общей? Защить ее во внутренее озу я смогу, а вот как их правильно написать, чтобы обе части нормально между собой работали. Т.е. вопрос в том как правильно пишуться такие вещи.

Пищу в CCS 3.3 под 5509A.

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


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

Вопрос как прикрутить эту дополнительную часть программы к общей? Защить ее во внутренее озу я смогу, а вот как их правильно написать, чтобы обе части нормально между собой работали. Т.е. вопрос в том как правильно пишуться такие вещи.

Я реализовывал такую задачу, и именно на таком процессоре. Делается крайне просто - по какому-то фиксированному адресу в "ядре" располагается таблица переходов, через которую вызываются функции ядра (например подключение подпрограммы к прерыванию, педерача блока по УСБ, подключение подпрограммы в цепочку подпрограмм обработки управляющих запросов от хоста, ну и т.д.). Ну функций у ядра может быть много. В загружаемом модуле - точно также - в самом начале модуля (например) имеются все необходимые точки входа, по которым может вызывать ядро этот модуль. Например начальная инициализация после загрузки, вход по idle из основного цикла, и т.п.

 

Далее, у проекта ядра - эта таблица переходов просто описана как секция с переходами. У проекта модуля - эта секция описана точно так же, но только она не линкуется в выходной файл. То есть линкуется, но не входит в состав таблицы загрузки модуля. Таким образом модуль корректно обращается к ядру, при загрузке его не портя.

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


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

Я реализовывал такую задачу, и именно на таком процессоре. Делается крайне просто - по какому-то фиксированному адресу в "ядре" располагается таблица переходов, через которую вызываются функции ядра (например подключение подпрограммы к прерыванию, педерача блока по УСБ, подключение подпрограммы в цепочку подпрограмм обработки управляющих запросов от хоста, ну и т.д.). Ну функций у ядра может быть много. В загружаемом модуле - точно также - в самом начале модуля (например) имеются все необходимые точки входа, по которым может вызывать ядро этот модуль. Например начальная инициализация после загрузки, вход по idle из основного цикла, и т.п.

 

Далее, у проекта ядра - эта таблица переходов просто описана как секция с переходами. У проекта модуля - эта секция описана точно так же, но только она не линкуется в выходной файл. То есть линкуется, но не входит в состав таблицы загрузки модуля. Таким образом модуль корректно обращается к ядру, при загрузке его не портя.

т.е. основной смысл работать через фиксированные адреса ? а как передавать параметры процедур между модулем и ядром и обратно, также через какой-то один фиксированный адрес? Ведь, если я правильно понял, таблица переходов представляет собой таблицу адресов процедур без параметров.

Вот, например, нам в модуле надо вызвать такую процедуру ядра: proc1(Ptr param1,int param2);

как я понял в модуле мы записываем param1 и param2 по конкретному адресу addr_param и вызываем переходную процедуру ядра proc1_1 (расположенную по фиксированному адресу addr_proc).

proc1_1 извлекает эти параметры из адреса addr_param и уже потом вызывает proc1(Ptr param1,int param2).

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

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


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

а как передавать параметры процедур между модулем и ядром и обратно, также через какой-то один фиксированный адрес? Ведь, если я правильно понял, таблица переходов представляет собой таблицу адресов процедур без параметров.

Нет, не надо параметры по фикс. адресу передавать. Достаточно следовать соглашению о передаче параметров, принятому в C для этого процессора.

Вот, например, нам в модуле надо вызвать такую процедуру ядра: proc1(Ptr param1,int param2);

Просто вызываете "proc1(param1, param2). Дальше все дело линкера.

 

файл "wrappers.asm" в проекте ядра:

        .arms_on
        .cpl_on
        .c54cm_off

        .ref    __TerminalWrite, __TerminalRead,__SendControlData
        .global    _TerminalWrite, _TerminalRead,_SendControlData

        .sect    ".wrappers"
        .vli_off
        
_TerminalWrite:      b    __TerminalWrite
_TerminalRead:      b    __TerminalRead
_SendControlData: b     __SendControlData
        
        .end

 

kernel.cmd:

MEMORY
{
   VECS       (  IX)   : origin = 000200h, length = 00300h
   WRAPPERS   (  IX)   : origin = 000500h, length = 00100h
.......
SECTIONS
{
.......
   .wrappers   > WRAPPERS
......

 

Соответственно из модулей теперь надо вызывать к примеру TerminalWrite, а в ядре описать функцию _TerminalWrite, ну а wrappers обеспечивает переход при вызове первой на вторую.

 

файл "wrappers.asm" в проекте модуля:

        .arms_on
        .cpl_on
        .c54cm_off

        .global    _TerminalWrite, _TerminalRead

        .sect    ".wrappers"
        .vli_off

        
_TerminalWrite:    nop
        nop
        nop
        nop

_TerminalRead:    nop
        nop
        nop
        nop

_SendControlData:
        nop
        nop
        nop
        nop
        
        .end

 

wrappers.h для модуля

short TerminalWrite(short *pData, short size);
short TerminalRead(short *pData, short size, short *cData);
short SendControlData(PCONTROLDATA pData);

 

линкерный cmd для модуля:

MEMORY
{
   VECS       (  IX)   : origin = 000200h, length = 00300h
   WRAPPERS   (  IX)   : origin = 000500h, length = 00100h
......
SECTIONS
{
.......
   .wrappers   > WRAPPERS
}

 

hex-генераторный .cmd для модуля:

debug\firmware.out
-i
-memwidth 8
-serial8
-v5510:2
-e _c_int00
SECTIONS { .text:boot, .cinit:boot, .const:boot}

 

Таким образом модуль линкуется корректно с вызовами нужных функций, (команда "b" и 4 нопа занимают одинаковое место) но в выходной hex-файл модуля секция wrappers не попадает, обеспечивая целостность этой секции в ядре.

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


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

Я реализовывал такую задачу...

С вашей идеей понятно, а вот с реализацией - я не совсем понимаю как это сделать. Поэтому есть следующий вопрос:

У нас в соседнем отделе (тот же проц.) возникла та же проблема. Есть прибор, который имеет "ядро" и несколько "оверлеев". "Оверлей" - этот в моём понятии - тот самый подгружаемый модуль. Их мож. быть несколько, но располагаются они по одним и тем же адресам, т.к. никогда не могут одновременно находиться в процессоре. Как сделать этот "менеджер оверлеев"? Тем более, что у нас эти оверлеи должны переключаться на лету (т.е. в процессе работы, а не в процессе загрузки), хотя и достаточно редко. Проект ядра кладётся во флэш по нулевому сектору. Проет первого оверлея - по первому сектору, проект второго - по второму. Так что я всегда знаю какой оверлей где лежит. Для певого оверлея - линкую его секции в MEMORY { PAGE 0: SARAM... }, для второго оверлея - линкую его секции в MEMORY { PAGE 1: SARAM... } и ядро линкую в MEMORY { PAGE 0: DARAM... }. Теперь из полученного исполняемого proj_name.out файла утилитой hex55 делаю три загрузочных модуля. Один - который содержит секции ядра, второй - секции овелея_1, третий - секции овелея_2. Прошиваю эти модули по нужным секторам во флэш. При загрузке - загружается только "ядро"; если оно соображает, что надо загрузить оверлей_2 - то своим "внутренним" загрузчиком загружает оверлей_2, и вызывает необходимую функцию оверлея_2 (по таблице переходов, или, возможно, напрямую, без таблицы). Далее в какой-то момент "ядро" узнаёт, что надо загрузить оверлей_1, оно своей "внутренней", "ядерной" функцией вызывает загрузчик, который загружает "оверлей_1" и потом вызывает необходимую функцию оверлея_1. И т.д. Естесственно - нельзя вызывать функции первого оверлея из второго.

Я правильно рассуждаю или где-то есть грубые ошибки или подводные камни?

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


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

to SM :

Большое спасибо. Все гениальное просто, я действительно размышлял не в том направлении.

Только мне кажется строчка -e _c_int00 в hex-генераторным .cmd для модуля не нужна. Мне ведь не надо, чтобы модуль сразу начал выполнялся. Мне надо,чтобы он загрузился в память и ждал своего часа.

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


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

Только мне кажется строчка -e _c_int00 в hex-генераторным .cmd для модуля не нужна.

Я использую точку входа как основную функцию модуля - таким образом я передаю ядру, что нужно вызывать у модуля во всех случаях жизни (а уже в зависимости от переданного ей параметра я там разбираюсь, что это было - начальная инициализация, или вызов по idle из основного цикла, или еще что). Ну, соответственно, стартап у модулей я переписал на свой лад, то есть ту самую _c_int00, чтобы она работала в модулях так, как мне надо, в случае вызова по инициализации выполняла С-шную инициализацию, потом main, а потом обязательно возвращалась (родная виснет в бесконечном цикле), ну и чтобы регистры стека не трогала, и прочее критичное. А в случае всех остальных вызовов - выполняла другую функцию, _kernel_call. Исходники рантайм-либы идут с композером, так что это без проблем. Второй вариант был - что _c_int00 это только инициализация, а ф-ция main возвращает ядру адрес таблицы переходов на свои функции, которые должны вызываться из ядра.

 

Мне ведь не надо, чтобы модуль сразу начал выполнялся.

Ну и не выполняйте, а просто в момент загрузки запомните точку входа. А выполните когда нибудь потом. Зато будете знать, что именно выполнять :)

 

Я правильно рассуждаю или где-то есть грубые ошибки или подводные камни?

Правильно. Только вот вовсе не стоит все в одном проекте держать, и потом разбирать его на несколько кусков. Имея такую секцию wrappers с переходами на все ф-ции ядра, можно сделать кажному оверлею свой проект. Ну и все вызовы делать через точку входа. Хранить же оверлеи есть смысл в стандартном формате TI Boot Table, который делает hex55, чтобы ядро его разбирало и грузило, сразу понимая, и где точка входа у оверлея, и куда что загрузить. Соответственно каждый оверлей тогда имеет свои .bss, .data, .const, .cinit и требует инициализации перед первым вызовом.

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


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

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

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

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

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

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

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

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

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

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