Я работаю над проектом, где некоторая служба прерываний должна обрабатываться на ассемблере. Функция обработчика вызывается из обертки вектора прерывания. Тело обработчика записывается на ассемблере и получает единственный (указатель) параметр в конкретном регистре.
Целью кода является MSP430, и он должен компилироваться как с MSP430-gcc, так и с компилятором TI. У меня уже есть рабочее решение для MSP430-gcc, и оно выглядит так:
static void __attribute__((naked)) _shared_vector_handler(Timer_driver_t *driver) {
__asm__(
" MOVX.W %c[iv_register_offset](R12),R14 ; \n"
" ADD @R14,PC ; \n"
" RETA ; \n"
" JMP CCIFG_1_HND ; Vector 2 \n"
" JMP CCIFG_2_HND ; Vector 4 \n"
" JMP CCIFG_3_HND ; Vector 6 \n"
" JMP CCIFG_4_HND ; Vector 8 \n"
" JMP CCIFG_5_HND ; Vector 10 \n"
" JMP CCIFG_6_HND ; Vector 12 \n"
"TIFG_HND: \n"
" MOVX.A %c[overflow_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n"
" RETA ; \n"
"CCIFG_1_HND: \n"
" MOVX.A %c[ccr1_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n"
" RETA ; \n"
"CCIFG_2_HND: \n"
" MOVX.A %c[ccr2_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n"
" RETA ; \n"
"CCIFG_3_HND: \n"
" MOVX.A %c[ccr3_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n"
" RETA ; \n"
"CCIFG_4_HND: \n"
" MOVX.A %c[ccr4_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n"
" RETA ; \n"
"CCIFG_5_HND: \n"
" MOVX.A %c[ccr5_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n"
" RETA ; \n"
"CCIFG_6_HND: \n"
" MOVX.A %c[ccr6_handle_offset](R12),R14 ; \n"
" MOVX.A %c[handler_param_offset](R14),R12 ; \n"
" MOVX.A %c[handler_offset](R14),R14 ; \n"
" CALLA R14 ; \n" ::
[iv_register_offset] "i" (offsetof(Timer_driver_t, _IV_register)),
[overflow_handle_offset] "i" (offsetof(Timer_driver_t, _overflow_handle)),
[ccr1_handle_offset] "i" (offsetof(Timer_driver_t, _CCR1_handle)),
[ccr2_handle_offset] "i" (offsetof(Timer_driver_t, _CCR2_handle)),
[ccr3_handle_offset] "i" (offsetof(Timer_driver_t, _CCR3_handle)),
[ccr4_handle_offset] "i" (offsetof(Timer_driver_t, _CCR4_handle)),
[ccr5_handle_offset] "i" (offsetof(Timer_driver_t, _CCR5_handle)),
[ccr6_handle_offset] "i" (offsetof(Timer_driver_t, _CCR6_handle)),
[handler_offset] "i" (offsetof(Timer_channel_handle_t, _handler)),
[handler_param_offset] "i" (offsetof(Timer_channel_handle_t, _handler_param)) :
);
}
Переведено на английский: структура драйвера содержит адрес регистра IV для некоторого определенного смещения. Содержимое по этому адресу добавляется в ПК, поэтому происходит переход к определенной метке (в зависимости от того, какой флаг прерывания установлен). Это рекомендуемое использование, как описано TI в руководстве пользователя , стр. 653. Все метки делают то же самое: они получают указатель на некоторый дескриптор из структуры драйвера с определенного смещения. Дескриптор снова имеет некоторый определенный указатель на функцию смещения (обработчик службы прерываний) и указатель на некоторый параметр, который должен быть передан обработчику. Структура вкратце:
typedef struct Timer_driver {
// enable dispose(Timer_driver_t *)
Disposable_t _disposable;
// base of HW timer registers, (address of corresponding TxCTL register)
uint16_t _CTL_register;
...
// interrupt vector register
uint16_t _IV_register;
// stored mode control
uint8_t _mode;
// amount of CCRn registers
uint8_t _available_handles_cnt;
// main (CCR0) handle
Timer_channel_handle_t *_CCR0_handle;
// up to six (CCRn) handles sharing one interrupt vector
Timer_channel_handle_t *_CCR1_handle;
Timer_channel_handle_t *_CCR2_handle;
...
}
1010 * * и
struct Timer_channel_handle {
// vector wrapper, enable dispose(Timer_channel_handle_t *)
Vector_handle_t vector;
// HW timer driver reference
Timer_driver_t *_driver;
// capture / compare control register
uint16_t _CCTLn_register;
// capture / compare register
uint16_t _CCRn_register;
// vector interrupt service handler
void (*_handler)(void *);
// vector interrupt service handler parameter
void *_handler_param;
...
}
Теперь проблема.
- смещения не известны до времени компиляции
- Я не могу передать ассемблеру какое-либо смещение (s, m)
- смещения зависят от используемой модели памяти (размер указателей 16 бит или 32 бита)
- смещения зависят от размера первого члена обеих структур, и этот размер зависит от определений препроцессора (1 указатель или 4 указателя)
- смещения не могут быть предварительно вычислены, потому что каждый компилятор добавляет некоторое выравнивание и отступ к структуре первого члена
- первый участник должен быть первым (изменение порядка не допускается)
- Компилятор TI не поддерживает передачу переменных времени компиляции во встроенный код сборки
Цель:
- поддержка обоих компиляторов
- не дублировать код, не исправлять смещения
- если возможно, избегайте извлечения всего обработчика в файл asm и включая заголовки через .cdecls (или #include в случае gcc). Оба компилятора обрабатывают в том числе заголовки C по-разному, смещения структуры также определяются по-разному, и потребуется некоторая нетривиальная перестройка заголовков, что, я считаю, почти невозможно.
Когда я компилирую это с TI-компилятором, я получаю следующую ошибку:
"../module/driver/src/timer.c", line 274: error #18: expected a ")"
"../module/driver/src/timer.c", line 285: warning #12-D: parsing restarts here after previous syntax error
1 error detected in the compilation of "../module/driver/src/timer.c".
gmake: *** [module/driver/src/timer.obj] Error 1
Моя сборка обрабатывается CMake, и я могу придумать одно решение - просто сгенерировать эти смещения в некоторый заголовочный файл, который должен быть включен в драйвер. Способ как это сделать описан здесь . Но, если возможно, я бы также хотел избежать этого шага, так как он должен компилироваться в Code Composer Studio, который не запускает cmake.
Итак, как мне создать цель CMake для создания этих смещений? Или есть другие идеи?