All streams
Search
Write a publication
Pull to refresh
88
0
Вадим Дерябкин @Vadimatorikda

Инженер-программист

Send message
Кстати, если вы только изменяемые пользвателем данные резервируете и только при старте проверяете, то ваш велосипед совсем плох.

Чем плох? Мы при старте убеждаемся, что все хорошо или делаем, чтобы все было хорошо и далее работаем точно зная, что данные целостны. Хотя, конечно. есть вероятность, что может что-то пойти не так из-за аппаратки. Но это уже не очень понятно как обходить. Конечно, идут проверки на != -1 (либо все 1. Если хоть 1 бит выбился, то не валиден флаг и т.д.). Но таких штук лично я почти не применяю. Не было случая, когда реально пригодилось.
Только отдельные сектора памяти, описание всех данных в общей структуре и выбор копии при старте. И никакого шаманства не нужно в 90% случаев — старые добрые указатели, либо копия структуры в ОЗУ.

Так вроде я и писал об этом. Ну только если копия повредилась, ее еще по другому экземпляру восстанавливают.
Вот, не хотелось бы обижать, но вот про это я и говорю ;) Тут вопрос, есть ли в фирме кто-то, кто это точно знает, применительно к используемым компилятором. Если есть, то это его зона ответственности. Если нет, то мы имеем то, что имеем — люди учатся на живом коде и где-то что-то летает, что содержит ошибки и непредсказуемое поведение.

А есть иные варианты, кроме как учеба на живом коде? О многих вещах даже не задумываешься до того, как не столкнешься. А на чтение документации обычно времени практически нет. Ибо в моем случае я не только пишу код, но еще и тесты, требования, и т.д. Хотя хотелось бы как вы, конечно. Просто сидеть и копать в свою область. Это полезно, я считаю.
И про malloc/free тоже. Эти функции используются стандартной библиотекой даже если вы вы их не пользуете и даже если вы стандартную библиотеку не используете. Например, если вы используете в плавающую точку в любом виде на GCC, то вы уже используете динамическую кучу.

Хочу обоснований. Я просто осматривал _sbrk. Она не дергалась вроде ничем при работы с плавающей кучей (есть аппаратный блок работы с плавающей точкой). Куда копать?
А если вы ещё и FreeRTOS используете, то нужно убедиться, что структура "_reent" переключается и работает правильно.

Вот честно. Даже не знал о существовании такой штуки. Об этом статей не видел. Вот и не знал. Смотрю, вы много знаете нюансов. Был бы рад, если бы хоть писали об этом. С радостью бы почитал (без сарказма).
MMU не просто нужен. Если есть, то он обязателен в абсолютно любом проекте, т.к. позволяет найти ошибки, которые иначе вообще никак не найдёшь. Настраивается он за 15 минут, если мы говорим про базовый MMU и разделение регионов памяти (запрет исполнения из ОЗУ, запрет записи в RO-области ОЗУ). Полноценная виртуальная память, которая тоже MMU, это уже сложнее.

С просони не так написал. Не MMU, а MPU. MMU нет на тех процессорах, с которыми приходится работать. А вот настроить MPU для той же защиты при переключения контекста FreeRTOS — хотелось бы. А то без дела лежит… Руки не доходят.
Ну, если мы про отказоустойчивость говорим, то это, как раз, хорошо и правильно. Это позволяет писать легко-верифицируемый код. Откуда уверенность что все критичные данные находятся в защищаемый секциях? А точно правильно работает переключение на резервный набор данных? Это обязательно должно быть собрано в одном месте иначе верифицуемость такого кода под большим вопросом.

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

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

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

Тут полностью согласен. Мы составляем внутреннее ТЗ, в котором явно прописано, что и где должно лежать.
В 90% случаев вообще можно обойтись фоновым контролем целостности RO-секции программы и переключением на резервную копию через перезагрузку.

