воскресенье, 30 августа 2009 г.

Разбор сообщений IAR icc430 в emacs

Существующий в моей версии (22.0.92) шаблон для разбора сообщений компилятора IAR не работает.
Исправил следующим образом (в .emacs):

;; To parse "IAR Embedded workbench 5.3 for MSP430" messages
(defun my-compile-setup ()
"Installs new regexps to compilation-error-regexp-alist"

;;Store new item "iar-msp430" into compilation-error-regexp-alist variable
(setq compilation-error-regexp-alist
(cons 'iar-msp430 (cons 'iar-msp430-include compilation-error-regexp-alist)))
;; Store iar-msp430 regexp into compilation-error-regexp-alist-alist variable
(setq compilation-error-regexp-alist-alist
(cons
'(iar-msp430
"^[^\n]+\n[ \t]+[\\^]\n\"\\(.*\\)\",\\([0-9]+\\)\\s-+\\(?:Fatal error\\|Error\\|Warnin\\(g\\)\\)\\[[a-zA-Z]+[0-9]+\\]:"
1 2 nil (3))
(cons
'(iar-msp430-include
"^[ ]+\"\\(.+\\)\", line \\([0-9]+\\)"
1 2)
compilation-error-regexp-alist-alist)))
)

(defvar my-compile-eval-after-form
'(progn (my-compile-setup))
"*Form executed when file is loaded.")

;; Install it
(eval-after-load "compile" my-compile-eval-after-form)
[...]

понедельник, 8 июня 2009 г.

GNU make для IAR EW

Ура! Сегодня соединились настроение и свободное время, чтобы поковырять компилятор IAR на предмет его использования из GNU make.

Вот за что люблю make, так это за то, что с его помощью решаются самые неожиданные задачи. И уж тем более, задача автоматизации компиляции ;)
Пропуская описание многочасового процесса чтения доки на компоненты IAR EW, сразу скажу, что у меня всё получилось (ну кто бы сомневался :).
Самые большие неудобства (и, соответственно, самые активные пляски с бубном) возникли из-за наличия пробелов в имени директории инсталляции IAR. Итак, ближе к телу.
Создаем makefile, прописываем исходные файли и #include-директории тестового проекта и параметры платформы:
project = test
test_include =
test_sources = $(wildcard *.c)

# IAR language extension
enable_iar_extensions = 1
# use hardware multiplier, varinats: 16,16s,32
# use_hwmul = 32
# the location of the hardware multiplier in hex (defaults to 0130)
# hwmul_location =
# use new runtime, runtime variants: normal, full
use_dlib = normal
# cpu type (case-sensitive)
cpu = MSP430F5438
# CPU core variant: 430 or 430X
cpu_core = 430X
# data model for 430X: small, medium, large
data_model = medium
# the size of the double floating point type (32 or 64)
# dfloat_size = 32
# enable generation of position independent code (yes or no)
# enable_pic = no
# stack and heap sizes
# stack_size = 160
# heap_size = 160
# heap20_size = 160
NOTE: Закомментированные опции не используются в данном примере чтобы оставить код более простым и легко обозримым. В полной реализации я уложился в триста с небольшим строк.
В качестве runtime-библиотеки выбираем рекомендованную DLib (CLib признана устаревшей и оставлена для совместимости). Юмор с переходом от CLib к DLib, по-моему, весьма оригинален, так что ему будет уделено несколько дополнительных строчек в makefile.
Для используемой платформы на MSP430F5438 выбираются вариант ядра 430X и модель данных medium. На основе этих данных далее вычисляется имя dlib-конфигурации (в данном случае испольуется её версия normal).

Для сборки в Windows определяем используемые команды и некоторые дополнительные функции:
ECHO   = echo
RM = erase /F /S >nul
RMDIR = rmdir
RMTREE = rmdir /Q /S
MKDIR = mkdir

fix_pathname = $(subst /,\,$(1))
mk_dir = cmd /c if not exist $(1) $(MKDIR) $(1)
rm_dir = cmd /c if exist $(1) $(RMTREE) $(1)

host_exe_ext = .exe
nonfs_char="^"

# service functions
empty=
space=$(empty) $(empty)
encode_path = $(subst $(space),$(nonfs_char),$(1))
decode_path = $(subst /,\,$(subst $(nonfs_char),$(space),$(1)))
Так как make плохо переварирвает пробелы в именах файлов и, как было сказано ранее, IAR по умолчанию ставится в такие директории, используется экранирование пробелов. Собственно, функции encode_path и decode_path и выполняют замену пробелов и восстановление исходного вида.

