Как стать автором
Обновить

К вопросу о делении и TI

Время на прочтение14 мин
Количество просмотров5.1K

«Не выпендривайтесь, Мария Ивановна, и слушайте Вашу любимую песню „Валенки“



Несмотря на заголовок, порядок изложения будет обратный — сначала о фирме Texas Instruments (конечно не о самой фирме, я все-таки инженер, а не дрессировщик бизнес-аналитик, поэтому о производимой фирмой продукции), а уже потом о делении.

Первая часть марлезонского балета.

Предметом рассмотрения будет относительно новое семейство СС13хх (СС1310/СС1350/СС1352), но оно лишь является отправной точкой для обсуждения ситуации в области программирования встроенных систем. Рассматриваемый МК предназначен для использования при проектировании устройств с беспроводными интерфейсами разного типа (мне не очень нравится новомодное слово IoT, тем более, что оно не исчерпывает возможности применения данного семейства).

МК построен на основе ядра М3 со вполне приемлемыми, хоть и не рекордными параметрами частоты и объема памяти программ и данных, имеет достаточный набор интерфейсов, но интересен отнюдь не этими параметрам. Фича состоит в том, что микросхема содержит внутри себя три ядра МК, одно центральное и два периферийных — для работы с внешними устройствами и для осуществления взаимодействия через эфир. Чем обусловлено подобное решение?

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

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

Я совершенно не уверен, что такое разделение функций было абсолютно необходимо и что требуемых параметров нельзя было достичь более простыми архитектурными решениями, но, если цена удерживается в разумных рамках, а необходимые возможности реализованы, то почему бы и нет. Тем более, что пост все равно не об аппаратной составляющей МК, это просто для освещения ситуации. Рассматривать же мы будем процесс создания программного обеспечения для данного класса МК.

Для начала — основное ядро, здесь все стандартно — и само ядро М3 хорошо известно, и тулчейн — gcc, сама фирма рекомендует две IDE — ccs и iar. С последней из их я работал много, поэтому решил попробовать, что за продукт породил сумрачный тевтонский гений коллективный разум TI. Code Composer Studio является разработкой фирмы и абсолютно бесплатен без каких либо ограничений, основан на (кто бы мог подумать) Eclipse и имеет все достоинства и недостатки, свойственные данной среде.

Единственное, по поводу чего сразу бы хотел выразить свое недоумение — фирма любезно предлагает необходимы для работы с данным МК дополнительные утилиты (для формирования загрузочного образа прошивки через эфир и для передачи его в МК), но написаны они, почему то, не на Java, которая является основой для среды программирования и исполняющая система для которого входит в состав установочного пакета, а на Phyton. Не то, чтобы я сильно не любил последний (хотя и это есть, я не приемлю задание структуры программы при помощи отступов, но в конце концов „на вкус и цвет все краски разные“), но просто непонятно, зачем привлекать явно излишние сущности. Причем сами утилиты ничего сложного из себя не представляют, специфических библиотек не используют и были перенесены автором в Java за весьма ограниченное время без малейших затруднений с увеличением длины программы на 20%, и, что немного странно, без заметного изменения быстродействия (учитывая, что основное время исполнения связано с чтением файлов, это не столь удивительно — позднее примечание Фунтика Ф...).

Вторая часть загадки заключается в том, что сама по себе Eclipse популярна в том числе и благодаря возможности легкого встраивания плагинов. С учетом данного факта, крайне загадочно выглядит решение разработчиков среды программирования заставить пользователя вызывать данные утилиты ручками из командной строки, предварительно отключив ручками терминал в среде разработки и запустив ручками программу приема данных на МК и потом восстанавливать терминал опять-таки ручками. Возможно, для „индусских программистов“ данное решение представляется единственно возможным и совершенно оправданным, но крупная фирма могла бы себе позволить и привлечение более квалифицированного персонала, наверное, я чего-то не знаю.

Далее, программирование контроллера сенсоров (название не очень удачное, но это прямая калька) осуществляется при помощи продукта Sensor Composer Studio. Сразу же очередной вопрос — а почему надо иметь отдельный продукт, не то, чтобы было очень сложно переключить окно, но все таки ..., тем более, что созданный код в конечном счете становится частью кода для основного МК (конечно же, он там не исполняется, но входит в общее адресное пространство памяти программы).

