Перейти к содержанию
    

Плавный переход C -> C++ под МК

12.10.2021 в 14:22, jcxz сказал:

единый общий класс, то он будет очень неоптимальным.

Возможно, что я Вас неправильно понял. Но считаю, что это возможно. Любой последовательный порт позволяет:

1. Открыть себя (с настройкой общепринятыми параметрами: скорость, количество бит данных и т.д.).

2. Писать в себя.

3. Читать из себя.

4. Закрыть себя (не использую).

А все нюансы конкретного железа запихать в конкретный класс драйвера. Это называется инкапсуляция. А наружу будет торчать условно четыре метода класса, описанные выше. И интерфейс будет одинаковым. И при подмене класса драйвера в пользовательском ПО разницы не будет. При этом единый класс - это абстрактный класс дарйвра. Который сам ничего не умеет, но предоставляет интерфейс, который должен реализовать конкретный класс драйвера.

Пример моего абстрактного класса.

З.Ы. Не знаю как закатать под спойлер(((

З.Ы.Ы. Не эксперт в Си++, потому качество кода не претендует на 5 с плюсом)

Скрытый текст

#pragma once
#ifdef __cplusplus
#include "libs\ret_vals.hpp"
#include "drv\bus\serial\base_serial_bus_drv.hpp"
#include <stdio.h>
namespace Drv {
    //template <size_t TxBuffSize, size_t RxBuffSize>
    class BaseUsart : public BaseSerialBus {
    public:
        enum class Baudrate {
            B19200      = 19200,
            B115200     = 115200,
            B921600     = 921600,
            B1280000    = 1280000,
        };
        enum class Parity : uint8_t {
            No, Odd, Even, Mark, Space,
        };
        enum class DataBits : uint8_t {
            Four, Five, Six, Seven, Eight,
        };
        enum class StopBits : uint8_t {
            One, Onehalf, Two,
        };
        enum class TransferMethod : uint8_t {
            Polling, Interrupt, Dma,
        };

        struct Settings {
            Baudrate        baudrate    = Baudrate::B115200;
            Parity          parity      = Parity::No;
            DataBits        dataBits    = DataBits::Eight;
            StopBits        stopBits    = StopBits::One;
            TransferMethod  txMethod    = TransferMethod::Interrupt;
            TransferMethod  rxMethod    = TransferMethod::Interrupt;
        };

        static const auto GENERAL_TIMEOUT_MS = 300;

        virtual RetVal write( const void * const src, const size_t count, size_t * const wr = nullptr, const size_t timeout_ms = GENERAL_TIMEOUT_MS ) = 0;
        virtual RetVal writeDirectly( const void * const src, const size_t count ) {return RetVal::Failed;}
        virtual RetVal read( void * dst, const size_t count, size_t * const rd = nullptr, const size_t timeout_ms = GENERAL_TIMEOUT_MS ) = 0;
        virtual void flush() = 0;
        virtual RetVal setSettings( const Settings settings ) = 0;
        virtual Settings getSettings() const {
            return m_settings;
        }
    protected:
        BaseUsart( const BusNum num )
            : BaseSerialBus(num) {}

        struct Msg {
            size_t txCount      = 0;
            const char * pSrc   = nullptr;
        };

        Settings m_settings;
    private:
    };
};
#endif

 

Реализация конкретного драйвера последовательного порта.

Скрытый текст

#pragma once
#ifdef __cplusplus
#include "base_usart_drv.hpp"
#include "drv\mcu\clk\stm32f0x1_rcc_drv.hpp"
#include "libs\usefulmacro.hpp"
#include "drv\mcu\interrupts\cortex_mx_nvic.hpp"
#include "drv\mcu\gpio\stm32f0x1_gpio_drv.hpp"
#include "ffwk\libs\debug\config_assert.hpp"
#include <ring_buffer_os_ext.h>

//static USART_TypeDef * const regArray[] = {
//    USART1, USART2, USART3, USART4,
//    USART5, USART6, USART7, USART8,
//};

namespace Drv {
    template <BaseSerialBus::BusNum Num, const size_t RxSize,
              typename RxPin, Drv::Pin::Function RxAF, typename TxPin, Drv::Pin::Function TxAF>
    class Stm32f0x1Usart : public BaseUsart {
    public:
#if defined (STM32F091)
        static const auto BUS_NUM = 8;
        static_assert( Num > BusNum::N0 && Num < BusNum::N9, "Invalid usart number!" );
#elif defined (STM32F051)
        static const auto BUS_NUM = 2;
        static_assert( Num == BusNum::N1 || Num == BusNum::N2, "Invalid usart number!" );
#elif defined (STM32F030)
        static const auto BUS_NUM = 1;
        static_assert( Num == BusNum::N1, "Invalid usart number!" );
#else
        #error "No MCU is selected!";
#endif
        Stm32f0x1Usart()
            : BaseUsart(Num) {}

        RetVal open() override {
            if (m_open == RetVal::Ok)
                return m_open;
            /// TODO automatically choose vector based on template args
            Nvic::install(USART1_IRQn, irqHandler);
            setNvic();
            enableClock();
            m_open = setSettings();
            return m_open;
        }
        RetVal close() override {
            return RetVal::Ok;
        }
        RetVal write( const void * const src, const size_t count, size_t * const wr, const size_t timeout_ms ) override {
            if (wr)
                *wr = count;
            if (!count)
                return RetVal::Ok;
            if (count == 1) {
                m_ctrl.pRegs->TDR = *static_cast<const char *>(src);
                while (!( m_ctrl.pRegs->ISR & USART_ISR_TXE ));
                return RetVal::Ok;
            }
            auto &msg = m_ctrl.msg;
            msg.txCount = count;
            msg.pSrc = reinterpret_cast<const char *>(src);
            m_ctrl.pRegs->CR1 |= USART_CR1_TXEIE;
            auto const result =  m_ctrl.event.wait(timeout_ms) ? RetVal::Ok : RetVal::TimeOut;
            if (result != RetVal::Ok && wr)
                *wr = 0;
            return result;
        }
        RetVal writeDirectly( const void * src, size_t count ) override {
            decltype( auto )ptr = static_cast<const char *>(src);
            while (count--) {
                m_ctrl.pRegs->TDR = *ptr++;
                while (!( m_ctrl.pRegs->ISR & USART_ISR_TXE ));
            }
            return RetVal::Ok;
        }
        RetVal read( void * dst, const size_t count, size_t * const rd, const size_t timeout_ms ) override {
            if (count == 0) {
                if (rd)
                    *rd = count;
                return RetVal::Ok;
            }
            char * cDst = reinterpret_cast<char *>(dst);
            const auto result = m_ctrl.rx.read(cDst, count, rd, timeout_ms);
            return result ? RetVal::Ok : RetVal::TimeOut;
        }
        void flush() override {
            m_ctrl.pRegs->CR1 &= ~USART_CR1_TXEIE;
            while (!( m_ctrl.pRegs->ISR & USART_ISR_TXE ));
            while (m_ctrl.pRegs->ISR & USART_ISR_RXNE)
                volatile auto dummy = m_ctrl.pRegs->RDR;
            m_ctrl.msg = Msg();
            m_ctrl.event.clear();
            m_ctrl.rx.flush();
        }
        RetVal setSettings( const Settings settings = Settings() ) override {
            m_settings = settings;
            setNvic();
            m_ctrl.pRegs->CR1 &= ~USART_CR1_UE;
            m_ctrl.pRegs->CR1 = 0
                                | USART_CR1_TE
                                | USART_CR1_RE
                                | USART_CR1_RXNEIE
            ;
            //m_ctrl.pRegs->CR3 |= USART_CR3_OVRDIS;
            const auto pclk = Stm32f0x1Rcc::getPeriphClk();
            m_ctrl.pRegs->BRR = pclk / static_cast<size_t>(m_settings.baudrate);
            m_ctrl.pRegs->CR1 |= USART_CR1_UE;
            setNvic(Nvic::Prio::Highest, true);
            return RetVal::Ok;
        }
    protected:
        struct ControlData {
            USART_TypeDef * const pRegs;
            Msg msg;
            OS::TEventFlag event;
            OS::RingBuffer<char, RxSize> rx;
//          TaskHandle_t tskHndl;
//          size_t rxcounter;
//          DmaCfg::PeriphConfig dmaTx;
//          DmaCfg::PeriphConfig dmaRx;
//          DmaDriver &dma;
//          std::uint32_t dummyRx;
            ControlData()
                : pRegs(( []() {
                static USART_TypeDef * const regArray[] = {
                    USART1, USART2, USART3, USART4,
                    USART5, USART6, USART7, USART8,
                };
                return regArray[static_cast<size_t>(Num) - 1];
            } )()) {
                TxPin::mode(Drv::Pin::Mode::Alternate);
                RxPin::mode(Drv::Pin::Mode::Alternate);
                TxPin::setAlternate(TxAF);
                RxPin::setAlternate(RxAF);
            }
        };
        Stm32f0x1Usart( const Stm32f0x1Usart & ) = delete;
        Stm32f0x1Usart( const Stm32f0x1Usart && ) = delete;
        void enableClock() {
            OsApi::CritSecSmart cs;
            switch (m_busnum) {
            case BusNum::N1:
                RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
                return;
            case BusNum::N2:
                RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
                return;
            case BusNum::N3:
                RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
                return;
            case BusNum::N4:
                RCC->APB1ENR |= RCC_APB1ENR_USART4EN;
                return;
            case BusNum::N5:
                RCC->APB1ENR |= RCC_APB1ENR_USART5EN;
                return;
            case BusNum::N6:
                RCC->APB2ENR |= RCC_APB2ENR_USART6EN;
                return;
            case BusNum::N7:
                RCC->APB2ENR |= RCC_APB2ENR_USART7EN;
                return;
            case BusNum::N8:
                RCC->APB2ENR |= RCC_APB2ENR_USART8EN;
                return;
            default:
                configASSERT(0);
            }
        }
        void setNvic( const Nvic::Prio prio = Nvic::Prio::Lowest, const bool on = false ) {
            IRQn irqn;
            switch (m_busnum) {
            case BusNum::N1:
                irqn = USART1_IRQn;
                break;
            case BusNum::N2:
                irqn = USART2_IRQn;
                break;
            default:
#if defined (STM32F091)
                irqn = USART3_8_IRQn;
#endif
                break;
            }
            Nvic::disable(irqn);
            Nvic::clearPending(irqn);
            if (on) {
                Nvic::setPriority(irqn, prio);
                Nvic::enable(irqn);
            }
        }
#pragma optimize=no_size_constraints
        __forceinline static void irqHandler() {
            OS::TISRW tisr;
            auto * const pRegs = m_ctrl.pRegs;
            auto &msg = m_ctrl.msg;
            while (pRegs->ISR & USART_ISR_RXNE)
                m_ctrl.rx.push_isr(pRegs->RDR);
            if (( pRegs->ISR & USART_ISR_TXE ) && msg.txCount) {
                if (--msg.txCount == 0) {
                    pRegs->CR1 &= ~USART_CR1_TXEIE;
                    m_ctrl.event.signal_isr();
                }
                pRegs->TDR = *msg.pSrc++;
            }
        }
        static const uint32_t INTERRUPT_MASK = USART_ISR_TXE | USART_ISR_RXNE;
        static ControlData m_ctrl;
    };

    template <BaseSerialBus::BusNum Num, const size_t RxSize, typename RxPin,
              Drv::Pin::Function RxAF, typename TxPin, Drv::Pin::Function TxAF>
    Stm32f0x1Usart<Num, RxSize, RxPin, RxAF, TxPin, TxAF>::ControlData
        Stm32f0x1Usart<Num, RxSize, RxPin, RxAF, TxPin, TxAF>::m_ctrl;
};
#endif

 

 

12.10.2021 в 14:41, jcxz сказал:

Всё равно в конце-концов придётся или обновлять или этот "сабмодуль" распадётся на 2 параллельные ветки.

Так их в любом случае нужно вносить эти исправления. Тут уже управление кодом больше встаёт вопрос.

12.10.2021 в 14:41, jcxz сказал:

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

А зачем ветки, если есть тэги и коммиты в нотации гита? С другими системами контроля версий не работал, но подозреваю, что что=то аналогичное там есть. Ветки плодить вообще не очень хорошо. Т.к. их нужно будет когда-то синхронизировать. А это уже аналог кучи папок и архивов от чего нужно уходить. Обычно в параллельной ветке, например названной develop, создают какие-то новые фичи. Предварительно "отбранчевав" её от master'а. Потом делают слияние данной ветки с мастером, чтобы новые наработки в него попали. Но это не значит, что все проекты с мастером сразу изменились, т.к. ссылаются на определённый коммит.

12.10.2021 в 14:41, jcxz сказал:

ещё тестирование в испытательной лаборатории (несколько недель)

Это всё я понимаю, т.к. работаю в данной сфере) И по объектам тоже иногда езжу. Другое дело, что всё это уже не влияет на форму ведения архива с кодом. Можно ничего не исправлять. Но в независимости от того, каким образом исправили, испытания делать в любом случае.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 hours ago, jcxz said:

Оказывается товарищи-ООП-программисты перетащили все свои исходники с ПК "как есть".

Ха-ха! Как мне знакома Ваша ситуация) Когда некоторые коллеги тянут подходы для программирования ПК в мир встраиваемых систем))) И потом оказывается, что STM32F091 не хватает по ресурсам (всего 32 кБ ОЗУ). Вместо него ставится двухядерный LPC4337 + 32 Мб SDRAM. К слову, память в проекте оказалась нелишней (много данных собирается, хотя можно было и обойтись гораздо меньшим объёмом не в ущерб функциональности прибора и немного по другому используя периферию). А вот ресурсов процессора выше крыши. Непрофессионально. Зато остальные программисты теперь в проекте используют new/delete, векторы, листы, std::map. И стэк мы уже увеличили для нескольких задач FreeRTOS до почти мегабайта. И размер кучи в скриптел линкера уже 2 Мб! Крассссота!