Теперь самое время сказать о необходимых телодвижениях.
Скрипт сборки подразумевает, что каталог инсталляции IAR хранится в переменной TOOLKIT_DIR. Поэтому следует добавить её определение в переменные среды или указать непосредственно перед вызовом make.
ARCH ?= 430
ifndef TOOLKIT_DIR
$(error TOOLKIT_DIR not defined!)
endif
Защищаемся на случай, если TOOLKIT_DIR не определёна и вводим переменную архитектуры - ARCH.
В соответствии с определённым TOOLKIT_DIR, определяем все зависящие переменные:
# IAR paths
IAR_DIR = $(call encode_path,$(TOOLKIT_DIR))/$(ARCH)
IAR_BIN = $(IAR_DIR)/bin
IAR_INCLUDE = $(IAR_DIR)/inc
IAR_LIB = $(IAR_DIR)/lib
IAR_CFG = $(IAR_DIR)/config

# IAR binaries
CC = $(call decode_path,"$(IAR_BIN)/icc$(ARCH)$(host_exe_ext)")
AR = $(call decode_path,"$(IAR_BIN)/xar$(host_exe_ext)")
AS = $(call decode_path,"$(IAR_BIN)/a$(ARCH)$(host_exe_ext)")
LD = $(call decode_path,"$(IAR_BIN)/xlink$(host_exe_ext)")
CXX = $(CC)
Пути к стандартным библиотекам и расширения файлов:
# include path for old C runtime
clib_include = $(IAR_INCLUDE)/clib
clib_lib = $(IAR_LIB)/clib
# include path for C/C++ runtime
dlib_include = $(IAR_INCLUDE)/dlib
dlib_lib = $(IAR_LIB)/dlib

# FIXME: ARCH-specific file extensions
obj_ext = .r43
exe_ext = .d43

c_ext = .c
cc_ext = .cc
asm_ext = .s43
Для разделения исходных текстов и результатов компиляции назначаем отдельный путь для сборки:
# root of project build tree
build_dir = build.$(build_type).$(ARCH)
Выполняем проверку переданных параметров, например, вот так:
# check and parse CPU core variant
cpu_core_list = 430 430X
ifdef cpu_core
cpu_core := $(strip $(cpu_core))
ifeq ($(filter $(cpu_core),$(cpu_core_list)),)
$(error Invalid CPU core variant: $(cpu_core). Available: $(cpu_core_list))
endif
else
cpu_core = $(firstword $(cpu_core_list))
endif
(Остальные подобные проверки опущены для простоты).
Проверяем корректность назначенной C-runtime и формируем имя кофигурации dlib (clib аналогично, чуть проще):
# check C/C++ runtime variant
ifdef use_dlib
use_dlib_list = none normal full
use_dlib := $(strip $(use_dlib))
ifeq ($(filter $(use_dlib),$(use_dlib_list)),)
$(error Invalid runtime configuration: $(use_dlib). Available: $(use_dlib_list))
endif
ifeq ($(use_dlib),none)
use_dlib =
else
# build name of dlib configuration
# add cpu core variant
dlib_config = dl
ifeq ($(cpu_core),430)
dlib_config += 430
else
dlib_config += 430x
# add data model for 430x
ifeq ($(data_model),small)
dlib_config += s
endif
ifeq ($(data_model),medium)
dlib_config += m
endif
ifeq ($(data_model),large)
dlib_config += l
endif
endif # cpu_core
# add double float size
ifeq ($(dfloat_size),32)
dlib_config += f
else
dlib_config += d
endif
# add dlib model suffix
ifeq ($(use_dlib),normal)
dlib_config += n
else
dlib_config += f
endif
# add PIC suffix
ifneq ($(enable_pic),)
dlib_config += p
endif
dlib_config := $(subst $(space),,$(dlib_config))
# check for library config exist (special decoding for function 'wilcard')
wilcard_dlib_config := $(subst $(space),\$(space),$(call decode_path,$(dlib_lib)/$(dlib_config).h))
ifeq ($(wildcard $(wilcard_dlib_config)),)
$(error DLib configuration "$(wilcard_dlib_config)" not exist)
endif
endif
endif
Интересным моментом является проверка существования файла сформированной конфигурации: для получения строки, пригодной для wildcard, используется экранирование пробелов символом '\'. Конечно, можно проверку опустить и предоставить компилятору сказать об этом.