Во время работы нет надобности контролировать RO секцию. Она анализируется только при включении устройства. Устройство должно включаться за очень малый промежуток времени (они на летающем аппарате. Хоть и без людей. Если бы там были люди, то такого вопроса бы вообще не было. Поскольку там была бы внешняя flash, аппаратно продублированная и прочие штуки).
Описывайте параметры своих ШИМов в отдельных структурах и потом объединяйте в одну.

Отписал, почему не согласен (принятое соглашение между разработчиками).
В общем, тут категорически не согласен — цель же условие ТЗ «4 копии каких-то данных иметь» исполнить, а ещё и отказоустойчивую систему сделать.

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

Ни в коем случае. Как писал выше, только то, что может быть изменено человеком, который будет интегрировать устройство в финальный комплекс.
А как насчёт всяких служебных, которые компилятор для себе генерирует? Там же до 30% RO-данных это служебные константы и всякие адреса переходов, которые компилятор нагенерировал. Их, что, защищать не нужно? А какой тогда смысл защищать «режим работы UART», если у вас в десять раз больше констант, относящихся к критичному коду (в том числе работы с тем же UART) оказываются незащищёнными? У вас в этом случае отказоустойчивость не на два порядка вырастает, как по ТЗ задумывалось, а на 5% — а смысл? В этом случае, как я говорил — делается защита вообще всей области RO-данных и несколько экземпляров этих данных с переключением с случае нарушения целостности рабочей копии. Это можно сделать только тем способом, что я описал. Иначе ТЗ, вроде бы как, выполняется, но отказоустойчивость не только растёт, но иногда и падает. Естественно, что настоящей проверки и сертификации такой код пройти не может (на практике в России и не такой проходит).

Вот тут отмечу то, чего не было в статье. Да, я не храню копию данных того, что генерирует компилятор. И констант, которые не меняются. Но тут есть нюанс. Дело в том, что я 100% знаю, что весь код моей программы в момент включение — без ошибок (аппаратных). Имею ввиду, что прошивка соответствует той, которая была зашита. Об этом заботится загрузчик при включении. Вся прошивка целиком тоже дублирована. Содержит свою контрольную сумму. В статье же речь именно о изменяемых константах, которые может менять пользователь в отрывае от прошивки (ведь эти параметры лежат совсем в других областях flash. Опять же повторюсь. Если бы речь шла о перевозке людей, тогда все эти данные лежали вообще в отдельных накопителях с аппаратным резервированием. Т.к. нельзя позволить микроконтроллеру писать свою же память. Хоть и в другой странице.).
Вопрос поддержки кода решается документацией и разделением квалификации исполнителей. Если студенты пишут критичный код, то проекту ничего не поможет.

Это попытка оскорбить или сравнение с типовым случаем?) (типа я тоже студент формально… Хоть и не долго осталось до окончания)
А если критичный код написан профессионалом, проверен, документирован и дальше в него никто не лезет, то можно в какие-то другие части с студентов пускать после прочтения инструкции.
Вопрос же заменяемости… Чтобы писать качественный отказоустойчивый год, нужен специалист соответствующего уровня. Если он есть и решение документировано, то он без проблем разберётся. Абсолютно ничего сложного в моём решении — любой, кто значет, как работает линкер без проблем разберётся. Мы же, в конце-концов, говорим про работу, требующую специалиста высокой квалификации и достаточно редкой специализации.

Его трудно найти, легко потерять, и нереально удержать в Красноярске… Тем более за не особо высокую ЗП… Тут уже далеко не технический вопрос. Но и про документацию и прочее. В фирмах, которых мне довелось работать, документация — это последнее, до чего доходят руки. ТЗ иметь на бумаге вот и то редкость. В текущей фирме только начал появляться весь этот задел. Когда сначала ТЗ, потом под-требования, тесты, потом код. И это хорошо. Но фирме больше 7 лет. Что говорить о фирмах, которые «со подрядчики» и т.д.? Довелось работать с людьми, которые разрабатывали многомиллионное оборудование по ТЗ на словах) Тесты — на заказчике. Устройство не серийное. Там устройство не летало. Это оборудование для весьма не маленького предприятия. И… Там двух слойная плата управления без особых защит, никакого резервирования и т.д. При неудачном стечении обстоятельств (вероятность которых в условиях цеха очень высока), оборудование придет в негодность. Ну а надо всем там было срочно, переделывать не надо. работает же. Прототип? И так пойдет… В общем, все зависит тут от конторы. Но я считаю, что чем решение топорнее, тем лучше. Проще будет разгрузиться с себя 100500 разных проектов и технический долг будет хоть капельку меньше.
Я прямо, даже растерялся — мы сейчас точно про разработку авионики говорим, что ушедшего специалиста заменит некем? Ну, в российской, как правило — да. В общем и целом — нет.

