惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
A
Arctic Wolf
K
Kaspersky official blog
V
Vulnerabilities – Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
L
LINUX DO - 热门话题
MongoDB | Blog
MongoDB | Blog
T
Threat Research - Cisco Blogs
D
Docker
爱范儿
爱范儿
T
Tenable Blog
C
Check Point Blog
B
Blog
C
Cisco Blogs
Vercel News
Vercel News
The Cloudflare Blog
T
Threatpost
NISL@THU
NISL@THU
T
Tor Project blog
V2EX - 技术
V2EX - 技术
P
Palo Alto Networks Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog
G
GRAHAM CLULEY
P
Privacy & Cybersecurity Law Blog
SecWiki News
SecWiki News
博客园 - 司徒正美
S
Security @ Cisco Blogs
GbyAI
GbyAI
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Cloudbric
Cloudbric
Webroot Blog
Webroot Blog
N
News and Events Feed by Topic
Y
Y Combinator Blog
博客园_首页
T
Troy Hunt's Blog
The Hacker News
The Hacker News
雷峰网
雷峰网
Google DeepMind News
Google DeepMind News
U
Unit 42
AWS News Blog
AWS News Blog
PCI Perspectives
PCI Perspectives
V
Visual Studio Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Проектируем с нуля калькулятор на FPGA, части 1 и 2
PatientZero · 2026-05-20 · via Все публикации подряд на Хабре

Идея этого проекта пришла ко мне в феврале 2021 года, когда в Остине (штат Техас) произошёл сбой энергосистемы. К сожалению, нам надолго запомнилось то, как правительство справлялось с ситуацией. Когда единственным источником тепла и света остался только газовый камин, а единственным окном в мир — слабое телефонное Интернет-соединение, у меня было много времени поразмыслить о том, что бы новое и интересное мне хотелось разработать. Я взял калькулятор HP-41CV и начал нажимать на кнопки. Как обычно, ощущения от этого были самыми приятными. И мне захотелось самому создать нечто подобное!

В начальной школе мне представилась возможность поиграть с HP-41CV. Я наблюдал, как калькулятор загружает программу с магнитной ленты и запускает её. Жужжание считывателя карт и тонкая магнитная лента, втягиваемая в устройство с одной стороны и выходящая с другой, внезапно изменяли поведение калькулятора, что произвело на меня очень сильное впечатление. Я и не подозревал, что оно повлияет на всю мою жизнь. Спустя несколько лет у меня появился Sinclair ZX81, потом ZX Spectrum, на котором я при помощи дизассемблера HiSoft Devpac MONS взламывал разные игры. Эти два устройства (калькулятор HP и микрокомпьютеры Sinclair) подтолкнули меня к разработке, программному обеспечению и исследованию внутренностей разных машин. Во многом я стал разработчиком именно благодаря этому.

Когда-то я изучал сам чип Z80, воссоздав его в виде A-Z80 и написав визуальный инструмент Z80 Explorer, отображающий его список связей. В каком-то смысле это ощущалось как закрытие темы одержимости Sinclair. Проект калькулятора ощущался как закрытие темы HP. Это не клон, не эмуляция, а реализация с нуля на основе тех же принципов. Мне хотелось изнутри разобраться в том, почему эти машины работали именно так.

The HP-41C, the calculator that started my journey.

Калькулятор HP-41C, с которого начался мой путь

Как работает научный калькулятор? Не в общих чертах, а в подробностях. Как он хранит числа? Какой алгоритм вычисляет sin(x)? Как функционирует его очень простой CPU?

В серии моих статей мы получим ответы на эти вопросы: в конечном итоге мы получим полностью работающий научный калькулятор, спроектированный и изготовленный с нуля, на собственном CPU, созданном на FPGA, с написанным вручную микрокодом, эталонными реализациями на C++ и физическим «железом», которое лежит у меня на столе и может вычислять точные ответы. И всё это в опенсорсе: вы можете увидеть это и попробовать самостоятельно.

The finished calculator. Custom CPU, 16-digit BCD arithmetic, 35 keys, OLED display. Designed and built from scratch.

Готовый калькулятор: собственный CPU, 16-разрядная BCD-арифметика, 35 клавиш, OLED-дисплей. Спроектирован и собран с нуля.

Насколько я понимаю, этот проект уникален: мне неизвестен ни один другой научный калькулятор (с тем же уровнем функциональности), реализованный на FPGA с собственным CPU и оригинальным рукописным ПО для его работы.

