PicoCalc
Алгоритм LUT8
На сегодня нейросеть в обвязке внутри «
Последний заметный прирост производительности дал алгоритм LUT8, о котором я расскажу чуть ниже, и можно было бы на этом поставить точку, но меня уговорили приобрести ПЛИС.

Это программируемая логическая интегральная схема, которую можно коммутировать внутри любым образом под конкретную задачу. Я собираюсь подключить её к «ПикоКалу» через разъём расширения, а в будущем разместить внутри корпуса — места там очень много. Не буду пока загадывать, но возможно с таким бустом удастся демонстрировать изумлённой публике быструю нейросеть в коробочке.
Теперь про LUT8 — идея в том, чтобы заменить умножение поиском по таблице и сложением для восьми активаций сразу.
Для восьми значений мы считаем таблицу сумм из 256 элементов. Если порядок в таблице представить в виде битовой маски, то получится восемь флагов (256 значений от 00000000 до 11111111) — учитывается ли каждая активация в сумме или нет. Причём таблица строится с помощью 255 сложений, на каждом шаге, кроме первого (там всегда ноль), мы используем уже вычисленную маску, отличающуюся от текущей ровно на один бит.
Дальше нам надо посчитать скалярное произведение восьми активаций и восьми весов. Веса разложены хитрым образом. Они у нас тут четырёхбитные, и восемь весов хранятся в четырёх байтах следующим образом: первый байт хранит восемь самых младших бит всех восьми весов, следующий — следующие биты и так далее.
Каждый такой байт — готовая маска, которая показывает, входит ли каждая активация в сумму или нет. Для четырёх байт мы четыре раза ходим в таблицу, умножая каждый раз на множитель — ×1, ×2, ×4, ×8. Для «ПикоКалка» это могут быть три умножения (на один умножать не нужно) или три сдвига, не принципиально, то и другое занимает один такт.
В итоге, для восьми элементов в LUT8 у нас нужно будет прочитать четыре маски, четыре раза обратиться к таблице, выполнить три умножения или сдвига и три сложения.
MK-61s и «Спектрум»
С момент как мне подарили «ПикоКалк» хотел поэксперементировать там с цветом. Поскольку у меня теперь основная оболочка для него — прошивка ретроконсоли «
Перебрав несколько, остановился на «Спектруме», отчасти

В качестве основы мы взяли готовый эмулятор, но его пришлось сильно доработать, чтобы достичь хорошей скорости выполнения программ на ядрах
Это связано со скоростью шины, её пришлось разогнать по максимуму, плюс сделать алгоритм частичной перерисовки экрана, но полную скорость это не дало, впрочем играть довольно комфортно.
Отдельным сюрпризом стало сколько всего модифицировали в «Спектруме» за прошедшие годы — там и игры с расширенной палитрой «ULAPlus», и память до мегабайта, и звуковые процессоры.