Не просто про Россию, а про Красноярск) Днем с огнем не сыщешь человека, который бы писал код хоть как-то вменяемо. При этом был готов если что переквалифицироваться в тех. писателя, пусконаладчика и т.д. Фулл-стек ембеддера короче).
Если «звонят после увольнения», то это проблемы звонящего и верный признак, что звонящий занимается не своим делом.

Это значит, что человек ушел и не оставил ни строчки пояснений, а его код местами выглядит вот так:
p = 1048576.0 - (double)adc_P;
	p = (p - (var2 / 4096.0)) * 6250.0 / var1;
	var1 = ((double)calibrationTabel.dig_P9) * p * p / 2147483648.0;
	var2 = p * ((double)calibrationTabel.dig_P8) / 32768.0;
	p = p + (var1 + var2 + ((double)calibrationTabel.dig_P7)) / 16.0;

Конечно, иногда помогает вдумчивое чтение документации. Но когда человек «для экономии ресурса» умножает 2 константы из документации и использует как одно число прямо в коде — догадаться об этом ОЧЕНЬ трудно. Да и человек порой писал код этот в свободное от командировок по интеграции устройств время. Так сказать «за чашкой чая». И в то время, когда компания только зарождалась — это было нормально и единственным решением чтобы выжить. Но те времена прошли и настало время уже следующему поколению сотрудников разбираться с этим… кодом) И «одна большая структура, которая лежит типа в оперативной памяти, но под нее зарезервировано с запасом 2 блока, в которые данные не пойми как попадают» — один из примеров. В вашем случае вы действительно их туда кладете. Я же столкнулся с реализацией, когда просто 2 страницы резервировались. И не важно, влезет туда структура или нет (конечно, обычно структуры 1-3 кб, а резервировалось 128, но все же).
Качественный код стоит денег, квалификации и определённой организации работы. А на практике, обычно оказывается, что вместо ушедшего специалиста посадили студента на 35 тысяч, который попутно изучает программирование. Ну, так это проблемы — оно за это деньги получает. Продают-то они этот код не за пять рублей.
Специалистов должно быть несколько, их уровень и опыт должны соответствовать задаче, а документация должна иметься.

3 человека хватит на все))) На каждом из которых куча проектов и задач не связанных непосредственно с программированием (например, понять почему упал борт, почему QNX внезапно начинает видеть битые блоки при включении при определенном стечении обстоятельств и прочие штуки).
В общем, тоже не согласен. Решение на самом деле достаточно несложное и если кто-то его не тянет, то возможно ему не стоит заниматься отказоустойчивыми системами, ибо, как я уже сказал, никак иначе по-настоящему отказоустойчивый код вы просто не напишите — так, симуляцию отказоустойчивости, может быть. Короче, если кто-то не обладает необходимыми знаниями для решения задачи, то это его проблемы, а не того, кто обладает.

Про достаточный уровень — соглашусь. Про нехватку кадров и надобность в более простых по реализации решений, но при этом не уступающим по качеству — отписал выше.
Про таблицу инициализации сегментов. Это выглядит примерно так:

Красиво! Реально интересная тема. Не видел раньше, чтобы так пользовались LD скриптом. Обычно +- редактируют LD из демонстрационного проекта и на этом заканчивают.
С точки зрения C/C++ это выглядит как массив структур:

Ну это понятно, да. Интересно. Очень интересно.
Массив в коде доступен под именем "__region_list_start__".
Стартап код до вызова любого другого кода (кроме инициализации стека, WatchDog и тактового генератора), вызывает процедуру (которая тоже находится стартап-сегменте и потому доступна с самого начала), которая проходит по таблице и обрабатывает все регионы с флагом «FREG_STARTUP».

Ну это следует из такой организации.
Потом проходим по таблицам инициализации стандартных библиотек и вызываем их код. Вы же, надеюсь, проходите по таблицам "__preinit_array_start__" и "__init_array_start__" — стандартные таблицы для GCC — без этого маcса глюков будет.

Вот тут уже не подскажу, но вроде это нужно только если вы C++ используете. Там конструкторы глобальных объектов и прочее. Если мне память не изменяет. В чистом C вроде не нужно. Но тут не уверен.
Потом стартуем стандартную библиотеку.

Всегда корю себя за то, что пользуюсь стандартными библиотеками. По-хорошему, в настоящей авиации нельзя. Ни printf ничего такого. Пока используем. Но вообще нельзя.
И уже в main завершаем инициализацию железа и снова вызываем ту же функцию инициализации сегментов. Но теперь уже она инициализирует только сегменты без флага «FREG_STARTUP» — находящиеся во внешней памяти, во всяких аппаратно-защищённых областях памяти т.п. Потом включаем защиту RO-областей памяти и кода в MMU если есть (а какой может быть отказоустойчивый код без этого) и, вот, после этого мы готовы стартовать основной код.

Мне пока так и не удалось кстати настроить MMU + FreeRTOS. Ну это пока никому не требовалось, но вообще сам понимаю, что это необходимо. Просто времени не хватает сделать как положено. Стараюсь эксперементировать с такими штуками дома. А то на работе оправдать то, что ты возился с MMU вместо решения «реальных задач» — достаточно сложно.
В ту же функцию инициализации сегментов можно затолкать функционал выбора неповреждённой копии сегмента при ошибке CRC. Если есть аппаратная поддержка через ремапинг адресов, то можно и код защищать. И только так можно по-настоящему что-то защитить дублированием. Иначе получается что-то совсем не то, что хотелось бы.
Именно так это делается в «best practices» и серьёзных библиотеках.

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

Мы используем стандартный FreeRTOS-овский. Он переопределяет malloc и free. Но вообще в наших устройствах, которые летают нет malloc/free/new/delite. Только статически размеченная ram. В угоду отказоустойчивости.
Вадим — вы очень странно работаете с настройками, то-есть со статическими данными.

Поясните. Как «не странно»?
1) Описываем в заголовке структуру с данными, которые должны дублироваться. И тип указателя на неё. Там же объявлем внешние символы с типом этого указателя и именами, описанными в скрите линкера (см. дальше).

Долгое время на работе работал с этой реализацией. У нее есть следующий недостаток: <<Кучу разных данных, никак не связанных между собой по смыслу требуется описывать в одном месте.>>. Сейчас на работе пришли к тому, чтобы было четкое разделение логики и аппаратной части. Например метод «tim_set_pwm_duty» внутри и «set_lcd_brightness» наружу. В этом случае у tim_set_pwm_duty могут быть настраиваемые параметры на уровне железа (например границы ШИМ), а у set_lcd_brightness — параметры на уровне пользователя (например, допустимый диапазон яркости при текущем режиме работы). Сам код прекрасно разносится по разным уровням абстракции и позволяет не менять логику при переходе на другой микроконтроллер. К нему претензий нет. А вот то, что обе эти функции должны знать о существовании общей глобальной структуры несколько огорчает (да, каждая знает только о своей части в этой структуре. Но глобальная структура на весь проект — не есть хорошо). В моем же подходе можно создавать сущности (переменные, структуры) в совершенно разных местах. При этом всегда в map можно посмотреть, где и что лежит, если требуется. Это основное преимущество.
2) Добавляем в проект исходный файл, в котором находится определение данной структуры в сегменте ".myPreciousData" (чтобы можно было несколько структур в одном файле располагать, например). Можно совершенно нормально задавать начальные значения, использовать сложне типы и т.п. Очень приятно и читабельно.

