uVision Keil. Помогите разобраться с компиляторами.

Кто любит RISC в жизни, заходим, не стесняемся.
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3562626#p3562626"]и это решило проблему в Keil'е, его компилятор сразу размещал константные экземпляры класса во Flash, используя значения, предоставленные в списке инициализации.

Keil это всё компилировал, но контроллер по факту вываливался в Hard Fault.[/uquote]
Сомневаюсь, что новый компилятор Clang скомпилировал бы подобный код приводящий к Hard Fault, скорее это их старый самописный компилятор поддерживающий максимум С++03. И с тех пор определение POD сильно изменилось, даже статья на вики разделена на POD до и после С++11, а в С++20 POD вообще уже deprecated и вместо него появились две новые категории с новыми нюансами. Я бы с этим сильно не заморачивался, не важно дело в конкретном компиляторе или новых стандартах языка, пытаясь разместить классы во флеш все равно много не сэкономишь, лучше сконцентрироваться на том почему при использовании наследования прошивка вырастает до 70KB :)
Реклама
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="Reflector",url="/forum/viewtopic.php?p=3562739#p3562739"][uquote="Satarych",url="/forum/viewtopic.php?p=3562626#p3562626"]и это решило проблему в Keil'е, его компилятор сразу размещал константные экземпляры класса во Flash, используя значения, предоставленные в списке инициализации.

Keil это всё компилировал, но контроллер по факту вываливался в Hard Fault.[/uquote]
Сомневаюсь, что новый компилятор Clang скомпилировал бы подобный код приводящий к Hard Fault, скорее это их старый самописный компилятор поддерживающий максимум С++03. И с тех пор определение POD сильно изменилось, даже статья на вики разделена на POD до и после С++11, а в С++20 POD вообще уже deprecated и вместо него появились две новые категории с новыми нюансами. Я бы с этим сильно не заморачивался, не важно дело в конкретном компиляторе или новых стандартах языка, пытаясь разместить классы во флеш все равно много не сэкономишь, лучше сконцентрироваться на том почему при использовании наследования прошивка вырастает до 70KB :)[/uquote]Как то вы немного странно меня процитировали, ну да ладно))
Тестируемый проект для Keil'а прилагаю. IDE-Version: µVision V5.25.2.0, C Compiler: ArmClang.exe V6.9
Результат смотрю через отладчик китайским ST-Link V2.0
Вылаетает в HardFault на всех уровнях оптимизации кроме нулевого. Как мне показалось, компилятор не генерирует 2 варианта всех функций шаблонного класса, а создаёт один "универсальный", у которого вместо GPIOA/GPIOD указано NullPtr и при попытке записи по нулевому адресу он вылетает в HardFault. Что происходит на нулевом я вообще не понял. Если в HardFault в отладчике было видно, что States увеличивается, то тут даже этого не просходит.