Правила

Первым делом я решил определиться, что же значит «с нуля» в этом проекте. Вот правила, которые я для себя установил:

  • Никаких готовых CPU. Настоящие мужики создают собственные процессоры. CPU будет создан самостоятельно, спроектирован на SystemVerilog и реализован в FPGA.

  • Никаких библиотек для работы с числами с плавающей запятой и специализированных IP-блоков. Вся арифметика BCD (двоично-десятичная), создаваемая из примитивных операций с нибблами (полубайтами).

  • Никаких эмулируемых ROM. Поведение калькулятора полностью задаётся написанным вручную ассемблерным кодом, не скопированным ни из одного готового калькулятора.

  • Никаких компромиссов в точности. Каждый алгоритм сверяется с эталонной реализацией на C++ при помощи тестовых векторов, не на глазок.

Стоит сказать о том, почему была выбрана FPGA. Можно было изготовить калькулятор на основе готового RISC, ARM или любого другого процессора CPU, использовать SoC и писать код на удобном C++, но уже существует много таких реализаций. В этом нет ничего нового; это даже не так сложно сделать. А если я выберу FPGA, то моим проектом станет сам CPU: при помощи собственного микрокода я создам набор команд, регистровый файл, операции АЛУ и все детали работы машины. Именно в этом и заключается удовольствие от творчества.

Выбор

BCD против двоичных чисел с плавающей точкой. В карманных калькуляторах традиционно использовалась двоично-десятичная арифметика, при которой каждый разряд хранится в виде 4-битного ниббла. HP-35, HP-41, HP-15C — всё это BCD-машины. BCD обеспечивает точное десятичное представление ценой усложнения арифметики. Двоичные числа с плавающей запятой (IEEE 754) не могут точно представить многие десятичные дроби. В случае калькулятора BCD — это верный выбор. В моём калькуляторе используется 16 разрядов BCD мантиссы (больше, чем в любом калькуляторе HP, и достаточно для корректного округления до последней отображаемой цифры). У каждой мантиссы есть соответствующая двухразрядная BCD-экспонента, обеспечивающая общий диапазон величин от 1.0e-99 до 9.999999999999999e+99 («денормализованные» числа отсутствуют).

RPN против алгебраической записи. RPN (Reverse Polish Notation, обратная польская запись) означает, что мы вводим операнды перед операторами. Для сложения 3 и 4 нужно нажать 3 ENTER 4 +. Скобки, приоритет операторов и клавиша «=» отсутствуют. Вместо этого есть четырёхуровневый стек (X, Y, Z, T), где хранятся промежуточные результаты. В калькуляторах HP использовалась RPN со времён HP-35 (1972 год). Она быстрее при длинных вычислениях, чётче даёт понять, что делает машина, и её предпочитают многие пользователи. Я тоже создаю RPN-калькулятор.

Фиксированные возможности против программируемых. Мой калькулятор имеет фиксированные функции: пользователь нажимает клавиши, получает результаты, но не может писать программы. Добавление программируемости было бы интересным расширением, и я занёс его в свой список пожеланий (понадобится ещё одна плата с дополнительной FRAM или другим типом постоянного хранилища). В моём проекте ближе всего к программируемой модели слой скриптинга (выражающий функции калькулятора в виде последовательности токенов), но он целиком расположен в прошивке.

Соотношение аппаратной реализации и микрокода. Возможные решения находятся в спектре от «каждая функция — это отдельная логическая цепь» (много вентилей, отсутствие ROM) до «каждая функция — это программная процедура» (минимум «железа», большой ROM). Мой проект ближе к программному краю спектра: CPU остаётся относительно простым и практически всё, от сложения до CORDIC, написано на языке ассемблера. Оборудование занимается сканированием клавиатуры, управлением ЖК-дисплеем и декодированием адресов; микрокод занимается арифметикой, форматированием отображения и стеком RPN. Это ещё и сильно упрощает отладку.

CORDIC для трансцендентных функций. Тригонометрические функции, логарифмы и степени вычисляются при помощи алгоритмов CORDIC — того же метода сдвига и сложения, который применялся в HP-35 и практически во всех карманных научных калькуляторах с 1972 года. Для них нужны только сложения, вычитания и сдвиг разрядов (во внутреннем цикле умножения нет) и они прекрасно работают с десятичной арифметикой. В частях 3 и 7 будет подробно рассмотрен этот алгоритм и его история.

Что мы будем рассматривать в этой серии