9 hours ago, Arlleex said:

грешу всякими if(i > 0 && --i) и т.д.

Читал у "Алена c++" в своё время, что так делать не надо)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, haker_fox сказал:

Читал у "Алена c++" в своё время, что так делать не надо)

Почему же? В соответствии со стандартом данная строчка не вызывает криминала:smile:
А то, о чем Вы говорите, скорее всего было о конструкциях вида i = i++; что в C++, вроде как, теперь не UB.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 minutes ago, Arlleex said:

вида i = i++;

И это тоже.

14 minutes ago, Arlleex said:

В соответствии со стандартом данная строчка не вызывает криминала

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 10/12/2021 at 2:01 PM, one_eight_seven said:

Это не проблема языка. Это проблема управления конфигурациями ПО. Уже, кстати, решена, и не раз.

Именно. С релизом проекта надо фиксировать и версию библиотечного репозитория. А изменения в библиотечном репозитории уже переносятся в ранние версии процедурой слияния (merge).
А иначе больше проблем будет от "супер Common" библиотеки.

 

Проект - это набор всех файлов необходимых для сборки, причем согласованных версий, а не просто что-то там подтягиваем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Следующий вопрос назрел. Наверное, больше по оформлению кода.

Вот есть в классе какие-то приватные данные и методы для работы с ними. И вот есть среди таких данных переменные, отвечающие за состояние, например, FSM-автомата. В моем случае (протокол SLCAN) интерфейс может находиться в открытом, закрытом и режиме listen-only. Эти режимы (состояния) переключаются кодом парсера, который есть ни что иное, как публичный метод parse() моего класса (вызывать его я буду, когда что-то в буфер данных UART прилетит). Т.е. будет у меня так

