Информация об изменениях

Сообщение Re[7]: Самый низкоуровневый язык, ага от 28.09.2023 21:35

Изменено 29.09.2023 13:11 vdimas

Re[7]: Самый низкоуровневый язык, ага
Здравствуйте, Marty, Вы писали:

V>>Если пользуешь Си, то для оперирования объявленными статическими данными (структурами, массивами) необходим CRT.

M>Что именно из CRT нужно для этого?

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

Упреждая пинг-понг вопросов-ответов:
int a = 42;
int b = calcB();

Здесь в бинарном образе в сегменте данных по адресу a будет число 42, по адресу b будет 0.

Внешний загрузчик грузит образ в память, переменная a уже инициализирована (это первая фаза), а переменная b будет инициализирована из глобального связанного списка инициализации, где каждый узел примерно такого вида:
struct __init_module {
  void * (fn)(void);
  __init_module * next; 
};

CRT пробегается по этому списку, вызывая код динамической инициализации — это вторая фаза стандартной инициализации С/С++.

Для инициализации модуля, содержащего динамическую инициализацию (здесь глобальной переменной b), будет порождена безымянная ф-ия примерно такого вида:
void __some_init_xxx() {
  b = calcB();
}

Указатель на эту ф-ию будет в одном из узлов __init_module в глобальном списке динамической инициализации.

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

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

На деле всё программируется через #define константы, либо через enum, т.е. через те техники, где "воздушность" констант гарантируется.


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

M>Весьма спорное утверждение.

Это реальность.


M>Из гарвардских видел только клоны MCS-51, хотя, не скажу, что большой опыт с различными семействами


У меня опыт достаточный, более десятка лет.

Для справки, мейнстримом для встраиваемых решений ARM не является до сих пор, не смотря на их засилье в смартфонах/планшетах, хотя гарвардские АРМ-ы тоже порой в ходу.

Повально там PIC-и, AVR-RISC, 51-я архитектура, MIPS-семейства (именно в гарварде, если ПЗУ на борту кристалла, т.е. почти всегда), недавно кучно пошли RISC-V, которые прямо по спецификации есть в исполнении для архитектуры общего назначения (фон-Нейман) и для встраиваемых (Гарвард):
https://www.sbir.gov/node/1654311

Практически все DSP-процы (включая якобы "аппаратные" микросхемы-кодеки звука или видео, кои есть в т.ч. на твоей материнке и видюхе — на самом деле не аппаратные кодеки, конечно, а программные — это просто ядро какого-нить мейнстримого DSP с заводской прошивкой) — это всё исключительно Гарвард.

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

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

В итоге, Гарвард проще/дешевле/эффективней, т.к. шины данных и кода работают независимо.
И еще Гарвард не подвержен атакам экзекутора данных.


M>Ничего не понял. Причем тут фон-неймановская архитектура?


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


M>В любом случае, в ОЗУ инициализированные переменные должны как-то попасть, от архитектуры тут ничего не зависит.


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

Тем более, во встраиваемых решениях почти всегда есть watch-dog, в т.ч. сугубо программный, который в любой момент обновит состояние системы до начального.
Вот ты можешь в любой момент в С/с++ перевызвать статическую и динамическую инициализацию модуля?
Нет.
Это только ручками.
И тогда инициализация, данная языком+CRT изкаробки резко становится ненужной. ))

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

Например, в паре проектов мы измеряли тактовую конкретной платы с точностью до 7-го знака и от этой частоты вычисляли некую константу-поправку (прецизионная техника была).
И эта константа шла в код 24 бита фиксированной запятой, а не в переменную в ОЗУ.

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


M>Да, вспомнил, про STM32. Там у них уже есть кое-какой прошитый код, который собственно, и занимается всей инициализацией для запуска прошивки.


Не сравнивай. ))
Эта балалайка изначально позиционировалась как система общего назначения, т.е. можно разрабатывать на её основе мини-компьютеры, интеллектуальные терминалы, подключать бесконечное разнообразие сложной периферии и т.д.

В то время как встраиваемый микрик — это всегда непременно SoC с некоторой периферией на борту.
(их не зря в одной серии десятки разнообразных вариантов, где варианты отличаются не только объемом ОЗУ/ПЗУ, но как раз набором периферии)

При таких раскладах в каждой конкретной конфигурации SoC фактор "общего назначения" отсутствует как класс. ))


M>Ещё этот встроенный бутлоадер анализирует пины BOOT0/BOOT1, и в зависимости от их состояния может переходить в режим общения по UART, через который можно обращаться к памяти читать/писать, или даже залить прошивку, без какого-либо ST-LINK'а