По поводу 70KB - это только при нулевой оптимизации и использовании наследования, так что не слишком страшно, но, как по мне, странно.
Вложения
Тест шаблонов STM32.zip
(96.15 КБ) 289 скачиваний
Реклама
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3562812#p3562812"]Тестируемый проект для Keil'а прилагаю. IDE-Version: µVision V5.25.2.0, C Compiler: ArmClang.exe V6.9[/uquote]
Понятно, компилятор таки новый, просто я как-то компилировал такую конструкцию с передачей GPIO_TypeDef* в шаблон под clang и выдавало ошибку, а сейчас проверил и ошибка действительно есть, но только в режиме С++17, а gcc выдает ошибку в любом случае. Похоже это баг clanga, по правилам можно передавать целочисленные константы или указатели/ссылки на объекты или функции, собственно gcc и пишет, что не может взять адрес... Если бы компилировалось и работало, то можно было поспорить, а так :)
По поводу 70KB - это только при нулевой оптимизации и использовании наследования, так что не слишком страшно, но, как по мне, странно.
Вопрос в том для какого по размеру проекта получатся эти 70КВ, потому что можно вместо newlib-nano выбрать стандартную библиотеку и словить пару десятков KB на одном printf :) Вообще на достаточно больших проектах -O0 может легко обгонять по размеру -O2 и тем более еще больше любящего инлайнить -O3.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="Reflector",url="/forum/viewtopic.php?p=3562874#p3562874"][uquote="Satarych",url="/forum/viewtopic.php?p=3562812#p3562812"]Тестируемый проект для Keil'а прилагаю. IDE-Version: µVision V5.25.2.0, C Compiler: ArmClang.exe V6.9[/uquote]
Понятно, компилятор таки новый, просто я как-то компилировал такую конструкцию с передачей GPIO_TypeDef* в шаблон под clang и выдавало ошибку, а сейчас проверил и ошибка действительно есть, но только в режиме С++17, а gcc выдает ошибку в любом случае. Похоже это баг clanga, по правилам можно передавать целочисленные константы или указатели/ссылки на объекты или функции, собственно gcc и пишет, что не может взять адрес... Если бы компилировалось и работало, то можно было поспорить, а так :)[/uquote]В файле CMSIS stm32f10x.h объявлен следующий тип данных

Код: Выделить всё

typedef struct
{
  __IO uint32_t CRL;
  __IO uint32_t CRH;
  __IO uint32_t IDR;
  __IO uint32_t ODR;
  __IO uint32_t BSRR;
  __IO uint32_t BRR;
  __IO uint32_t LCKR;
} GPIO_TypeDef;
Соответственно GPIO_TypeDef - тип, GPIO_TypeDef* - тип "указатель на объекты этого типа", а GPIOA, GPIOB и пр. - непосредственно указатели на объекты этого типа. Однако, учитывая тот факт, что GCC компилятор не принимает GPIOx в качестве параметра моего шаблона, очевидно, что я что-то путаю. Поправьте где именно я не прав.

Эксперимента ради:

Код: Выделить всё

Stm32Gpio <GPIOA, 1<<8>	led0; // ошибка
Stm32Gpio <(GPIO_TypeDef *)0x40010800, 1<<8>	led1; // таже самая ошибка
GPIO_TypeDef	GPIO_1;
Stm32Gpio <&GPIO_1, 1<<8>	led2; // а вот это, хоть и бессмысленно, но компилируется
Не могу понять, чем "&GPIO_1" отличается от "(GPIO_TypeDef *)0x40010800" с точки зрения типа данных?

PS Ваш вариант использовать GPIOx_BASE мне не очень нравится потому что обязывает заранее создать шаблоны для всех GPIO, что по сути дублирует CMSIS. В противном случае ничто не защитит от написания бессмыслицы типа

Код: Выделить всё

template<uint32_t pin, uint32_t af = 0>
using PinX = PinT<I2C1_BASE, pin, af>;
Реклама
Эиком - электронные компоненты и радиодетали
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3562994#p3562994"]Не могу понять, чем "&GPIO_1" отличается от "(GPIO_TypeDef *)0x40010800" с точки зрения типа данных?[/uquote]
GPIO_1 отличается тем, что это реальный объект, причем даже к нему предъявляется ряд требований, например, касательно линковки. Единственную константу которую можно в таком случае передать - это (GPIO_TypeDef*)0, т.е. обозначающую нулевой указатель.
PS Ваш вариант использовать GPIOx_BASE мне не очень нравится потому что обязывает заранее создать шаблоны для всех GPIO, что по сути дублирует CMSIS.
Не понял, класс GpioA дублирует GPIOA из CMSIS? Потому что имена похожи? Так это вроде наоборот хорошо... А функционал у моего класса примерно как и у твоего, так что никакого дублирования я не вижу, тем более совсем не обязательно создавать псевдонимы для каждого порта, можно сделать хоть так:

Код: Выделить всё

Pin<'A', 1> led;
Или так:

Код: Выделить всё

