Доступ к битам через указатель на структуру или манипулирование битами периферии в gdb

Вначале посмотрим как это делается на arm-none-eabi.

При отладке программ под arm-none-eabi, для просмотра регистров в отладчике
arm-none-eabi-gdb в тексте программы описываю структуру регистра,
объявляю указатель на структуру и присваиваю указателю адрес регистра и вуаля,
в arm-none-eabi-gdb print печатает содержимое регистра и позволяет менять
значения битов и все в человеческом виде.

На msp430 не все так радужно.



На платформе arm-none-eabi делается это так.
Например мне нужен для препарирования регистр TIM3.
Описываем структуру TIM.
В структуре описываем байты к примеру CR1 и CR2

typedef struct cr1_type {
    unsigned CEN:  1;
    unsigned UDIS: 1;
    unsigned URS:  1;
    unsigned OPM:  1;
    unsigned DIR:  1;
    unsigned CMS:  2;
    unsigned ARPE: 1;
    unsigned CKD:  2;
    unsigned Res:  6;
  } cr1;
    typedef struct cr2_type {
    unsigned Res0: 3;
    unsigned CCDS: 1;
    unsigned MMS:  3;
    unsigned TI1S: 1;
    unsigned Res1: 8;
  } cr2;
typedef struct
{
  volatile cr1       CR1;            /*!< TIM control register 1,                      Address offset: 0x00 */
  uint16_t      RESERVED0;       /*!< Reserved,                                                    0x02 */
  volatile cr2       CR2;            /*!< TIM control register 2,                      Address offset: 0x04 */
  uint16_t      RESERVED1;       /*!< Reserved,                                                    0x06 */
  volatile uint16_t SMCR;            /*!< TIM slave Mode Control register,             Address offset: 0x08 */
  uint16_t      RESERVED2;       /*!< Reserved,                                                    0x0A */
  volatile uint16_t DIER;            /*!< TIM DMA/interrupt enable register,           Address offset: 0x0C */
  uint16_t      RESERVED3;       /*!< Reserved,                                                    0x0E */
  volatile uint16_t SR;              /*!< TIM status register,                         Address offset: 0x10 */
  uint16_t      RESERVED4;       /*!< Reserved,                                                    0x12 */
  volatile uint16_t EGR;             /*!< TIM event generation register,               Address offset: 0x14 */
  uint16_t      RESERVED5;       /*!< Reserved,                                                    0x16 */
  volatile uint16_t CCMR1;           /*!< TIM  capture/compare mode register 1,        Address offset: 0x18 */
  uint16_t      RESERVED6;       /*!< Reserved,                                                    0x1A */
  volatile uint16_t CCMR2;           /*!< TIM  capture/compare mode register 2,        Address offset: 0x1C */
  uint16_t      RESERVED7;       /*!< Reserved,                                                    0x1E */
  volatile uint16_t CCER;            /*!< TIM capture/compare enable register,         Address offset: 0x20 */
  uint16_t      RESERVED8;       /*!< Reserved,                                                    0x22 */
  volatile uint32_t CNT;             /*!< TIM counter register,                        Address offset: 0x24 */
  volatile uint16_t PSC;             /*!< TIM prescaler register,                      Address offset: 0x28 */
  uint16_t      RESERVED10;      /*!< Reserved,                                                    0x2A */
  volatile uint32_t ARR;             /*!< TIM auto-reload register,                    Address offset: 0x2C */
  volatile uint16_t RCR;             /*!< TIM  repetition counter register,            Address offset: 0x30 */
  uint16_t      RESERVED12;      /*!< Reserved,                                                    0x32 */
  volatile uint32_t CCR1;            /*!< TIM capture/compare register 1,              Address offset: 0x34 */
  volatile uint32_t CCR2;            /*!< TIM capture/compare register 2,              Address offset: 0x38 */
  volatile uint32_t CCR3;            /*!< TIM capture/compare register 3,              Address offset: 0x3C */
  volatile uint32_t CCR4;            /*!< TIM capture/compare register 4,              Address offset: 0x40 */
  volatile uint16_t BDTR;            /*!< TIM break and dead-time register,            Address offset: 0x44 */
  uint16_t      RESERVED17;      /*!< Reserved,                                                    0x26 */
  volatile uint16_t DCR;             /*!< TIM DMA control register,                    Address offset: 0x48 */
  uint16_t      RESERVED18;      /*!< Reserved,                                                    0x4A */
  volatile uint16_t DMAR;            /*!< TIM DMA address for full transfer register,  Address offset: 0x4C */
  uint16_t      RESERVED19;      /*!< Reserved,                                                    0x4E */
  volatile uint16_t OR;              /*!< TIM option register,                         Address offset: 0x50 */
  uint16_t      RESERVED20;      /*!< Reserved,                                                    0x52 */
} TIM_TypeDef_bit;


