Проблемы с кастингом при использовании макросов бит-бэндинга с заранее заданным адресом на Cortex-M3

TL;DR:

  1. Почему (unsigned long)(0x400253FC) не эквивалентно (unsigned long)((*((volatile unsigned long *)0x400253FC)))?
  2. Как я могу заставить макрос, который работает с первым, работать со вторым?

Исходная информация

Окружающая обстановка

Я работаю с процессором ARM Cortex-M3, LM3S6965 от TI, с их StellarisWare (бесплатная загрузка, экспорт с контролем). Я использую gcc версии 4.6.1 (Sourcery CodeBench Lite 2011.09-69). Stellaris предоставляет определения для примерно 5000 регистров и адресов памяти в «inc/lm3s6965.h», и я действительно не хочу переделывать все это. Однако они кажутся несовместимыми с макросом, который я хочу написать.

Битовая полоса

В ARM Cortex-M3 часть памяти имеет псевдоним с одним 32-битным словом на бит пространства памяти периферийного устройства и ОЗУ. Установка памяти по адресу 0x42000000 в 0x00000001 установит первый бит памяти по адресу 0x40000000 в 1, но не повлияет на остальную часть слова. Чтобы изменить бит 2, измените слово по адресу 0x42000004 на 1. Это удобная и чрезвычайно полезная функция. Согласно Техническому справочному руководству ARM, алгоритм вычисления адреса следующий:

bit_word_offset = (byte_offset x 32) + (bit_number × 4)
bit_word_addr = bit_band_base + bit_word_offset

где:

  • bit_word_offset — это позиция целевого бита в битовой области памяти.
  • bit_word_addr — это адрес слова в области памяти псевдонимов, которая соответствует целевому биту.
  • bit_band_base — это начальный адрес региона псевдонима.
  • byte_offset — это номер байта в области битового диапазона, который содержит целевой бит.
  • bit_number — битовая позиция от 0 до 7 целевого бита

Реализация битового бандинга

Файл "inc/hw_types.h" включает следующий макрос, реализующий этот алгоритм. Чтобы было ясно, он реализует его для модели на основе слов, которая принимает слова, выровненные по 4 байтам, и смещения от 0 до 31 бит, но результирующий адрес эквивалентен:

#define HWREGBITB(x, b)                                               \
    HWREGB(((unsigned long)(x) & 0xF0000000) | 0x02000000 |               \
           (((unsigned long)(x) & 0x000FFFFF) << 5) | ((b) << 2))

Этот алгоритм берет базу, которая находится либо в SRAM по адресу 0x20000000, либо в пространстве периферийной памяти по адресу 0x40000000), и выполняет операцию ИЛИ с 0x02000000, добавляя базовое смещение битовой полосы. Затем он умножает смещение от основания на 32 (эквивалентно сдвигу влево на пять позиций) и добавляет номер бита.

Упомянутый HWREG просто выполняет необходимое приведение для записи в заданное место в памяти:

#define HWREG(x)                                                              \
    (*((volatile unsigned long *)(x)))

Это хорошо работает с такими заданиями, как

HWREGBITW(0x400253FC, 0) = 1;

где 0x400253FC — магическое число для отображаемого в память периферийного устройства, и я хочу установить бит 0 этого периферийного устройства в 1. Вышеприведенный код вычисляет (конечно, во время компиляции) битовое смещение и устанавливает это слово в 1.

Что не работает

К сожалению, вышеупомянутые определения в "inc/lm3s6965.h" уже выполняют преобразование, выполненное HWREG. Я хочу избежать магических чисел и вместо этого использовать предоставленные определения, такие как

#define GPIO_PORTF_DATA_R       (*((volatile unsigned long *)0x400253FC))

Попытка вставить это в HWREGBITW приводит к тому, что макрос больше не работает, так как мешает приведение:

HWREGBITW(GPIO_PORTF_DATA_R, 0) = 1;

Препроцессор генерирует следующую кашу (добавлены отступы):

(*((volatile unsigned long *)
    ((((unsigned long)((*((volatile unsigned long *)0x400253FC)))) & 0xF0000000)
    | 0x02000000 |
    ((((unsigned long)((*((volatile unsigned long *)0x400253FC)))) & 0x000FFFFF) << 5)
    | ((0) << 2))
)) = 1;

Обратите внимание на два экземпляра

(((unsigned long)((*((volatile unsigned long *)0x400253FC)))))

Я считаю, что эти дополнительные приведения являются причиной сбоя моего процесса. Следующий результат предварительной обработки HWREGBITW(0x400253FC, 0) = 1; действительно работает, подтверждая мое утверждение:

(*((volatile unsigned long *)
    ((((unsigned long)(0x400253FC)) & 0xF0000000) 
    | 0x02000000 |
    ((((unsigned long)(0x400253FC)) & 0x000FFFFF) << 5)
    | ((0) << 2))
)) = 1;

Оператор приведения (type) имеет приоритет справа налево, поэтому должно применяться последнее приведение, а unsigned long используется для побитовой арифметики (которая в этом случае должна работать правильно). Нигде нет ничего неявного, никаких преобразований с плавающей запятой в указатель, никаких изменений точности/диапазона... самое левое приведение должно просто аннулировать приведения вправо.

Мой вопрос (наконец-то...)

  1. Почему (unsigned long)(0x400253FC) не эквивалентно (unsigned long)((*((volatile unsigned long *)0x400253FC)))?
  2. Как заставить работать существующий макрос HWREGBITW? Или как написать макрос для выполнения той же задачи, но без сбоев при задании аргумента с уже существующим приведением?

person Kevin Vermeer    schedule 04.06.2012    source источник


Ответы (1)


1- Почему (unsigned long)(0x400253FC) не эквивалентно (unsigned long)((*((volatile unsigned long *)0x400253FC)))?

Первый представляет собой целочисленный литерал, и его значение равно 0x400253FCul, а второе представляет собой значение unsigned long, хранящееся в (памяти или GPIO) адресе 0x400253FC.

2- Как заставить работать существующий макрос HWREGBITW? Или как написать макрос для выполнения той же задачи, но без сбоев при задании аргумента с уже существующим приведением?

Вместо этого используйте HWREGBITW(&GPIO_PORTF_DATA_R, 0) = 1;.

person ouah    schedule 04.06.2012
comment
Вы должны заметить, что в выражении (unsigned long)((*((volatile unsigned long *)0x400253FC))) первый * является оператором косвенного обращения, и он разыменовывает указатель. В этом выражении этого не делается: (unsigned long)(0x400253FC). Эффект & в вызове макроса HWREGBITW в моем ответе заключается в отмене присутствия оператора * в GPIO_PORTF_DATA_R. - person ouah; 04.06.2012