Jump to content
    

Можно ли заставить несколько составных литералов использовать одну и ту же память стека?

В 10.04.2025 в 22:03, Arlleex сказал:

Т.е. здесь в стеке создается анонимный объект типа sCANFrame и передается функции отправки.

А зачем так громоздко?

Получается - все отправляемые в кадре данные (и служебные поля CAN-кадра) = const. Известны задолго до момента отправки. И программа у вас копирует эти данные сначала из флеша в стек. Затем:

15 часов назад, Arlleex сказал:

can_Send() добавляет фрейм в очередь отправки, которая разгребается по мере отправки в шину

видимо can_Send() эти данные копирует из стека в очередь (в ОЗУ?). А затем ISR CAN эти данные снова копирует из очереди в ОЗУ в mailbox (или в FIFO) в CAN-периферии? Итого = 3 копирования из одной памяти в другую!

(Или под "очередью" имеется в виду аппаратное FIFO в CAN-периферии? если таковое есть в вашем МК)

Зачем столько переливаний из пустого в порожнее перетаскиваний из одной памяти в другую? Почему нельзя сразу в нужном месте записать эти const данные сразу в mailbox CAN? Внутри ISR CAN.

В can_Send() передать просто id кадра (хранящегося во flash). Пускай этот id - это просто индекс одного из кадров в массиве таких же во флешь. И тогда его передача в can_Send() будет просто через регистр R0. Вообще без использования стека. И без кучи лишних копирований.

Share this post


Link to post
Share on other sites

12 минут назад, jcxz сказал:

А зачем так громоздко?

Получается - все отправляемые в кадре данные (и служебные поля CAN-кадра) = const. Известны задолго до момента отправки.

Ну нет конечно, я для примера показал с константными данными. В реальности ID, данные и длина формируются в рантайме, чаще всего. Да, есть и полностью константные, но их объем ничтожно мал, чтобы раздувать логику очереди для укорочения обработки таких данных.

Share this post


Link to post
Share on other sites

26 минут назад, Arlleex сказал:

Ну нет конечно, я для примера показал с константными данными. В реальности ID, данные и длина формируются в рантайме, чаще всего. Да, есть и полностью константные, но их объем ничтожно мал, чтобы раздувать логику очереди для укорочения обработки таких данных.

Ну ок, а если сделать перечисление: enum CAN_FRAME_ID {CAN_FRAME_ID_STEP1, CAN_FRAME_ID_STEP2, CAN_FRAME_ID_STEP...}; в can_Send() передавать CAN_FRAME_ID, а уже непосредственно при заполнении mailbox-а, внутри ISR (или где у вас заполняются mailbox-ы) вызывать callback(CAN_FRAME_ID id), в которой: switch (id) { case CAN_FRAME_ID_STEP1: {} case CAN_FRAME_ID_STEP...: {}} с заполнением сразу регистров mailbox-а в периферии CAN?

Или вообще всю логику обработки запрос-ответ (прикладного уровня протокола над CAN) вынести в ISR? Я обычно так делаю.

Exception стек - его же менее жалко, чем стеки задач.

Также будет намного оптимальнее по коду. Меня, честно говоря, напрягало бы такое количество простых переливаний из одной памяти в другую, без какой-либо обработки.  :sarcastic:

Share this post


Link to post
Share on other sites

Как пример

sCANID id;

can_TreeGetClimateStateTxID(&id);

can_Send(CAN_BUS_1, &(sCANFrame){
  .id      = id.id,
  .ext     = id.ext,
  .rtr     = 0,
  .dlc     = 5,
  .word[0] = Climate.exch.isAlive                                           | // перепаковка: B0    <- connected
             BIT_FLD(B2 | B1, ~Climate.exch.rxFrameTemperature.word[0])     | //              B2:B1 <- B1:B0   (испр. внешн. датчиков)
             BIT_FLD(B4 | B3,  Climate.exch.rxFrameTemperature.word[0], 24) | //              B4:B3 <- B25:B24 (испр. внутр. датчиков)
             Climate.exch.rxFrameTemperature.byte[1] << 24,                   //              температура в салоне
  .word[1] = Climate.exch.rxFrameTemperature.byte[2]                          //              температура снаружи
});