В тексте программы объявляем указатель на TIM3.
volatile TIM_TypeDef_bit* p_tim3_bit = (TIM_TypeDef_bit*)TIM3;

В отладчике arm-none-eabi-gdb имеем возможность используя указатель просматривать битовое поле и менять его значение.
p p_tim3_bit->CR1.CEN
$1 = 1

Таймер включен
set p_tim3_bit->CR1.CEN = 0

Отключили таймер

Кроме того, описав такой указатель на структуру можно в тексте программы манипулировать битами регистра не через битовые операции, а напрямую через манипуляции с битами. Оно конечно не всегда имеет смысл, но в ряде случаев вполне себе иеет право на жизнь.
p_tim3_bit->CR1.CEN = 1;// включили таймер
p_tim3_bit->CR1.CEN = 0;//отключили таймер


А вот на архитектуре msp430 подобный пердимонокль не всегда работает.
Например описали структуру порта так
typedef struct port_8 {
    unsigned p0:  1;
    unsigned p1:  1;
    unsigned p2:  1;
    unsigned p3:  1;
    unsigned p4:  1;
    unsigned p5:  1;
    unsigned p6:  1;
    unsigned p7:  1;
  } t_port_8;

В программе объявили указатель и прописали в нем порт P1OUT или P4OUT.

  volatile t_port_8* p_aout = (t_port_8*)(PAOUT_+0);
  volatile t_port_8* p_bout = (t_port_8*)(PBOUT_+0);

Используем указатель для доступа к битам порта так
p_aout->p0 = 1;
	p_bout->p7 = 0;
	delay(10000);
	p_aout->p0 = 0;
	p_bout->p7 = 1;
	delay(10000);


А вот что у нас в ассемблерном выхлопе
Выхлоп msp430-gcc-4.9

	MOV.W	2(R1), R12
	MOV.B	@R12, R13
	BIS.B	#1, R13
	MOV.B	R13, @R12
	MOV.W	@R1, R12
	MOV.B	@R12, R13
	AND.B	#127, R13
	MOV.B	R13, @R12
	MOV.W	#10000, R12
	CALL	#delay
	MOV.W	2(R1), R12
	MOV.B	@R12, R13
	BIC.B	#1, R13
	MOV.B	R13, @R12
	MOV.W	@R1, R12
	MOV.B	@R12, R13
	BIS.B	#-128, R13
	MOV.B	R13, @R12
	MOV.W	#10000, R12
	CALL	#delay


Выхлоп msp430-gcc-4.6.3

        mov	-6(r4), r15
	mov	@r15, r14
	bis	#1, r14
	mov	r14, @r15
	mov	-4(r4), r15
	mov	@r15, r14
	and	#llo(-129), r14
	mov	r14, @r15
	mov	#10000, r15
	call	#delay
	mov	-6(r4), r15
	mov	@r15, r14
	and	#llo(-2), r14
	mov	r14, @r15
	mov	-4(r4), r15
	mov	@r15, r14
	bis	#128, r14
	mov	r14, @r15
	mov	#10000, r15
	call	#delay


Пример экспериментальной поддержки MSP430 в clang-3.5
Экспериментальная потому, что приходится вначале получать LLVM-байт код, и его уже преобразовывать в целевой ассемблерный

.LBB1_1:                                ; %delay.exit4
                                        ;   in Loop: Header=BB1_2 Depth=1
        mov.w   -4(r4), r12
.LBB1_2:                                ; =>This Loop Header: Depth=1
                                        ;     Child Loop BB1_4 Depth 2
                                        ;     Child Loop BB1_7 Depth 2
        bis.b   #1, &514
        and.b   #127, &547
        mov.w   #0, -8(r4)
        mov.w   #0, -6(r4)
        jmp     .LBB1_4
.LBB1_3:                                ; %.lr.ph.i
                                        ;   in Loop: Header=BB1_4 Depth=2
        add.w   -6(r4), -8(r4)
        add.w   #1, -6(r4)
