LINUX.ORG.RU

Описание разреженных структур на языке C

 


0

5

Доброе время суток!

Имеется описание структуры в духе:

struct reg_map
{
  u8  spacer1[0x3C];
  u32 status_reg1;
  u32 status_reg2;
  u8  spacer2[0x2424];
  u32 conf_reg1;
  u32 conf_reg2;
};
//....
  struct reg_map *my_dev = ptr_to_dev;
  my_dev->conf_reg1 = CONST_HOST_TO_DEV32(0x01);

Можно делать иначе - определять просто адреса регистров:

#define MYDEV_CONF_REG1 0x2468
write_reg32(my_bar0 + MYDEV_CONF_REG1, 0x01);

Вопрос: как сделать что-то более интеллигентное, наподобие адовского «at cause»? Чтобы было и наглядно, и трудно ошибиться.

WORD : constant := 4;  --  storage unit is byte, 4 bytes per word

   type STATE         is (A,M,W,P);
   type MODE          is (FIX, DEC, EXP, SIGNIF);

   type BYTE_MASK     is array (0.. 7) of BOOLEAN;
   type STATE_MASK    is array (STATE) of BOOLEAN;
   type MODE_MASK     is array (MODE)  of BOOLEAN;

   type PROGRAM_STATUS_WORD is

     record
       SYSTEM_MASK        : BYTE_MASK;
       PROTECTION_KEY     : INTEGER range 0 .. 3;
       MACHINE_STATE      : STATE_MASK;
       INTERRUPT_CAUSE    : INTERRUPTION_CODE;
       ILC                : INTEGER range 0 .. 3;
       CC                 : INTEGER range 0 .. 3;
       PROGRAM_MASK       : MODE_MASK;
      INST_ADDRESS       : ADDRESS;
    end record;

  for PROGRAM_STATUS_WORD use
    record at mod 8;
        SYSTEM_MASK      at 0*WORD range 0  .. 7;  
        PROTECTION_KEY   at 0*WORD range 10 .. 11; -- bits 8,9 unused
        MACHINE_STATE    at 0*WORD range 12 .. 15;
        INTERRUPT_CAUSE  at 0*WORD range 16 .. 31;
        ILC              at 1*WORD range 0  .. 1;  -- second word
        CC               at 1*WORD range 2  .. 3;
        PROGRAM_MASK     at 1*WORD range 4  .. 7;
        INST_ADDRESS     at 1*WORD range 8  .. 31;
    end record;

  for PROGRAM_STATUS_WORD'SIZE use 8*SYSTEM.STORAGE_UNIT;
Deleted

Последнее исправление: cetjs2 (всего исправлений: 1)

Если регистры разреженные, какой смысл их структурами описывать? Считаю такое решение достаточно «интеллигентным»:

#define VPint            *(volatile unsigned int *)

#define MYDEV_REG_BASE   0x24680000
#define MYDEV_REG_STATUS VPint(MYDEV_REG_BASE + 0x00)
#define MYDEV_REG_READ   VPint(MYDEV_REG_BASE + 0x40)
#define MYDEV_REG_WRITE  VPint(MYDEV_REG_BASE + 0x44)
С макросом VPint можно вместо write_reg32() делать напрямую MYDEV_REG_WRITE = 0x0A. По-моему, достаточно наглядно. Ада это вообще ужас какой-то, действительно «язык ада».

Кастую Eddy_Em в тред.

mtk
()
Ответ на: комментарий от mtk

Оба способа используются — в зависимости от того, каковы ресурсы микроконтроллера. Если дохлые (скажем, как STM8), то лучше через макросы делать (сам так и делаю), если же вполне себе (скажем, как STM32), то можно и через структуры запилить.

Единственное превосходство структур — большая наглядность в случае работы с одинаковым набором регистров (скажем, для одного из N USART'ов).

С другой стороны, так, как ты написал, тоже вполне удобно будет делать. Насчет разыменования указателей, так, пожалуй, компилятору пофиг — он в одинаковый код должен (по идее) оба варианта привести.

В общем, все от конкретной ситуации зависит.

Eddy_Em ☆☆☆☆☆
()

WORD : constant := 4

const Pi = 6.28 :) Надорвало мозг слегка вначале. Ну, это субьективная придирка, привык что 4-м равен DWORD.

Считаю, что если удасться запихнуть в структуру - наглядней структура.

С другой стороны, иногда бывает, что сишные битовые поля недостаточны (например, поле требует «разрыва», часто на взаимодействии с системами с big-endian натыкался) - в таком случае поступал решительным образом: весь доступ к полям заворачивал в кучу инлайнящихся функций