namespace SLCAN {
  enum eIFState : u32 {
    CLOSED, OPENED, LISTEN
  };
  
  class cSlave {
    public:
      ...
      void parse(...);
    
    private:
      eIFState ifState;
  };
}


И вот теперь вопрос: в дебрях parse() я буду менять состояния ifState, присваивая ему значения из eIFState. Но это как бы выглядит несколько убого, хотелось бы в исходнике видеть адекватные понятные названия действий, например close(), open(), listen(). Запихнув приватными методами вот такой сахар

void open() {
  ifState = OPENED;
}
void close() {
  ifState = CLOSED;
}
void listen() {
  ifState = LISTEN;
}

, можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы?

То же самое касается функций каких-нибудь проверок, например,

eIFState isIFOpened() {
  return ifState == OPENED;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 minutes ago, Arlleex said:

можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы?

добавить перед названием метода ключевое слово "inline", что собственно очевидно ))

можно усилить эффект, добавив еще и __attribute__((always_inline)) на примере ARM компилятора (у GCC вроде также).

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

51 minutes ago, Arlleex said:

, можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы?

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

можно ли быть уверенным, что компилятор не создаст реальных функций, а встроит вызовы?

Нет. В си нет средств, чтобы явно указать: "сделать функцию обязательно inline". Всё на откуп компилятору - как он решит.