enum {PA, PB, PC... };
Gpio<PB, 0x0FF0> port8;
Реклама
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="Reflector",url="/forum/viewtopic.php?p=3563015#p3563015"]Не понял, класс GpioA дублирует GPIOA из CMSIS? Потому что имена похожи? Так это вроде наоборот хорошо... А функционал у моего класса примерно как и у твоего, так что никакого дублирования я не вижу, тем более совсем не обязательно создавать псевдонимы для каждого порта, можно сделать хоть так:

Код: Выделить всё

Pin<'A', 1> led;
Или так:

Код: Выделить всё

enum {PA, PB, PC... };
Gpio<PB, 0x0FF0> port8;
[/uquote] Возможно я не совсем правильно выразился. Я не имел ввиду, что функции класса дублируют возможности CMSIS. Я имею ввиду что в CMSIS'е идёт объявление доступной периферии (GPIOx, SPIx, I2Cx и пр.) и ваш подход предполагает в том или ином виде сделать аналогичное объявление (с помощью частично специализированных шаблонов или перечисления).

А по поводу размещения объектов классов во Flash я частично разобрался.
В отличие от компилятора Keil'а, GCC компилятор рассматривает const объекты как "исходное значение будет известно только во время выполнения программы, но в дальнейшем его будет менять нельзя". Для объектов, чьё "исходное значение должно быть определено на этапе компиляции" следует использовать constexpr. В целом это логичное поведение, в большей степени соответствующее смыслу const и constexpr. Просто после работы в Keil'е (который во флеш клал и const объекты, значение которых по факту было известно на этапе компиляции) оно непривычно лично для меня. Итого:

Код: Выделить всё

class Test
{
public:
	constexpr Test (uint16_t number) : m_number(number) {}
	void setNumber (uint16_t number) {m_number = number;}
private:
	uint16_t m_number;
};

Test A(15); // Объект в оперативной памяти
A.setNumber (10); // Изменить значение можно

const Test B1(15); // Объект всеравно в оперативной памяти
const Test B2(<имя объекта>.getValue();) // Зато можно инициализировать чем угодно, через тот же USART
B1.setNumber (10); // Ошибка

constexpr Test C1 (15); // Объект находится во флеше
constexpr Test B2(<имя объекта>.getValue();) // По понятным причинам так уже сделать не получится
Но, как и в случае в шаблонами, так сделать не получится:

Код: Выделить всё

class Test
{
public:
	constexpr Test (GPIO_TypeDef *number) : m_number(number) {}
private:
	GPIO_TypeDef *m_number;
};

constexpr Test A(GPIOA); // Error: constexpr variable 'A' must be initialized by a constant expression
Как по мне, константнее некуда)) Могу предположить, что компилятор рассматривает приведения типов через reinterpret_cast как функцию, которая не имеет ключевого слова constexpr в своём объявлении.
Реклама
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3563422#p3563422"]Возможно я не совсем правильно выразился. Я не имел ввиду, что функции класса дублируют возможности CMSIS. Я имею ввиду что в CMSIS'е идёт объявление доступной периферии (GPIOx, SPIx, I2Cx и пр.) и ваш подход предполагает в том или ином виде сделать аналогичное объявление (с помощью частично специализированных шаблонов или перечисления).[/uquote]
Как функции класса могут дублировать возможности CMSIS, если в нем таких функций просто нет? Зато по функционалу твой класс GPIO мало чем отличается от моего, значит и у тебя есть такое дублирование :)

Касательно constexpr... Конечно можно сделать класс с constexpr конструктором и даже будет работать:

Код: Выделить всё

class Gpio
{
public:
	auto base() const { return (GPIO_TypeDef*)gpio; }
	constexpr Gpio(int gpio, uint16_t pinsMask) : gpio(gpio), pinsMask(pinsMask) {}
	void write(uint16_t value) const { base()->BSRR = (pinsMask << 16) | value; }

private:
	int gpio;
	uint16_t pinsMask;
};

	constexpr Gpio g1(GPIOA_BASE, 0x0FF0);
	g1.write(123);
