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

В 12.12.2023 в 05:00, tonyk_av сказал:

Можно теперь послушать тех, кто изучал математику ЭВМ, численные методы и знает, как обрабатывать ошибки вычислений FPU?

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

Закладываться на обработку ошибок FPU, это примерно то же самое, что закладываться на срабатывание WDT (из-за ошибок в ПО) как на штатную работу программы.

6 часов назад, tonyk_av сказал:

Странно, что люди выполняют расчёты, но даже не знают, как проверить их достоверность, иначе сразу бы отослали меня к этому РМ.

Видимо "люди выполняющие расчёты" не лепят костыли по вашему методу. Не задумывались?

3 часа назад, tonyk_av сказал:

Такие ситуации невозможно выявить проверками до начала вычислений

Сомнительное утверждение.

3 часа назад, tonyk_av сказал:

Не такая уж и узкая и специфическая. Дело в другом. Если кто-то выполняет вычисления на FPU в задачах управления, то перед выдачей управляющих воздействий он должен проверить достоверность результата хотя бы с точки зрения математики, а то получит NaN и фиганёт его в виде 0xFFFFFFFF на ЦАП.

Для этого проверяются исходные данные на их допустимость. Чтобы не было NaN и пр. Условия возикновения NaN вроде как везде прописаны - в чём проблема проверить эти условия?

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


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

6 hours ago, jcxz said:

Видимо "люди выполняющие расчёты" не лепят костыли по вашему методу. Не задумывались?

Шедеврально! Я даже в лёгком шоке, ибо не могу сходу ответить на обзывание костылями того, без чего не работает ни одно серьёзное приложение.

Что вы увидите, когда запустите вашу программу под Виндой, в которой есть строчка?

x /= 0;

Не BSoD, а сообщение о недопустимой операции. Не задумывались, откуда оно взялось?

6 hours ago, jcxz said:

Нужно их не допускать. А обработка - только для тех ошибок, которые не смогли обнаружить

Так именно это и делается. Вычислительный алгоритм может быть сложным, да ещё и циклическим, поэтому при одном наборе данных алгоритм может давать нормальное число, а при другом- не число.

6 hours ago, jcxz said:

Условия возникновения NaN вроде как везде прописаны - в чём проблема проверить эти условия?

Посмотрите для начала список исключений, которые могут возбуждать команды FPU. Большинство команд могут возбуждать по несколько исключений в зависимости от значений своих операндов, поэтому общепринятой практикой является отсутствие проверок перед выполнением команд FPU и информирование пользователя в том или ином виде, что при выполнении вычислений произошла ошибка. Вы в своих программах перед каждым сложением или вычитаем проверяете каждый целочисленный операнд да допустимость их значений, гарантирующих, что при сложении/вычитании не произойдёт переполнения? Представляете, во что выливается такая проверка?

 

Ниже пример реализации целочисленного сложения.

image.png.7d36dd3ea20e879b06bc2a8e45c8024c.png

Код этого функционального блока:

Spoiler
// В - флаг заёма;
// С - флаг переполнения;
// Z - флаг нулевого результата.

void PLC_base::ADD( void )
{
    if( hasnotPulsFlag ) 
    {
        int16
            a = getValue(),
            b = getValue();
            
        if
        ( 
            ( ( b > 0 ) && ( a > SHRT_MAX - b ) ) ||
            ( ( b < 0 ) && ( a < SHRT_MIN - b ) )
        )
        {
            if( ( a > 0 ) && ( b > 0 ) )
            {
                clearSpecialMarker( 8021 ); // B
                setSpecialMarker( 8022 );   // C
            }
            else
            {
                setSpecialMarker( 8021 );   // B
                clearSpecialMarker( 8022 ); // C
            }
        }
        else
        {
            clearSpecialMarker( 8021 );     // B
            clearSpecialMarker( 8022 );     // C
        }
        
        acc.i16[ 0 ] = a + b;
            
        // Z
        ( acc.i16[ 0 ] == 0 ) ?
            ( setSpecialMarker( 8020 ) ) : ( clearSpecialMarker( 8020 ) );
        
        putValue();
    }
    else
    {
        pc += 6;
    }
}

FPU при работе на аппаратном уровне вычисляет и результат, и флаги, причём делает это очень быстро. Так нафига мне делать туеву хучу проверок, когда FPU просто дёрнет прерывание в случае недопустимых данных? Зачем _программно_ дублировать _аппаратный_ функционал FPU? Не задумывались? Или все люди имбицилы, которые только и занимаются делением на ноль и извлечением корня из отрицательного числа? Меня окружают люди, которые понимают математику, поэтому их программы в процессе вычислений крайне редко делают ошибки, так зачем тратить кучу времени на проверки, существенно замедляющие работу, когда можно в редких случаях обработать прерывание от FPU при ошибке вычислений и установить флаг недостоверности результата вычислений?

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


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

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