или (переправка из одной CAN-сети в другую, изменив лишь свойства кадра; данные уже в полях данных, т.к. принялись ранее из другой CAN-шины)

can_TreeGetClimateErrorCodesTxID(&id);

Climate.exch.rxFrameErrorCodes1.id  = id.id;
Climate.exch.rxFrameErrorCodes1.ext = id.ext;
Climate.exch.rxFrameErrorCodes1.rtr = 0;
Climate.exch.rxFrameErrorCodes1.dlc = 8;

can_Send(CAN_BUS_1, &Climate.exch.rxFrameErrorCodes1);


До оптимизаций еще придется дожить, т.к. в текущем проекте слишком быстро все меняется - и состав внешних по отношению ко мне сопрягаемых частей оборудования, у которых разная логика обмена и т.д. Поэтому сделано сейчас универсально, но так, чтобы это не парило мозги: т.е. заведена кольцевая очередь фреймов, в которую пользовательский код пишет абстрактно вызовом can_Send() и больше не задумывается, что там дальше происходит.

Оптимизировать тут можно (потом) с другой стороны: например, сделать функцию can_GetMailboxFIFO(), которая вернет указатель на свободный слот в программном FIFO. В который я все закопирую и выставлю в нем признак "готов к отправке", затем пну can_Send(), чтоб не расслаблялся. Варианты есть, я понимаю.

Share this post


Link to post
Share on other sites

5 hours ago, jcxz said:

Также будет намного оптимальнее по коду. Меня, честно говоря, напрягало бы такое количество простых переливаний из одной памяти в другую, без какой-либо обработки.  :sarcastic:

Да вот и я по сути про то же.

В добавок - гипотетическая ситуация - вот пропал абонент принимающий (единственный!) - и опять появился через какое то время опять. 
И - на него наваливается нонстопом куча сообщений - всё то, что он профукал во время отсутствия. Ибо - не передали же, подтверждения небыло, они так и стоят в очереди. Текущие данные не уходят, ибо...
А у него уже чуть другой расклад действий после, скажем, ресета. И устаревшие данные как зайцу стопсигнал.

А если это ядерный реактор или айрбаги в промежутке успели бабахнуть, а вы им всё ещё педаль в пол посылаете?

И память, память наполняется...! Хламом.

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

Quote

Я уверен, и подтвердил свою мысль о баге среди других источников, и надеюсь, что этот баг исправят, ибо это очевидный косяк.

Да с этим понятно. Не про то ща....
Баг или особенность компилятора - это уже нюансы. Поменять поочередность действий, и всё норм.

void foo(void){
   u8 i = 0xFF;
   while(i--){;};
}

Вот что должен делать компилятор? Это задержка? Или функция ниочем попадающая под return сразу и безоговорочно?

Share this post


Link to post
Share on other sites

5 hours ago, jcxz said:

Или вообще всю логику обработки запрос-ответ (прикладного уровня протокола над CAN) вынести в ISR? Я обычно так делаю.

По идее, пока обрабатываешь, можешь потерять следующий фрейм в тот же слот. Да и по этому их даже иногда делают два на приём, как бы параллельно.
ИМХО правильнее разгружать, и потом уже при возможности обрабатывать. В ISR только переброс в FIFO или что то там, и флажок.
Если одно ID - ну пускай. А если весь поток хочется переваривать? Скажем, перебрасывать из одного субнета на другой? И с другого на первый? А если загрузка CAN больше каких то 80 процентов? 

Share this post


Link to post
Share on other sites

В 11.04.2025 в 21:33, Arlleex сказал:

А как это Вы адрес r-value берете так?🙂

Если только вот так

sCANFrame frame;

frame=(sCANFrame){};
can_Send(CAN_BUS_1, &frame);

frame=(sCANFrame){};
can_Send(CAN_BUS_1, &frame);