Но нельзя сделать даже банальное

Код: Выделить всё

constexpr auto mask = pinsMask;
Соответственно также нельзя использовать constexpr if и constexpr функции. У меня в либе для F1 есть такой код:

Код: Выделить всё

if constexpr (pinsMask & 0x00FF)
{
    static constexpr uint32_t mask = qmask(pinsMask);
    base()->CRL = base()->CRL & ~mask | (uint32_t(mode) & mask);
}
if constexpr (pinsMask & 0xFF00)
{
    static constexpr uint32_t mask = qmask(pinsMask >> 8);
    base()->CRH = base()->CRH & ~mask | (uint32_t(mode) & mask);
}
Qmask() берет маску пинов и рекурсивно получает из нее четверную маску, причем это гарантированно будет константа времени компиляции, даже для -O0. У тебя в коде в этом месте два цикла, вот у меня без constexpr тоже с -O2 будут циклы, -O3 не всегда, но помогает, только из-за него существенно вырастет размер остального кода. Так что с точки зрения эффективности шаблоны все равно лучше, хотя у constexpr классов преимущество в том, что их можно как обычные параметры передавать...
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

[uquote="Reflector",url="/forum/viewtopic.php?p=3563589#p3563589"]Как функции класса могут дублировать возможности CMSIS, если в нем таких функций просто нет?[/uquote] Но я и писал, что НЕ имею это в виду)))

[uquote="Reflector",url="/forum/viewtopic.php?p=3563589#p3563589"]Так что с точки зрения эффективности шаблоны все равно лучше, хотя у constexpr классов преимущество в том, что их можно как обычные параметры передавать...[/uquote]Да я на шаблонах и буду реализовывать по итогу, только с наследованием. Тот код, что я тут выкладывал, был эксперимента ради. В любом случае, спасибо за советы.

PS

Код: Выделить всё

static void set() { base()->BSRR = pinMask; }
static void clear() { base()->BSRR = 0x10000 << pin; } // Почему не base()->BRR = pinMask; ?
Компилятор сам всё посчитает и сведёт выражение в правой части присваивания к константе? Или в этой серии контроллеров банально нет этого регистра? Работал только с F1 и L0.

И ещё один вопрос, весьма наивный, но вдруг) В векторе прерываний на каждое прерывание отводится по 4 байта, ровно под указатель на обычную C функцию. Я правильно понимаю, что вызов метода класса (не статический) туда не впихнуть, ибо он банально не влезет в 4 байта?
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3564409#p3564409"]Компилятор сам всё посчитает и сведёт выражение в правой части присваивания к константе? Или в этой серии контроллеров банально нет этого регистра? Работал только с F1 и L0.[/uquote]
BRR есть не везде, например, нет у F4.
И ещё один вопрос, весьма наивный, но вдруг) В векторе прерываний на каждое прерывание отводится по 4 байта, ровно под указатель на обычную C функцию. Я правильно понимаю, что вызов метода класса (не статический) туда не впихнуть, ибо он банально не влезет в 4 байта?
Не влезет, но даже если бы можно было запихнуть туда 8 байт, мк все равно не смог бы этот адрес правильно обработать.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

Reflector, а не могли ли бы вы пояснить про используемый вами атрибут "_always_inline_"? Это что-то подобное?

Код: Выделить всё

#define	_always_inline_	__attribute__((always_inline))
Если да, то есть ли какие-то стандарты или общепринятые принципы именования пользовательских атрибутов?
Reflector
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Reflector »

[uquote="Satarych",url="/forum/viewtopic.php?p=3566116#p3566116"]Это что-то подобное?[/uquote]
Конкретно у меня оно объявлено как

Код: Выделить всё

#define _inline_ __attribute__((always_inline)) inline
причем обычный inline там тоже нужен, без него местами даже не компилируется...
Про общепринятые принципы именования ничего не скажу, в том же CMSIS есть всякие __STATIC_INLINE, но я такое точно в свои проекты не вставлю :)
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