.LBB1_4:                                ; %.lr.ph.i
                                        ;   Parent Loop BB1_2 Depth=1
                                        ; =>  This Inner Loop Header: Depth=2
        cmp.w   #10000, -6(r4)
        jl      .LBB1_3
; BB#5:                                 ; %delay.exit
                                        ;   in Loop: Header=BB1_2 Depth=1
        mov.w   -8(r4), r12
        and.b   #-2, &514
        bis.b   #-128, &547
        mov.w   #0, -4(r4)
        mov.w   #0, -2(r4)
        jmp     .LBB1_7
.LBB1_6:                                ; %.lr.ph.i3
                                        ;   in Loop: Header=BB1_7 Depth=2
        add.w   -2(r4), -4(r4)
        add.w   #1, -2(r4)
.LBB1_7:                                ; %.lr.ph.i3
                                        ;   Parent Loop BB1_2 Depth=1
                                        ; =>  This Inner Loop Header: Depth=2
        cmp.w   #10000, -2(r4)
        jl      .LBB1_6

А вот для сравнения описание структуры для доступа к порту размером в слово

typedef struct port_16 {
    unsigned p0:  1;
    unsigned p1:  1;
    unsigned p2:  1;
    unsigned p3:  1;
    unsigned p4:  1;
    unsigned p5:  1;
    unsigned p6:  1;
    unsigned p7:  1;
    unsigned p8:  1;
    unsigned p9:  1;
    unsigned p10: 1;
    unsigned p11: 1;
    unsigned p12: 1;
    unsigned p13: 1;
    unsigned p14: 1;
    unsigned p15: 1;
  } t_port_16;

Используем в программе так
Объявление указателя

  volatile t_port_16* p_aout = (t_port_16*)(PAOUT_+0);
  volatile t_port_16* p_bout = (t_port_16*)(PBOUT_+0);

Использование в коде

	p_aout->p0 = 1;
	p_bout->p15 = 0;
	delay(10000);
	p_aout->p0 = 0;
	p_bout->p15 = 1;
	delay(10000);

Тут следует помнить, что 15 бит порта B и 7 бит порта 4 у MSP430 одно и то же
Вот выхлоп ассемблера у msp430-gcc-4.9

        MOV.W	2(R1), R12
	MOV.W	@R12, R13
	BIS.W	#1, R13
	MOV.W	R13, @R12
	MOV.W	@R1, R12
	MOV.W	@R12, R13
	AND.W	#32767, R13
	MOV.W	R13, @R12
	MOV.W	#10000, R12
	CALL	#delay
	MOV.W	2(R1), R12
	MOV.W	@R12, R13
	BIC.W	#1, R13
	MOV.W	R13, @R12
	MOV.W	@R1, R12
	MOV.W	@R12, R13
	BIS.W	#-32768, R13
	MOV.W	R13, @R12
	MOV.W	#10000, R12
	CALL	#delay

И вот какой выхлоп ассемблера у msp430-gcc-4.6.3

        mov	-6(r4), r15
	mov	@r15, r14
	bis	#1, r14
	mov	r14, @r15
	mov	-4(r4), r15
	mov	@r15, r14
	and	#32767, r14
	mov	r14, @r15
	mov	#10000, r15
	call	#delay
	mov	-6(r4), r15
	mov	@r15, r14
	and	#llo(-2), r14
	mov	r14, @r15
	mov	-4(r4), r15
	mov	@r15, r14
	bis	#llo(-32768), r14
	mov	r14, @r15
	mov	#10000, r15
	call	#delay

То есть msp430-gcc-4.9 и clang-3.5 видят разницу между структурами размером в байт и размеров в два байта,
а msp430-gcc-4.6.3 разницы не видит и мапит адреса как Бог на душу положит.

Таким образом, если используем msp430-gcc-4.6.3, структура, через которую получаем доступ к битовым полям, должна быть выровнена по двум байтам, иначе через указатель вы получите доступ к левым адресам в памяти.
И похоже лечится это указанием ключа -fpack-struct. Тогда ассемблерный выхлоп такой будет.

        mov	-6(r4), r15
	mov.b	@r15, r14
	bis.b	#1, r14
	mov.b	r14, @r15
	mov	-4(r4), r15
	mov.b	@r15, r14
	and.b	#127, r14
	mov.b	r14, @r15
	mov	#10000, r15
	call	#delay
	mov	-6(r4), r15
	mov.b	@r15, r14
	and.b	#llo(-2), r14
	mov.b	r14, @r15
	mov	-4(r4), r15
	mov.b	@r15, r14
	bis.b	#llo(-128), r14
	mov.b	r14, @r15
	mov	#10000, r15
	call	#delay