Вы в своих программах перед каждым сложением или вычитаем проверяете каждый целочисленный операнд да допустимость их значений, гарантирующих, что при сложении/вычитании не произойдёт переполнения?

Очевидно, что проверять нужно только там, где это необходимо. И да - я проверяю везде где это необходимо. Т.е. - везде где операция может привести к исключению (переполнению, делению на 0 и т.п.). И не вижу никаких проблем сделать это.

Перед каждым это не делать не нужно. Вы в школе когда-нить учились? Если учились, то должны знать, что например если складываете 2 аргумента, про значения которых известно, что они занимают не более N бит каждое, то сумма их займёт не более (N+1) бит. Если перемножаете два значения, то разрядность их произведения не должна превышать суммы разрядности операндов.

Отсюда можно догадаться - когда и где нужно проверять.

 

Вроде очевидные вещи. Не думал, что на этом форуме кто-то может не знать этого.  :unknw:

 

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

Представляете, во что выливается такая проверка?

Представляю - в одну команду CMP на каждое сравнение.

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

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

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

Допустим - есть у меня драйвер или библиотека, функционально законченная, выполняющая какую-то математику. С моим подходом - она легко переносима из проекта в проект, к тому-же - на разные платформы (хоть embedded, хоть win/linux).

А как прикажете переносить ваши костыли в другой проект? Или на другую платформу? А если в том проекте уже реализовали ваш костыльный метод - как будете объединять?

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


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

9 hours ago, jcxz said:

Зачем городить кучу кода, в котором определять - где именно случилось это прерывание?

А зачем определять где? Как оказалось, достаточно знать, что при вычислениях была ошибка. Выполнение основного потока команд продолжается, пусть и с прерыванием на установку флага ошибки. Так что кода там мизер. Пример в PM0214.

9 hours ago, jcxz said:

С моим подходом - она легко переносима из проекта в проект, к тому-же - на разные платформы (хоть embedded, хоть win/linux).

Здесь STM32, поэтому я просто использую доступный на STM32 функционал. Никого же не смущает, что код работы с UART на STM32 не работает под Виндой.

9 hours ago, jcxz said:

И да - я проверяю везде где это необходимо. Т.е. - везде где операция может привести к исключению (переполнению, делению на 0 и т.п.). И не вижу никаких проблем сделать это.

То есть тупо дублируете функционал FPU. Зачем? Не доверяете FPU? Ведь проще выполнить операцию и посмотреть флаги результата её выполнения, чем городить кучу кода для проверок или вычисление флагов. Пример выше, в функции ADD, где значения флагов B, C, и Z определяются _до_ выполнения операции сложения, хотя если уйти от кросплатформенности, то можно просто копировать в маркеры значения флагов процессора после выполнения сложения, что будет значительно быстрей и потребует значительно меньше кода.

Ещё раз: это нормальный способ. На разных платформах разные механизмы обработки.

Изменено пользователем tonyk_av

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


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

Я тоже не понимаю, что делать в обработчике исключения FPU. Сохранять дамп стека для диагностики и уходить в рестарт?

Чем не устраивает проверка результата на NaN?

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


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

17 minutes ago, amaora said:

Я тоже не понимаю, что делать в обработчике исключения FPU

Пример в PM0214. Плюс установить для пользователя флаг ошибки вычислений

18 minutes ago, amaora said:

Чем не устраивает проверка результата на NaN?

NaN только пример. У FPU есть ещё исключения.

Ещё раз: смысл обработки исключений FPU в том, чтобы не тратить время на проверки перед выполнением операций, дублируя функционал FPU, а реагировать на ошибки, выявляемые FPU в процессе его работы. Ведь ошибки должны возникать редко, так какой смысл тратить на проверки значений операндов команд времени больше, чем выполняется сама команда?

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


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

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

Ещё раз: смысл обработки исключений FPU в том, чтобы не тратить время на проверки перед выполнением операций, дублируя функционал FPU, а реагировать на ошибки, выявляемые FPU в процессе его работы. Ведь ошибки должны возникать редко, так какой смысл тратить на проверки значений операндов команд времени больше, чем выполняется сама команда?

Обычно в серьёзной программе математики бывает много. Она бывает часто размазана по множеству файлов, функций. Эти функции могут вызываться из разных задач ОС и ISR.

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

