В этой части мы:
- Запустим Design Rule Check, чтобы проверить возможность произвести нашу схему
- Запустим Device Extraction для сравнения нашего Layout с целевой схемой.
- Произведём сравнение наших компонентов из Layout и нашей схемы, которую мы нарисовали в XSCHEM.
- Сгенерируем netlist с паразитными конденсаторами и резисторами (PEX).
- Просимулируем netlist с паразитными конденсаторами и резисторами.
- Сгенерируем LEF файл.
- Подведём итоги этой серии статей.
▍Запускаем Magic VLSI
Для запуска Magic, нужно выполнить следующую команду. Ключ -rcfile говорит Magic-у взять настройки из этого файла.
И открываем Magic VLSI из папки netgen
, чтобы потом во время шага LVS/PEX результаты, сразу же записались в папку netgen
:
cd netgen
magic -rcfile $PDK_ROOT/sky130A/libs.tech/magic/sky130A.magicrc
Вуаля!
Я почитал, как им пользоваться и всё очень нелогично, поэтому будет лучше, если я приведу сюда небольшую кучу кнопок и горячих клавиш, а затем перейдём к DRC/LVS/PEX/LEF:
Для zoom-а используйте Z и Shift + Z. zoom будет произведён в центр окна.
Дальше нам нужно понять, как выбирать ящик (box), где наши действия будут производиться. Нажмите левой кнопкой для установки нижнего левого угла и правый клик для правого верхнего угла.
Используйте X и Shift + X, чтобы раскрыть подкомпоненты внутри ящика. Иногда количество полигонов отрисовки достигает миллионов. Не рекомендую раскрывать большое количество подкомпонентов.
Больше информации о Magic можно найти вот тут.
Ну ладно, прекращаем играться и запускаем DRC.
Я загуглил Magic VLSI DRC и нашёл документации для команды DRC и туториал. Чтобы провести DRC, нам нужно скопировать команды указанные внизу и в консоли Magic VLSI вставить их, используя Shift и Insert. Затем нажмите Enter, чтобы увидеть результат исполнения. Если последняя команда не исполнилась, нажмите Enter.
gds read ../gds/my_nand.GDS
load my_nand
select top cell
drc euclidean on
drc style drc(full)
drc catchup
magic::drc_save_report my_nand ../gds/my_nand.drc.rpt
В итоге у вас будет следующее в консоли:
Первая команда, загружает GDS файл. Вторая команда загружает конкретную ячейку my_nand из my_nand.GDS. Третья команда выбирает всю ячейку. Если не выбрать всю ячейку, DRC не выводит ошибки в отчёт, а только лишь ту часть, которую мы выбрали. По умолчанию не выбрано ничего, а значит, ошибок в отчёте не будет.
Затем мы включаем DRC в режим drc(full), чтобы использовать все правила и euclidean distance.
Командой drc catchup
просим провести полный DRC и дожидаемся конца.
magic::drc_save_report
сохраняет DRC репорт для ячейки my_nand в файл ../gds/my_nand.drc.rpt
в виде текстового файла.
Последняя пустая строка нужна, чтобы Magic VLSI автоматически исполнил наши команды.
▍Исправление ошибок DRC
Взглянем на репорт gds/my_nand.drc.rpt
^
my_nand {my_nand 8}
----------------------------------------
Local interconnect spacing < 0.17um (li.3)
----------------------------------------
0.720 2.465 0.775 2.470
0.775 2.465 1.050 2.470
0.605 2.635 0.775 2.640
0.775 2.635 1.065 2.640
1.065 2.635 1.220 2.640
0.605 2.635 0.720 2.640
1.050 2.635 1.065 2.640
1.065 2.635 1.220 2.640
0.320 0.865 0.550 0.905
0.300 1.035 0.630 1.075
0.300 1.035 0.320 1.075
0.550 1.035 0.630 1.075
0.990 0.865 1.160 0.870
0.990 0.865 1.480 0.870
1.160 1.035 1.490 1.040
1.480 1.035 1.490 1.040
0.630 0.080 0.775 0.085
0.775 0.080 0.800 0.085
0.630 0.080 0.775 0.085
0.775 0.080 1.065 0.085
0.970 0.080 1.065 0.085
0.800 0.250 0.970 0.255
----------------------------------------
All nwells must contain metal-connected N+ taps (nwell.4)
----------------------------------------
-0.190 1.305 0.000 2.910
0.000 1.305 1.960 2.910
----------------------------------------
P-diff distance to N-tap must be < 15.0um (LU.3)
----------------------------------------
0.340 1.485 0.600 2.485
0.750 1.485 1.020 2.485
1.170 1.485 1.430 2.485
----------------------------------------
N-diff distance to P-tap must be < 15.0um (LU.2)
----------------------------------------
0.340 0.235 0.600 0.885
0.750 0.235 1.020 0.885
1.170 0.235 1.430 0.885
----------------------------------------
Видим 4 группы ошибок. Первая ошибка подсказывает нам, что расстояние LI1 от LI1, должно быть хотя бы 170нм. Посмотрим на эти ошибки в самом KLayout. Открываем KLayout в отдельном докере, либо используем уже открытый, если мы его не закрыли.
cd ..
LD_LIBRARY_PATH=/opt/klayout-v0.27.4/bin-release /opt/klayout-v0.27.4/bin-release/klayout -e -nn $PDK_ROOT/sky130A/libs.tech/klayout/sky130A.lyt -l $PDK_ROOT/sky130A/libs.tech/klayout/sky130A.lyp gds/my_nand.GDS
Если вы запустили новый KLayout, рекомендую выполнить шаги настройки рулетки, сетки и слоёв повторно. Скрываем все слои кроме LI1 и меняем паттерн на сплошной и цвет слоя, на что-то более удобное.
Я выбрал слой TEXT и сделал его видимым. Затем выбрал слой, поменял паттерн на сплошной и выбрал Box, координаты которого выставил, теми же, что в ошибке. Таким образом, у нас будет прямоугольник там, где произошла ошибка DRC:
Должно получиться вот так:
Давайте измерим. Берём рулетку и мерим:
Померили, теперь исправляем. Двойной клик по LI1 и уменьшаем размер на 0.01:
Опля! Не забываем удалить прямоугольник на месте DRC.
Думаю, что и снизу мы совершили эту же ошибку. Надо проверить:
Упс, исправляем.
Удаляем Box нарисованный в слое text и запускаем DRC, смотрим, исправились ли проблемы или же нет.
my_nand {my_nand 11}
----------------------------------------
Local interconnect spacing < 0.17um (li.3)
----------------------------------------
0.320 0.865 0.550 0.905
0.300 1.035 0.630 1.075
0.300 1.035 0.320 1.075
0.550 1.035 0.630 1.075
0.990 0.865 1.160 0.870
0.990 0.865 1.480 0.870
1.160 1.035 1.490 1.040
1.480 1.035 1.490 1.040
----------------------------------------
All nwells must contain metal-connected N+ taps (nwell.4)
----------------------------------------
-0.190 1.305 0.000 2.910
0.000 1.305 1.960 2.910
----------------------------------------
P-diff distance to N-tap must be < 15.0um (LU.3)
----------------------------------------
0.340 1.485 0.600 2.485
0.750 1.485 1.020 2.485
1.170 1.485 1.430 2.485
----------------------------------------
N-diff distance to P-tap must be < 15.0um (LU.2)
----------------------------------------
0.340 0.235 0.600 0.885
0.750 0.235 1.020 0.885
1.170 0.235 1.430 0.885
----------------------------------------
Видим, что проблем меньше. Теперь исправим остальные проблемы. Рисуем снова Box в слое Text.drawing. Снова измеряем.
Есть много решений, но я решил проблему, левого li1 тем, что понизил его на 0.04, чтобы расстояние от верхнего li1 было 0.17мкм. Получилось так.
Очевидно, в итоге licon1 не окружён с двух сторон 0.08мкм. Поэтому я продлил li1 налево на 0.02 и направо на 0.08. В итоге получилось вот так.
Затем я просто понизил правый licon1.
Сохраняемся и снова производим DRC и видим, что все ошибки li1 исправлены! Опять же не забываем удалить наши прямоугольники из слоя текста.
my_nand {my_nand 4}
----------------------------------------
All nwells must contain metal-connected N+ taps (nwell.4)
----------------------------------------
-0.190 1.305 0.000 2.910
0.000 1.305 1.960 2.910
----------------------------------------
P-diff distance to N-tap must be < 15.0um (LU.3)
----------------------------------------
0.340 1.485 0.600 2.485
0.750 1.485 1.020 2.485
1.170 1.485 1.430 2.485
----------------------------------------
N-diff distance to P-tap must be < 15.0um (LU.2)
----------------------------------------
0.340 0.235 0.600 0.885
0.750 0.235 1.020 0.885
1.170 0.235 1.430 0.885
----------------------------------------
Три последние ошибки, жалуются на то, что PWELL и NWELL не подключены к источникам напряжения и не имеют металлических контактов. Эти ошибки мы проигнорируем, поскольку NWELL/PWELL будут подключены инструментом Place and route, используя Tap Cell. Чтобы в этом удостоверится, мы посмотрим на DRC инвертора из библиотеки sky130_fd_sc_hd
. Приводить команды DRC снова не буду, поскольку вам нет необходимости за мной повторять.
sky130_fd_sc_hd__inv_1
----------------------------------------
All nwells must contain metal-connected N+ taps (nwell.4)
----------------------------------------
-0.190 1.305 0.000 2.910
0.000 1.305 1.570 2.910
----------------------------------------
P-diff distance to N-tap must be < 15.0um (LU.3)
----------------------------------------
0.340 1.485 0.600 2.485
0.750 1.485 1.010 2.485
----------------------------------------
N-diff distance to P-tap must be < 15.0um (LU.2)
----------------------------------------
0.340 0.235 0.600 0.885
0.750 0.235 1.010 0.885
----------------------------------------
Видим, что те же проблемы есть и у инвертора. Значит эти ошибки можно спокойно игнорировать.
Layout Versus Schematic comparison
Хорошо. Убедились в том, что DRC чист (почти) и переходим к LVS. Суть LVS в том, чтобы сравнить схему и его layout. Если layout не соответствует схеме, нам придётся проанализировать почему и исправить. Для этого заставим Magic VLSI проанализировать layout и записать в файл, транзисторы и другие базовые компоненты, которые он найдёт.
В окне Magic VLSI запустим следующие команды:
gds read ../gds/my_nand.GDS
load my_nand
select top cell
extract all
ext2spice lvs
ext2spice subcircuit on
ext2spice -o ../netgen/my_nand.lvs.spice
С первыми тремя командами мы уже знакомы. extract all
записывает в .ext файл для каждого компонента. ext2spice lvs
ставит конфигурацию в режим LVS. Про это можно найти в документации ext2spice Magic VLSI, а туториал я нашёл вот тут. Некоторые вещи я спросил напрямую у разработчика, а другие вещи я узнал в туториале sky130-hello-world.
В результате в папке netgen появятся два файла: netgen/my_nand.lvs.spice
и netgen/my_nand.ext
. Второй файл нас не интересует, а первый мы сравним с SPICE netlist-ом сгенерированным XSCHEM.
Видим ошибку и игнорируем его, поскольку так сказал разработчик Magic VLSI. Да я спросил его лично:
Error: Ran out of space for device types!
Перед тем как сравнить их, сгенерируем PEX файл, не отходя от кассы. Копируем в консоль Magic VLSI:
gds read ../gds/my_nand.GDS
load my_nand
select top cell
extract all
ext2spice lvs
ext2spice cthresh 0
ext2spice rthresh 0
ext2spice subcircuit on
ext2spice -f ngspice -o ../netgen/my_nand.pex.spice
С двумя командами мы уже знакомы. ext2spice lvs
ставит настройки в LVS, затем включает расчёт конденсаторов командой ext2spice cthresh 0
.
ext2spice rthresh 0
не работает, судя по отсутствию резисторов.
▍LVS
Взглянем на наш LVS:
* NGSPICE file created from my_nand.ext - technology: sky130A
.subckt my_nand Y A B VGND VPWR VPB VNB
X0 Y B a_150_47# VNB sky130_fd_pr__nfet_01v8 ad=1.69e+11p pd=1.82e+06u as=1.755e+11p ps=1.84e+06u w=650000u l=150000u
X1 a_150_47# A VGND VNB sky130_fd_pr__nfet_01v8 ad=0p pd=0u as=1.69e+11p ps=1.82e+06u w=650000u l=150000u
X2 Y A VPWR VPB sky130_fd_pr__pfet_01v8 ad=2.7e+11p pd=2.54e+06u as=5.2e+11p ps=5.04e+06u w=1e+06u l=150000u
X3 VPWR B Y VPB sky130_fd_pr__pfet_01v8 ad=0p pd=0u as=0p ps=0u w=1e+06u l=150000u
.ends
Видим 4 транзистора, которые соответствуют нашим транзисторам. Сравнивать вручную неудобно, особенно для больших схем. Поэтому возьмём netgen и скомандуем ему, сравнить наш SPICE netlist из XSCHEM и netlist сегрегированный Magic VLSI. Закрываем Magic VLSI и в терминале вводим следующую команду.
netgen -batch lvs "../xschem/my_nand_tb.spice my_nand" "../netgen/my_nand.lvs.spice my_nand" $PDK_ROOT/sky130A/libs.tech/netgen/sky130A_setup.tcl my_nand.comp.out
"-batch lvs" запускает LVS
.
"../xschem/my_nand_tb.spice my_nand"
указывает netgen первую схему для сравнения, а "../netgen/my_nand.lvs.spice my_nand"
вторую схему. В отличие от других LVS netgen требует название .subckt
вместе с файлом netlist.
$PDK_ROOT/sky130A/libs.tech/netgen/sky130A_setup.tcl
указывает netgen на файл с настройками. Если его не указать, netgen не сможет объединить транзисторы и не будет знать о том, что source и drain могут быть подменены (swapped) местами.
Последний аргумент, указывает netgen файл, в который нужно записать результат сравнения.
▍PEX
Как мы уже обсудили в самом начале статьи извлечение паразитных сопротивлений и конденсаторов является важной частью разработки компонентов. Очевидно, что достигнуть 100% точности невозможно, но именно PEX наиболее приближает нашу модель к идеальной.
Для этого в Magic VLSI используем этот скрипт, чтобы извлечь паразитные компоненты:
gds read ../gds/my_nand.GDS
load my_nand
select top cell
extract all
ext2sim labels on
ext2sim
shell mv my_nand.ext.nodes my_nand.nodes
extresist all
ext2spice cthresh 0
ext2spice rthresh 0
ext2spice subcircuit on
ext2spice extresist on
ext2spice -f ngspice -o ../netgen/my_nand.pex.spice
После того как мы сгенерировали модели с паразитными компонентами, нам нужно их просимулировать. Для этого копируем наш SPICE netlist для testbench-а и заменяем компоненты внутри нашей схемы списком компонентов из PEX netlist-а.
Учтите, что поочередность объявления пинов в нашем компоненте не соответствует сгенерированному PEX-ом компоненту, поэтому нужно заменить всё внутри компонента, кроме строки .subckt, которая содержит название и пины компонента. Сохраняя поочередность, которая используется в оригинальном testbench-е.
В итоге у вас должен получиться testbench, в котором пины соответствуют пинам в оригинальном netlist-е, но при этом внутренности .subckt это наш компонент с паразитными компонентами.
rthresh 0 is meaningless because lumped resistance is used only by IRSIM. If you want parasitic resistances in your output, you need a more complicated script that uses the extresist command (but which currently will not correctly extract capacitors, bipolars, and diodes, and which I am working on a fix right now).
Нам придётся подождать, пока Tim Edwards исправит extresist, а пока этого не произошло, оставим скрипт таким. И так сойдёт!
UPD: Проблема решена, но резисторы всё ещё генерируются с ошибками. Я собирался использовать скрипт сверху, но он не работает. Баг зарепорчен, ждём решения.
UPD: Баг исправлен, используем скрипт!
(конец спойлера)
Производим симуляцию командой:
ngspice -r "my_nand_tb_pex.raw" "my_nand_tb_pex.spice"
Рисуем графики в консоли NGSPICE:
plot v(output_net) input0_net input1_net
Получается почти идентичный результат. Можно измерить параметры, но делать я этого конечно же не буду. Очевидно, что в данном случае они соответствуют требованиям.
▍Генерация LEF файла
LEF файл это файл, который содержит минимум информации, чтобы Place and route инструмент знал, где можно подключить слои металла, а где проводить металлические полигоны запрещено. Чтобы сгенерировать LEF файл, скопируйте и вставьте в консоль Magic VLSI следующие команды. Не забывайте два раза нажать Enter, чтобы команды исполнились.
gds read ../gds/my_nand.GDS
load my_nand
lef write my_nand
Закрываем Magic VLSI и смотрим, что получилось.
Magic VLSI сгенерировал my_nand.ext
, netgen/my_nand.lvs.spice
, netgen/my_nand.pex.spice
и gds/my_nand.lef
.
Взглянем на наш .lef используя KLayout:
LD_LIBRARY_PATH=/opt/klayout-v0.27.4/bin-release /opt/klayout-v0.27.4/bin-release/klayout -e -nn $PDK_ROOT/sky130A/libs.tech/klayout/sky130A.lyt -l $PDK_ROOT/sky130A/libs.tech/klayout/sky130A.lyp gds/my_nand.lef
Получилось вот так:
▍Проверка нашей ячейки
В идеале мы должны были запустить OpenLane с нашей ячейкой, даже со всеми совместимыми ячейками и провести DRC проверку, чтобы удостовериться, что наша ячейка работает и совместима со всеми другими ячейками. Также мы должны были сгенерировать .lib файл, в котором будут описываться характеристики нашей ячейки, но эта статья получилась слишком большой, и если она наберёт достаточно отклика, сделаю продолжение.
UPD: Оказывается все слои должны иметь 0.005 шаг, иначе будут неприятные баги. Если я где-то ошибся, учтите это. Также наша ячейка, неправильно встаёт на Cell Grid из-за того, что она не следует тем же размерам по горизонтали, который требуется стандартной библиотекой High Density, так что если вы захотите нашу ячейку использовать, вам понадобится сначало это исправить.
Заключение
Эта статья даже близко не покрывает основы разработки микросхем. Если мне удалось вас заинтересовать, то рекомендую прочитать про разработку микросхем значительно больше, даже для личного интереса! Здесь мы покрыли первые шаги разработки Standard Cell Library, но это лишь незначительная её часть, а Standard Cell Library является маленьким куском Digital Design Flow. Кроме неё есть ещё Custom Design Flow, Memory compiler, FLASH, EEPROM ячейки, ячейки ввода-вывода, ячейки ESD, компоненты SerDes и миллион всего.
Существует огромное количество микросхем и просто ОГРОМНОЕ количество компонентов для этих микросхем. Даже такие компании, как Apple использует десятки, если не сотни компонентов от самых разных компаний.
Что касается Open Source инструментов разработки микросхем, то прогресс есть и значительный. Раньше нельзя было, даже шагу сделать, не наступив на грабли. Теперь весь цикл разработки можно сделать, практически не выходя из Open Source инструментов без больших проблем.
Да, баги, отсутствующие фичи, проблемы и даже несовместимости есть и инструментам Open Source нужно пройти значительный путь до того, как они будут использоваться повсеместно. Тем не менее, уже сейчас Open Source может использоваться для разработки сложных микросхем.
В будущем я собираюсь рассказать про разработку ячейки ввода-вывода, которые я хочу отправить в производство на MPW-4, которое будет в Декабре!
PS: Про то, как продолжить, будет в следующих частях, так как статью нужно было публиковать прямо сейчас, ибо непонятно насколько она будет актуальна через несколько месяцев, когда статья будет покрывать весь SKY130. Даже если бы я очень захотел, я бы не смог покрыть всю сферу разработки микросхемы, сколько бы я не написал статей, поэтому предлагаю вам самим попробовать разобраться и получить невероятный Экспириенс :D.
Об автореМеня зовут Арман и я окончил институт Synopsys по профилю VLSI разработки микросхем. В основном я занимаюсь разработкой цифровых компонентов для микросхем и IP для ПЛИС. В прошлом я был программистом, писал бекенд на Node.js и разрабатывал под микроконтроллеры, но решил пересесть на что-то сложнее, попробовать себя, так сказать.Я открыт к найму, поэтому, если вас интересует найм разработчика цифровых микросхем с тремя годами опыта и очень интересными проектами (Радио модемы, преобразователи интерфейсов, процессоры и очень многое), напишите мне в личку.
Автор: Arman Avetisyan