Как вариант объявить структуру с атрибутом __attribute__((packed))
typedef struct port_8 {
    unsigned p0:  1;
    unsigned p1:  1;
    unsigned p2:  1;
    unsigned p3:  1;
    unsigned p4:  1;
    unsigned p5:  1;
    unsigned p6:  1;
    unsigned p7:  1;
  } __attribute__((packed)) t_port_8;

Тогда выхлоп ассемблерный будет так же корректный

        mov	-6(r4), r15
	mov.b	@r15, r14
	bis.b	#1, r14
	mov.b	r14, @r15
	mov	-4(r4), r15
	mov.b	@r15, r14
	and.b	#127, r14
	mov.b	r14, @r15
	mov	#10000, r15
	call	#delay
	mov	-6(r4), r15
	mov.b	@r15, r14
	and.b	#llo(-2), r14
	mov.b	r14, @r15
	mov	-4(r4), r15
	mov.b	@r15, r14
	bis.b	#llo(-128), r14
	mov.b	r14, @r15
	mov	#10000, r15


Иначе говоря, что бы в программе манипулировать битами через указатель на структуру, нужно что бы либо указатель был на структуру размером в два байта,, либо структура с атрибутом __attribute__((packed)), либо ключ -fpack-struct использовать с msp430-gcc-4.6.3.

Или просто компилить модуль шлангом или gcc-4.9.

Для манипулирования через указатель на структуру в отладчике, можно вручную поправить адрес в указателе и тогда доступ через байтовую структуру будет работать и в случае использования msp430-gcc-4.6.3.
  • +4
  • 03 ноября 2015, 13:24
  • fr0ster

Комментарии (12)

RSS свернуть / развернуть
mspgcc давно уже obsolete, discontinued, abadoned и not recommended. В смысле, тот, что с саурсфорджа, 4.6. Увы. То, что сделал РедХат выглядит уродливее, да, но хоть как-то поддерживается.

А вот клангом для msp430 надо бы научиться пользоваться, я вообще кланг клюблю больше gcc :)
0
Есть еще mspgcc который TI курирует, он GPL и он новее.
Вот только он с mspdebug не дружит, а что вместо mspdebug взять консольное для unix не знаю :(
А clang конечно интересная вещь, хотя «gcc наше фсе»(C) :)

По поводу clang и msp430 есть статья и перевод.
Пока clang не умеет генерить бинарник ни elf, ни obj напрямую.
Я себе Makefile сделал, и собираю по цепочке c->ll->s->o->elf.
Соответственно c->ll это clang, ll->s это llc, а s->o и o->elf это mspgcc, даже 4.6 справляется :)
CC=msp430-gcc -O
CLANG=clang -O3
LLC=llc
RM=rm
INCLUDE_DIR=/usr/msp430/include/
CFLAGS=-D__MSP430F5529__ -I /usr/msp430/include/
LDFLAGS=-L /usr/msp430/lib/ldscripts/msp430f5529/
MSPFLAGS=-mmcu=msp430f5529
SOURCES=blink.c
ASMFILES=$(LLFILES:.ll=.s)
LLFILES=$(SOURCES:.c=.ll)
OBJECTS=$(ASMFILES:.s=.o)
EXECUTABLE=blink.elf
CTAGS=ctags

#all: $(SOURCES) $(LLFILES) $(ASMFILES) $(EXECUTABLE)
all: $(EXECUTABLE)
        @printf "all\n\r"

$(EXECUTABLE): $(OBJECTS)
        @printf "$(OBJECTS)\n\r"
        @$(CC) $(MSPFLAGS) $(OBJECTS) -o $@

$(OBJECTS): $(ASMFILES)
        @printf "$(ASMFILES)\n\r"
        @$(CC) -c $(ASMFILES) $(MSPFLAGS) -o $@

$(ASMFILES): $(LLFILES)
        @printf "$(LLFILES)\n\r"
        @$(LLC) -march=msp430 $< -o $@

$(LLFILES): $(SOURCES)
        @printf "$(SOURCES)\n\r"
        @$(CLANG) --target=msp430 -S -emit-llvm -c $< $(CFLAGS) -o $@
        @$(CTAGS) -a $<