Что значит "тратить на проверки значений операндов команд времени больше, чем выполняется сама команда" если предлагаемый вами способ приведёт как раз к увеличению трат? При таком событии, чтобы найти где именно возникла ошибка, и как на неё реагировать, придётся вводить какие-то флажки состояний. По которым ISR FPU сможет понять - где произошла ошибка? К тому же - эти флажки нужно оперативно переключать при переключении задачи ОС и при активации прерывания (в котором тоже могут быть вычисления).

У вас весь вычислительный код будет утыкан переключениями флажков. А также - переключатели контекста задач ОС должны содержать средства для сохранения/восстановления этих флажков в контексте прерванной задачи ОС.

Это породит кучу лишнего кода. При том, что и сам анализ в ISR FPU будет тоже развесистым.

 

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


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

16 minutes ago, jcxz said:

При таком событии, чтобы найти где именно возникла ошибка,

Искать не нужно, адрес в стэке.

17 minutes ago, jcxz said:

В каких-то из этих функций - ошибку можно проигнорить (сбросить)

Ошибка FPU сбрасывается всегда. Для пользователя выставляется флаг ошибки, с которым он волен делать что хочет.

20 minutes ago, jcxz said:

При таком событии, чтобы найти где именно возникла ошибка, и как на неё реагировать, придётся вводить какие-то флажки состояний

Ессно! В используемой модели данных ПЛК уже есть флаги ошибок вычисления. Мне просто нужно их устанавливать.

21 minutes ago, jcxz said:

По которым ISR FPU сможет понять - где произошла ошибка?

Читаем PM0214. И какая ошибка, и где она возникло- всё известно при входе в обработчик.

23 minutes ago, jcxz said:

эти флажки нужно оперативно переключать при переключении задачи ОС

У каждой задачи свой набор исключений, так что это не проблема.

24 minutes ago, jcxz said:

при активации прерывания (в котором тоже могут быть вычисления)

Ну так сделайте приоритет прерывания FPU выше, чем у прерываний, использующих FPU. В чём проблема?

25 minutes ago, jcxz said:

У вас весь вычислительный код будет утыкан переключениями флажков

Какими переключателями? Нет там никаких переключателей флажков, в этом-то и смысл использования прерывания FPU.

Это же ПЛК, там нет кучи флагов, а есть условное исполнение: загрузил инверсию флага ошибки в LRO и всё. В зависимости от этого флага все команды, зависящие от него или будут, или не будут исполнятся.

image.thumb.png.71ffaf285038743116d029252e826bc2.png

 

 

36 minutes ago, jcxz said:

переключатели контекста задач ОС должны содержать средства для сохранения/восстановления этих флажков в контексте прерванной задачи ОС.

Используется FreeRTOS, для которой написана обёртка на С++. При создании задачи ОС в параметрах задачи сохраняется указатель на экземпляра класса задачи, в котором будет флаг ошибки FPU. Думаю, в FreeRTOS API найдётся функция извлечения дескриптора выполняемой задачи.

image.thumb.png.ce3c2f6dc07e9718a5564484c85b243f.png

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


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

19 минут назад, tonyk_av сказал:

Искать не нужно, адрес в стэке.

И дальше что? Как этот адрес будете сопоставлять конкретному участку си-кода? Разбивать этот код на кучу мелких функций?

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

А если та функция вызывает из себя другую функцию?

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

19 минут назад, tonyk_av сказал:

Ошибка FPU сбрасывается всегда. Для пользователя выставляется флаг ошибки, с которым он волен делать что хочет.

А ничего, что сбросить её можете и там, где не нужно? Где её быть не должно, а возникла она из-за программного бага?

19 минут назад, tonyk_av сказал:

Какими переключателями? Нет там никаких переключателей флажков, в этом-то и смысл использования прерывания FPU.

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

А значит нужно сохранять/восстанавливать этот флажок при переключении задач в контексте прерванной задачи.

40 минут назад, tonyk_av сказал:

Это же ПЛК, там нет кучи флагов

Кроме кода ПЛК в программе что-ли нет другого кода, использующего FPU?

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


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

On 12/14/2023 at 5:32 PM, jcxz said:

И дальше что? Как этот адрес будете сопоставлять конкретному участку си-кода? Разбивать этот код на кучу мелких функций?

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

 

On 12/14/2023 at 5:32 PM, jcxz said:

А ничего, что сбросить её можете и там, где не нужно?

Код ошибки запоминается в поле задачи.

Spoiler
class Task : protected TaskBase 
{
	...

    public:

        enum FPU_error                  // Флаги ошибок FPU
        {
            NO_ERROR            = 0x00, // нет ошибок;