void buf_set_magic_value(buffer *; const char  *, size_t);
void buf_set_delta(buffer *, double);
void buf_set_starting_flag(buffer *, bool);
void buf_set_exсeption_reg_valuе(buffer *, int);

void buf_get_magic_value(const buffer *; const char  **out_ptr, size_t *out_size);
void buf_get_delta(const buffer*, double * out_value);
// другие get_...

у тебя вроде как контекст глобальный, поэтому на параметре biffer* съэкономишь. Плюсы очевидны - реализация логики по чтению и записи полей локализована и предоставлен удобный интерфейс. Реализация обычно и на структурах и на макросах-смещениях, дополняют друг друга, и содержат минимальные вычисления или проверки.

Deleted
()
Ответ на: комментарий от Eddy_Em

Оба способа используются — в зависимости от того, каковы ресурсы микроконтроллера.

Научись пилить хеадеронли либы/сорцы и инлайн, тогда разницы не будет. Хотя в целом будет ещё быстрее.

С другой стороны, так, как ты написал, тоже вполне удобно будет делать. Насчет разыменования указателей, так, пожалуй, компилятору пофиг — он в одинаковый код должен (по идее) оба варианта привести.

А делать что-то типа:

register void * rdi asm("rdi"), * rsi  asm("rsi"), * rdx  asm("rdx");

void test(uint64_t a, uint64_t b, uint64_t c) {
  volatile uint64_t arg_a = rdi, arg_b = rsi, arg_c = rdx;
  fprintf(stderr, "%lu, %lu, %lu\n", arg_a, arg_b, arg_c);
}

int main(void) {
  test(123, 124, 125);
}

Гцц поидее должен уметь это на твоей/ваших армках, либо что там у автора.

Как вы смешением по базе обращаетесь к регистрам?

carb_blog10
()
Ответ на: комментарий от carb_blog10

А делать что-то типа

не пробовал. Да и на кой черт напрямую к служебным регистрам обращаться?

А еще ты какую-то фигню внутри функции написал: забыл разыменовать указатели + не написал __attribute__((unused)) перед неиспользуемыми аргументами функции или же внутри нее void (a); ..

Иначе ведь этот пример даже не скомпиляется (по крайней мере, у меня, с моими-то -Wall -Werror)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

передать генератору создание структур

А это мысль!

Deleted
()
Ответ на: комментарий от Eddy_Em

Да и на кой черт напрямую к служебным регистрам обращаться?

Да и на кой черт не напрямую к служебным регистрам обращаться?

А еще ты какую-то фигню внутри функции написал:

Если ты не способен понять смысла этой функции - это твои проблемы, а не функции.

забыл разыменовать указатели

Чего? Ты крякнутый чтоли? Зачем мне их разименовывать? Если ты не способен понять смысл этого кода - не отвечай ничего, либо спроси, но не пиши мне херню.

не написал __attribute__((unused)) перед неиспользуемыми аргументами функции или же внутри нее void (a); ..

А с какого хрена я это должен писать?

Иначе ведь этот пример даже не скомпиляется (по крайней мере, у меня, с моими-то -Wall -Werror

Да мне насрать что там у тебя соберётся, а что нет. Мб мне о каждом дауне с -std=krc заботиться?

Давай попроще тебе объясню - -Werror - твоя блажь, которая для меня ничего не значит, тем более в данном случае. Если тебе ответить нечего - не отвечай, либо так и напиши «ответить нечего». Зачем ты пишешь мне тотальную херню?

carb_blog10
()
Ответ на: комментарий от anonymous

Хм, я думал, что -Wall все включает. Спасибо, буду в дальнейшем еще -Wextra добавлять.

А про -Wpedantic man gcc молчит...

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

в более старых gcc просто -pedantic. это тоже не все ворнинги включает, просто лень разбираться — в clang есть -Weverything

anonymous
()
Ответ на: комментарий от Eddy_Em

-Wpedantic

соответствие стандарту, который ты выбрал -std=cXX

Stil ★★★★★
()
Ответ на: комментарий от carb_blog10

Как вы смешением по базе обращаетесь к регистрам?

Это регистры периферийного устройства. Обращение к ним - как к адресам памяти. BAR0 - base address register zero, базовый адрес данного устройства. Смещение - из документации на устройство. Так почти все устройства работают.

Deleted
()

А что мешает задействовать inline функции:

inline int reg_map_get_status_reg1(void * reg_map) {
  return *(int*)(reg_map + 0x3C);
}

AlexVR ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.