Но уже не красиво... Да, стек уже не расходуется почем зря, но исчезают все плюсы синтаксического сахара, даваемого литералом.

Всегда есть костыль-запятая:

void test()
{
    CANFrame frame;
    can_Send((frame = (CANFrame){}, &frame));
    can_Send((frame = (CANFrame){}, &frame));
}

ЗЫ. А в С++ не ругается на исходный вариант.

Share this post


Link to post
Share on other sites

42 минуты назад, AHTOXA сказал:

ЗЫ. А в С++ не ругается на исходный вариант.

Интересно, почему... хотя, может и не удивительно)

Share this post


Link to post
Share on other sites

28 минут назад, Arlleex сказал:

Интересно, почему... хотя, может и не удивительно)

Я лично наоборот, удивился, почему там rvalue. Мы присваиваем переменной значение, и берём её адрес. В плюсах логичнее:)

Share this post


Link to post
Share on other sites

11 минут назад, AHTOXA сказал:

Я лично наоборот, удивился, почему там rvalue. Мы присваиваем переменной значение, и берём её адрес. В плюсах логичнее:)

Мне казалось, что в этом плане плюсы и обычный Си должны были быть одинаковыми, но я слышал про существование неких r-value-выражений в плюсах, которые в Си , на самом-то деле, отсутствуют. В Си результат оценки выражения присваивания - есть значение после присваивания. И от значения нельзя взять адрес, поэтому не сказал бы, что в плюсах логичнее🙂 Я думаю, что в плюсах это возможно только лишь по причине наличия концепции ссылок. Насколько я помню, в плюсах конструкция вида int &i = 5 вполне законна, поэтому это про то же.

Share this post


Link to post
Share on other sites

6 минут назад, Arlleex сказал:

Мне казалось, что в этом плане плюсы и обычный Си должны были быть одинаковыми, но я слышал про существование неких r-value-выражений в плюсах, которые в Си , на самом-то деле, отсутствуют. В Си результат оценки выражения присваивания - есть значение после присваивания. И от значения нельзя взять адрес, поэтому не сказал бы, что в плюсах логичнее🙂

В плюсах я привык, что operator=(T const& other) возвращает ссылку на self, что ожидаемо lvalue. В стандарте сходу не нашёл указаний на это.

Share this post


Link to post
Share on other sites

5 минут назад, AHTOXA сказал:

В плюсах я привык, что operator=(T const& other) возвращает ссылку на self, что ожидаемо lvalue. В стандарте сходу не нашёл указаний на это.

Выше дополнил пост, как раз про ссылки, поэтому, видимо, это оно.

Ладно, думаю, правильные поцыки из LLVM допилят и мы все будем рады, что составные литералы будут более логичными, как и обычные именованные переменные.

 

В 11.04.2025 в 20:11, _pv сказал:

навести "красоту" макросами, но это ужОс конечно.

Ужос - это файл моих вспомогательных макросов, а это так, ерунда:crazy:

Но, конечно, в силу уже более ядерного синтаксиса, писать я так не стану. В функциях, где пара-тройка таких вызовов оставлю как в первом посте, а там, где вызовов поболее - перепишу с использование отдельной локальной структуры sCANFrame.

Share this post


Link to post
Share on other sites

5 минут назад, Arlleex сказал:

Выше дополнил пост, как раз про ссылки, поэтому, видимо, это оно.

Да, логично. В си же нет ссылок, поэтому возвращается rvalue. В плюсовом стандарте нашёл нужные буквы, там возвращается lvalue-ссылка:

Цитата

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a
modifiable lvalue as their left operand and return an lvalue referring to the left operand.

 

Share this post


Link to post
Share on other sites

1 минуту назад, AHTOXA сказал:

В плюсовом стандарте нашёл нужные буквы, там возвращается lvalue-ссылка:

:good:

А вот тот же пунктик в Си

Цитата

An assignment operator stores a value in the object designated by the left operand. An assignment expression has the value of the left operand after the assignment,124) but is not an lvalue.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...