Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Компиляция ARM GCC, куда пропадают циклы while?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
venomsoldier
Начал изучать арм, написал функцию:

Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    while(*((uint32_t *)(0x2000B898))&0x80000000);
    *((uint32_t *)(0x2000B8A0))=val+channel;
}

Функция читает регистр статуса по адресу 0x2000B898 и ждет нолика в старшем бите, после этого записывает в 0x2000B8A0 значения.

Компилятор версии ARM/GNU C Compiler 4.7.3, шел в комплекте с Atmel Studio 6.1 (папка с именем 4.7.3.1029)

Опции компилятора использую следующие:
Код
-marm -DBCM2835 -DDEBUG  -O0 -ffunction-sections -mlong-calls -g2 -Wall -mcpu=arm1176jzf-s -c -std=gnu99 -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"

Опции линкера:
Код
-marm -nostartfiles -nodefaultlibs -nostdlib -Wl,-Map="$(OutputFileName).map" -Wl,--start-group -lm  -Wl,--end-group -L"../cmsis/linkerScripts"  -Wl,--gc-sections -mcpu=arm1176jzf-s


Компилирую с оптимизацией 0 (-O0), получаю листинг:
Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    8028:    e52db004     push    {fp}; (str fp, [sp, #-4]!)
    802c:    e28db000     add    fp, sp, #0
    8030:    e24dd00c     sub    sp, sp, #12
    8034:    e50b0008     str    r0, [fp, #-8]
    8038:    e1a03001     mov    r3, r1
    803c:    e54b3009     strb    r3, [fp, #-9]
    while(*((uint32_t *)(0x2000B898))&0x80000000);
    8040:    e1a00000     nop; (mov r0, r0)
    8044:    e59f3028     ldr    r3, [pc, #40]; 8074 <MailboxWrite+0x4c>
    8048:    e5933000     ldr    r3, [r3]
    804c:    e3530000     cmp    r3, #0
    8050:    bafffffb     blt    8044 <MailboxWrite+0x1c>
    *((uint32_t *)(0x2000B8A0))=val+channel;
    8054:    e59f301c     ldr    r3, [pc, #28]; 8078 <MailboxWrite+0x50>
    8058:    e55b1009     ldrb    r1, [fp, #-9]
    805c:    e51b2008     ldr    r2, [fp, #-8]
    8060:    e0812002     add    r2, r1, r2
    8064:    e5832000     str    r2, [r3]
}
    8068:    e28bd000     add    sp, fp, #0
    806c:    e8bd0800     pop    {fp}
    8070:    e12fff1e     bx    lr
    8074:    2000b898     .word    0x2000b898
    8078:    2000b8a0     .word    0x2000b8a0

Вроде всё работает по плану, только какие-то ненужные манипуляции с sp и fp, вобщем, много лишнего.

Компилирую с оптимизацией 1 (-O1), получаю листинг:
Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    while(*((uint32_t *)(0x2000B898))&0x80000000);
    8020:    e59f301c     ldr    r3, [pc, #28]; 8044 <MailboxWrite+0x24>
    8024:    e5933898     ldr    r3, [r3, #2200]; 0x898
    8028:    e3530000     cmp    r3, #0
    802c:    ba000003     blt    8040 <MailboxWrite+0x20>
    *((uint32_t *)(0x2000B8A0))=val+channel;
    8030:    e0810000     add    r0, r1, r0
    8034:    e59f3008     ldr    r3, [pc, #8]; 8044 <MailboxWrite+0x24>
    8038:    e58308a0     str    r0, [r3, #2208]; 0x8a0
    803c:    e12fff1e     bx    lr
    8040:    eafffffe     b    8040 <MailboxWrite+0x20>
    8044:    2000b000     .word    0x2000b000

Вот тут лишних манипуляций не происходит, но пропадает цикл while, один раз проверяется статусный бит (802с) и проц уходит в бесконечный цикл (8040).

Компилирую с оптимизацией 2 (-O2), получаю листинг:
Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    while(*((uint32_t *)(0x2000B898))&0x80000000);
    8020:    e59f3018     ldr    r3, [pc, #24]; 8040 <MailboxWrite+0x20>
    8024:    e5932898     ldr    r2, [r3, #2200]; 0x898
    8028:    e3520000     cmp    r2, #0
    802c:    ba000002     blt    803c <MailboxWrite+0x1c>
    *((uint32_t *)(0x2000B8A0))=val+channel;
    8030:    e0810000     add    r0, r1, r0
    8034:    e58308a0     str    r0, [r3, #2208]; 0x8a0
    8038:    e12fff1e     bx    lr
    803c:    eafffffe     b    803c <MailboxWrite+0x1c>
    8040:    2000b000     .word    0x2000b000

Тут тоже самое, что и О1, но не происходит перезагрузки регистра с адресом, цикл while так же отсутствует.

Компилирую с оптимизацией 3 (-O3), получаю листинг:
Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    8020:    e59f3018     ldr    r3, [pc, #24]; 8040 <MailboxWrite+0x20>
    8024:    e5932898     ldr    r2, [r3, #2200]; 0x898
    8028:    e3520000     cmp    r2, #0
    802c:    aa000000     bge    8034 <MailboxWrite+0x14>
    8030:    eafffffe     b    8030 <MailboxWrite+0x10>
    while(*((uint32_t *)(0x2000B898))&0x80000000);
    *((uint32_t *)(0x2000B8A0))=val+channel;
    8034:    e0810000     add    r0, r1, r0
    8038:    e58308a0     str    r0, [r3, #2208]; 0x8a0
    803c:    e12fff1e     bx    lr
    8040:    2000b000     .word    0x2000b000

Тут уже сразу зацикливается никуда не перепрыгивая (8030)

Компиляция с оптимизацией s (-Os), приводит к результату -O2.

Еще есть опция -Ofast
Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    8020:    e59f3018     ldr    r3, [pc, #24]; 8040 <MailboxWrite+0x20>
    8024:    e5932898     ldr    r2, [r3, #2200]; 0x898
    8028:    e3520000     cmp    r2, #0
    802c:    aa000000     bge    8034 <MailboxWrite+0x14>
    8030:    eafffffe     b    8030 <MailboxWrite+0x10>
    while(*((uint32_t *)(0x2000B898))&0x80000000);
    *((uint32_t *)(0x2000B8A0))=val+channel;
    8034:    e0810000     add    r0, r1, r0
    8038:    e58308a0     str    r0, [r3, #2208]; 0x8a0
    803c:    e12fff1e     bx    lr
    8040:    2000b000     .word    0x2000b000


Подскажите, как можно ришить мою проблему? Может еще какие опции добавить компилятору? Может я неверно пишу программу??? ))

MrYuran
Не хватает квалификатора volatile, который объяснил бы компилятору, что проверяемая переменная может измениться асинхронно текущему потоку.
Иначе переменная в функции нигде больше не изменяется, и компилятор резонно оптимизирует цикл, выкидывая ненужные проверки.

А ещё по стилю замечу, что магические числа в коде - это очень плохо.
venomsoldier
Магические числа - это адреса регистров периферии, при замене их на дефайны ничего не меняется.
Покажите пример кода, который бы компилировался правильно. Где написать волитэил?
_Артём_
Цитата(venomsoldier @ Aug 22 2013, 13:23) *
Магические числа - это адреса регистров периферии, при замене их на дефайны ничего не меняется.

Меняется - читаемость кода повышается.




Цитата(venomsoldier @ Aug 22 2013, 13:23) *
Покажите пример кода, который бы компилировался правильно. Где написать волитэил?


Код
#define    STATUS_REG    (*(volatile uint32_t *)0x2000B898)
#define STATUS_READY_MASK 0x80000000

void WaitReady()

{

  while (( STATUS_REG &  STATUS_READY_MASK)==0);

}


venomsoldier
Спасибо огромное.
Пока ходил погулять, до меня дошло, перед адресом нужно указать, что это относится к периферии.

Для АВРов было что-то вроде _SFR_IO8(0x26) и _SFR_MEM8(0x60).

Завтра попробую откомпилить указанный вами код. Еще раз спасибо.
MrYuran
Цитата(venomsoldier @ Aug 22 2013, 14:49) *
Спасибо огромное.
Пока ходил погулять, до меня дошло, перед адресом нужно указать, что это относится к периферии.

Для АВРов было что-то вроде _SFR_IO8(0x26) и _SFR_MEM8(0x60).

А готовых описаний нету чтоли? Что за контроллер такой?
_Артём_
Цитата(MrYuran @ Aug 22 2013, 16:05) *
Что за контроллер такой?

Raspberry Pi наверное...

Цитата(venomsoldier @ Aug 22 2013, 14:49) *
Для АВРов было что-то вроде _SFR_IO8(0x26) и _SFR_MEM8(0x60).

Что наверное соответствует

Код
__no_init volatile unsigned char
MrYuran
Цитата(_Артём_ @ Aug 22 2013, 16:16) *
Что наверное соответствует

Код
__no_init volatile unsigned char

В MSPGCC вот как сделали:

Код
#define sfrb_(x,x_) volatile unsigned char x asm("__" #x)
#define sfrb(x,x_) extern sfrb_(x,x_)

#define sfrw_(x,x_) volatile unsigned int x asm("__" #x)
#define sfrw(x,x_) extern sfrw_(x,x_)

#define sfra_(x,x_) volatile unsigned long int x asm("__" #x)
#define sfra(x,x_) extern sfra_(x,x_)


...

Код
#define ADC10DTC0_            0x0048    /* ADC10 Data Transfer Control 0 */
sfrb(ADC10DTC0, ADC10DTC0_);
#define ADC10DTC1_            0x0049    /* ADC10 Data Transfer Control 1 */
sfrb(ADC10DTC1, ADC10DTC1_);
#define ADC10AE0_             0x004A    /* ADC10 Analog Enable 0 */
sfrb(ADC10AE0, ADC10AE0_);
#define ADC10AE1_             0x004B    /* ADC10 Analog Enable 1 */
sfrb(ADC10AE1, ADC10AE1_);

#define ADC10CTL0_            0x01B0    /* ADC10 Control 0 */
sfrw(ADC10CTL0, ADC10CTL0_);
#define ADC10CTL1_            0x01B2    /* ADC10 Control 1 */
sfrw(ADC10CTL1, ADC10CTL1_);
#define ADC10MEM_             0x01B4    /* ADC10 Memory */
sfrw(ADC10MEM, ADC10MEM_);
#define ADC10SA_              0x01BC    /* ADC10 Data Transfer Start Address */
sfrw(ADC10SA, ADC10SA_);


Цитата(_Артём_ @ Aug 22 2013, 16:16) *
Raspberry Pi наверное...

А, точно..

C library for Broadcom BCM 2835 as used in Raspberry Pi


Цитата
Functions

uint32_t bcm2835_peri_read (volatile uint32_t *paddr)
uint32_t bcm2835_peri_read_nb (volatile uint32_t *paddr)
void bcm2835_peri_write (volatile uint32_t *paddr, uint32_t value)
void bcm2835_peri_write_nb (volatile uint32_t *paddr, uint32_t value)
void bcm2835_peri_set_bits (volatile uint32_t *paddr, uint32_t value, uint32_t mask)
venomsoldier
Спасибо всем откликнувшимся.

Написал следующее:
Код
#define _SFR(_addr) (*(volatile uint32_t *)_addr)
#define MAILBOX_STATUS        _SFR(0x2000B898U)
#define MAILBOX_WRITE        _SFR(0x2000B8A0U)

void MailboxWrite(uint32_t val, uint8_t channel)
{
    while(MAILBOX_STATUS&0x80000000);
    MAILBOX_WRITE=val+channel;
}


Получил листинг с -O2
Код
void MailboxWrite(uint32_t val, uint8_t channel)
{
    while(MAILBOX_STATUS&0x80000000);
    8020:    e59f2018     ldr    r2, [pc, #24]; 8040 <MailboxWrite+0x20>
    8024:    e5923898     ldr    r3, [r2, #2200]; 0x898
    8028:    e3530000     cmp    r3, #0
    802c:    bafffffc     blt    8024 <MailboxWrite+0x4>
    MAILBOX_WRITE=val+channel;
    8030:    e59f3008     ldr    r3, [pc, #8]; 8040 <MailboxWrite+0x20>
    8034:    e0810000     add    r0, r1, r0
    8038:    e58308a0     str    r0, [r3, #2208]; 0x8a0
    803c:    e12fff1e     bx    lr
    8040:    2000b000     .word    0x2000b000

теперь не понятно, для чего он дважды загружает регистр с базовым адресом, получается лишняя строка кода(8030), но уже всё работает верно.
AHTOXA
Цитата(venomsoldier @ Aug 23 2013, 05:11) *
теперь не понятно, для чего он дважды загружает регистр с базовым адресом, получается лишняя строка кода(8030), но уже всё работает верно.

Это у GCC такая особенность. Чтобы он грузил базовый адрес один раз, надо ему немного помочь - объединить регистры в структуру:
Код
typedef struct {
    uint32_t volatile const status;
    uint32_t reserved;
    uint32_t volatile data;
}MailboxRegs;

#define MAILBOX ((MailboxRegs*)0x2000B898UL)

void MailboxWrite(uint32_t val, uint8_t channel)
{
    while(MAILBOX->status & 0x80000000);
    MAILBOX->data = val+channel;
}

Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.