Только нафига это в микроволновке, стиралке, цифровом приёмнике или рации?
Re[7]: Самый низкоуровневый язык, ага
Здравствуйте, Marty, Вы писали:

V>>Если пользуешь Си, то для оперирования объявленными статическими данными (структурами, массивами) необходим CRT.

M>Что именно из CRT нужно для этого?

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

Упреждая пинг-понг вопросов-ответов:
int a = 42;
int b = calcB();

Здесь в бинарном образе в сегменте данных по адресу a будет число 42, по адресу b будет 0.

Внешний загрузчик грузит образ в память, переменная a уже инициализирована (это первая фаза), а переменная b будет инициализирована из глобального связанного списка инициализации, где каждый узел примерно такого вида:
struct __init_module {
  void (*fn)(void);
  __init_module * next; 
};

CRT пробегается по этому списку, вызывая код динамической инициализации — это вторая фаза стандартной инициализации С/С++.

Для инициализации модуля, содержащего динамическую инициализацию (здесь глобальной переменной b), будет порождена безымянная ф-ия примерно такого вида:
void __some_init_xxx() {
  b = calcB();
}

Указатель на эту ф-ию будет в одном из узлов __init_module в глобальном списке динамической инициализации.

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

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

На деле всё программируется через #define константы, либо через enum, т.е. через те техники, где "воздушность" констант гарантируется.


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

M>Весьма спорное утверждение.

Это реальность.


M>Из гарвардских видел только клоны MCS-51, хотя, не скажу, что большой опыт с различными семействами


У меня опыт достаточный, более десятка лет.

Для справки, мейнстримом для встраиваемых решений ARM не является до сих пор, не смотря на их засилье в смартфонах/планшетах, хотя гарвардские АРМ-ы тоже порой в ходу.

Повально там PIC-и, AVR-RISC, 51-я архитектура, MIPS-семейства (именно в гарварде, если ПЗУ на борту кристалла, т.е. почти всегда), недавно кучно пошли RISC-V, которые прямо по спецификации есть в исполнении для архитектуры общего назначения (фон-Нейман) и для встраиваемых (Гарвард):
https://www.sbir.gov/node/1654311

Практически все DSP-процы (включая якобы "аппаратные" микросхемы-кодеки звука или видео, кои есть в т.ч. на твоей материнке и видюхе — на самом деле не аппаратные кодеки, конечно, а программные — это просто ядро какого-нить мейнстримого DSP с заводской прошивкой) — это всё исключительно Гарвард.

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

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

В итоге, Гарвард проще/дешевле/эффективней, т.к. шины данных и кода работают независимо.
И еще Гарвард не подвержен атакам экзекутора данных.


M>Ничего не понял. Причем тут фон-неймановская архитектура?


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


M>В любом случае, в ОЗУ инициализированные переменные должны как-то попасть, от архитектуры тут ничего не зависит.


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

Тем более, во встраиваемых решениях почти всегда есть watch-dog, в т.ч. сугубо программный, который в любой момент обновит состояние системы до начального.
Вот ты можешь в любой момент в С/с++ перевызвать статическую и динамическую инициализацию модуля?
Нет.
Это только ручками.
И тогда инициализация, данная языком+CRT изкаробки резко становится ненужной. ))

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

Например, в паре проектов мы измеряли тактовую конкретной платы с точностью до 7-го знака и от этой частоты вычисляли некую константу-поправку (прецизионная техника была).
И эта константа шла в код 24 бита фиксированной запятой, а не в переменную в ОЗУ.

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


M>Да, вспомнил, про STM32. Там у них уже есть кое-какой прошитый код, который собственно, и занимается всей инициализацией для запуска прошивки.


Не сравнивай. ))
Эта балалайка изначально позиционировалась как система общего назначения, т.е. можно разрабатывать на её основе мини-компьютеры, интеллектуальные терминалы, подключать бесконечное разнообразие сложной периферии и т.д.

В то время как встраиваемый микрик — это всегда непременно SoC с некоторой периферией на борту.
(их не зря в одной серии десятки разнообразных вариантов, где варианты отличаются не только объемом ОЗУ/ПЗУ, но как раз набором периферии)

При таких раскладах в каждой конкретной конфигурации SoC фактор "общего назначения" отсутствует как класс. ))


M>Ещё этот встроенный бутлоадер анализирует пины BOOT0/BOOT1, и в зависимости от их состояния может переходить в режим общения по UART, через который можно обращаться к памяти читать/писать, или даже залить прошивку, без какого-либо ST-LINK'а


Только нафига это в микроволновке, стиралке, цифровом приёмнике или рации?