Если он даже из просто куска кода функции, может сам создать отдельную подпрограмму (там где в исходнике никакой функции не было), то что говорить о действительно отдельных функциях?

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

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

Просто  везёт. Даже если Вы напишите функцию: f() {} как макрос препроцессора: #define f() (...), даже в этом случае нет гарантий что в коде не окажется BL.

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

 

PS: Единственное что можно посоветовать - выделить такие участки кода (где вызываются inline-функции), в отдельный файл, и для него поставить максимальную оптимизацию по скорости или балансную.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 minutes ago, jcxz said:

чтобы увидеть это воочию во многих участках кода.

В моём случае это было в паре проектов в одном-двух местах. Поэтому контролировать было довольно просто.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Forger сказал:

добавить перед названием метода ключевое слово "inline", что собственно очевидно ))

27 минут назад, haker_fox сказал:

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

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

class A
{
  void func() {
    // some code
  }
};

равносильно

class A
{
  inline void func();
};

inline void A::func() {
  // some code
}


Можно, конечно, попробовать __attribute__((always_inline)). Хотя (если я правильно понял написанное по ссылке выше), в C++ встроенные функции будут итак изо всех сил пытаться реально "встроиться".
 

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

Нет. В си нет средств, чтобы явно указать: "сделать функцию обязательно inline". Всё на откуп компилятору - как он решит.

