Перестроим наш проект таким образом, чтобы использовать иерархию схем. Поскольку тема изучения gEDA, в конечном счете, важнее проектируемой вещицы, мы сделаем несколько небольших отступлений. Иерархические схемы, это когда ты кликаешь символ компонента, а перед тобой открывается его внутреннее устройство. Если ты еще при этом можешь что-то изменить, вернуться обратно и получить новые данные, то это уже иерархическая симуляция. Мы увидим, что возможности автоматизации работы в бесплатном комплекте программ gEDA ничем не уступают коммерческим продуктам, если чуть-чуть поработать напильником и мозгами.
Начнем с того, что можно сделать весь проект в большой степени, если вообще не полностью, независимым от системных библиотек. Это очень полезно, если у нас большие расхождения, например, в графических обозначениях, пинах или мы используем что-то нестандартное. Немалая выгода есть и в переносимости с места на место. Весь проект можно перенести на другой компьютер, где есть программы gEDA, и работать там с правами обычного пользователя, как на собственном.
Создадим пустой каталог с любым именем. (Я создал hisim.) В этот каталог поместим файл с именем gafrc со следующим содержимым на языке, очень похожем на LISP:
( source-library "sktlib" )
( component-library "digsym" "My Digsym" )
( component-library "analog" "My Analog" )
И создадим три каталога с именами analog, digsim, и sktlib. Переименуем старый digsim, чтобы он стал невидимым из системной конфигурации. Это будет нам мешать.
Теперь мы можем запускать gschem и gnetlist из этого нового каталога и все утилиты увидят и прочитают файл gafrc. И мы увидим в окне статуса, что этот файл прочитан. Он обеспечит нам все необходимые пути к файлам. Таким образом, мы получим независимость от системных библиотек.
В каталог digsim скопируем все наши цифровые символы, а в каталоге analog сделаем что-нибудь еще. Например, пусть это будут символы для усилителя с одним входом, и символ таймера ne555. Последний мы можем нарисовать сами или скопировать lm555-1.sym из системной библиотеки и добавить к нему недостающие атрибуты.
Поскольку в схемах символы ссылаются друг на друга, то можно устроить путешествие по страницам подсхем. В этом и состоит иерархическая организация схем. Каталог sktlib будет содержать подсхемы, связанные с теми или иными символами, которые мы создаем.
Теперь закинем в hisim файл comp.sch, где у нас была подсхема компаратора. Проследим, чтобы пины были названы корректно, например: P1, P2, ... Пусть полежит.
Займемся другой схемой. Нам понадобится усилитель сигнала после триггера. Мы могли бы взять модель xspice gain, но на самом деле тут есть одна неприятная вещь. Модель gain "усилителя" выходного сигнала, которую мы можем взять из XSPICE-расширений симулятора, - неуправляемая. А нам нужно, чтобы выходное напряжение таймера не превосходило напряжения его питания. Поэтому мы можем собрать собственную модель на основе компаратора, которым занимались недавно. Для этого удалим один вход и перепишем условие для источника B1:
Подсхему gain тоже скопируем в каталог sktlib.
Создадим пустую схему test.sch. Нам придется создать свои символы, так как библиотечные имеют неудачные метки пинов. Поэтому откроем символ opamp-1.sym или opamp-2.sym и просто сохраним его копию в нашей библиотеке analog (не подумайте, что она имеет отношение к почтенной компании из Норвуда, Массачусетс. Просто там у нас "аналоговые" элементы, чтобы не смешивать их с цифровыми.) как comp.sym. Выправить файл можно вручную, в текстовом редакторе, правя нужные атрибуты и удаляя лишние.
Затем откроем этот же файл в gschem и убедимся, что все в порядке. Сохраним его копию как gain.sym и будем ее править. Надо удалить одну ногу на входе и переименовать/перенумеровать все пины.
Теперь начнем постепенно собирать модель таймера, проверяя ее работу на каждом этапе. Такой подход позволяет отловить ошибки в момент их возникновения. Первое, что нам нужно сделать, это обеспечить иерархию схем. Сделать это совершенно несложно. Вот тестовая схема из файла test.sch:
На ней источник питания, генератор треугольного сигнала и схемка тестируемого компаратора. Элемент X1 - это подсхема, и он имеет атрибут value=comp. Но у X1 есть еще один атрибут: source=comp.sch Если будете повторять схему, добавьте этот атрибут, а потом откройте файл test.sch в текстовом редакторе и сами найдите строку с этим атрибутом. Поэтому, когда мы кликнем по значку выделенного элемента правой кнопкой мыши и выберем соответствующий пункт:
Перед нами откроется его подсхема:
Точно так же мы можем перейти наверх командой Up. Те же команды можно найти в меню Hierarchy.
Файл comp.sch находится, должен находиться, в каталоге sktlib, библиотеке подсхем. Мы прописали это в файле gafrc в нашем рабочем каталоге.
Но что, если мы попробуем сгенерить нетлист: gnetlist -g spice-sdb -o test.cir test.sch? Мы получим:
*******************************
* Begin .SUBCKT model *
* spice-sdb ver 4.28.2007 *
*******************************
.SUBCKT comp
*============== Begin SPICE netlist of main design ============
.include comp.cir
R2 0 1 1k
R1 1 Vcc 1k
X1/R4 2 out 100
X1/R3 0 Vcc 8k
X1/B1 2 0 V = V(in_p)-V(in_n) > 0 ? V(vcc)*0.9 : V(vcc)*0.1
X1/R2 in 0 100k
X1/R1 1 0 100k
V2 in 0 pwl( 0 0 0.1 5 0.2 0 )
.tran 1m 0.2
V1 Vcc 0 5V
.ends comp
*******************************
Но это совсем не то, что нам нужно. Это не будет работать. Здесь нарушен синтаксис SPICE. Попробуйте сами передать эту туфту ngspice. Смотреть схемы "в глубину", это здорово, но что делать с таким геморроем? gnetlist имеет еще кое-какие возможности по работе с иерархическими схемами, я еще не разобрался с ними. "Но я буду работать над этим. Пока у меня не получится." (лт. Коломбо)
Пока одним из решений, самым простым, будет убрать или заменить атрибут source. Тогда будет сгенерирована нормальная схема и все будет по-человечески. Но каждый раз кликать и править... а если еще таких подсхем косой десяток? Но мы ведь не пользователи Windows, которые годами униженно бьют поклоны разработчикам коммерческих продуктов. А потом радуются, как дети, получив долгожданную пиченьку. (Ура, бля! Вышла новая версия штанов. С пришитыми пуговицами!) Сейчас мы мигом все исправим. Для этого мы напишем простой скрипт:
#!/bin/bash
# Скрипт для симуляции иерархических проектов
# сделанных в редакторе gschem.
# Использование:
# $ sim <имя_файла>
# Файл указывается без расширения! Оно
# подразумевается как .sch
# Ищем в переданном файле строки source и
# фильтруем их во временном файле t. Мы
# подставляем допустимый, и ни на что не
# влияющий атрибут компонента.
cat $1.sch | awk '
/^source/{ print "comment=stub"; next }
{ print $0 }' > t
gnetlist -g spice-sdb -o $1.cir t
unlink t
Сделаем его исполняемым (по образцу sim из ранних постов по теме). И теперь совсем другое дело:
* gnetlist -g spice-sdb -o test.cir t
*********************************************************
* Spice file generated by gnetlist *
* spice-sdb version 4.28.2007 by SDB -- *
* provides advanced spice netlisting capability. *
* Documentation at
http://www.brorson.com/gEDA/SPICE/ *
*********************************************************
*============== Begin SPICE netlist of main design ============
.include comp.cir
R2 0 1 1k
R1 1 Vcc 1k
X1 1 in Vcc 0 out comp
V2 in 0 pwl( 0 0 0.1 5 0.2 0 )
.tran 1m 0.2
V1 Vcc 0 5V
.end
Файл comp.cir делается из схемы comp.sch и должен быть в рабочем каталоге. Поскольку схема comp.sch находится в библиотечном каталоге sktlib, то можно получить ее копию в рабочий каталог, получить файл comp.cir, и удалить ненужную копию - не оригинал из библиотеки! - comp.sch. В общем, должно действовать правило: из библиотеки берется только копия в рабочий каталог. Можно для каждой схемы .sch иметь файл .cir в библиотечном каталоге, это только вопрос организации хранения данных. Но, поскольку данные меняются, то появляется головная боль по поводу поддержания их в актуальном состоянии.
Это можно сделать при помощи утилиты make. Обычно ее используют для компиляции сложных проектов на языке C, но ее природа позволяет применить ее к чему угодно. Пока это идея только на заметку. Тем более, что еще есть над чем подумать в gnetlist.
Получим копию файла в текущий каталог и обработаем ее при помощи gnetlist:
$ cp sktlib/comp.sch ./
$ sim comp
. . .
$ rm comp.sch
Теперь можно запускать ngspice и изучать работу схемы. Убедившись, что компаратор работает, продолжаем дополнять схему новыми компонентами и связями:
Поскольку новых подсхем здесь не появилось, X2 не в счет, это дубликат, то нам остается только добавить библиотеку с цифровыми моделями digsim.lib. Снова делаем список узлов и расставляем квадратные скобочки для векторов:
* test.cir
*
* постепенная сборка таймера
*
A10 8 pullup
.include sktlib/digsim.lib
A6 [9] [q2] dac
A5 [7] [q1] dac
A4 [7 6] 9 nand
A3 [8 5 9] 7 nand
A2 [4] [6] adc
A1 [2] [5] adc
R3 0 3 5k
X2 3 in Vcc 0 4 comp
.include comp.cir
R2 3 1 5k
R1 1 Vcc 5k
X1 in 1 Vcc 0 2 comp
V2 in 0 pwl( 0 0 0.1 5 0.2 0 )
.tran 1m 0.2
V1 Vcc 0 5V
.end
Испытываем работу "макета":
Пока все идет хорошо. Добавим инвертор к триггеру, как того требует логика 555.
Здесь мы добавим еще один сигнал reset, и ограничим ток в идеальных элементах резистором. Получим новый список соединений и определим там векторы. (Пора бы уже автоматизировать эту нудную работу.) Схема работает, но ее логика не совсем правильна.
Добавим еще один инвертор на выходе трехвходового элемента И-НЕ:
Теперь получаем нужный результат:
Графики показывают, ради чего, собственно, была изобретена схема 555. Это очень хороший, стабильный триггер Шмитта, с вспомогательными цепями, прекрасно подходящий для промышленных систем автоматики, которые работают в нелегких условиях, и по питанию, и по температуре. Неудивительно, что для этой схемы существует огромное количество приложений.
От выхода q1 должна работать разрядная цепь. Это транзистор с открытым коллектором. Также необходимо еще раз инвертировать сигнал от данной точки и усилить его простым усилителем с одним входом. Тогда таймер, наконец, заработает как функциональное устройство.
Но пора внести некоторые усовершенствования. Наш небольшой опыт с элементами вида A уже подсказывает несколько правил для несложного парсера, при помощи которого можно расставлять квадратные скобки:
1. Цифровые элементы начинаются с A.
2. Заканчиваются на adc, dac, nand (nor), not.
3. Векторов для adc и dac всегда два по одному.
4. Элемент nand (nor) имеет только 1 вектор на входе.
5. not всегда скалярный.
6. Число входов nand (nor) всегда можно определить.
По этим правилам можно написать скрипт. Вернее, дописать sim, чтобы он экономил нам драгоценные минуты. Возьмем awk и напишем:
#!/bin/bash
# Скрипт для симуляции иерархических проектов
# сделанных в редакторе gschem.
# Использование:
# $ sim <имя_файла>
# Файл указывается без расширения! Оно
# подразумевается как .sch
# Ищем в переданном файле строки source и
# фильтруем их во временном файле t. Мы
# подставляем допустимый, и ни на что не
# влияющий атрибут компонента.
cat $1.sch | awk '
/^source/{ print "comment=stub"; next }
{ print $0 }' > t
gnetlist -g spice-sdb -o $1.cir t
# Расставим квадратные скобки в файле .cir
cat $1.cir | awk '
/^A.*adc *$/||/^A.*dac *$/{
print $1" ["$2"] ["$3"] "$4; next }
/^A.*nand *$/||/^A.*nor *$/{ s = $1" ["
for( i=2; i t
mv t $1.cir
В этом файле код не на все случаи в жизни, а только на то, с чем мы здесь сталкивались. Но он работает, можете проверять его сколько угодно. При случае можно расширить список шаблонов и внести добавления в алгоритм, если это потребуется. Только что я потратил несколько минут и посмотрел на описания цифровых и гибридных моделей. Результаты обнадеживают. Больших проблем с квадратными скобками не предвидится.
Но вернемся к схеме. Добавим в нее разрядную цепь на транзисторе и еще один инвертор. Тогда схема подрастет и станет не очень обозримой. Тогда можно разбить схему на две страницы. По именам узлов gnetlist догадается, что надо сделать, если передать ему несколько файлов. Для этого надо слегка переделать скрипт, чтобы передавать ему произвольное число файлов схемных страниц из командной строки:
#!/bin/bash
# Скрипт для симуляции иерархических проектов
# сделанных в редакторе gschem.
# Использование:
# $ sim <файл1> [ <файл2> ... ]
# Файл(ы) указывае(ю)тся без расширения! Оно
# подразумевается как .sch
# Имя выходного файла: <файл1>.cir
# Ищем в переданном файле строки source и
# фильтруем их во временном файле t. Мы
# подставляем допустимый, и ни на что не
# влияющий атрибут компонента.
for i in $*
do
cat $i.sch | awk '
/^source/{ print "comment=stub"; next }
{ print $0 }' > $i.t~
done
gnetlist -g spice-sdb -o $1.cir $(ls *.t~)
rm *.t~
# Пишем свою шапку:
cat << END > t
* $1.cir
*
*
*
END
# Расставим квадратные скобки в файле .cir
cat $1.cir | awk '
/^A.*adc *$/||/^A.*dac *$/{
print $1" ["$2"] ["$3"] "$4; next }
/^A.*nand *$/||/^A.*nor *$/{ s = $1" ["
for( i=2; i> t
mv t $1.cir
Опять вернемся к схеме, на этот раз окончательно. Часть компонентов, а именно вспомогательных, всякие источники, генераторы, кирпичи с директивами и опциями, можно выделить и скопировать командой Y,C. Затем создадим новый лист схемы, назовем его, допустим, power.sch и вставим скопированное командой Y,P. Сохраним. gnetlist сам разберется с соединениями, если мы присвоили им имена. Надо лишь передать ему два файла: test.sch и power.sch
Для этого и был переписан скрипт. Кто разбирается в программировании для оболочки UNIX, тот уже все понял. Остальным нужно иметь в виду, что в конечном счете формируется вот такая командная строка: gnetlist -g spise-sdb -o test.cir test.t~ power.t~ Это уже обработанные файлы test.sch и power.sch, в которых мы подавили мешающий атрибут source. Теперь почти все делается на автомате.
Схемы выглядят так, power.sch:
и test.sch:
Цель этого этапа посмотреть, что будет на транзисторе. Мы получим этот результат последовательностью команд:
$ sim test power
...
$ ngspice test.cir
и дальнейшим диалогом с ngspice.
Простая модель транзистора находится в библиотеке analog.lib, в которой есть и модели comp и gain (закомментированные звездочками):
*
* ==== библиотека нецифровых моделей ====
*
* компаратор
*.SUBCKT comp in_p in_n vcc vdd out
*R4 1 out 100
*R3 vdd vcc 8k
*B1 1 vdd V = V(in_p)-V(in_n) > 0 ?
*+ V(vcc)*0.9 : V(vcc)*0.1
*R2 in_n vdd 100k
*R1 in_p vdd 100k
*.ends comp
* усилитель
*.SUBCKT gain in vcc vdd out
*R3 1 out 10
*R2 vdd vcc 8k
*B1 1 vdd V = V(in)*5 > V(vcc)*0.9 ? V(vcc)*0.9 :
*+ V(in)*5 < V(vcc)*0.1 ? V(vcc)*0.1 : V(in)*5
*R1 in vdd 100k
*.ends gain
* модели транзисторов
.model npn1 npn(is=1p bf=100)
* модели диодов
.model diode d(is=3p cjo=5p)
Поскольку в схемы могут быть внесены изменения, то лучше было бы под конкретные проекты получать копии из библиотек в рабочий каталог. Но любая стандартизация - это результат длительного опыта, так что не стоит спешить тут с окончательным решением.
Последний шаг на пути к таймеру - это добавление каскада усиления выхода q. Поскольку на выходе A11 будет минимум 0.7 В, то усилитель с коэффициентом усиления 5 даст 3.5 В, что нам никак не подойдет. Мы могли бы установить еще пару диодов на выход элемента A11, как это сделано для транзистора. Но в таких случаях гораздо проще добавить смещение в формулу для выходного сигнала. И схема от этого упростится, и симулятору будет проще делать расчеты.
Вставляем gain, он получает атрибут source=gain.sch. Его схему надо немного переделать. Выражение для источника B1 выглядит слишком уж длинным. Поэтому немного упростим его. Вот способ сделать это:
Источник B2 воткнут в землю, и его потенциал висит в воздухе. На самом деле это просто способ ввести функцию, заданную уравнением в файл SPICE. Независимая переменная тут v(in) - входное напряжение, значение функции v(a) - смещенное и усиленное напряжение. Прежний B1 теперь играет роль лишь ограничителя напряжения. Добавляя источники B, заданные уравнением, можно сделать очень многое.
Кстати, вот еще важное замечание. Наши подсхемы могут работать как схемы с плавающим потенциалом. Сейчас они привязаны к земле, для простоты. Если вам когда-нибудь захочется симулировать схему, скажем, драйвера полумоста находящегося на высокой стороне, то в этом случае в аналогичной схеме вместо V(vcc) придется писать V(vcc)-V(vdd) что лишь немного усложняет выражения.
Наконец, таймер готов. Он ощипан и уже лежит в кастрюле. Сейчас мы его сварим и останется только подать:
* ne555.cir
*
* Таймер 555
*
.include digsim.lib
.include analog.lib
.include comp.cir
.include gain.cir
.SUBCKT ne555 vdd 5 22 17 2 1 20 vcc
X3 15 vcc vdd 22 gain
D1 21 19 diode
R5 vdd 19 1k
R4 13 21 400
A9 [13] [18] adc
Q1 20 19 vdd npn1
A10 18 14 not
A6 9 12 not
A5 [17] [16] adc
A2 16 10 not
A11 [14] [15] dac
A8 [12] [13] dac
A7 [9 8] 11 nand
A3 [10 7 11] 9 nand
A4 [6] [8] adc
A1 [3] [7] adc
R3 vdd 4 5k
X2 4 5 vcc vdd 6 comp
R2 4 2 5k
R1 2 vcc 5k
X1 1 2 vcc vdd 3 comp
.ends ne555
Определенные неудобства вызывает нумерация пинов. Ведь gnetlist'у это абсолютно похер. В связи с этим возникает задача: описать алгоритм перестановки номеров на уже имеющемся их множестве. Мы берем первый (логический)пин девайса и смотрим, как он называется? Он называется vdd. Его всюду надо заменить на 1, а 1, соответственно, на vdd. Это не нарушит схему. Затем берем второй, и так далее. В конце, так как контекст имен уже совершенно безумен, хотя схема правильная, мы заменяем имена на номера, добавляя их к последнему и самому старшему. Это задачка по программированию, для профилактики склероза и маразма.
Задача поддержания работающей, не геморройной иерархии, она же "вертикаль", в проекте - это вопрос отношений на множестве файлов. Обрабатывающие скрипты (правила игры) должны быть логично привязаны к этой системе отношений. Тогда у разработчика будут развязаны руки и он сможет легко править схемы и модели, внося изменения на любую глубину.
С другой стороны, хорошо отлаженная модель не нуждается в бесконечных правках. Тогда можно сделать более горизонтальные отношения между моделями, но за счет некоторого дублирования кода и описаний. Ведь теперь каждый компонент содержит в себе все схемы и модели и имеет только внешнюю шкурку-символ. Его внутреннее описание становится недоступным, а сам он сложнее. Доверять ему становится рискованней, нужен больший опыт работы с ним.
Ладно, пусть таймер пока остывает, а в следующем посте будем подавать его к столу. Да и модель симистора добавим к библиотеке. Наконец, мы окончательно разберемся со схемой, а если повезет, внесем больше порядка в работу с проектами.
Близится время, когда мы возьмемся за разводку платы в редакторе pcb, и займемся темой посадочных мест - созданием своих компонентов и использованием их из системных библиотек, довольно обширных. Но, к сожалению, не всегда содержащих в точности необходимый компонент. Поэтому и приходится создавать свои.
Дальше