Формирование корректного имени xcl-файла и определения типа процессора, вычисление списка include-директорий:
# linker CPU-specific command file
xcl_file = lnk$(subst MSP,,$(cpu)).xcl
# CPU-specific defines
defines = __$(cpu)__

sys_include = $(IAR_INCLUDE) $(if $(use_dlib),$(dlib_include),$(clib_include))
includes = $(sys_include) $($(project)_include)
Для проекта формируем имена объектных файлов и целевого файла:
$(project) = $(build_dir)/$(project)$(exe_ext)
objs = $(patsubst %,$(build_dir)/%$(obj_ext),$(basename $(sources)))
Флаги для компилятора и линкера:
IAR_CFLAGS = $(foreach dir,$(includes),-I $(call decode_path,"$(dir)")) \
$(foreach def,$(defines),-D$(def)) \
--core $(cpu_core) \
$(if $(enable_iar_extensions),-e) \
$(if $(dlib_config),--dlib_config $(call decode_path,"$(dlib_lib)/$(dlib_config).h")) \
$(if $(data_model),--data_model $(data_model)) \
-Ol \
--debug \
--no_wrap_diagnostics --only_stdout --silent

IAR_CXXFLAGS = --eec++ $(IAR_CFLAGS)

IAR_LDFLAGS = \
-D_STACK_SIZE=$(stack_size) -D_DATA16_HEAP_SIZE=$(heap_size) -D_DATA20_HEAP_SIZE=$(heap20_size) \
-S -L -xs -xn -xe -r -s __program_start \
-f $(call decode_path,"$(IAR_CFG)/$(xcl_file)") \
-C $(if $(dlib_config),$(call decode_path,"$(dlib_lib)/$(dlib_config)$(obj_ext)"))
Для простоты показана сборка отладочной версии. Важно не забыть корректное значение для --dlib_config и указать линкеру размеры стека/хипа и точки старта.

Наконец, собственно правила сборки. Они достаточно просты:
.PHONY: all clean

all: $($(project))
@$(ECHO) Done.

clean:
@$(ECHO) (RM) $(build_dir)/
-@$(call rm_dir,$(build_dir))

$(build_dir)/%$(obj_ext): %.c
@$(call mk_dir,$(call decode_path,"$(@D)"))
@$(ECHO) (CC) $(<F)
@$(CC) $(IAR_CFLAGS) --output $@ $^

$(build_dir)/%$(obj_ext): %.cc
@$(call mk_dir,$(call decode_path,"$(@D)"))
@$(ECHO) (CXX) $(<F)
@$(CC) $(IAR_CXXFLAGS) --output $@ $^

$($(project)): $(objs)
@$(ECHO) (LD) $(@F)
@$(LD) $(IAR_LDFLAGS) -o $@ $^

На этом приготовления закончены и можно выполнять сборку:
c:\home\mike\iar-test>make clean all
(RM) build.debug.430/
(CC) main.c
(LD) test.d43
Done.
c:\home\mike\iar-test>

Создаем в IAR IDE проект "Externally built executable", включаем туда получившийся test.d43, удаляем readme.txt (предварительно его прочитав), выставляем настройки в разделах "General options" (пункт "Device") и "Debugger" и запускам отладку!
[...]

воскресенье, 17 мая 2009 г.

Разбираясь с примерами

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

Продолжаю ковырять MSP-EXP430F5438.
Чтобы быстрее разобраться, кроме чтения доки смотрю на многочисленные "Application Note" от TI.
Единственный (из найденного) пример программирования моей платы - slac227, остальные в большинстве своем не рассчитаны на архитектуру F5 вообще. Поэтому, я приобретаю бесценный опыт, портируя разные интересные AN под свою платку.
Итак, обещанный пример.
В swra141 сделана попытка реализовать HAL. Закрывающий, в частности, работу с отдельными пинами GPIO (файл hal_digio.c).
Вот отрывок реализации:

static ISR_FUNC_PTR port1_isr_tbl[8] = {0};
static ISR_FUNC_PTR port2_isr_tbl[8] = {0};

// ...