            INVALID_OPERATION   = 0x01, // неправильный код команды FPU;
            DIVIZION_BY_ZERO    = 0x02, // деление на ноль;
            OVERFLOW            = 0x04, // переполнение вверх;
            UNDERFLOW           = 0x08, // переполнение вниз;
            INEXACT             = 0x10, // потеря точности;
            INPUT_DENORMAL      = 0X80  //  денормализапция числа.
        }
        fpu_error;
	...
}

////////////////////////////////////////////////////////////////////////////////

void
Task::handleFPUerror( Task::FPU_error _error )
{
    if
    ( _error & \
        (
            Task::FPU_error::INVALID_OPERATION  |
            Task::FPU_error::DIVIZION_BY_ZERO   |
            Task::FPU_error::OVERFLOW           |
            Task::FPU_error::UNDERFLOW          |
            Task::FPU_error::INEXACT            |
            Task::FPU_error::INPUT_DENORMAL
        )
    )
    {
        __disable_irq();
        {
            fpu_error = _error;
        }
        __enable_irq();
    }
}

////////////////////////////////////////////////////////////////////////////////

void
FPU_device::IRQ_Handler( void )
{
    volatile uint32
        fpscr_val;

    // Читаем регистр флагов состояния VFP.
    asm volatile( "VMRS %0, FPSCR" : "=r" ( fpscr_val ) );

    // Получаем указатель на класс выполняющейся сейчас задачи.
    Task*
        curr_task = Task::getCurrentTask();

    if( curr_task != 0 )
    {
        curr_task -> handleFPUerror( ( Task::FPU_error )( fpscr_val & 0x9F ) );
    }

    // Сбрасываем все флаги ошибок.
    fpscr_val &= ( uint32 )~0x9F;
    asm volatile("VMSR FPSCR, %0" : : "r" ( fpscr_val ) );

    // Очищаем запрос прерывания.
    IRQ::clearIRQ( FPU_IRQn );
}

 

 

On 12/14/2023 at 5:32 PM, jcxz said:

Кроме кода ПЛК в программе что-ли нет другого кода, использующего FPU?

Пока нет. Я понимаю о чём речь.

Приоритет прерывания от VFP выше, чем у планировщика FreeRTOS, поэтому пока не отработает прерывание VFP, задачи не переключатся. В других прерываниях VFP не используется, но если потребуется, то нужно будет сохранять его контекст при входе в обработчик, но пока при входе в другие обработчики я не делаю даже ленивого сохранения.

Изменено пользователем tonyk_av

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


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

13 минут назад, tonyk_av сказал:

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

Ну если так, то наверное будет работать.

Но нужно быть аккуратнее с использованием чужого кода (если будете его использовать). Кто его знает - что там в глубинах чужого кода делается? Вдруг там по какой-то ветке операции FPU выполняются... а вы контекст не сохранили... Так можно получить трудноуловимый баг.

Я тоже так раньше делал (отключал ленивое сохранение контекста для экономии стека прерываний), так как использовал FPU только в одной задаче. Но нужно быть уверенным, что знаешь все места, где в твоём коде используется FPU.

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


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

18 minutes ago, jcxz said:

Но нужно быть аккуратнее с использованием чужого кода

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

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


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

В некоторых случаях видел как компилятор реализовывал примитивный цикл копирования 32-битных слов через 64-битные регистры fpu. Долго ловил жука, не ожидал, что инструкции fpu окажутся в том месте где работы с fp не было ни в каком виде.

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


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

7 hours ago, amaora said:

В некоторых случаях видел как компилятор реализовывал примитивный цикл копирования 32-битных слов через 64-битные регистры fpu. Долго ловил жука, не ожидал, что инструкции fpu окажутся в том месте где работы с fp не было ни в каком виде.

О как. Возьму на заметку, буду обращать внимание на дизассемблер.

По-моему, такие фишки должны управляться опциями компилятора, разве нет?

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


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

7 часов назад, amaora сказал:

В некоторых случаях видел как компилятор реализовывал примитивный цикл копирования 32-битных слов через 64-битные регистры fpu. Долго ловил жука, не ожидал, что инструкции fpu окажутся в том месте где работы с fp не было ни в каком виде.

Согласен. Тоже сталкивался с таким. Даже где-то здесь на форуме писал об этом (можно поискать). Именно после этого я и перестал выключать сохранение контекста FPU.

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

По-моему, такие фишки должны управляться опциями компилятора, разве нет?

IAR это делает автоматом. Иногда. И отключить это можно только полным запретом использования FPU (насколько помню - к такому выводу пришли в результате обсуждения тут).

10 часов назад, tonyk_av сказал:

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

А стандартную библиотеку Вы какую используете? С плавающей точкой или без?  :wink:

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

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


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

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

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

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

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

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

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

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

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

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