Отписал выше. Нарушается инкапсуляция и падает логика. Хорошо заметно на больших проектах, когда твоя структура под пару сотен элементов в куче по типу: «границы ШИМ каналов таймеров + параметров интерфейса uart + режим работы USB»…
3) *** МАГИЯ *** Компилируем этот исходный файл в несколько РАЗНЫХ объектных. Реализация зависит от системы сборки. В Eclipse CDT и некоторых других можно нашаманить несколько «ссылок» на файл с разными именами, что позволяет скомпилировать один исходник под несколькими именами (вообще сказака). В CMAKE, make и прочих руками описываем компиляцию этого файла несколько раз с разными выходными именами (объектных файлов) — тоже отлично. В тяжёлых случаях ставим хук на компиляцию и тупо копируем исходный объектный файл под несколькими именами (тем более, что у меня всегда есть хук на линкер, т.к. есть автоинкремент версии сборки с подстановкой даты сборки) — кривова-то уже.
4) В линкере спокойно раскидываем наши файлы (с указанием имени файла и нашего сегмента ".myPreciousData", чтобы лишнего не попало) по разным секциям создавая символы начала секций. Секции располагаем где хотим. Инициализацию секций в ОЗУ делаем так же, как и для обычных сегментов данных в ОЗУ (т.е если проект правильно построен, то прописываем в таблицы инициализации).
5) В исходный текстам импортируем описанные в скриптах ликера имена с типом указателя на нашу структуру.

В подходе, который был на работе, делалось проще. Просто в RAM создавалась структура (та самая, глобальная). Просто пустая. Ее начальные данные в flash. В модуль работы с этим делом из компоновщика передавались указатели на области flash. Все. По сути, это недоработанный ваш метод. Вашим можно еще узнать, сколько заполнено и выводить это. Хотя makefile/CMakeLists будет выглядеть действительно страшно. А я категорически против «интересных» решений в этой области. Это делает код сложно воспринимаемым и ведет к вопросу у тех, кто должен поддерживать это, когда вас нет на рабочем месте или вы вовсе его сменили. Но это делает вас не заменяемым на работе… Настолько, что вам звонят даже после увольнения и просят рассказать, как оно работает… Потому что «нужно поддерживать», а никто не знает почему оно вообще работает… Прошу прощения. Немного накипело на эту тему. Приходилось сталкиваться с очень хитрыми системами сборки.
И ещё маленький секрет — у меня не просто раздельные таблицы иницализации секций data, bss и т.п.

Что такое «таблица инициализации data, bss...»? Интересно посмотреть.
На выходе полнейшая радость и удобство с нулевым оверхедом. И никакой магии на уровне исходного кода.