uint8 halDigioIntConnect(const digioConfig *p, ISR_FUNC_PTR func)
{
istate_t key;
HAL_INT_LOCK(key);
switch (p->port)
{
case 1: port1_isr_tbl[p->pin] = func; break;
case 2: port2_isr_tbl[p->pin] = func; break;
default: HAL_INT_UNLOCK(key); return(HAL_DIGIO_ERROR);
}
halDigioIntClear(p);
HAL_INT_UNLOCK(key);
return(HAL_DIGIO_OK);
}

// ...

#pragma vector=PORT1_VECTOR
__interrupt void port1_ISR(void)
{
register uint8 i;
if (P1IFG)
{
for (i = 0; i < 8; i++)
{
register const uint8 pinmask = 1 << i;
if ((P1IFG & pinmask) && (P1IE & pinmask) && (port1_isr_tbl[i] != 0))
{
(*port1_isr_tbl[i])();
P1IFG &= ~pinmask;
}
}
__low_power_mode_off_on_exit();
}
}

// ...
Здесь после адаптации у меня получились немотивированные вызовы (суб-)обработчиков прерывания. И хотя причина такого поведения была в другом, я захотел взглянуть собственно на реализацию первичных обработчиков.
Первое что пришло в голову - "ЭТО - embedded код"???
Начнем по порядку.
1. "register uint8 i;"
У процессора 12 регистров общего назначения, неужели есть подозрение, что компилятор всё-таки воспользуется стеком для локальной переменной?
2. "register const uint8 pinmask = 1 << i;"
Вместо выноса операции из цикла разработчик попросил компилятор поместить переменную в регистр и... см. замечание 1.
3. Откуда уверенность в невозможности возникновения прерывания с неинициализированной функцией-обработчиком? Если такое случится, то в отсутствие "P1IFG &= ~pinmask;" прерывание будем обрабатывать бесконечно.

Исправляя очевидное, пишем код для второго обработчика (для сравнения):

#pragma vector=PORT2_VECTOR
__interrupt void port2_ISR(void)
{
if (P2IFG) {
uint8 pinmask;
ISR_FUNC_PTR *isr = port2_isr_tbl;

for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
if (P2IFG & P2IE & pinmask) {
if (*isr)
(*isr)();
P2IFG &= ~pinmask;
}
}
__low_power_mode_off_on_exit();
}
}
Ухудшило ли такое преобразование понимание алгоритма?
По-моему нет.

Ну и, раз уж пошла такая пьянка, посмотрим на получившийся код (port1_ISR - результат компиляции для исходного алгоритма).

port1_ISR:
005C30 145F pushm.a #6,R15
if (P1IFG)
005C32 93C2 021C tst.b &PAIFG_L
005C36 2422 jeq 0x5C7C
for (i = 0; i < 8; i++)
005C38 434B clr.b R11
005C3A 3C1B jmp 0x5C72
register const uint8 pinmask = 1 << i;
005C3C 431C mov.w #0x1,R12
005C3E 4B4E mov.b R11,R14
005C40 13B0 6B1C calla #?ShiftLeft16
005C44 4C4A mov.b R12,R10
if ((P1IFG & pinmask) && (P1IE & pinmask) && (port1_isr_tbl[i] != 0))
005C46 BAC2 021C bit.b R10,&PAIFG_L
005C4A 2412 jeq 0x5C70
005C4C BAC2 021A bit.b R10,&PAIE_L
005C50 240F jeq 0x5C70
005C52 4B4E mov.b R11,R14
005C54 064E rlam.a #2,R14
005C56 1800 4E5E 1C7C movx.a 0x1C7C(R14),R14
005C5C 03DE tsta R14
005C5E 2408 jeq 0x5C70
(*port1_isr_tbl[i])();
005C60 4B4E mov.b R11,R14
005C62 064E rlam.a #2,R14
005C64 1800 4E5E 1C7C movx.a 0x1C7C(R14),R14
005C6A 134E calla R14
P1IFG &= ~pinmask;
005C6C CAC2 021C bic.b R10,&PAIFG_L
for (i = 0; i < 8; i++)
005C70 535B inc.b R11
for (i = 0; i < 8; i++)
005C72 927B cmp.b #0x8,R11
005C74 2BE3 jnc 0x5C3C
__low_power_mode_off_on_exit();
005C76 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005C7C 165A popm.a #6,R15
005C7E 1300 reti