А вот дальше еще одна особенность — нам ничего не говорят об этом ядре (о его архитектуре), кроме того, что оно 16-разрядное, низко-потребляющее и собственное. В общем то и ладно, работает и хорошо работает, но «осадочек остается». Далее следует описание команд данного ядра, по нему можно предположить, что это модификация 430, хотя и с особенностями типа команд организации циклов. Приводится расположение регистров периферии в общем и локальном адресном пространстве, а вот потом опять начинаются странности — многие регистры, в том числе регистры периферии, сопровождаются фразой „используется только библиотекой TI“, причем для некоторых из регистров все равно даны назначения битовых полей, а для некоторых — нет. Не то, чтобы мне эти описания регистров сильно мешали, но зачем их давать, если использование пользователем не планируется — лично я не слишком понимаю.

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

Следующая часть — ядро для работы с радио, построено на базе М0, исполняет программу из собственного ПЗУ (скорее всего это часть энергонезависимой памяти, маловероятно, чтобы на современном МК стояла масочная память), которая не может быть модифицирована (по крайней мере об этом в документации ни слова) пользователем. Информация о внутреннем устройстве радио-тракта крайне скудная, по сути ее можно извлечь только из описания команд настройки режимов, но обычному „встроенному“ разработчику она и не нужна.

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

Думаю, читателю стало понятно, что написание полноценной программы для данного МК не представляет собой простой задачи, вполне доступно для продвинутого профессионала, но что же делать „обычному“ разработчику (как недавно написал Олег Артамонов, „Вы уже поняли, что сильно ошиблись с выбором профессии?“). Фирма позаботилась о таком случае и, вместе с средой разработки, предоставляет большой пакет (набор примеров) программ на все случаи жизни, который называется SimpleLink. Причем решения даются в двух вариантах — как с применением операционной системы реального времени TI-RTOS (по моему мнению, это более удобный путь), так и в супер цикле (на тот случай, если вы так ненавидите встроенные ОС, что „кушать не можете“). Я спокойно отношусь к ОСРВ на МК, поэтому применяю первый вариант и советую делать то же самое, особенно если разобраться в конфигурировании процесса сборки ОС и адаптировать его для своего класса задач, удобство использования сохраняется, а затраты на поддержание этого удобства резко сокращаются.

Мое личное отношение к данному пакету двойственное — с одной стороны, замечательное начинание, действительно кардинально упрощающее применение МК (поэтому я его применяю), но с другой стороны — »трэш, угар и содомия", великолепная иллюстрация к фразе «Если стоимость хорошей архитектуры кажется Вам высокой, подумайте о стоимости плохой архитектуры» (поэтому я его ругаю). Речь идет не только о распределении функций по модулям и взаимосвязях между ними (хотя и тут не все гладко), но и о распределении модулей по файлам, именам файлов, распределения файлов по директориям и их названиях и структуре и так далее и тому подобное. Ну а нарушения принципов KISS и DRY является почти правилом для разработчиков пакета. Но, если не вчитываться в исходники пакета (я никак от этой дурацкой привычки избавиться не могу), и использовать его «как есть», то все работает нормально, по крайней мере, я в конкретном проекте пока проблем не обнаружил (не считая оскорбленного эстетического чувства и потерянной вере в человечество).

А вот теперь можно плавно перейти к основному постулату данного поста (лучше поздно, чем никогда). Я всегда считал, что крайне трудно написать фреймворк, сочетающий в себе настоящую универсальность и высокую эффективность. Предложенные фирмой примеры разработки представляют именно такой фреймворк с упором на универсальность, средства настройки приложений отсутствуют почти полностью, все только на уровне корректировки сырков. Берите один из наших многочисленных примеров, изменяйте небольшой кусочек, связанный с измерениями и анализом (ну и передачей, конечно), и все готово. Разумеется, получившийся код в общем случае будет весьма раздутым, но мы предложим Вам различные примеры для отличающихся условий применения, и Вам останется лишь выбрать наиболее подходящий для Вашей задачи. В крайнем случае, в МК встроен достаточно значительный объем памяти программ, чтобы не пришлось думать об экономии этого ресурса. Более того, существенная часть исполняющих библиотек уже спрятана внутри ПЗУ и Вам остается только аккуратно их вызвать.

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

  1. тщательно спроектирована,
  2. аккуратно запрограммирована,
  3. исчерпывающе документирована,
  4. универсальна,
  5. эффективна.

И если последние два пункта всего лишь желательны (весьма желательны, но тем не менее ...), то первые три необходимы.

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

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

1б) Распределение модулей по файлам с продуманной структурой директорий — скорее три с плюсом, чего стоит только многократное повторении текста программных модулей в различных файлах.

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