А есть ли тут люди, использующие связку Visual Studio + VisualGDB? В частности VladislavS, рекомендовавший мне её?
Наткнулся на странность в поведении стандартной библиотеки. При подключении файлов библиотеки в новом стиле, все имена автоматически находятся в глобальном пространстве имён. VisualGDB использует файлы стандартной библиотеки, которые он сам же и загрузил вместе с GNU GCC (путь ...\SysGCC\arm-eabi\arm-eabi\sys-include\...).

Отсюда первый вопрос: эти файлы модифицированы создателями VisualGDB? Или это стандартные файлы, распространяемые с GCC компилятором, и с другой IDE, использующей GCC компилятор, будет наблюдаться аналогичное поведение? И второй вопрос: как отучить VisualGDB/GCC от такого поведения?

Код: Выделить всё

#include <cstdint>
uint32_t A = 0;	// Ок
// Но в моём понимании, тут должна выдаваться ошибка, т.к. uint32_t
// должен находиться в пространстве имён std, но это компилируется

std::uint32_t B = 0;	// Ок
using std::uint32_t;	// Ок
uint32_t C = 0;		  // Ок
UPD: Попробовал скомпилировать с помощью GCC, который скачивал отдельно для Eclipse с сайта ARM. Результат тот же, содержание файлов стандартной библиотеки практически тоже самое. Разницу заметил только в шапке с лицензией
Copyright (C) 2007-2017 Free Software Foundation, Inc.
В GCC с сайта ARM год исправили на 2018))
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение VladislavS »

Помнится мне, CMSIS внутри где-то stdint.h подключает.
Satarych
Первый раз сказал Мяу!
Сообщения: 27
Зарегистрирован: Вт июн 04, 2013 20:25:13

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение Satarych »

Это да, но я проверял без подключения CMSIS, пустой cpp файл с приведённым выше кодом.
Аватара пользователя
СКАЗОЧНИК
Идёт направо - песнь заводит, Налево - сказку говорит.
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение СКАЗОЧНИК »

Подскажите еще, где в Кейле включить в Дебаге просмотр собственных переменных? Вроде было такое окно, а сейчас уже все перетыкал, не могу найти.

Добавлено after 2 minutes 45 seconds:
Ой. все, вроде разобрался. Есть там окно симболс. С которого можно отправить переменные из файла мэин.С в окно Вотч. )
Станислав
Аватара пользователя
amv2000
Опытный кот
Сообщения: 720
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение amv2000 »

C:/Users/New/AppData/Local/Arm/Packs/Keil/STM32F1xx_DFP/2.4.1/Device/StdPeriph_Driver/src/misc.c(131): error: no member named 'IP' in 'NVIC_Type'
Доброго дня! если не трудно подскажите по компилятору версии 6 в последних версиях Keil. При компиляции проекта не находит элемент IP в misc.c.
Изображение А где он изначально объявлялся то, реально нигде не находит.
Аватара пользователя
VladislavS
Собутыльник Кота
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение VladislavS »

Смотри core_cm3.h В CMSIS 6.0 он как IPR определён.
Аватара пользователя
amv2000
Опытный кот
Сообщения: 720
Зарегистрирован: Вт апр 26, 2011 13:58:36
Откуда: Ростовская область

Re: uVision Keil. Помогите разобраться с компиляторами.

Сообщение amv2000 »

Добрый вечер! Компилирую проект в Keil компилятором v6.22, CMSIS 6.1, компилируется без ошибок. Но на экране дисплея ничего нет, пробежался дебагером зависает на строке

Код: Выделить всё

SoftTimers = ( softtimer_type_data * )realloc( SoftTimers, SOFTTIMER_INDEX * sizeof( softtimer_type_data ) );
ИзображениеНе пойму, что не нравится? Подскажите кто знает.
Вложения
(KEIL)PSL-3604 GLCD_29.06.24_.rar
(332.24 КБ) 47 скачиваний
Ответить

Вернуться в «ARM»