Надо разбираться, но в C++ ключевое слово inline и все его средозависимые подобия имеет эффект гораздо сильнее, чем в Си. Что-то было в этом роде... Но пока не берусь утверждать. Возможно что различия совершенно не в этом, а в, например, поведении вкупе с extern/static...

P.S. Ладно. Оставлю функциями, надеюсь компилятор не дурак, сделает по красоте.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, Arlleex said:

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

Нет, по опыту это совсем не так. Нет никакого значения для компилятора где объявлена и где реализована функция 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13.10.2021 в 05:14, haker_fox сказал:

Возможно, что я Вас неправильно понял. Но считаю, что это возможно. Любой последовательный порт позволяет:

1. Открыть себя (с настройкой общепринятыми параметрами: скорость, количество бит данных и т.д.).

2. Писать в себя.

3. Читать из себя.

4. Закрыть себя (не использую).

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

Я же вам описал пример для UART. Читали или нет?

Повторю:

В одном МК для инита UART-а нужно ему передать только указатель на блок регистров UART и скорость. В другом МК, для драйвера с аналогичным функционалом - ещё пару параметров к предыдущим. Например: для драйвера необходимо FIFO, и в одном МК оно есть и больше никаких телодвижений делать не надо; в другом МК (вспоминаем всеми любимые "народные" МК :wink: ) - FIFO у UART нет, и его нужно создать посредством DMA, соответственно - в функцию инициализации передавать номер DMA-канала. А в другом проекте - нужно ещё и формат UART-а (кол-во бит, чётность, ...) иметь возможность установить.

Чтобы сделать универсальное, нужно везде городить функции с множеством передаваемых параметров. Отсюда и оверхед (в том МК, где достаточно было только скорости). Можно конечно передавать в качестве аргумента структуру, но опять будет оверхед, особенно когда встретится задача, которой нельзя передать const-структуру.

Другой пример: в одном проекте у вас только одна задача работает с UART, а в другом проекте - к UART возможны обращения из разных задач. Так городить вам в вашем универсальном драйвере семафоры для доступа функционалу UART или нет? А ведь в третьем проекте может вообще потребоваться работа без ОС (или в режиме "с ОС" и "без ОС" в пределах одного проекта). И вот добвите Вы все эти семафоры и пр. в проект, и в проекте где они не нужны, получите оверхед.

 

Цитата

Пример моего абстрактного класса.

Вот как раз на вашем примере и виден оверхед:

Зачем член m_settings? А если мне он не нужен? А ОЗУ на него всё равно будет потрачена.

Или зачем функция write() возвращает какое-то значение?

Ну и многое другое....

13.10.2021 в 05:40, haker_fox сказал:

Зато остальные программисты теперь в проекте используют new/delete, векторы, листы, std::map. И стэк мы уже увеличили для нескольких задач FreeRTOS до почти мегабайта. И размер кучи в скриптел линкера уже 2 Мб!

Самое веселье начнётся, когда выяснится, что где-то происходят утечки этой памяти. Особенно если девайс работает в режиме 24/7.

Вот тогда эта кажущаяся простота куч и аукнется длительными поисками места утечки....  :dash2:

22 минуты назад, haker_fox сказал:

В моём случае это было в паре проектов в одном-двух местах. Поэтому контролировать было довольно просто.

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

Так что если "контролировать" - то после каждой минимальной правки исходника, после каждой компиляции. Веселье ещё то....  :dash2: :sarcastic:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Надо разбираться, но в C++ ключевое слово inline и все его средозависимые подобия имеет эффект гораздо сильнее, чем в Си. Что-то было в этом роде... Но пока не берусь утверждать.