3. Документация на твердую четверку — уже то, что авторы не прибегли к «мощи и выразительности» Doxygen в части документирования, дает минимум 1 балл, есть система контекстных ссылок, есть описания принципов функционирования — пятерку не ставлю лишь потому, что по части документирования не ставлю ее никогда, даже себе.

4. Не больше четверки в силу отсутствия развитого конфигурирования, но об этом я уже упоминал.

5. Сложно сказать, я обычно смотрю на реализацию SPI — он, с одной стороны достаточно прост, чтобы оценить отсутствие стандартных грабель, с другой стороны, достаточно сложен, чтобы было куда их напихать. Так вот, в данном пакете есть эталонно неэффективные процедуры записи/чтения байта, но если погрузится в глубины, то можно обнаружить реально используемые варианты с использованием ПДП, о них пока ничего сказать не могу.

Замечание по поводу глубин — они действительно глубоки (через 4-5 вложенных функций) и тут не могу не упомянуть одну особенность пакета — он написан на С. Это не я забыл добавить два плюса после буквы, это их там действительно нет. Для тех, кто в теме, многое становится понятно, всем остальным рекомендую пройти увлекательный квест с целью определения набора функций, выполняемых на аппаратном уровне при реализации нетривиального объекта. Конечно, при использовании классов подобная задача становится тривиальной, но это не путь джедаев из TI. Понимаю, что это вынужденная забота о тех пользователях, кто пренебрегает плюсами, но почему останавливаться на достигнутом, а как же несчастные пользователи ассемблера, за что их обидели.

И в заключение — хочу подчеркнуть, «чтобы меня правильно поняли наверху», я совсем не ругаю ни семейство МК, ни среду разработки, ни программный пакет, а всего лишь хочу, чтобы они стали еще лучше, еще удобнее и ее более привлекательны для пользователя. У меня есть свои счеты с фирмой TI и я никогда им не прощу поглощения National или еще более раннего приобретения Luminary с последующим убийством интересной линейки МК (хотя в последнем случае они сами себя наказали), а также то, что они вернули мне в 2014 году деньги за заказанные кристаллы (хотя я точно Крым не трогал), но это глубокое чувство не мешает мне быть объективным — они сделали хорошую работу. Предлагаемая фирмой концепция, вынесенная в эпиграф, мне не слишком близка, но, наверное, они правы, и другого пути для сложных кристаллов нет. Это тренд и бороться с ним бессмысленно.

А то, что это тренд, подтверждает положение с новыми кристаллами управления питанием от Vicor. Сам по себе кристалл хороший (обратное было бы удивительно для уважаемой фирмы), параметры очень неплохи, а упомянул я его только в связи с разделом о выборе внешних компонентов, конкретно индуктивности. В документации данный раздел составляет только один абзац, в котором указана конкретная модель индуктивности от конкретного производителя и прямо сказано, что другие варианты не рассматриваются, смотри эпиграф. Полностью понимая резоны разработчика кристалла (частота переключений высокая, токи значительны, проектирование индуктивности для таких условий задача нетривиальная), тем не менее должен отметить, что пока еще такой подход к проектированию для меня является непривычным, «но ключевое здесь „пока“. Может, следовало бы продавать эти два компонента в комплекте, тогда и вопросов бы не возникало.

Вторая часть марлезонского балета.

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

Сама по себе задача простая и на языке C решение очевидно и выглядит, как

$(pointer1-pointer0)/sizeof(type)$

. Дьявол прячется в реализации — команда деления так и не стала стандартно реализуемой в распространенных архитектурах МК, поэтому она не является быстрой. Если делитель является переменной величиной, то хорошего решения нет от слова совсем, а вот для константного значения есть варианты, основанные на умножении. Благодаря замечательному свойству умножения

$ (a+b)*(c+d)=a*c+a*d+b*c+b*d$

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

Итак, вместо деления (на константу, это важно) мы хотим применить умножение, следите за руками

$ a/c=a*(1/c)=a*(N/(N*c))=a*(N/c)/N$

, где N — некоторая дополнительная константа. Возникает логичный вопрос — что за фигня, ведь теперь у нас две операции деления вместо одной, да еще и умножение, за счет чего выигрыш. Ответ в правильно выбранном N, если он будет степенью двойки, но деление превращается в сдвиг числа вправо, что намного дешевле, а если показатель степени кратен 8, то деление превращается в пере-нумерацию байтов числа, что вообще ничего не стоит. Поскольку сомножитель N/c константный, вычисляем его заранее и все вроде бы хорошо, если бы не одна деталь — точность представления этого сомножителя.