У вас дикий оверхед на уровне сборки. Чтобы в этом разобраться — придется не слабо повозиться, если изначально не знать всех нюансов. В моем же случае есть небольшой наклодняк со стороны кода (хоть и невидимый для программиста), но никаких проблем на уровне сборки.
Да. Авионика. Конечно же, статика, защиты от зависания, стеки в несколько раз больше, чем нужно и прочие штуки по стандартам. Но если есть аппаратная возможность, почему бы еще и ей не воспользоваться? А уронить самому, понятное дело… Начиная с тестов алгоритмов в симуляции (как-нибудь напишу про это), заканчивая тестами на длительное выполнение.
У меня зачастую работа с устройствами, в которых «нужно выжить любой ценой и не уронить аппарат». Так что там как правило задается с очень сильным запасом все. Тут скорее имею ввиду, что не получится ситуации, когда перешедший за пределы выделенного стек задачи не перетрет данные, с которыми работает программа. То есть это дает возможность избежать ситуации, когда код продолжает работать, но уже с битыми данными. И начинаются приключения типа «в конфиге же было задание шим от 0.1 до 0.7 держать (скважность), почему вдруг резко начал выдавать 0.9? (то есть значение момент обновилось в таймере, а там уже было битое значение из-за того, что стек соседней задачи был поблизости и перетер данные, с которыми работает другой поток.
Да. И это тоже. Вообще, по опыту, CCMRAM идеальное место для хранения стеков/внутренних структур объектов FreeRTOS (если используется в проекте). Позволяет решить вопрос с тем, когда переполнение стека задачи затрагивает данные, с которыми работает прошивки в основной RAM.
Да. Был опыт работы над устройством измерения частот с 8 каналов. Каждый импульс приходило прерывание. Из него я узнавал на каком канале произошел импульс и его длительность (ведется статистика отклонений (наибольшее, наименьшее) и прочие штуки по тех заданию). И вот. Параметры, которые следует фиксировать задаются пользователем. Для того, чтобы удовлетворить требованиям пришлось разместить методы обработчики в RAM, как и саму таблицу векторов прерываний. И каждый раз дергать данные из FLASH, выполняя обработчик из RAM оказалось очень накладно. Конкретных цифр привести не могу. Со временем пришло понимание, что именно настройки даже лучше размещать не в RAM, а в CCRAM. Чтобы разгрузить шину (код выполняется из RAM, а данные берутся параллельно из CCRAM). Про CCRAM справедливо только для F405 (с которым работал). Возможно и для других мк/серий тоже.
Задумайтесь, как по-вашему начальные значения попадают (или как должны) в переменные в озу и чем это отличается от вашего случая?

При старте МК попадаю в обработчик по reset. Там копирую из области data (во flash) в область data в ram. Область bss, noinit заполняю 0-ми. Но причем здесь это? Тут ведь речь про данные, которые должны быть в отрыве от прошивки МК. Они должны быть в строго определенных местах, чтобы в случае чего можно было снять dump памяти и посмотреть, с какими последними параметрами стартовало устройство.
У вас и так для каждой секции линкер поддерживает два адреса — адрес в образе и адрес времени исполнения

А нужно 4 по ТЗ. Резервирование. Надежность.
чем это отличается от вашего случая?

Тем, что то, что попадет в RAM зависит от того, целостны ли данные в момент подачи питание или после перезагрузки в резервированных блоках или их стоит заменить начальными из текущей актуальной прошивки.
RTFM, же!

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

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

Но ведь я тут про дополнение linker-скрипта распинался…
И это хорошо ;) т.к. конкуренция меньше, но все же!

Людей, умеющих адекватно работать с ld и прочими низкоуровневыми штуками и так мало… Не надо уменьшать.
И не нужно делать макрос отдельно для массивов. Нужен просто макрос, задающий имя секции. И ставить его в коде при определении массивов, структур, переменных и прочего.

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

У нас обычно структуры задаются с атрибутом упаковки. Так что внутри них может и не быть адекватного выравнивания. Код к этому готов. А вот сами структуры/массивы и прочие в отдельности обязаны быть выравнены на 4. По-умолчанию компоновщик пытается все объявленные переменные через макрос выше складывать подряд. Без выравнивания. Тут атрибут выравнивания скорее дополнительная гарантия, что этого не произойдет. Уже сталкивался на практике с ситуацией, когда переменные лежали без выравнивания, а обращение к ним шло с использованием взятия ассемблерной команды по кратному 4 адресу. И падение в соответствующий обработчик гарантировано.
И таблицы секций тоже можно и нужно делать через скрипт ликера.

Но ведь я там их и объявил…
Вместе с указателями размерами, флагами и чем хотите — всё необходимое в лимонное есть.

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

Не понял смысла предложения. Поясните пожалуйста, что вас не устроило. Я в mem.ld резервирую место под CRC в областях памяти, реализующих резервирование настроек пользователя.
Платите, но ваш велосипед ужасен и вреден.

