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

推荐订阅源

V2EX - 技术
V2EX - 技术
P
Privacy International News Feed
Security Latest
Security Latest
H
Hacker News: Front Page
T
Tenable Blog
The Hacker News
The Hacker News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Project Zero
Project Zero
O
OpenAI News
AI
AI
Spread Privacy
Spread Privacy
C
CERT Recently Published Vulnerability Notes
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Scott Helme
Scott Helme
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
NISL@THU
NISL@THU
A
Arctic Wolf
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
N
News and Events Feed by Topic
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
U
Unit 42
S
Security Affairs
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
博客园 - 【当耐特】
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
月光博客
月光博客
Engineering at Meta
Engineering at Meta
腾讯CDC
F
Full Disclosure
Cyberwarzone
Cyberwarzone
S
SegmentFault 最新的问题
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
The Cloudflare Blog

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет 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 и тысячами тестовых векторов.