В серии постов мы пошагово рассмотрим проект от первооснов до готового оборудования:

Часть 2 — поиск пути: прототипирование алгоритмов, расположение клавиш, конечный автомат ввода и принтер.

Часть 3 — подробное описание численных методов: BCD-арифметика, CORDIC, разряды защиты и что же такое на самом деле «корректное округление».

Часть 4 — фреймворк разработки: мы поговорим о том, как один и тот же исходный код на Verilog работает в ModelSim, Verilator, в десктопном симуляторе Qt, в браузерном демо на WebAssembly и в реальной FPGA, не требуя при этом никаких модификаций.

Часть 5 — первое оборудование: проектирование печатных плат в EasyEDA и их изготовление в JLCPCB.

Часть 6 — проектирование CPU: набор команд, АЛУ, структура памяти и итеративный процесс проектирования архитектуры набора команд.

Часть 7 — написание микрокода: ассемблерные файлы, слой скриптинга и ощущения от написания ПО для изобретённой тобой машины.

Часть 8 — физическая сборка: Rev A (две платы, соединённые кабелем), Rev B (одна плата) и долгая дорога к изготовлению корпуса.

Часть 9 — пересмотр проекта в 2025 году: переписывание арифметического движка под полную 16-разрядную точность, добавление полного набора тригонометрических функций и новые команды CPU.

Часть 10 — Заключение и выводы.

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

Прежде, чем мы приступим

Этот проект не для новичков, но он и не такой страшный, как можно подумать. Я намеренно стремился к тому, чтобы текст оставался простым и удобным для читателей с разными уровнями опыта. Если вам любопытно попробовать сделать что-то более сложное или у вас возникли вопросы, то можете писать мне на электронную почту, я с радостью отвечу и помогу.

Весь исходный код выложен на GitHub.

Часть 2: Поиск пути

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

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

В этой части задокументирован такой этап развития проекта калькулятора. Хоть я писал и выбрасывал множество других, для меня остались полезными три программы, по одной на каждый заданный вопрос. Пост завершается неожиданным уходом в тему инфракрасного светодиода и древнего принтера HP.

Немного расскажу об инструментах. Обычно код, создаваемый в процессе поиска пути, считают одноразовым: некрасивым, незадокументированным и пригодным только для того, чтобы ответить на какой-то вопрос. Фред Брукс в своей книге «Мифический человеко-месяца» назвал этот паттерн так: «план, рассчитанный на выкидывание; рано или поздно вы это сделаете». Он утверждал, что прототипы неизбежны, и вопрос только в том, планируете ли вы их, или вас ждут сюрпризы. Я планировал и писал всё аккуратно.

Доказательство: смогу ли я это сделать?

Я знал, что смогу реализовать четыре основные операции (+,-,*,/), в этом сомнений не было. Но логарифмические и трансцендентные функции — дело другое. Обычно при разработке ПО для них используют библиотеки, но мне было любопытно, как различные библиотеки вычисляют операции высокого порядка, как это делают калькуляторы и как они делали это в 70-х. Чтобы понять эти алгоритмы полностью, мне нужно было реализовать их.

Мои исследования закодированы в проекте Proof. Это грязный исследовательский код на C++, проверяющий конкретные алгоритмы для каждой функции высокого порядка. Он не имеет качества продакшена, это не окончательная реализация, она нужна только для проверки того, работает ли такой подход.

В нём охвачены следующие функции:

  • Квадратный корень: множество потенциальных решений со сравнением сходимости и погрешности. Логичным кандидатом был метод Ньютона-Рафсона: x_{n+1} = (x_n + N/x_n)/2. Это один из самых старых численных методов. На вавилонской глиняной табличке YBC 7289 из коллекции Йельского университета, датированной примерно 1800 годом до нашей эры, показаны вычисления √2 с точностью до шести знаков после запятой. Вероятно, для них использовался тот же итеративный подход.

  • Логарифм и антилогарифмln(x) и exp(x), реализованные при помощи только основных арифметических операций

  • Тригонометрические функции: базовые tan(x) и atan(x), которые нам нужны

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

