Печатаем чипы на клавиатуре: Как Verilog сделал "железо" доступным каждому
Привет! Возможно, ты когда-нибудь задумывался о том, каким образом человечество, получившее цифровой компьютер лишь в середине прошлого века, в наши дни уже может симулировать настоящий мозг мухи, строить виртуальных ассистентов и поддерживать сложнейшую инфраструктуру, созданную человеком – интернет. Или каким образом люди могут начать с комфортом программировать и добиваться неплохих результатов, не изучив при этом ни одного раздела высшей математики. Само собой понятно, что ответ на этот вопрос кроется в "технологическом прогрессе", однако давай в этой статье сбросим вуаль с одной из важнейших технологий - Verilog.
Начнём нашу историю с краткого рассказа о появлении первого цифрового компьютера. Как и большинство технологий XX века, идея компьютера появилась именно как "военная технология". В апреле 1943 года учёные Электротехнической школы Мура Пенсильванского университета Джон Преспер Эккерт и Джон Уильям Мокли предложили военным силам США проект под названием ENIAC, которые испытывали трудности в быстрых расчётах баллистики. Однако, хоть сам компьютер был завершён лишь в мае 1945 года, талант такого полезного изобретения стал пользоваться популярностью в целом ряде других задач, где требовались большие вычисления. Так например, физик Дуглас Хартри решал с его помощью проблему обтекания воздухом крыла сверхзвукового самолёта.

Восход эры полупроводников
Одна из проблем компьютеров, над которыми время от времени думали инженеры был их размер. Они были громоздкие, требующие тонкой настройки, сотен тысяч паек и крайне внимательной отладки. К концу 1950-х годов инженеры столкнулись с проблемой, которую назвали "Тиранией Чисел": чем сложнее становился компьютер, тем больше деталей ему требовались, и тем чаще он ломался из-за дефектов пайки или обрыва проводов. Решение нашли независимо друг от друга два американских инженера – Джек Килби (лето 1958 года) и Роберт Нойс (начало 1959 года).
Килби пришёл к выводу, что если все компоненты схемы делать из одного и того же полупроводникового материала, их можно разместить на одной пластине. В сентябре 1958 года он продемонстрировал прототип из германия. Это была тонкая длинная пластинка, где элементы соединялись тонкими навесными проволочками. Нойс придумал более совершенный подход, который используется до сих пор. Вместо германия он взял кремний и применил планарную технологию. Он предложил формировать транзисторы в глубине кремниевой пластины, а соединения между ними наносить прямо на поверхность в виде тончайших дорожек из алюминия поверх защитного слоя оксида кремния. Навесные провода больше не требовались – схема стала полностью монолитной.

Это был прорыв, позволивший сильно ускорить как производство существующих компьютеров, так и исследования в этом направлении. Благодаря названным выше лицам (особенно Килби), калькулятор превратился из чего-то дорогого и доступного компаниям в маленькое устройство, которое мы сейчас воспринимаем чем-то естественным, как часы.
Ван ПЛИС, плиз
Классические интегральных схемы – это хорошо, но и они были не без недостатков. Обычные ИС создавались под конкретную задачу (например, калькулятор или управление ракетой). Если в схеме находили ошибку, инженерам приходилось заново проектировать чип, менять фотошаблоны и запускать дорогое производство на заводе. Это занимало месяцы.
Такой расклад устраивал далеко не всех, поэтому в начале 1970-х на основе технологий памяти родилась идея ПЛИС (Программируемая Логическая Интегральная Схема). Инженеры создали чипы, внутри которых была сетка из проводников, соединённых плавкими перемычками (микроскопическими предохранителями). При помощи подачи высокого тока "ненужные" перемычки выжигались. Так появилась первая матричная логика (PLA и PAL). По сути, это были микросхемы, в которых пользователь мог сам один раз "выжечь" простую логическую схему прямо у себя на столе.
Затем всё пошло по накатанной. Плавкие перемычки заменили на транзисторы с плавающим затвором, позволив перезаписывать конфигурацию чипа тысячи раз, и, в 1985 году, очередную революцию совершила компания Xilinx, выпустив первый чип класса FPGA (Field-Programmable Gate Array). Они кардинально изменили подход, заменив жёсткую матрицу на тысячи мелких универсальных логических блоков (LUT, Look Up Table), а между блоками проложили гибкую сеть дорожек с тысячами "электронных переключателей".