.c.ll:
        @printf "$<\n\r"
        @$(CLANG) --target=msp430 -S -emit-llvm -c $< $(CFLAGS) -o $@
        @$(CTAGS) -a $<

.ll.s:
        @printf "$<\n\r"
        @$(LLC) -march=msp430 $< -o $@

.s.o:
        @printf "$<\n\r"
        @$(CC) -c $< $(MSPFLAGS) -o $@

clean:
        @printf "clean\n\r"
        @$(RM) -f $(EXECUTABLE) $(OBJECTS) $(ASMFILES) $(LLFILES) tags

tags:
        @printf "tags\n\r"
        @./ctags_with_dep.sh $(SOURCES)
        @$(CTAGS) -a $(SOURCES)

flash:
        @printf "flash\n\r"
        @mspdebug tilib "prog $(EXECUTABLE)"
0
Есть еще mspgcc который TI курирует, он GPL и он новее.
Я его и назвал РедХатовским, потому что по факту его пилят люди из РедХата по заказу ТИ.

Вообще-то для линукса, винды и макоси у него в комплекте есть прокси для gdb, да и gdb тоже там есть внутри в полном комплекте. Я не очень понимаю, что значит, что он не дружит с mspdebug, как вообще связан компилятор и отладчик, можете пояснить? Мне вот, на FreeBSD, сложнее, там нет прокси для gdb, но можно собрать libmsp430.so, которую умеет грузить mspdebug (драйвер «tilib») и с отладочными плдатами оно у меня работало.

Статью про clang я нашёл, но ей больше года, уже clang 3.7 вышел, 3.8 на подходе, надо посмотреть, может там что поменялось. Но встаёт вопрос libc и вообще рантайма в такой ситуации.
0
Грубо говоря есть gdb-клиент и gdb-сервер.
Первый входит в тулчейн и должен понимать бинарник, который тулчейн компилит, а второй должен уметь управлять отладчиком.
Второй это openocd, jlink-gdb-server и mspdebug. Первый это arm-none-eabi-gdb, msp430-elf-gdb.
И оба должны понимать протокол общения.
Так вот, когда я говорю, что mspgcc от TI/RedHat, то это значит протокол общения редхатовско-тишного клиента сломали/допилили/поменяли, или существующий mspdebug не полностью поддерживает протокол общения.
Если запустить mspdebug и подключаться к нему msp430-elf-gdb от TI/RedHat, то после коннекта пишет что-то про нераспознанную строку, полученную от mspdebug и коннект рвется.
В комплекте есть gdb-server вместо mspdebug?

А насчет clang-3.8, уже его можно скачать. Сачал, собрал, по отношению к msp430 пока дело слабо сдвинулось, напрямую бинарник генерить не может пока(а может и не пока).
0
В комплекте есть gdb-server вместо mspdebug?
Да, если качать полный пакет с соглашением с лицензией на сайте и установщиком, а не архив.

Ну а mspdebug, надеюсь, автор починит под новый gdb, он его активно пилит же.
0
Надеюсь mspdebug починят, хотя видимо придется gdb_agent_console использовать.

Не люблю я инсталляхи в линуксе, там обычно свои пакетные системы есть.
0
Да, понимаю :) Вообще пакет этот RH'ом безобразно сделан. Я им жаловался, они ответили «так захотел заказчик, что бы не было зависимостей вообще, так мы и сделали».
0
У меня в винде нашелся gdb-agent, но с ним его родной msp430-elf-gdb тупит и не может ничего сделать.
0
А надо ли напрямую генерить? Лучше уж хороший асм, чем плохой бин
Или нужно, чтобы те же TI/RH подключились к разработке бэкенда
0
Чтобы не мучаться с очередными Res5 и Reserved25 можно опускать их имена, т.е. вместо unsigned Res: 6; писать unsigned: 6; и вместо uint16_t RESERVED19; — uint16_t: 16;
0
хм… я всегда ставлю __packed для всех структур, потому как стандарт не запрещает на каждый бит выделять хоть по байту, хоть по четыре, хоть по 8. а вот атрибут __packed вполне себе указание компилятору, что данные должны быть максимально компактными
0
Оно неспроста выравнивается. Поэтому не рекомендуется паковать структуры без строгой необходимости (таковой являютя, например, структуры, определяющие форматы файлов или сетевых пакетов).
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.