И не надо. Уже >20 лет пишу только на с++ - про него и говорю.

Пример (IAR) - функция (написана в .h-файле):

#pragma inline = forced
inline u32 AtomicOr(u8 volatile *ptr, u32 arg)
{
  u32 r, i1, i2;
  asm(
    "p01: LDREXB   %0, [%3]    \n"
    "     ORRS     %1, %0, %4  \n"
    "     STREXB   %2, %1, [%3]\n"
    "     CMP      %2, #0      \n"
    "     BNE      p01           "
    : "=&r"(r), "=&r"(i1), "=&r"(i2)
    : "r"(ptr), "r"(arg)
    : "cc", "memory");
  i1 = i1; i2 = i2;
  return r;
}

Даже не просто inline, а ещё и #pragma inline = forced!

Ставим оптимизацию на LOW, получаем:

       _Z12ConfigMdfNtfm: (+1)
0xB580             PUSH     {R7,LR}                    
 AtomicOr(&configRst, map);                            
0x0001             MOVS     R1,R0                      
0x....             LDR.N    R0,??DataTable20_15        
0x.... 0x....      BL       _Z8AtomicOrPVtm            

Ставим оптимизацию MEDIUM:

       _Z12ConfigMdfNtfm: (+1)                         
0xB510             PUSH     {R4,LR}                    
 AtomicOr(&configRst, map);                            
0x....             LDR.N    R4,??DataTable26_8         
       ??p01: (+1)                                     
0xE8D4 0x3F5F      LDREXH   R3, [R4]                   
0xEA53 0x0200      ORRS     R2, R3, R0                 
0xE8C4 0x2F51      STREXH   R1, R2, [R4]               
0x2900             CMP      R1, #0                     
0xD1F7             BNE      ??p01                      

Т.е. - даже forced не помогает.  :cray:

 

Цитата

P.S. Ладно. Оставлю функциями, надеюсь компилятор не дурак, сделает по красоте.

Дурак он.  :wink:  Вот у меня программа, делает тяжёлые вычисления, соответственно оптимизация - по скорости. Но есть участки, где скорость выполнения неважна (инициализация например). И в этой инициализации есть заполнение каких-то массивов. В цикле конечно. А этот дурак циклы эти разворачивает! Создавая портянки кода из нескольких десятков инструкций там где совершенно не нужно. А мог бы догадаться..... :wink:

Или например - в функции в начале и в конце есть обращение к некоей константе. Итого = 2 раза. И этот дурак (иных эпитетов не находится :wink: ), сохраняет значение указателя в регистре на протяжении всей функции. Раздувая таким образом список сохраняемых в PUSH/POP регистров. А ведь гораздо оптимальнее - просто заново загрузить этот указатель там где он потребовался 2-й раз и не тратить регистр. Но что тут поделаешь - ДУРАК!  :biggrin:

А ведь бывает намного хужее. Например: в одном месте кода вызываются 2 функции. Обе тяжёлые - долго выполняются, редко вызываются. Соответственно - цена входа/выхода в них ничтожна на общем времени выполнении и толку от их инлайна = 0. Но он, собака, их инлайнит! И не думает даже, что и в той и в другой функции есть большие массивы на стеке. И когда функции вызывались последовательно, то массивы занимали одно и то же место в ОЗУ и всё было тип-топ. А когда они заинлайнились, то компилятор вынес оба массива в родительскую функцию (забыв при этом про union). И получаем резкое увеличение требований к размеру стека!  :dash2:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

...компилятор вынес оба массива в родительскую функцию (забыв при этом про union). И получаем резкое увеличение требований к размеру стека!

Да, такое мне тоже стреляло по коленям один разок на максимальной оптимизации. Лечил через атрибут noinline (не знаю, есть ли это в IAR, у меня Keil).

Цитата

Даже если Вы напишите функцию: f() {} как макрос препроцессора: #define f() (...), даже в этом случае нет гарантий...

ИМХО, это зависит реально от того, насколько тривиальна та функция или дефайн. У меня, например, привычка уже выработалась в пуре-Си обзывать #define()-ми какие-то простейшие, но вполне логично обзываемые операции. Теперь эту манеру я хотел перетащить в C++, но #define() штука глобальная, это минус, а вот private-методы самое оно. Они "видны" там, где это нужно, и не более. Для меня это жирный плюс:smile: Почему только разработчики компиляторов не хотят сделать прагму __attribute__((boss_sredi_inline)), чтобы 100% инлайнил... Гадство:to_take_umbrage:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...