Кому платить? Подводя итог, что именно ужасно? Выше аргументировал каждый пункт вашего недовольства.
Тут есть несколько вариантов. Не так давно сталкивался с этим снова. На новом arm-none-eabi (9-я версия. Последняя на момент написания комментария).
Убедитесь, что:
  1. Стека задачи достаточно (тут я так понял вы используете snprintf. Пишете в буфер и потом буфер отсылаете. Ради интереса можно задать буфер сильно больше нужного.
  2. В прерываниях не происходит переполнения стека. Как стека задачи, в которой произошло прерывание, так и соседних задач. Может у вас есть задача, которая сбрасывает wdt, а в ней стек на 128 элементов. Появляется прерывание. Пишет 4 килобайна, вы возвращаетесь в задачу wdt, планировщик меняет задачу на другую и тут все падает.
  3. У вас достаточно места в «куче». Имею ввиду не freertos кучу, а та, что «не занятое место» между буферной зоной стека и bss областью. Ее пользует функция _sbrk. Она считает, что место от конца bss области до начала зарезервированного стека (физический конец оперативной памяти) принадлежит ей (функции _sbrk). Тут надо оставить хотя бы 1 кб. Реально используется около 500 байт, когда вы устанавливаете например единый буфер.
  4. Можно явно задать буфер, с которым будет работать printf/scanf. setvbuf функцией (помните, что когда вы их вызовите, случится еще дерганье sbrk функции, которая сохъст 500 байт в «куче» (та что не принадлежит freertos). Кстати. Устанавливать буферы надо до запуска планировщика.

Если это соблюдено, то можно попробовать:
Ничего себе какая прелесть. Пошел читать про это!)
Похоже имеют ввиду, что даже CubeMX научился генерировать define по имени пина, чтобы не было этих безымянных MODEX_Y.
А, и самое главное. Проверять перед отправкой, а не после. Чтобы по-максимуму использовать SPI время.
Тут (на хабре) некоторое время назад была статья про то, как достигать максимальной скорости из SPI (даже без DMA). Сколько помню, все свелось к тому, что нужно проверять флаг того, что данные передались из DR во внутренний сдвиговый и сам DR сейчас «пуст» (в него можно писать). И, по сути, за счет этого достигается эффект непрерывной передачи. Типа данные еще передаются, а мы уже заготовили в DR следующую пачку. Ну а вообще лучше DMA конечно. Но это чуть усложнит пример (ИМХО).
Ну так ведь «отладка перепрошивкой» — это достаточно распространенная проблема у 8-ми биток. Я вот только у atmega16+ видел jtag и 1-wire у attiny2313 (говорят что с недавнего времени и у младших появились). Ту же atmega8a отлаживаешь по принципу «зашил и смотришь». Вроде у stm8 есть какое-то подобие отладки. Но тут уже не удалось поработать.
Лично мне из названия было ничего не понятно до тех пор, пока я не открыл статью и не дочитал до примера кода на C#. В заголовке и тегах об этом ни слова.
Ну в целом впечатления смешанные. Но безусловно стоит продолжать. Пока еще не совсем ясно, что и зачем. Да, интересно. Да, на вид круто. Но что и куда — не ясно. К тому же не ясно, как даже попробовать это все (тут многие используют Linux...).
Что-то меня это сообщение задело и я даже пошел, проверил. Да, вот. Как раз в это время разбираться с constexpr. Тогда же примерно задавал вопросы по этой теме. Потому что тогда переходил с C++11 на C++14.
Спрашивал. Не соглашусь правда что за основы (можно ссылки, пожалуйста? Помню что не так давно спрашивал про constexpr. Даже потом статью запилил. Но там, ИМХО, были не очень очевидные вещи с неявным cast-ом объектов к структурам). А что смешного в том, что я относительно рано начал изучать эту предметную область (ну и нашел работу, потому что уже имел некоторый опыт)?
В embedded сталкивался. Когда cmake не верно передавал g++ параметры linker-а. В 3.10.1 кажется. В 3.14 уже поправили.

Information

Rating
Does not participate
Location
Красноярск, Красноярский край, Россия
Date of birth
Registered
Activity

Specialization

Software Developer, Embedded Software Engineer
Lead
From 250,000 ₽
C++
STM32
Linux
Circuitry
Python
Assembler
Programming microcontrollers
Embedded system
Software development
Object-oriented design