Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Проектируемый компьютер на сверхминиатюрных электронных лампах хоть и является радиационно-стойким, однако работает на электричестве. Кроме того, восстановить в сжатые сроки производство электронных ламп в условиях постапокалипсиса будет довольно сложной задачей. На руинах цивилизации гораздо проще организовать массовое производство логических элементов, работающих на эффекте прилипания струи воздуха к стенке — при этом сам элемент можно лепить хоть из глины! И мало того, что для создания потока воздуха не обязательно использовать электричество — теоретически такой процессор сможет работать на энергии ударной волны ядерного взрыва! Но обо всём по порядку. Для тех, кто следит за проектом DekatronPC — не пугайтесь, ему ничего не угрожает.
В прошлый раз в статье «Пневмоника и влажные мечты стимпанка» я уже рассказывал о струйных логических элементах и поделился мыслями о том, что в принципе на этой технологии можно собрать полноценное вычислительное устройство.
Принцип работы элемента на эффекте прилипания струи воздуха к стенке
Известный из прошлой статьи базисный элемент работает на эффекте прилипания ламинарной струи воздуха к стенке. Вырываясь из сопла, струя воздуха натурально присасывается к одной из двух стенок элемента, а с помощью управляющих сигналов её можно перекинуть на противоположную сторону. При этом у дискретного элемента между выходами имеется вихреобразователь, который создёт барьер для струи и гарантирует строгость переключения элемента.
Такой базисный элемент лёг в основу известных мне серийных струйных элементов серии ВОЛГА и СМСТ-2. Рисунки элементов СМСТ найти не удалось — повезло лишь отыскать книгу описания серии в целом, а вот по элементам ВОЛГА нашёлся полноценный альбом элементов! Мой товарищ, Антон Н., помог в оцифровке нескольких рисунков. После печати на фотополимерном принтере на свет появился элемент №1 — СТ41. Он имеет два входа справа и по выходному каналу с каждой стороны. В правом канале реализована логическая функция 2ИЛИ-НЕ, а в левом — 2ИЛИ.
Реплика элемента серии ВОЛГА СТ41 из фотополимерного принтера
Для проверки его работоспособности я напечатал пару крыльчаток, задача которых — вращаться от потока воздуха. Собственно, основная проблема струйных элементов — очень слабый выход. Никакого толку от установки манометра не будет — он просто ничего не покажет. А вот поставить крыльчатку можно. Несмотря на вдвое бо́льшие размеры сопла и большой расход питающего воздуха, элемент уверенно заработал. Правда не так, как надо… Согласно альбому схем, по умолчанию воздух должен идти в правый канал и при подаче управляющих сигналов справа переключаться на левый. А у элемента №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), который обеспечивает дискретность работы элемента. Угол между выходным каналом и вихреобразователем должен отсекать часть струи на себя, чтобы она пошла по радиусу отсекателя и образовала вихрь в центре элемента. Он отрежет неактивный канал от потока воздуха, а главное — дополнительно прижмёт струю к левой стенке. Из этого кадра видно, что образуемый вихрь очень странный — что решается опять-таки изменением геометрии самого завихрителя. В видео выше элемент уже перерисован на сплайнах и эти моменты были исправлены. Как итог — струя воздуха ламинарна вплоть до поворота выходного канала.
Элемент стал работать лучше — вся входная струя идёт на выход. Но по-прежнему не на тот, который нужен. На видео заметно, как в самом начале струя пытается прилипнуть к правой стенке за счёт небольшого угла в её сторону, но подсос воздуха от обводного канала отталкивает её к противоположной. Смотрим на альбом схем ещё раз:
Цитата из «Атласа конструкций элементов схем пневмоавтоматики. Часть II. Элементы струйной системы ВОЛГА»
Конструктивная схема элемента представлена на рис. 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 года и видим, что в действительности обводной канал находится справа. Сравнив сам элемент и его рисунок в альбоме, я понимаю, что авторы фривольно подошли к его чертежу.
Фотография воссозданного (слева) и оригинального (справа) элементов СТ41
Имеет смысл отсканировать настоящий элемент и воспроизвести его на фотополимернике во второй ревизии, однако первая ревизия справляется со своей функцией, поэтому пока оставим как есть.
▍ Синтезируем вычислитель
С элементом разобрались. Теперь нужно понять, сколько их требуется для создания двоичного сумматора с последовательным переносом. Вручную рисовать схему мне лень, так что напишем всю логику на языке 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-кассеты…
Верхний слой будущего бутерброда. Матрица из 10 струйных элементов, каждый размером 10 х 15 мм. Требуется калибровка печати (или 8К матрица)
Звучит это, конечно, круто, но за отсутствием автоматизированного конвертера пришлось остановиться на трубках… Хотя для лампового компьютера такой подход тоже может пригодиться. Вы же помните, что ламповый компьютер я изначально пишу на Verilog? Как только я с этим закончу, yosys мне засинтезирует схему соединений логических элементов и количество их напишет. Удобно, однако.
В следующей части соберём всё воедино и убедимся, что openSource вполне пригоден для создания процессора постапокалипсиса. А самые нетерпеливые могут посмотреть в этом видео, что получилось:
▍ Литература
Некоторое количество литературы по струйной логике.
- Рехтен А.В, Струйная техника: Основы, элементы, схемы. — М, изд. Машиностроение, 1980, 237 с.
- Лебедев И.В., Трешкунов С.Л. Яковенко В.С, Элементы струйной автоматики. — М, Машиностроение, 1973, 360 с.
- Вулис Л.А., Кашкаров В.П. Теория струи вязкой жидкости. — М, изд. Наука, 1965, 431 с.
- Залмазон Л.А, Теория элементов пневмоники. — М, изд. Наука, 1968, 508 с.
- Кулешова Н.А., Власов Ю.Д., Леладзе И.С. Атлас конструкций элементов систем пневмоавтоматики. Часть 1. Элементы систем УСЭППА и КЭМП. — М, 1995.
- Кулешова Н.А., Власов Ю.Д., Леладзе И.С. Атлас конструкций элементов схем пневмоавтоматики. Часть 2. Элементы струйной системы ВОЛГА. — М, 1996.
- Кулешова Н.А., Власов Ю.Д., Леладзе И.С. Атлас конструкций элементов схем пневмоавтоматики. Часть 3. Типовые схемы на элементах серий УСЭППА, КЭМП, ВОЛГА. — М, 2000.
- Касимов А.М. Система модулей струйной техники СМСТ-2 (дискретная ветвь). — М, 1971.
Telegram-канал с полезностями и уютный чат