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

Известный из прошлой статьи базисный элемент работает на эффекте прилипания ламинарной струи воздуха к стенке. Вырываясь из сопла, струя воздуха натурально присасывается к одной из двух стенок элемента, а с помощью управляющих сигналов её можно перекинуть на противоположную сторону. При этом у дискретного элемента между выходами имеется вихреобразователь, который создаёт барьер для струи и гарантирует строгость переключения элемента.
Такой базисный элемент лёг в основу известных мне серийных струйных элементов серии ВОЛГА и СМСТ-2. Рисунки элементов СМСТ найти не удалось — повезло лишь отыскать книгу описания серии в целом, а вот по элементам ВОЛГА нашёлся полноценный альбом элементов! Мой товарищ, Антон Н., помог в оцифровке нескольких рисунков. После печати на фотополимерном принтере на свет появился элемент №1 — СТ41. Он имеет два входа справа и по выходному каналу с каждой стороны. В правом канале реализована логическая функция 2ИЛИ-НЕ, а в левом — 2ИЛИ.

Для проверки его работоспособности я напечатал пару крыльчаток, задача которых — вращаться от потока воздуха. Собственно, основная проблема струйных элементов — очень слабый выход. Никакого толку от установки манометра не будет — он просто ничего не покажет. А вот поставить крыльчатку можно. Несмотря на вдвое бо́льшие размеры сопла и большой расход питающего воздуха, элемент уверенно заработал. Правда не так, как надо… Согласно альбому схем, по умолчанию воздух должен идти в правый канал и при подаче управляющих сигналов справа переключаться на левый. А у элемента №1 всё с точностью до наоборот. Пора расчехлять тяжёлую артиллерию.
Элемент №1 работает, однако по умолчанию струя воздуха идёт не туда, куда надо
▍ Симуляция струйной логики в OpenFOAM
Чтобы понять, что не так с элементом, я решил воспользоваться пакетом openFOAM — это openSource-система численного моделирования механики сплошных сред. openFOAM нагибает половину суперкомпьютеров списка ТОР500. А вот со стороны простого пользователя у него есть две большие проблемы. Первая — у openFOAM нет графического пользовательского интерфейса: все действия с пакетом производятся через консоль, правда прекрасно автоматизируются скриптами. Вторая проблема — OpenCFD Ltd зарабатывает деньги на поддержке и, как итог, — документация на пакет есть, есть туториалы, но без пол-литра с ними не разберёшься. Обе проблемы частично решаются с помощью САПР FreeCAD с установленным в нём расширением CFDof. Эта связка позволяет нарисовать сам струйный элемент, обозначить его границы (стенки, входы, выходы), а также настроить условия симуляции и запустить расчёт. Впрочем, появляется другая проблема — вам придётся использовать FreeCAD :)
Я проверил работу связки на разных платформах. Наиболее стабильная связка — при установке на Linux. Сам openFOAM отлично живёт под wsl, но вот FreeCAD лично у меня там не работает.
Установка openFOAM и FreeCAD под Linux
#загружаем FreeCAD
wget https://github.com/FreeCAD/FreeCAD/releases/download/0.20/FreeCAD-0.20.0-Linux-x86_64.AppImage
#устанавливаем openFOAM
curl https://dl.openfoam.com/add-debian-repo.sh | bash && apt-get -y install openfoam2206-default
FreeCAD лучше брать самой последней версии, но как минимум не ниже 0.20 — под Linux на 0.19 я столкнулся с проблемами во время установки расширений.
Второй вариант — под Windows. Устанавливайте openfoam-mingw — эту версию проще всего скрестить со скриптами CfdOf. FreeCAD опять-таки ставьте не ниже 0.20.
Во FreeCAD необходимо поставить два расширения для работы — CFDof и plot. Это делается в меню Инструменты — Менеджер дополнений. Позднее в настройках укажите папки с установленными openFOAM, paraview и gmsh. В окне настроек, к слову, можно скачать и запустить на установку все необходимые пакеты. Под Windows мои настройки выглядят так:

Дабы не перегружать статью вопросами использования графического интерфейса расширения CFDof, я сделал видео, где за 15 минут я создаю простую модель, указываю её границы в терминах симуляции (стенки, вход, выход) и запускаю симуляцию.
Раз есть элемент №1, значит, есть и его модель… Загоняем её в openFOAM и производим расчёт:
Хорошая новость в том, что симуляция повторяет поведение реального элемента. Плохая — и в реальности, и в симуляции он работает не так, как надо, что и нужно исправить.
На самом деле это видео — не первая симуляция элемента. В первый раз результат выглядел несколько хуже:

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

Конструктивная схема элемента представлена на рис. 5, где 1 — канал питания, 2, 3, 6 — каналы входа, 4, 5 — каналы выхода, 7, 8 — атмосферные каналы для сбрасывания неиспользуемого расхода воздуха в атмосферу и исключения режима автоколебаний, 9 — часть платы, отделённой от неё каналом 10, который соединяет рабочую камеру с атмосферой (при этом величина вакуума при отсутствии сигнала управления уменьшается, что облегчает переброс струи в другой канал).
При отсутствии входных сигналов Х2, Х3, Х6 в каналах 2, 3 и 6 силовая струя, вытекая из канала питания 1 под действием поперечного перепада давления и небольшого наклона канала питания 1, попадает в канал 4, на выходе которого устанавливается единичный сигнал У4=1.
Звучит вроде бы логично — переключение напечатанного элемента требует небольшого ощутимого давления, и обводной канал должен этот процесс облегчить, однако неправильный режим по умолчанию в реальности превращает элемент ИЛИ-НЕ в условно бесполезный инвертор. Попробуем очевидное, на первый взгляд, решение — перенести обводной канал на левую сторону:
Это помогло. Теперь по умолчанию сигнал идёт в правый канал, а распечатанный элемент тоже стал работать как полноценный элемент 2ИЛИ-НЕ. Вопрос в другом — почему в альбоме схем канал всё же справа? А главное — почему в элементе «защёлка» (СТ42) канал есть с каждой стороны?! И утверждается, что оба состояния — устойчивые… Чтобы враг не догадался? Если бы! Вскрываем оригинальный СТ41 прямиком из 1982 года и видим, что в действительности обводной канал находится справа. Сравнив сам элемент и его рисунок в альбоме, я понимаю, что авторы фривольно подошли к его чертежу.