Разумеется мне захотелось реализовать вообще все возможности, раз уж я занялся. В принципе, всё удалось реализовать без заметных проблем, но со звуковыми процессорами пришлось повозиться. Код,
Пришлось его разогнать до 200 МГц, это помогло, но выглядело
В случае «ПикоКалка» такое устройство нашлось — оказалось, что у него есть аналог южного моста — микроконтроллер «STM32F103C8T6» с процессором
В общем, мне в голову пришла довольно очевидная идея — гонять по шине команды в этот процессор для синтеза звука, раз центральный не справляется.
У него отдельная прошивка, для доступа к которой надо разбирать корпус. Чтобы постоянно этим не заниматься, я сделал заливку прошивки через ту же шину, сделав для этого отдельную систему команд. Плюс реализовал возможность запуска там небольших приложений — оперативной памяти там совсем мало.
Ещё у меня была идея ускорить таким образом инференс модели для программирования на
LUT и SWAR
Ночные эксперименты с нейросеткой на «ПикоКалке» дали, кстати, неплохой результат — теперь на токен тратится чуть больше секунды. Тут есть вклад оборудования, например, разгона центрального процессора до 270 МГц (выше плата зависает), но есть и чисто программные моменты. Я испытал несколько способов перемножить несколько чисел — в вычислениях любой нейросетки такого много.
Быстрее всего оказался обычный INT8 — прямое целочисленное умножение. Веса у меня четырёхбитные, чтобы сетка поместилась в память: в одном байте хранятся два веса. Для расчёта они распаковываются и по отдельности умножаются на восьмибитные активации.
Произведения складываются в
Пока с этим возился, познакомился с несколькими интересными методами считать такие суммы, о двух таких алгоритмах хочу рассказать. В моём «ПикоКалке» стоит RP2040 с целочисленным умножителем, но нет FPU — аппаратного блока вычислений с плавающей точкой.
В каждом случае будем считать скалярное произведение двух векторов: веса — 3, 6, 1, 4 и активации — 2, 3, 5, 7.
LUT
Первый называется LUT («
Сначала веса раскладываем на степени двойки (то есть по битам):
3 = 1 + 2 6 = 2 + 4 1 = 1 4 = 4
Теперь нам надо получить скалярное произведение вектора весов и вектора активаций:
(1 + 2) × 2 + (2 + 4) × 3 + (1) × 5 + (4) × 7
Если раскрыть скобки и сгруппировать слагаемые по степеням двойки, каждый разряд весов будет выбирать свой набор активаций:
1 × (2 + 5) + 2 × (2 + 3) + 4 × (3 + 7)
Из четырёх активаций получается шестнадцать возможных наборов, включая пустой. Заранее считаем сумму каждого набора и записываем в таблицу LUT.
Чтобы дать наборам адреса, назначаем позициям активаций флажки 1, 2, 4, 8. Адрес набора, или маска, — сумма флажков выбранных позиций. Например, младшие биты весов 3, 6, 1, 4 дают выбор 1, 0, 1, 0: берём первую и третью активации, то есть 2 и 5. Адрес этого набора — 1 + 4 = 5, поэтому LUT[5] = 2 + 5 = 7.
Для разряда ×2 выбираются первая и вторая активации: адрес 1 + 2 = 3, сумма LUT[3] = 2 + 3 = 5. Для разряда ×4 — вторая и четвёртая: адрес 2 + 8 = 10, сумма LUT[10] = 3 + 7 = 10. Нужное скалярное произведение таким образом находится по таблице:
1 × LUT[5] + 2 × LUT[3] + 4 × LUT[10] = 7 + 10 + 40 = 57
Таблицы строятся для текущего вектора активаций и используются для всех строк матрицы весов. У каждой строки свои маски, а таблицы готовых сумм общие. При смене вектора активаций их пересчитывают.
Максимальная ширина входа слоя у меня — 384 активации. Делим их на четвёрки, для каждой храним 16 сумм. Одна сумма в моём варианте LUT_FLOAT32 занимает четыре байта, получается: (384 / 4) × 16 × 4 = 6144 байта. Столько внутренней SRAM занимает весь набор таблиц LUT.
SWAR
Второй метод называется SWAR («SIMD Within A Register»), где SIMD — «Single Instruction, Multiple Data».
Тут мы считаем два произведения одной командой. Проще всего проиллюстрировать принцип в привычной нам десятичной системе. Пусть нам надо подсчитать следующее скалярное произведение:
3 × 2 + 6 × 3
Для этого мы применяем такой любопытный приём:
(6 × 100 + 3) × (2 × 100 + 3) или 603 × 203 = 122409
Если взять средние две цифры, это будет сумма произведений:
3 × 2 + 6 × 3 = 24
Почему это работает? Станет понятно, если
12 × 10000 + (6 × 3 + 3 × 2) × 100 + 9
Остаются вопросы — как мы получаем среднюю часть числа и где тут экономия, ведь в раскрытой формуле куча умножений?
Эта формула объясняет результат одной команды. В коде мы не вычисляем её слагаемые по отдельности: упаковываем числа сдвигами и битовыми масками, выполняем одно обычное умножение и извлекаем нужную часть результата.
В настоящем расчёте вместо основания 100 используется 65536: два числа упаковываются в
Размеры полей и диапазоны чисел нужно подбирать так, чтобы переносы между полями не испортили результат. Для отрицательных активаций нужны дополнительные поправки; положительный десятичный пример показывает только принцип.
MK-61s: обвязка на PicoCalc
Сегодня 6 ноября, у нас в Татарстане дополнительный выходной, по этому поводу мне захотелось заняться
Решил реализовать заведомо непрактичную, но давно засевшую у меня в голове идею — запустить внутри ретроконсоли «
Для этого я выбрал русскоязычную модель «
Даже сеть таких скромных размеров в памяти ретроконсоли не поместилась бы, несмотря на то, что я выбрал самую мощную аппаратную платформу, которая у меня есть — «ПикоКалк», поэтому пришлось её квантировать до четырёх бит.
Так же я пробовал сети побольше с квантированием до двух бит, а так же несколько вариантов гибридного квантирования, по соотношению сообразительность/размер выиграла эта конфигурация.
Было использовано 462 листинга, материалы девяти книг и несколько сотен синтетических упраженений. Полученные программы выполнялись в эмуляторе.

Чтобы ускорить матричные вычисления, пришлось использовать предвычисленные таблицы — LUT и оба ядра процессора (все остальные процессы в этот момент замирают), разогнанные с 125 до 250 МГц.
Несмотря на все усилия, скорость генерации невелика — около 4,67 секунды на токен. Математического сопроцессора нет, всё считается ядрами центрального процессора.

С такими параметрами этой обвязкой, конечно, в здравом уме пользоваться не будешь, тем более что и сеть получилась тупенькой — из трёхсот заданий с тремя параметрами она правильно выполняет лишь девятнадцать.
Я, наверное, потренирую её ещё немного и сотру, даже в репозиторий класть не буду — интересно было повозиться, но никакого смысла такой результат сохранять нет.
«MK-61s»: ещё приложения
Может показаться, что я занялся другими проектами и совсем забыл про ретроконсоль
Моё молчание отчасти объясняется тем, что почти все токены я сейчас трачу на попытку сделать нормальную арифметику для «ПикоКалка» — хочется, чтобы сишный тип double вёл себя там, как в стандартном Си и при этом все операции с ним выполнялись быстро, а это непросто.
Тем не менее и другими вещами я тоже занимаюсь, в частности в коллекции «

Первое — это приложение для работы с таблицами. Попытка ответить на вопрос: «А для чего я мог бы использовать эту ретроконсоль в реальной жизни?». Я участвую в проекте создания этой ретроконсоли для собственного развлечения, но иногда в команде возникает вопрос: а могло бы это пригодиться
На мой взгляд ответ получился удачным, на «ПикоКалке» приложением даже приятно пользоваться —
Формулы записываются в привычной для
Ссылку не обязательно набирать: можно выбрать нужную ячейку прямо в таблице. Есть и фиксация строки или столбца знаком доллара, как в больших электронных таблицах. Циклическая ссылка показывает ошибку, но не мешает считать независимые ячейки.

Следующая программа — игра «Погоня», написанная на языке калькулятора
Я давно хотел понять нельзя ли сделать на калькуляторе динамическую игру хоть с
На всю гонку даётся три жизни, проехать нужно двадцать участков. Машина может занимать пять положений. За столкновение со стеной или соперником отнимается жизнь, после удара есть короткая передышка, когда повторный урон не наносится.
Даже в динамике не очень очевидно, что происходит на экране, но ориентироваться помогает нижняя метка: она указывает свободный путь, к которому надо подрулить. Графика тут довольно условная, зато её мог бы осилить обычный сегментный экран «Электроники».

Следующую игру я написал, чтобы обкатать идею управления динамической игрой на калькуляторе не через переключатель тригонометрических режимов, а другими клавишами. У меня было несколько идей, хотелось их опробовать. В итоге я оказался от идеи добавить в команды калькулятора дополнительные возможности, а выкрутился другим способом, о котором расскажу ниже.
У «
Смысл игры — собрать по три клада на каждом из трёх уровней. На первом мешает один враг, дальше их два. Они бегают сами, умеют пользоваться лестницами и вполне могут поймать, пока разглядываешь экран.
От преследователя можно уйти, прожигая пол слева или справа. Герой через такую дырку падает на этаж ниже, а враг застревает. Через четыре секунды пол восстанавливается, и пойманный враг возвращается к месту старта. Бесконечно дырявить всё вокруг нельзя: одновременно открыт только один прожиг, лестницы и нижний этаж защищены.
MK-61s внутри PicoCalc

На работе на прошедший в сентябре день рождения подарили «ПикоКалк» — набор для сборки кибердеки с «Распберри» внутри. Заказали из Китая заранее, но ехал долго, поэтому вручили только в эту пятницу.
Вчера адаптировал под него интерфейс «
Вчера бо́льшую часть дня прикидывал на рендере интерфейс — управление и внешний вид, а к утру нейросеть мне его адаптировала. Какое же это восхитительное чувство — проектируешь как надо, утром просыпаешься, а на экране всё как задумано, и выглядит, и работает!
Тем более у меня целая груда приложений на любой вкус, которыми можно протестировать логику. Хорошо, что я
Сейчас «
В общем, интересное устройство, приятно повозиться, только очень уж громоздкое — ему бы быть раза в три потоньше, а то карманным его не назовёшь — толщина у него около трёх сантиметров.