Часть 11: ИНТЕРПРЕТАТОРЫ


plug

11-2: Интерфейс Lisp

plug



Этот раздел поясняет интерфейс языка интерпретатора Lisp в Electric, системе разработки VLSI . Этот интерфейс встроен в верхней части ELK Lisp 3.0, диалекта Scheme (см. http://www-rn.informatik.uni-bremen.de/software/elk/elk.html).

На протяжении этого раздела примеры кода Lisp будут появляться underlined (подчеркнутыми). Например, предикат "getarcproto" получает имя прототипа дуги и возвращает указатель на этот объект. Это кодируется, как (getarcproto 'Metal-1), что вычисляется по указателю формы #[arcproto Metal-1].

В разделе предполагается, что читатель очень хорошо знает Electric, и как-то знаком с внутренним строением системы. Внутреннее руководство (Internals Manual) (документ, доступный на Static Free Software) обеспечивает широкий, С-ориентированный взгляд на информацию, описанную здесь. Для пользователей Lisp, однако, этот раздел суммирует важные аспекты внутреннего руководства. В общем, наилучший путь к пониманию этого раздела - выполнить каждую команду по мере ее объяснения.

Управление сессией

Для вызова интерпретатора Lisp используйте субкоманду LISP... команды Language Interpreter меню Tools. На некоторых системах, возможно, понадобится переместить курсор в окно сообщений (текстовое окно), чтобы интерпретатор "слышал" вас.

Если у вас есть дисковый файл с кодом Lisp, вы можете прочитать его в интерпретатор, введя:
  (load 'FILENAME)

Чтобы вернуться в Electric из Lisp, введите ^D (удерживайте на клавиатуре клавишу Control и нажмите "D"). В  Windows вы должны нажать клавишу ESC.

Структура базы данных

Полная база данных Electric - это коллекция объектов, каждый из которых имеет произвольное количество атрибутов. Этот раздел кратко показывает типы объектов и то, как они связаны. Дальнейшее знакомство можно продолжить по внутреннему руководству (Internals Manual). См. в разделе 11-5 список атрибутов этих объектов.

Индивидуальные компоненты внутри схемы описаны NODEINST объектами (образцы узлов), а индивидуальные провода описаны ARCINST объектами (образцы дуг).  Соединения между компонентами и провода описаны  PORTARCINST объектами (образцы портов, которые соединены с дугами). Поскольку и компоненты, и провода имеют геометрию, каждый, тем не менее, имеет ассоциированный объект GEOM, и все объекты GEOM в фасете организованы пространственно в R-дерево с набором RTNODE объектов.

Класс объектов также существует для описания всех индивидуальностей данного типа. Объект NODEPROTO описывает компоненты прототипов, которые могут иметь много индивидуальных NODEINST объектов, ассоциированных с ними. Например, CMOS P-транзистор описан единственным объектом NODEPROTO, и есть много NODEINST объектов для каждого образца такого транзистора в любой цепи. Иерархия реализована вводом сложных компонент, больше известных как фасеты, представленных таким же образом, как примитивы компонент, такие как транзисторы. Например, цепь ALU описана единственным объектом NODEPROTO, а каждый образец этой цепи выше в иерархии описан объектом NODEINST.

Объект CELL собирает разные виды и версии схемы. Каждый из них назван "facet" (представленный объектом NODEPROTO), и фасет имеет и указатель VIEW, и номер версии.

Дополнительно к прототипам компонент ARCPROTO описывает классы проводов, а PORTPROTO описывает классы проводов соединения компонент (component-wire connections). Дополнительный объект PORTEXPINST существует для экспортов. Объект NETWORK описывает электрически соединенные ARCINST и PORTPROTO объекты внутри FACET.

В качестве дальнейшей агрегации объектов есть  LIBRARY - коллекция ячеек и фасет. TECHNOLOGY - набор примитивов компонент (NODEPROTOs) и всех классов проводов (ARCPROTOs).

Дополнительно к выше описанным указателям на объекты есть некоторые стандартные типы значений, которые могут ассоциироваться через getval:

integer

32-bit integer

address

32-bit integer

char

8-bit byte

string

null-terminated string of bytes

float

32-bit floating point number

double

64-bit floating point number

fract

fractional integer number that is multiplied by 120

short

16-bit integer

window

window partition object

window-frame

display window object

constraint

constraint system object

graphics

graphical attributes object

Также есть возможность иметь отображаемые переменные (те, чьи значения появляются на объекте) с ключевым словом: displayable.

Проверка базы данных

Для начала поиска в базе данных важно знать текущую библиотеку. Это сделано так:
  (curlib)
возвращает указатель на объект LIBRARY (например,  #[library noname]). Отсюда текущий фасет может быть получен с помощью:
  (getval (curlib) 'firstnodeproto)

Существенно, что любой атрибут может быть проверен с помощью getval, и новый атрибут может быть создан с помощью setval. Getval имеет следующий формат:
  (getval OBJECT 'ATTRIBUTE)
где OBJECT - ассоциируемый объек, а ATTRIBUTE - запрашиваемые атрибуты. Список всех существующих атрибутов на объектах Electric дан в конце этого документа.

Новые атрибуты могут быть созданы на любом объекте с помощью setval. В общем, многие из существующих атрибутов, которые описаны в конце этого документа, не могут быть установлены с помощью setval, но управляются специальными предикатами модификации базы данных. Формат setval:
  (setval OBJECT 'ATTRIBUTE VALUE OPTIONS)
где OPTIONS это либо 0, либо 'displayable для показа этого атрибута при отображении объекта. Например, для добавления нового атрибута, названного "power-consumption" к компоненту транзистор "t1", и придания ему значения 75, используйте:
  (setval t1 'power-consumption 75 0)
Для добавления отображаемого имени узлу "t1" используйте:
  (setval t1 'NODE_name "Q1" 'displayable)
Для установки массива значений используйте vectors. Например, для установки очертания узла чисто слоя - pure-layer node "metal" со звездочкой используйте:
  (setval metal 'trace (vector -1000 0 0 1000 1000 0 0 -1000) 0)

Единственный элемент в массиве атрибутов может быть установлен с помощью:
  (setind OBJECT 'ATTRIBUTE INDEX VALUE)
где INDEX - это 0-базируемый элемент массива.

И, наконец, атрибут может быть удален с помощью:
  (delval OBJECT 'ATTRIBUTE)
Однако только те атрибуты, которые были созданы  setval, могут быть удалены таким образом. Другие атрибуты защищены.

Основы синтеза

Для создания нового фасета в текущей библиотеке используйте:
  (newnodeproto 'FACETNAME (curlib))
возвращающий указатель NODEPROTO, который может быть использован при последующих вызовах, размещающих компоненты и провода в этом фасете.

Для получения адреса существующего NODEPROTO используйте:
  (getnodeproto 'FACETNAME)
возвращающий тот же тип значения, что и newnodeproto. Таким образом, код:
  (define myfacet (newnodeproto 'adder{lay} (curlib)))
тот же, что и код:
  (newnodeproto 'adder{lay} (curlib))
  (define myfacet (getnodeproto 'adder{lay}))
а оба - часть представления "layout" ячейки, названной "adder".

В качестве отступления, предикат getcell может быть использован для получения объекта CELL, как здесь:
  (getcell 'adder)
возвращая то же, что и:
  (getval myfacet 'cell)

Для создания компонента в фасете используйте:
  (newnodeinst PROTO LOWX HIGHX LOWY HIGHY TRANSPOSE ANGLE FACET)
где PROTO это NODEPROTO компонента, который будет создан, LOWX, HIGHX, LOWY и HIGHY - это рамки компонента, ANGLE число десятков градусов поворота компонента, TRANSPOSE это не нуль для транспонирования ориентации компонента (после поворота), а FACET - NODEPROTO, в котором будет размещаться компонент.

Четыре ограничивающих значения это нечто путанное для вычисления. Для примитивов компонент (таких, как транзистор) любое значение приемлемо, и компонент будет масштабирован. Однако все еще хорошо бы знать значение по умолчанию, которое может быть получено из NODEPROTO с помощью getval следующим образом:
  (define tran (getnodeproto 'P-Transistor))
  (define lowx (getval tran 'lowx))
  (define highx (getval tran 'highx))
  (define lowy (getval tran 'lowy))
  (define highy (getval tran 'highy))
Когда сложные компоненты (фасеты) размещаются, контуры ДОЛЖНЫ быть точно такими же, как рамки содержимого фасета. Эта информация доступна вышеприведенным образом. В качестве примера newnodeinst, и получения вычисления выше показанного контура, предопределенный размер P-транзистора создается в "adder" фасета с помощью:
  (define t1 (newnodeinst tran lowx highx lowy highy 0 0 myfacet))
Возвращаемый указатель на компонент транзистор будет использован позже при соединении.

Для соединения двух компонент необходимо знать следующие четыре вещи:

Места соединения названы PORTPROTOs и ассоциированы с NODEPROTOs. Для получения адреса используйте:
  (getportproto NODEPROTO 'PORTNAME)
Например, для получения порта поликремния на левой стороне MOSIS CMOS P-транзистора используйте:
  (define polyleft (getportproto tran 'p-trans-poly-left))
К сожалению, нет хороших способов получить список имен портов на примитивах компонент. Есть, однако, некоторые упрощения. Например, если есть только один порт (как в случае с большинством контактов и выводов), тогда его имя не нужно:
  (define port (getval tran 'firstportproto))
Этим будет получен первый порт на компоненте P-транзистор. Для получения координат порта для соединения используйте:
  (portposition NODE PORT)
Возвращается вектор с координатами. Например:
  (define portpos (portposition t1 polyleft))
и будут получены координаты порта "p-trans-poly-left" на вновь созданном P-транзисторе t1. Значение X будет (vector-ref portpos 0), а значение Y (vector-ref portpos 1).

Последняя часть необходимой информации - тип дуги и ширина дуги. Задавая имя дуги, можно получить тип с помощью:
  (getarcproto 'ARCNAME)
Задавая ARCPROTO, предопределенная ширина будет получена с помощью:
  (getval ARCTYPE 'nominalwidth)
Когда вся информация готова, вызов:
  (newarcinst ARCTYPE WIDTH BITS NODEA PORTA XA YA NODEB PORTB XB YB FACET)
разместит проводку. Вы можете игнорировать значение BITS и установить его в ноль.

Вот полный пример размещения транзистора, контакта и проведения соединения между ними (результат показан здесь же).

  ; create a facet called "tran-contact" in the current library
  (define myfacet (newnodeproto 'tran-contact (curlib)))

  ; get pointers to primitives
  (define tran (getnodeproto 'P-Transistor))
  (define contact (getnodeproto 'Metal-1-Polysilicon-1-Con))

  ; get default sizes of these primitives
  (define tlowx (getval tran 'lowx))
  (define thighx (getval tran 'highx))
  (define tlowy (getval tran 'lowy))
  (define thighy (getval tran 'highy))
  (define clowx (getval contact 'lowx))
  (define chighx (getval contact 'highx))
  (define clowy (getval contact 'lowy))
  (define chighy (getval contact 'highy))

  ; get pointer to Polysilicon arc and its default width
  (define arctype (getarcproto 'Polysilicon-1))
  (define width (getval arctype 'nominalwidth))

  ; create the transistor and the contact to its left
  (define c1 (newnodeinst contact clowx chighx clowy chighy
    0 0 myfacet))
  (define t1 (newnodeinst tran (+ tlowx 8000) (+ thighx 8000)
    tlowy thighy 0 0 myfacet))

  ; get the transistor's left port coordinates
  (define tport (getportproto tran 'p-trans-poly-left))
  (define tpos (portposition t1 tport))

  ; get the contacts's only port coordinates
  (define cport (getval contact 'firstportproto))
  (define cpos (portposition c1 cport))

  ; run a wire between the primitives
  (newarcinst arctype width 0
    t1 tport (vector-ref tpos 0) (vector-ref tpos 1)
    c1 cport (vector-ref cpos 0) (vector-ref cpos 1) myfacet)

Figure 11.1

Иерархия

Фасеты, как созданные newnodeproto, могут быть размещены в других фасетах с помощью newnodeinst. Образцы просто используют комплексные поля NODEPROTO, а не примитивы NODEPROTOs, как в вышеприведенном примере. Например, следующий код создает новый фасет, названный "two-trans" и размещает два образца фасета "tran-contact", один над другим.

  ; create a facet called "two-trans"
  (define higherfacet (newnodeproto 'two-trans (curlib)))

  ; get pointer to the "tran-contact" facet
  (define t-c (getnodeproto 'tran-contact))

  ; get size of this facet
  (define lowx (getval t-c 'lowx))
  (define highx (getval t-c 'highx))
  (define lowy (getval t-c 'lowy))
  (define highy (getval t-c 'highy))

  ; create the two facet instances, one above the other
  (define o1 (newnodeinst t-c lowx highx lowy highy
    0 0 higherfacet))
  (define o2 (newnodeinst t-c lowx highx
    (+ lowy 10000) (+ highy 10000) 0 0 higherfacet))

Figure 11.2

Другой необходимой особенностью при создании иерархии является возможность разместить провода между местами соединения на образцах фасета. Чтобы сделать это, необходимо создать экспорты. При этом берется порт на примитиве компонента (например, транзистора или контакта в фасете "tran-contact") и устраивается в экспорте на текущем фасете. Выполняется с помощью:
  (newportproto FACET NODE-IN-FACET PORT-ON-NODE 'PORTNAME)
где FACET - это фасет, содержащий компонент, чей порт экспортируется, NODE-IN-FACET это тот компонент, а PORT-ON-NODE это частный порт на этом узле, который будет экспортирован. Например, для экспорта верхнего и нижнего портов фасета "tran-contact" (как показано здесь), может быть добавлен следующий код:


  (newportproto myfacet t1
    (getportproto tran 'p-trans-diff-top) 'topdiff)
  (newportproto myfacet t1
    (getportproto tran 'p-trans-diff-bottom) 'botdiff)

Figure 11.3

И затем компоненты "o1" и "o2" в фасете "two-trans" могут быть соединены, используя порты, названные "topdiff" и "botdiff":

  ; get pointer to P-Active arc and its default width
  (define darctype (getarcproto 'P-Active))
  (define dwidth (getval darctype 'nominalwidth))

  ; get the bottom facet's top port
  (define lowport (getportproto myfacet 'topdiff))
  (define lowpos (portposition o1 lowport))

  ; get the top facet's bottom port
  (define highport (getportproto myfacet 'botdiff))
  (define highpos (portposition o2 highport))

  ; run a wire between the primitives
  (newarcinst darctype dwidth 0
    o1 lowport (vector-ref lowpos 0) (vector-ref lowpos 1)
    o2 highport (vector-ref highpos 0)
      (vector-ref highpos 1) higherfacet)

Figure 11.4

Модификация

Два вида модификации может быть выполнено для существующих объектов: удаление и изменение. Для удаления фасета используйте:
  (killnodeproto FACET)

Чтобы скопировать фасет (внутри той же библиотеки или из одной библиотеки в другую) используйте:
  (copynodeproto FROM-FACET TO-LIBRARY 'TO-FACET-NAME)
где FROM-FACET - оригинальный фасет (NODEPROTO) и TO-LIBRARY - библиотека-получатель (destination). Используйте (curlib) для копирования в ту же самую библиотеку. Новое имя фасета - последний параметр. Предикат возвращает адрес нового фасета (NODEPROTO).

Для удаления компонента используйте:
  (killnodeinst NODE)
До удаления компонента все провода и экспорты должны быть удалены.

Для изменения размера или ориентации компонента используйте:
  (modifynodeinst NODE DLOWX DLOWY DHIGHX DHIGHY DROTATION DTRN)
где DLOWX, DLOWY, DHIGHX и DHIGHY изменения позиции и размера. DROTATION и DTRN изменения ориентации.

Для изменения прототипа компонента используйте:
  (replacenodeinst OLD-NODE NEW-PROTOTYPE)
где старый компонент - OLD-NODE, а новый NODEPROTO, который должен быть на его месте, это NEW-PROTOTYPE. Этот новый прототип должен быть способен соединиться со всеми существующими дугами. Предикат возвращает адрес нового компонента.

Для удаления проводов используйте:
  (killarcinst ARC)

Для изменения ширины или позиции проводов используйте:
  (modifyarcinst ARC DWIDTH DX1 DY1 DX2 DY2)
где DWIDTH, DX1, DY1, DX2 и DY2 - изменения ширины, X/Y позиции end 1, и X/Y позиции end 2. Заметьте, что изменения позиции не сказываются на перемещении соединенных узлов, так что изменения могут быть только небольшими, работающими внутри портов.

Для изменения прототипа провода используйте:
  (replacearcinst OLD-ARC NEW-PROTOTYPE)
где OLD-ARC - формирователь провода, а NEW-PROTOTYPE - новый ARCPROTO для использования. Узлы на обоих концах должны быть доступны этому новому типу провода. Предикат возвращает адрес нового провода.

Для удаления экспорта используйте:
  (killportproto FACET PORT)
что удалит порт PORT на фасете FACET.

Для перемещения экспорта с одного компонента на другой (сохраняя присоединенные провода) используйте:
  (moveportproto FACET OLD-PORT NEW-NODE PORT-ON-NEW-NODE)
где старый порт - OLD-PORT в фасете FACET, а его теперь перемещенный компонент - NEW-NODE (который также в фасете FACET), порт PORT-ON-NEW-NODE этого компонента.

Поиск

Общей операцией является поиск всех компонент в фасете. Следующий код печатает имена всех компонент в фасете "myfacet":

  (do
    (
      (node (getval myfacet 'firstnodeinst)
        (getval node 'nextnodeinst))
    )
    ((null? node))

    (format #t "Found ~s node~%" (describenode node))
  )

Где describenode - определено так (имя узла находится в разных местах, в зависимости от того, является ли он примитивом или сложным NODEPROTO):

  (define describenode
    (lambda (node)
      (define proto (getval node 'proto))
      (if (= (getval proto 'primindex) 0)
        (getval (getval proto 'cell) 'cellname)
          (getval proto 'primname)
      )
    )
  )

А следующий код печатает имена всех проводов в фасете "myfacet":

  (do
    (
      (arc (getval myfacet 'firstarcinst)
        (getval arc 'nextarcinst))
    )
    ((null? arc))

    (format #t "Found ~s arc~%"
      (getval (getval arc 'proto) 'protoname))
  )

Для осуществления поиска всех узлов и дуг в прямоугольной области фасета вначале вызовите:
  (initsearch LOWX HIGHX LOWY HIGHY FACET)
где LOWX, HIGHX, LOWY и HIGHY - координаты поиска в фасете FACET (NODEPROTO). Этот предикат вернет ключ поиска, который затем повторно будет передан:
  (nextobject SEARCHKEY)
что вернет GEOM объекты каждого узла и дуги в области поиска. Когда предикат возвращает Null, поиск завершается. GEOM объекты могут указывать либо на узел, либо на дугу, в зависимости от их атрибута "entryisnode". Затем атрибут "entryaddr" укажет на актуальный NODEINST или ARCINST. Вот пример кода, который печатает имена всех узлов и дуг в области (2000 <= X <= 10000, -3000 <= Y <= 3000). Выбранная область показана здесь как черный ящик.

  (define key (initsearch 2000 10000 -3000 3000 myfacet))
  (do
    (
      (object (nextobject key) (nextobject key))
    )
    ((null? object))

    (define isnode (getval object 'entryisnode))
    (if (= isnode 0)
      (format t "Found ~s arc~%"
        (getval
          (getval
            (getval object 'entryaddr)
          'proto)
        'protoname)
      )
      (format t "Found ~s node~%"
        (describenode (getval object 'entryaddr))
      )
    )
  )

Figure 11.5

Виды

Вид (отображение) - объект, описывающий ячейку. Есть много стандартных отображений: Layout, Schematic, Icon, Simulation-snapshot, Skeleton, VHDL, Verilog, Document, Unknown и много вариантов Netlist. Дополнительно может быть создан новый вид с помощью "newview":
  (newview 'VIEWNAME 'ABBREVIATION)
и вид может быть удален с помощью killview (стандартные виды не могут удаляться):
  (killview VIEW)
Для получения объекта вида используйте getview на его имени.

Для ассоциации разных видов ячейки предикаты iconview и contentsview достигают разных фасет. Например:
  (iconview myfacet)
находит ассоциированный фасет иконки ячейки, в которой находится "myfacet".

Библиотеки

В примерах выше всегда используются текущие библиотеки. Это определяется вызовом:
  (curlib)
Однако могут быть и другие библиотеки. Для получения специально названной библиотеки используйте:
  (getlibrary 'LIBNAME)

Для создания новой библиотеки используйте:
  (newlibrary 'LIBRARYNAME 'LIBRARYFILE)
где LIBRARYNAME - используемое имя, а LIBRARYFILE - имя, где эта библиотека будет хранится. Этот предикат возвращает адрес нового объекта библиотеки, который может быть использован, когда создается фасет.

Только одна библиотека является текущей, и для переключения вы должны использовать:
  (selectlibrary LIBRARY)

Библиотека может быть удалена:
  (killlibrary LIBRARY)

Библиотека может быть вытерта (erased) (ее фасеты удаляются, но не сама библиотека) с помощью:
  (eraselibrary LIBRARY)

Технологии

Технология - это окружение разработки, включающее примитивы компонент и прототипы проводов. Текущая технология может быть получена с помощью:
  (curtech)

Определенная технология может быть получена по имени с помощью:
  (gettechnology 'TECHNAME)

Все технологии могут быть найдены в списке, начинающемся с технологии, которая называется "Generic".

Инструменты

Инструмент - это кусочек кода синтеза или анализа, который может оперировать на базе данных. Обычный объект инструмента может быть получен с помощью:
  (gettool 'TOOLNAME)
где возможные имена инструментов:

compaction

circuit compaction

compensation

geometry compensation

drc

design-rule checking

erc

electrical-rule checking

io

input/output control

logeffort

logical effort analysis

network

network maintenance

pla

programmable logic array generator

project

project management

routing

automatic wire routing

silicon-compiler

netlist-to-layout silicon assembler

simulation

simulation

user

the user interface

vhdl-compiler

VHDL-to-netlist compiler

Некоторое количество инструментов доступно через:
  (maxtool)
И частные инструменты, индексированные от 0 до (maxtool)-1, могут быть получены:
  (indextool INDEX)

Инструменты можно переключать с помощью:
  (toolturnon TOOL NO-CATCH-UP)
где TOOL объект инструмента, а NO-CATCH-UP не нулевое для сдерживания активности, обычно предусмотрено, когда инструмент отключается.

Инструмент можно отключить с помощью:
  (toolturnoff TOOL)

Инструменту можно передать специальные инструкции:
  (telltool TOOL PARAMETERS)
Например, список всех технологий, используя следующий код:
  (telltool (gettool 'user) 'show 'technologies)
Эти команды исходят от низкоуровневого интерпретатора команд, который полностью задокументирован в  Internals Manual.

Разное

Все изменения в базе данных выстраиваются во внутреннюю очередь в "batch", который включает изменения и любые вынужденные сторонние эффекты этих изменений. Новый пакет (batch) создается для каждой сессии Lisp интерпретатора (также для каждой команды Electric, которая вызывается с клавиатуры/мышкой). Для отказа от последнего пакета изменений используйте:
  (undoabatch)

Множественные вызовы этим предикатом в единственном пакете отменят множество пакетов. Для очистки списка пакетов изменений используйте:
  (noundoallowed)

Если вы создаете проводник, который делает много изгибов, необходимо создать специальные узлы, называемые "pins" на каждом изгибе. Для отыскания, какого рода вывод использовать для данного типа проводника, используйте:
  (getpinproto ARC-PROTO)
где ARC-PROTO - тип проводника, а предикат возвращает тип компонента (NODEPROTO) вывода.

Сетевые объекты могут быть получены по имени предикатом getnetwork, который берет имя и фасет для поиска. Например, код:
  (getnetwork 'insig myfacet)
получает адрес сети, названный "insig" в фасете myfacet.

Общая функция образца узла может быть определена с помощью:
  (nodefunction NODE)
возвращая значения из списка констант в файле С заголовка "efunction.h". Это значение совершенно то же самое, что было бы получено при просмотре полей "userbits" прототипа узла. Однако некоторые компоненты, имеющие общие прототипы, будут в большей степени специфицироваться этим предикатом.

Чтобы получить значение атрибута из образца выше в иерархии используйте:
  (getparentval 'name DEFAULT HEIGHT)
где name - имя атрибута, DEFAULT - значение по умолчанию, возвращаемое, если атрибут не найден, а HEIGHT - число уровней иерархии, по которым нужно подняться при поиске атрибута (0 для бесконечности). Как и горячая клавиша для поиска значений параметра, есть четыре макроса, которые используют этот шаблон:


Prev

Предыдущий

   

Contents

Содержание

   

Следующий

Next