__interrupt void port2_ISR(void)
{
port2_ISR:
005C80 145F pushm.a #6,R15
if (P2IFG) {
005C82 93C2 021D tst.b &P2IFG
005C86 2419 jeq 0x5CBA
ISR_FUNC_PTR *isr = port2_isr_tbl;
005C88 008B 1C9C mova #0x1C9C,R11
for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
005C8C 435A mov.b #0x1,R10
005C8E 3C10 jmp 0x5CB0
if (P2IFG & P2IE & pinmask) {
005C90 425E 021D mov.b &P2IFG,R14
005C94 F25E 021B and.b &P2IE,R14
005C98 BA4E bit.b R10,R14
005C9A 2407 jeq 0x5CAA
if (*isr)
005C9C 0B0E mova @R11,R14
005C9E 03DE tsta R14
005CA0 2402 jeq 0x5CA6
(*isr)();
005CA2 0B0E mova @R11,R14
005CA4 134E calla R14
P2IFG &= ~pinmask;
005CA6 CAC2 021D bic.b R10,&P2IFG
for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
005CAA 5A4A rla.b R10
005CAC 00AB 0004 adda #0x4,R11
for (pinmask = 0x01; pinmask; pinmask += pinmask, ++isr) {
005CB0 934A tst.b R10
005CB2 23EE jne 0x5C90
__low_power_mode_off_on_exit();
005CB4 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005CBA 165A popm.a #6,R15
005CBC 1300 reti
Компилятор, естественно, заменил цикл for на цикл с постусловием а сложение - сдвигом.
Учитывая это, попробуем неочевидную оптимизацию:

__interrupt void port2_ISR(void)
{
if (P2IFG) {
uint8 pinmask = 0x01;
ISR_FUNC_PTR *isr = port2_isr_tbl;

do {
if (P2IFG & P2IE & pinmask) {
if (*isr)
(*isr)();
P2IFG &= ~pinmask;
}
++isr;
} while (pinmask <<= 1);
__low_power_mode_off_on_exit();
}
}
Результат:

__interrupt void port2_ISR(void)
{
port2_ISR:
005C80 145F pushm.a #6,R15
if (P2IFG) {
005C82 93C2 021D tst.b &P2IFG
005C86 2418 jeq 0x5CB8
uint8 pinmask = 0x01;
005C88 435A mov.b #0x1,R10
ISR_FUNC_PTR *isr = port2_isr_tbl;
005C8A 008B 1C9C mova #0x1C9C,R11
if (P2IFG & P2IE & pinmask) {
005C8E 425E 021D mov.b &P2IFG,R14
005C92 F25E 021B and.b &P2IE,R14
005C96 BA4E bit.b R10,R14
005C98 2407 jeq 0x5CA8
if (*isr)
005C9A 0B0E mova @R11,R14
005C9C 03DE tsta R14
005C9E 2402 jeq 0x5CA4
(*isr)();
005CA0 0B0E mova @R11,R14
005CA2 134E calla R14
P2IFG &= ~pinmask;
005CA4 CAC2 021D bic.b R10,&P2IFG
++isr;
005CA8 00AB 0004 adda #0x4,R11
} while (pinmask <<= 1);
005CAC 5A4A rla.b R10
005CAE 934A tst.b R10
005CB0 23EE jne 0x5C8E
__low_power_mode_off_on_exit();
005CB2 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005CB8 165A popm.a #6,R15
005CBA 1300 reti
И в завершение, включаем оптимизацию:

__interrupt void port1_ISR(void)
{
?cstart_end:
port1_ISR:
005C28 147F pushm.a #8,R15
if (P1IFG)
005C2A 93C2 021C tst.b &PAIFG_L
005C2E 241B jeq 0x5C66
for (i = 0; i < 8; i++)
005C30 434B clr.b R11
005C32 4038 1CE4 mov.w #0x1CE4,R8
register const uint8 pinmask = 1 << i;
005C36 431C mov.w #0x1,R12
005C38 4B4E mov.b R11,R14
005C3A 13B0 82AE calla #?ShiftLeft16
005C3E 4C4A mov.b R12,R10
if ((P1IFG & pinmask) && (P1IE & pinmask) && (port1_isr_tbl[i] != 0))
005C40 BCC2 021C bit.b R12,&PAIFG_L
005C44 2409 jeq 0x5C58
005C46 BCC2 021A bit.b R12,&PAIE_L
005C4A 2406 jeq 0x5C58
005C4C 080E mova @R8,R14
005C4E 03DE tsta R14
005C50 2403 jeq 0x5C58
(*port1_isr_tbl[i])();
005C52 134E calla R14
P1IFG &= ~pinmask;
005C54 CAC2 021C bic.b R10,&PAIFG_L
for (i = 0; i < 8; i++)
005C58 535B inc.b R11
005C5A 5228 add.w #0x4,R8
for (i = 0; i < 8; i++)
005C5C 927B cmp.b #0x8,R11
005C5E 2BEB jnc 0x5C36
__low_power_mode_off_on_exit();
005C60 C0B1 00F0 0020 bic.w #0xF0,0x20(SP)
}
005C66 1678 popm.a #8,R15
005C68 1300 reti
__interrupt void port2_ISR(void)
{
port2_ISR:
005C6A 145F pushm.a #6,R15
if (P2IFG) {
005C6C 93C2 021D tst.b &P2IFG
005C70 2415 jeq 0x5C9C
uint8 pinmask = 0x01;
005C72 435A mov.b #0x1,R10
ISR_FUNC_PTR *isr = port2_isr_tbl;
005C74 403B 1D04 mov.w #0x1D04,R11
if (P2IFG & (P2IE & pinmask)) {
005C78 425E 021D mov.b &P2IFG,R14
005C7C F25E 021B and.b &P2IE,R14
005C80 BA4E bit.b R10,R14
005C82 2406 jeq 0x5C90
if (*isr)
005C84 0B0E mova @R11,R14
005C86 03DE tsta R14
005C88 2401 jeq 0x5C8C
(*isr)();
005C8A 134E calla R14
P2IFG &= ~pinmask;
005C8C CAC2 021D bic.b R10,&P2IFG
++isr;
005C90 522B add.w #0x4,R11
} while (pinmask <<= 1);
005C92 5A4A rla.b R10
005C94 23F1 jne 0x5C78
__low_power_mode_off_on_exit();
005C96 C0B1 00F0 0018 bic.w #0xF0,0x18(SP)
}
005C9C 165A popm.a #6,R15
005C9E 1300 reti
В исходной функции компилятор так и не заменил в цикле сдвиг (хотя, наверное, и мог бы это сделать) и не догадался упростить выражение с проверкой маски. Вычисление индекса таблицы в цикле заменено на приращение указателя, но приращение и проверка индекса никуда не делись.
Пример с обработчиком прерывания - один из многих. Пусть написать код с первого раза не получается, но после написания потратить пять минут на ревизию одного цикла - разве долго? Или кодописатели в TI способны только добавить "register"?
[...]

Новая платформа

Начал недавно разработку под MSP430. Так как в этой платформе я новичок, начал с поиска приемлемых для себя средств разработки и отладки.

Первое, что неприятно поразило - отсутствие полноценной поддержки платформы gcc.

Да, конечно, существует mspgcc, поддержкой которого занимается небольшая команда энтузиастов, НО...
Захожу на из форум. Выясняю, что собственно развитием компилятора занимаются 2-3 человека. Остальные - группа поддержки. Результат, естественно, вызывает слезу умиления: текущая версия - 3.2 (судя по всему, это первый и последний рабочий порт gcc для платформы), поддержка архитектуры MSP430X - в зачаточном состоянии.
Средства прошивки и отладки в составе пакета mspgcc - на уровне платформ трехлетней давности, для новых архитектур просто не работают.
Усугубляет состояние позиция TI: поддерживаются только средства разработки под Windows и только проприетарные.
Получается как известной поговорке - "рыба ищет где глубже, а человек - где рыба". Коммерческие разработчики, не имеющие времени на разборки с тем, почему требуемая им платформа не поддерживается в mspgcc, используют проприетарные средства.
Результат - "Code Composer Essentials" и "IAR Embedded Workbench"доминируют, группа поддержки mspgcc практически не увеличивается.

Помыкавшись с попытками использовать средства mspgcc для своей MSP-EXP430F5438, оставил себе задачу разобраться с ним на будущее, а пока переключился на IAR. После emacs использовать IAR Workbench IDE для меня - пытка, поэтому пока загружаю туда проект для компиляции/прошивки/отладки, а в качестве редактора продолжаю использовать emacs. Если совсем IDE осточертеет - попробую прикрутить IAR-тулзы к GNU utils (если раньше не настрою mspgcc).
[...]