На этом этапе меня ещё не волновало конкретное представление BCD и окончательная точность, я делал упор только на самих алгоритмах: их сходимости и вычислительных затратах. Если один способ вычисления ln(x) для получения приемлемого результата с 16 разрядами точности требовал 16 итераций многочленов, а другой сходился за 8, то второй был лучше; выяснить это можно как раз на этом этапе проекта. Я измерял сходимость эмпирически, наблюдая, сколько итераций требуется каждому алгоритму и как снижается погрешность на каждом из шагов. Приемлемая скорость схождения, даже в арифметике с плавающей запятой, обеспечивала уверенность, что BCD-реализацию можно итерировать с достаточной точностью. Вторым критерием была алгоритмическая сложность каждой функции: я мог полагаться только на основные операции.

Более сложные функции калькулятора можно создать из основных. Как только они заработали, пусть даже грубо, я нашёл ответ на вопрос о реализуемости проекта.

Макет: как должны располагаться клавиши?

Разобравшись с вычислительной реализуемостью, можно было приступать к клавиатуре.

Кажется, что выбор раскладки клавиш — простая задача, но на самом деле это не так. Чтобы спроектировать качественную клавиатуру калькулятора, нужно в первую очередь избегать незначительных ошибок. Часто проблемы оказываются неочевидными: действия ощущаются неправильными, если рукам требуется совершать больше работы или нажимать лишние клавиши. Должен ли ENTER находиться с правого края или где-то ближе к центру? Должна ли быть RCL основной функцией или альтернативной? Где должна находиться клавиша десятичного разделителя относительно цифровых клавиш? HP, TI и Casio разработали собственные стандарты, и многие люди предпочитают определённого производителя, потому что уже привыкли к его раскладке.

Для удобства я написал на C# Windows-приложение Mockup, которое позволило мне экспериментировать с различными раскладками. Описания раскладок хранятся в текстовых файлах (layout1.txtlayout2.txt, etc.), которые приложение загружает через диалоговое окно открытия файла. Чтобы приложение казалось более реальным, в нём реализовано большинство калькуляторных функций, применён реальный шрифт HD44780 (тот же шрифт, который используется на аппаратном дисплее), поэтому экран выглядит попиксельно точным.

В процессе изучения расположения клавиш популярных коммерческих калькуляторов я выполнил множество итераций. Самым важным (и неудивительным) уроком стало то, что расположение клавиш альтернативных функций (включаемых клавишей Shift) значит больше, чем можно было бы ожидать: функция, которой часто пользуешься, но доступная только по нажатию Shift, начинает утомлять уже после часа работы. В итоге я остановился на раскладке с ENTER в правом нижнем углу, сгруппированными вместе основными операциями и самыми часто используемыми научными функциями (sin, cos, tan, log, exp). Ничего экстраординарного или новаторского, но, наверно, в этом и был смысл.

FPGA Calculator mockup

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

Ввод чисел сложнее, чем кажется

Третьим неотвеченным вопросом был ввод чисел. Кажется, что это тривиально: пользователь вводит цифры, мы храним их в буфере. Но в научном калькуляторе с BCD-мантиссами, экспонентами, сменой знаков, удалениями и потенциальными переполнениями при вводе конечный автомат имеет неожиданно много неочевидных пограничных случаев.

Допустим, что произойдёт, когда пользователь вводит 0.005e10? Мантисса создаётся в одном буфере, экспонента в другом, а реальная внутренняя экспонента (та, которая записывается в регистр) отличается от введённой пользователем. Это отображаемая экспонента (та, которую видит пользователь: +10), скомбинированная с виртуальной экспонентой, вычисленной по расположению десятичного разделителя в мантиссе (-3, потому что 0.005 = 5 × 10⁻³). Реальная хранимая экспонента равна +7.

Мантисса хранится в нормализованном виде: если это не истинный ноль, первый разряд числа должен быть ненулевым, даже если пользователь сначала введёт нули.

Что произойдёт, если пользователь нажмёт на «e», не введя сначала цифры мантиссы? (Ответ: мантиссе будет присвоено значение 1, то есть e5 даёт 1E+05.) А если мантисса такая длинная, что занимает на дисплее область экспоненты? (Ответ: игнорируем нажатия на e, пока пользователь не удалит несколько разрядов.) Как различается поведение backspace в режимах экспоненты и мантиссы? Как только начинаешь об этом размышлять, сразу всплывают всевозможные безумные пограничные случаи. Я протестировал имеющиеся у меня калькуляторы, и в конечном итоге решил в основном перенять поведение HP.

Проект Input — это написанный на C++ интерактивный симулятор, реализующий эвристику ввода. Пользователь вводит нажатия клавиш в терминал, который после каждого нажатия показывает полное внутреннее состояние: содержимое экрана, внутренние буферы мантисс, реальную экспоненту, отображаемую экспоненту, местоположение курсора и флаги.