Имеет смысл отсканировать настоящий элемент и воспроизвести его на фотополимернике во второй ревизии, однако первая ревизия справляется со своей функцией, поэтому пока оставим как есть.
▍ Синтезируем вычислитель
С элементом разобрались. Теперь нужно понять, сколько их требуется для создания двоичного сумматора с последовательным переносом. Вручную рисовать схему мне лень, так что напишем всю логику на языке verilog, просимулируем корректность работы в Icarus Verilog и засинтезируем схему соединений в Yosys. Оба пакета прекрасно чувствуют себя как под linux, так и WSL, так что выбирайте наиболее удобный вам вариант.
Ставим iverilog
(Документация)
git clone https://github.com/steveicarus/iverilog.git
cd iverilog
git checkout --track -b v11-branch origin/v11-branch
git pull
sh autoconf.sh
./configure
make
sudo make install
Ставим yosys
(Документация)
sudo apt-get install build-essential clang bison flex \
libreadline-dev gawk tcl-dev libffi-dev git \
graphviz xdot pkg-config python3 libboost-system-dev \
libboost-python-dev libboost-filesystem-dev zlib1g-dev
git clone https://github.com/YosysHQ/yosys.git
cd yosys
make config-gcc
make
sudo make install
Код модуля сумматора предельно прост — в два бита co и out заносим результат операции сложения трёх однобитовых слагаемых A, B и С, а дальше сигналами переноса соединяем модули однобитовых сумматоров в цепочку необходимой длины (или ширины?), получив сумматор с последовательным переносом. Можно, конечно, намудрить с параллельным переносом, например по схеме Брента-Кунга, но для столь малой ширины это лишено смысла. Элементов потребуется больше, а задержка вычислений в итоге будет такая же.
adder.v
module FA(
input wire ci,
input wire A,
input wire B,
output wire out,
output wire co
);
assign {co,out} = A + B + ci;
endmodule
module adder #(
parameter WIDTH=4
)(
input wire ci,
input wire [WIDTH-1:0] A,
input wire [WIDTH-1:0] B,
output wire [WIDTH-1:0] out,
output wire co
);
genvar i;
generate
for (i=0; i < WIDTH; i=i+1) begin: Add
wire c_out;
if (i==0) begin
FA fa(.A(A[i]), .B(B[i]), .out(out[i]),.ci(ci), .co(c_out));
end
else begin
FA fa(.A(A[i]), .B(B[i]), .out(out[i]),.ci(Add[i-1].c_out), .co(c_out));
end
end
endgenerate
Подготовим тестбенч, задача которого — подать на вход сумматора случайные значения чисел и сравнить число на выходе с ожидаемым. Так мы убедимся, что код работает как надо.
adder_tb.v
module adder_tb();
parameter WIDTH=4;
parameter TEST_NUM=WIDTH*WIDTH;
reg clk;
reg [WIDTH-1:0] A;
reg [WIDTH-1:0] B;
wire [WIDTH-1:0] out;
wire co;
wire [WIDTH:0] res = {co, out};
reg [$clog2(TEST_NUM):0] test;
reg [$clog2(TEST_NUM):0] correct;
adder #(.WIDTH(WIDTH)) Adder(.A(A), .B(B), .out(out), .co(co));
initial begin
$dumpfile("adder.vcd");
$dumpvars(0, adder_tb);
clk = 0;
test = 0;
correct = 0;
A = $urandom();
B = $urandom();
forever #1 clk = ~clk;
end
wire[WIDTH:0] RC = A + B;
always @(clk) begin
if (clk) begin
test <= test + 1;
if (test >= TEST_NUM) begin
$display("TESTS: %d/%d", correct, test);
$finish();
end
if (out == RC) begin
correct <= correct + 1;
end
else begin
$display("%d: %d + %d: Got: %d, exp: %d", test, A, B, res, RC);
end
end
else begin
A <= $urandom();
B <= $urandom();
end
end
endmodule
И запустим наш код на проверку:
iverilog -o adder -P adder_tb.WIDTH=4 -s adder_tb adder_tb.v adder.v -g2012
./adder
TESTS: 16/16
Отлично, всё работает! Теперь посмотрим, во что оно синтезируется. Для этого с помощью презентации по yosys составим tcl-скрипт для синтеза.
run_synt.tcl
yosys -import
yosys read_verilog [lindex $argv 0]
yosys chparam -set WIDTH [lindex $argv 2] adder
# read design
hierarchy -check
yosys synth -top [lindex $argv 1]
# # high-level synthesis
yosys proc
yosys opt
yosys memory
yosys opt
yosys fsm
yosys opt
# # low-level synthesis
yosys techmap
yosys opt
set cell_lib "fluidic.lib"
# # map to target architecture
yosys read_liberty -lib $cell_lib
yosys dfflibmap -liberty $cell_lib
yosys abc -liberty $cell_lib
# # split larger signals
yosys splitnets -ports
yosys opt
# # cleanup
yosys clean
# # write synthesized design
yosys write_verilog [lindex $argv 1]_synth.v
# # write intermediate language
yosys write_ilang [lindex $argv 1]_ilang.txt
# # show
yosys show -format dot -lib [lindex $argv 1]_synth.v -prefix [lindex $argv 1]
yosys stat
yosys stat -liberty $cell_lib
exec dot -Tpng [lindex $argv 1].dot > [lindex $argv 1].png
На примере библиотеки КМОП-элементов cmos_cells.lib и прошлой статьи накидаем библиотеку струйных элементов. Наш базисный элемент можно использовать как элемент НЕ, 2ИЛИ, 2ИЛИ-НЕ, а также в роли буферного элемента, так как мощность выхода позволяет подключить только два элемента. В качестве параметра Area укажем количество базисных элементов, необходимых для создания логического модуля каждого типа — то есть 1. Оставим также оригинальное описание элементов DFF и DFFSR — это синхронные триггеры, без которых yosys отказывается синтезировать — уж больно ему хочется иметь элементы синхронной логики.
fluidic.lib
library(fluidic) {
cell(BUF) {
area: 1;
pin(A) { direction: input; }
pin(Y) { direction: output;
function: "A"; }
}
cell(NOT) {
area: 1;
pin(A) { direction: input; }
pin(Y) { direction: output;
function: "A'"; }
}
cell(AND) {
area: 1;
pin(A) { direction: input; }
pin(B) { direction: input; }
pin(Y) { direction: output;
function: "(A&B)"; }
}
cell(OR) {
area: 1;
pin(A) { direction: input; }
pin(B) { direction: input; }
pin(Y) { direction: output;
function: "(A+B)"; }
}
cell(NOR) {
area: 1;
pin(A) { direction: input; }
pin(B) { direction: input; }
pin(Y) { direction: output;
function: "(A+B)'"; }
}
cell(DFF) {
area: 10;
ff(IQ, IQN) { clocked_on: C;
next_state: D; }
pin(C) { direction: input;
clock: true; }
pin(D) { direction: input; }
pin(Q) { direction: output;
function: "IQ"; }
}
cell(DFFSR) {
area: 10;
ff("IQ", "IQN") { clocked_on: C;
next_state: D;
preset: S;
clear: R; }
pin(C) { direction: input;
clock: true; }
pin(D) { direction: input; }
pin(Q) { direction: output;
function: "IQ"; }
pin(S) { direction: input; }
pin(R) { direction: input; }
; // empty statement
}
}
Осталось объединить всю рутину в общем скрипте:
synt.sh
Где файл split помогает разделить dot-файл с несколькими графами на множество файлов с одним графом в каждом. Это нужно потому, что команда dot обрабатывает только самый первый граф в файле, сколько бы их там не оказалось. Его код выглядит так:
#!/bin/bash
set -Eeuo pipefail
WIDTH=4
if [ -f *.vcd ]; then rm *.vcd fi
iverilog -o adder -P adder_tb.WIDTH=${WIDTH} -s adder_tb adder_tb.v adder.v -g2012
./adder
echo 'tcl run_synt.tcl adder.v adder '${WIDTH} > tcl.txt
yosys < tcl.txt
gvpr -f split adder.dot
rm -f *.png
for file in $(ls *.dot); do
echo $file; dot -Tpng $file -O
done
Где файл split помогает разделить dot-файл с несколькими графами на множество файлов с одним графом в каждом. Это нужно потому, что команда dot обрабатывает только самый первый граф в файле, сколько бы их там не оказалось. Его код выглядит так:
BEG_G {
fname = sprintf("%s.dot",$G.name);
writeG($G, fname);
}
И запустить его. В случае успешного синтеза вывод будет оканчиваться следующей статистикой:
22. Printing statistics.
=== adder ===
Number of wires: 53
Number of wire bits: 53
Number of public wires: 14
Number of public wire bits: 14
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 44
NOR 27
NOT 9
OR 8
Chip area for module '\adder': 44.000000
Так как в роли Area мы выставили число элементов, общий параметр Chip area тут можно интерпретировать именно как оценку требуемого количества модулей. Всего на 4-битовый сумматор нужно 44 штуки. А если точнее, то 40. Сейчас объясню почему.
Посмотрим на картинку соединений, полученную из dot-файла:

Синтезированный сумматор с последовательным переносом
Всё логично. Хотели 4 бита — получили 4 полных сумматора, последовательно соединённых по цепи переноса. Сами полные сумматоры выглядят следующим образом:

На каждый полный сумматор требуется 10 логических элементов. Почему на рисунке их 11? Потому что элементы 191 и 192 — OR и NOR — можно заменить одним элементом, используя оба выхода — их входы распараллелены. Просто в lib-файле я не указал, что у элемента есть два выхода разных типов.
В принципе на данном этапе уже можно брать десяток-другой струйных элементов и соединять их трубками, согласно данной схеме. Однако, если итоговая схема значительно сложнее, хотелось бы большей автоматизации процесса. Например, превратить dot-файл в схему соединений для KiCAD. Вообще, dot-файл — это текстовое представление графа. И в нашем случае неплохо бы было иметь автоматизированный конвертер соединений логических элементов из dot-файла во что-то, что можно импортировать в kiCAD. Представьте, если на месте библиотеки fluidic.lib была бы К155СЕРИЯ.lib. Пишем модуль процессора на Verilog, синтезируем схему соединений, импортируем в kiCAD — и у нас готовая схема соединений логических элементов на 155-й серии микросхем малой степени интеграции! Дальше разводим печатную плату, и в итоге получаем самый натуральный ASIC! Правда с очень специфичными IC в конце. В общем, такого конвертера нет. Из ближайшего, что может помочь — kiCAD кушает ASCII-формат Eagle 6. И в него в принципе можно написать конвертер. Но это не точно.
Можно, конечно, перерисовать вручную. Благо достаточно нарисовать только один бит, и дальше стекировать по модулям:

Это перерисованная схема полного сумматора на базовых элементах OR-NOR. Три входа, два выхода. Аналогично соединяем сигналы переноса и получаем готовый блок сумматора.

По задумке автора было бы неплохо взять элементы меньшего размера, например СМСТ-2 габаритами 10 х 15 мм. Разместить их десяток-другой на одной пластине, и это будет верхний слой бутерброда. В том же KiCAD разводим многослойную плату соединений, на ЧПУ фрезеруем каналы в платах и собираем всё в очень компактный пневмо-бутерброд. В итоге 8-битовый сумматор можно уместить в габариты VHS-кассеты…

Звучит это, конечно, круто, но за отсутствием автоматизированного конвертера пришлось остановиться на трубках… Хотя для лампового компьютера такой подход тоже может пригодиться. Вы же помните, что ламповый компьютер я изначально пишу на Verilog? Как только я с этим закончу, yosys мне засинтезирует схему соединений логических элементов и количество их напишет. Удобно, однако.
В следующей части соберём всё воедино и убедимся, что openSource вполне пригоден для создания процессора постапокалипсиса. А самые нетерпеливые могут посмотреть в этом видео, что получилось:
▍ Литература
Некоторое количество литературы по струйной логике.
- Рехтен А.В, Струйная техника: Основы, элементы, схемы. — М, изд. Машиностроение, 1980, 237 с.
- Лебедев И.В., Трешкунов С.Л. Яковенко В.С, Элементы струйной автоматики. — М, Машиностроение, 1973, 360 с.
- Вулис Л.А., Кашкаров В.П. Теория струи вязкой жидкости. — М, изд. Наука, 1965, 431 с.
- Залмазон Л.А, Теория элементов пневмоники. — М, изд. Наука, 1968, 508 с.
- Кулешова Н.А., Власов Ю.Д., Леладзе И.С. Атлас конструкций элементов систем пневмоавтоматики. Часть 1. Элементы систем УСЭППА и КЭМП. — М, 1995.
- Кулешова Н.А., Власов Ю.Д., Леладзе И.С. Атлас конструкций элементов схем пневмоавтоматики. Часть 2. Элементы струйной системы ВОЛГА. — М, 1996.
- Кулешова Н.А., Власов Ю.Д., Леладзе И.С. Атлас конструкций элементов схем пневмоавтоматики. Часть 3. Типовые схемы на элементах серий УСЭППА, КЭМП, ВОЛГА. — М, 2000.
- Касимов А.М. Система модулей струйной техники СМСТ-2 (дискретная ветвь). — М, 1971.
Telegram-канал с полезностями и уютный чат