Итого, инженеры получили самый настоящий чип-конструктор. Загружая в него специальный код, можно мгновенно перестраивать миллионы внутренних связей. Сегодня ПЛИС может утром быть процессором для видеокарты, а днём - контроллером искусственного интеллекта.
Verilog
Мы уже рассмотрели несколько ключевых изобретений, а теперь, со знанием дела, можем поговорить о том, как вообще я, ты и кто-либо ещё могут дома самостоятельно спроектировать свою интегральную схему. Для программирования ПЛИС используется язык, очень похожий на язык программирования, но это скорее плохо, чем хорошо, – программистам может быть так же неприятно его учить, как и тем, кто только постигает азы "повелевания компьютерами" из-за того, что программирование науськивает программистов правилу, что "всё идёт по очереди, одно за другим". Однако, это проблемы исключительно программистов, а мы дошли до этого момента, чтобы жевать другую тему.
Речь идёт о языке Verilog - описательном языке, с помощью которого была спроектирована не одна плата, сидящая сейчас в наших девайсах. Язык достаточно прост, особенно, если ты знаешь о существовании логических гейтов. Их на уровне операций проходят на информатике ещё в школе: И, ИЛИ, НЕ, и так далее. Язык предлагает нам оперировать двумя базовыми примитивами, совершая над ними операции (как например упомянутые И / ИЛИ / НЕ). Первый примитив - wire (провод), - глупый, не умеет ничего хранить, только может передавать сигнал дальше. И второй - reg (register, ячейка памяти), - тоже глупый, но уже умеет хранить подаваемые данные и отдавать их наружу.
Давай посмотрим, как инженер описывает схему на Verilog. Не транзисторы, не дорожки и не напряжения, а именно логику будущего устройства.
// Модуль полу-сумматора, принимающий A и B, а отдающий сумму и флаг переноса
module half_adder(
input wire a, // наш первый входящий провод
input wire b, // и второй входящий провод
output wire sum, // выходящй провод получившейся суммы
output wire carry // и перенос, если сумма вывалилась за 0/1
);
assign sum = a ^ b; // задаём сумму как a XOR b
assign carry = a & b; // а перенос - как a AND b
endmodule
Тут немного поясню: XOR (операция ^, исключающее ИЛИ), позволяет нам получить сумму двух битов без учёта переноса. В двоичной арифметике сложение двух однобитных чисел даёт следующий результат:
- 0 + 0 = 0
- 0 + 1 = 1
- 1 + 0 = 1
- 1 + 1 = 2 (ой!)
В двоичной системе двойка записывается как 10. В выходной провод суммы мы, получается, отправим 0. В теории, XOR'ом мы получим тот же результат, но здесь мы используем XOR для более явного поведения, потому что для XOR сложение будет выглядеть так:
- 0 ^ 0 = 0
- 0 ^ 1 = 1
- 1 ^ 0 = 1
- 1 ^ 1 = 0
То есть, всегда ровно один бит, как мы и хотим.
Если попробуем синтезировать наш код выше в схему (например, через DigitalJS), то получим вот такую красоту:

Но это всего лишь полу-сумматор. Давай превратим его в полный сумматор, расширив входы и выходы:
// Тут оставляем всё как есть
module half_adder(
input wire a,
input wire b,
output wire sum,
output wire carry
);
assign sum = a ^ b;
assign carry = a & b;
endmodule
// А теперь добавляем ещё один модуль, уже полного сумматора
module full_adder(
input wire a, // опять же, вход №1
input wire b, // и вход №2
input wire cin, // вход для переноса с другого сумматора
output wire sum, // получившаяся сумма
output wire cout // и перенос уже нашего сумматора
);
// Добавляем провода, по которым будут гулять наши данные
wire w_sum1; // выход суммы первого полу-сумматора
wire w_c1; // выход переноса первого полу-сумматора
wire w_c2; // выход переноса второго полу-сумматора
// Теперь создаём наш первый полу-сумматор
// Внутри мы описываем через запятую названия проводов
// и с чем они связаны ".<название>(<путь>)"
half_adder u_ha1(
.a(a),
.b(b),
.sum(w_sum1),
.carry(w_c1)
);
// Добавляем второй полу-сумматор
half_adder u_ha2(
.a(w_sum1),
.b(cin),
.sum(sum),
.carry(w_c2)
);
// После всех вычислений получаем выход для нашего флага переноса
assign cout = w_c1 | w_c2;
endmodule
И вот уже всё выглядит куда интереснее:

В том же DigitalJS мы можем интерактивно "потрогать" нашу схему, чтобы проверить её работу:

Вуаля! Мы получили сумматор, который примерно в таком виде используется в современных интегральных схемах. Домашние ПК, аппараты МРТ, платы для разработки содержат внутри своих центральных чипов именно такой сумматор (только шире, для больших данных). Получается, мы уже не просто "что-то проектировали", а сделали вполне полезную вещь, которая может пригодиться позже.
Любой процессор внутри твоего девайса, с которого ты читаешь эту статью, содержит тысячи подобных строительных блоков. Арифметические устройства, регистры, конвейеры — всё это когда-то начиналось с таких же простых схем, как та, что мы только что собрали.
Итог
Тот путь, что человечество проделало от комнаты-компьютера до личного ассистента, работающего локально на телефоне сложно осознать, прочтя всего одну статью в интернете. Однако я считаю своё дело выполненным – показать, что начать хотя бы постигать азы проектирования интегральных схем не только несложно, но и не требует вложений. Достаточно иметь при себе лишь компьютер и желание совершенствоваться. Возможно, эта статья станет началом чьего-то большого приключения или будет кратким историческим экскурсом в мир микроэлектроники. Эпоха ENIAC уже давно ушла, но прямо сейчас у тебя есть возможность прикоснуться к этой кремниевой эволюции и, кто знает, оставить в ней собственный след.
Ссылки
- Статья на Хабре о цифровом мозге мухи
- О появлении компьютера ENIAC
- Хабр – про ИС и "Тиранию Чисел"
- DigitalJS с кодом полного сумматора
ПыСы
Если будет интересно заняться такого проектированием в игровой форме, то советую посмотреть в сторону игры "Turing Complete" – она и расскажет, и покажет, как постепенно собрать все модули восьмибитной архитектуры процессора. Ссылка на страницу игры в Steam.