S 0123456789012345   M 0123456789012345   E  P  Di=2 Si=7 Mi=5 FLAG_EDIT_EXP=1
   3.1415     E+24     3141500000000000  +24 24
         |                  |

Я изначально написал этот проект на C++, намеренно упрощая код и используя простые языковые конструкции, чтобы облегчить себе его портирование на язык ассемблера. Если конечный автомат реализован правильно, то его портирование превращается в трансляцию, а не становится процессом открытий.

FPGA Calculator: Input

Принтер: инфракрасный светодиод и древний принтер HP

Этот раздел будет более глубоким, чем другие, потому что принтер был первой частью реальной работы с FPGA в проекте — я впервые писал на Verilog для реальных аппаратных сигналов (инфракрасных), а не для симуляции.

У HP 82240A/B есть небольшой термопринтер, который HP разработала для своей линейки калькуляторов, созданной в 1980-х. Он получает данные по инфракрасному каналу, не требуя ни кабелей, ни разъёмов; только инфракрасный светодиод, направленный на датчик принтера. У меня есть дома такой принтер, поэтому я хотел, чтобы калькулятор мог печатать на нём.

В сообществе пользователей HP протокол получил прозвище «Красный глаз» (Red Eye). Он был разработан за семь лет до появления IrDA: Infrared Data Association основали в 1993 году, а первый свой стандарт она опубликовала в 1994 году. Red Eye и IrDA несовместимы: в них используется разное битовое кодирование, разные несущие частоты, разные структуры кадров. Несмотря на это, один и тот же протокол Red Eye без изменений применялся с HP-18C (1986 год) по HP-50g (на протяжении более двадцати лет и десятка моделей калькуляторов). Подобное долголетие протокола, впопыхах разработанного для выпуска новой линейки калькуляторов, — свидетельство или хорошей инженерной работы, или большой удачи; подозреваю, что дело и в том, и в другом.

Для реализации поддержки я исключительно работал с FPGA, на грубой тестовой плате с ИК-светодиодом, подключенным к контакту GPIO. Первый шаг заключался в анализе протокола.

В Technical Interfacing Guide HP 82240B задокументирован формат кадра данных: каждый байт передаётся в виде 12-битного кадра, состоящего из 4 бит коррекции ошибок, за которыми следуют 8 бит данных. Каждый бит закодирован в виде двух полубитов: 1 передаётся как импульс с последующей паузой (X_), а 0 — как пауза с последующим импульсом (_X). Данным предшествует начальная последовательность из трёх полубитных импульсов. Тайминги берутся от часов, работающих с частотой примерно 32 кГц.

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

FPGA Calculator: RevA with HP printer

Реализация на SystemVerilog представляет собой конечный автомат с пятью состояниями: READY (ожидание байта), START (отправка начальной последовательности из трёх полубитов), DATA и BIT (тактирование 12 бит по два полубита за раз) и DELAY (межкадровый пробел перед следующим байтом). Выходной сигнал busy сообщает CPU, что не нужно отправлять следующий байт, пока не завершён текущий кадр. Чтобы упростить конструкцию, я использовал опрос.

Я отлаживал систему на реальном «железе», наблюдая на экране осциллографа за реальными сигналами. В конструкции есть три тестовых точки GPIO: сигнал тайминга одной четвёртой такта, включение ИК-светодиода и сырой вывод светодиода. Наблюдая за этими тремя сигналами в осциллографе, я мог чётко видеть, что делает конечный автомат на каждом из этапов, и сравнивать показания с диаграммами таймингов протокола из руководства.

HP 82240B Infrared Printer Timing and Encoding

Получив первую распечатку на принтере HP (с голой платы FPGA, на GPIO которой висел светодиод), я убедился в правильности реализации протокола. Ура! Пока переходить к следующей задаче.

Что мне дал поиск пути

В конце этого этапа я получил ответы на все вопросы:

  • Реализуемость: да, tanexpln и sqrt вычисляемы из примитивов. Алгоритмы работают, и к тому же я понял, какие алгоритмы следует использовать.

  • Раскладка клавиш: было протестировано множество кандидатов; работа с одними ощущается более удобной, чем с другими.

  • Ввод чисел: полный конечный автомат ввода, корректный и протестированный, готовый к портированию на язык ассемблера.

  • Принтер: работающая реализация протокола инфракрасной передачи данных, проверенная на оборудовании.

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