Рассмотрим конкретный случай деления на 10, который возникает при преобразовании чисел из двоичной в десятичную, тогда, приняв Н=256, рассчитываем константу для умножения 256/10 = 25.6 и возникает ошибка округления до целого 25(-2.3%) либо 26(+1.6%). Тогда, к примеру, 100*26=2600=256*10+40 и старшая часть результата равна 10, что соответствует ожидаемому 100/10=10. Можно рассчитать, при каких значениях делимого результат отклонится от правильного более, чем на единицу, но для этого нам придется решать уравнения вида

$[n/10]=[n*k/N]+1$

(где скобки означают целую часть с округлением вниз), а это не слишком понятная процедура, проще провеси численное моделирование и убедиться в верности результата до определенного n. Можно ввести корректирующую добавку и компенсировать потерю точности по формуле

$(н*26-н/2+1)/256 $

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

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

Рассмотрим магическое число 143 и исследуем его забавные свойства 77*143=11011, его младшая часть 011=1=77/7. Нетрудно видеть, что 784*143=112112, его младшая часть 112=784/7 и так далее для всех чисел, не превосходящих 999*7=6993. Вот оно, естественное ограничение метода, но взяв другое магическое число 7143, мы продлим диапазон возможностей до 9999*7=69993. Нетрудно найти и другие магические числа, обладающие подобными магическими свойствами.

Как же получить эти числа — мы хотим найти число, при умножении на которое делимого можно получить результат, содержащий в своей младшей части результат деления, в данном случае на 7. Звучит заумно, но на самом деле все просто, для входного числа 7 мы хотим получить ххх001, предположим, что ххх=001 и вот она простая уличная магия 1001/7=143. Очевидно, что 143*7=1001, тогда для любого числа=н*7 верно (н*7)*143=н*(7*143)=н*1001, и младшая часть результата равна н, чтд.

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

Нахождение магических чисел для делителей, отличных от 7, равно как и доказательство их существования, оставим на долю пытливого читателя. Интересно также построить график множителей в зависимости от делителя и увидеть на нем провалы и пики, наверное, их наличие как то соотносится с теорией чисел. Например, для составного 21=3*7 данное число =381 (разумеется, минимальное число, остальные нас не интересуют) явно меньше произведения 667*143=95381 и даже ему не кратно, как я наивно полагал, хотя 381=381.

Еще одно интересное обстоятельство заключается в том, что магические числа будут разными в разных системах счисления. Например, в десятичной системе не существует константы для деления на 5, поскольку ни одно из чисел вида х...1 не может иметь своим делителем пятерку и наш метод не срабатывает. Но, в то же время, в двоичной (шестнадцатеричной) системе данная задача решается, поскольку 1024+1=1025=0х401 на 5 чудесно делится с результатом 205=0хCD и наш алгоритм опять работает, например 130*205=0х82*0хCD=0хFAFA=>FA=26=130/5. Более того, можно доказать (ну я так думаю), что теперь задача нахождения множителя решается для любого нечетного делителя, а любой четный превращается в нечетный конечным количеством сдвигов (это я точно могу доказать). Насколько все-таки удобная и полезная вещь двоичное представление, нам с ним очень повезло.

PS. Постоянные мои читатели (льщу себя надеждой, что такие есть) находятся в явном недоумении – пост подошел к концу, а где «плач Ярославны». Не волнуйтесь, дорогие мои, вот и он.

В отладочных платах для CC1350 и CC1352 используется один кристалл антенного коммутатора (используется по разному, но это неважно), а именно XMSSJE3G0PA (не пытайтесь прочитать это в русской транскрипции, я уверен, что muRata ничего подобного в виду не имела). Так вот, коммутатор со скромными характеристиками – полоса частот до 2.7гГц, затухание прохождения – 0.28 дБ, затухание изоляции – 33 дБ, коммутируемая мощность – 20 дБ. Но все это компенсируется двумя параметрами – габариты 1,5*1,5мм и стоимость – 0.9$, несмотря на применяемые технологии „Silicon On Insulator“ и „gallium arsenide».

КАК они это делают – я в первую очередь про цену, ну и второй — почему мы так не делаем – вопрос риторический
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Трабиционный опрос о данном посте
33.33% Обе части какие то непонятные и сумбурные, этому не место на Хабре12
2.78% Первая часть интересная, но непонятная вторая портит пост1
11.11% Первая часть непонятная, а вот вторая интересная4
52.78% Обе части неплохи, автор, пиши еще.19
Проголосовали 36 пользователей. Воздержались 15 пользователей.
Теги:
Хабы:
+5
Комментарии4

Публикации