Ifi research: Nanoelectronic: Resources

Cadence - tips og triks.

Cadence - tips og triks

Formål

Denne siden har som formål å gi en kjapp innføring i noen litt mer avanserte aspekter ved Cadence. Den tar deg kun et lite stykke på vei og man må selv søke dokumentasjonen dersom man ønsker å lære mer.

Berørte emner

Hvordan finne frem i cadence-dokumentasjonen

Cadence har en dokumentasjon som er tilgjengelig fra Help menyen. For eksempel kan man klikke seg inn på Help->Cadence documentation og da starter en liten Java applet med linker til dokumentasjon under forskjellige kategorier.  Når du skal åpne dokumenter fra denne browseren kan det være viktig at du har mozilla eller netscape allerede kjørende.
Dersom du browser lokalt ved en maskin ved Universitetet kan du prøve å klikke deg inn her eller her for versjon 5.033 og  her eller her for versjon 5.1.41. I undermapper av denne mappen fins den samme dokumentasjonen av Cadence i html og pdf format, som du vil finne gjennom browseren.

Noen korte ord om SKILL

SKILL er en Lisp-dialekt som kan brukes i cadence. Hvis man skriver noe i icfb-vinduet leses dette inn som SKILL og programmet forsøker å utføre de handlinger den blir bedt om. For å få en kjapp introduksjon til SKILL anbefales det at du ser på kapittel 1 i 'SKILL Language User Guide'. Dette dokumentet finner du under doc/sklanguser/ under cadence installasjonsmappa. Først skal vi forklare hvordan du kan laste inn og bruke en funksjon som andre har laget:

Å laste å bruke en funksjon i SKILL:
; ";" er et kommentar tegn
load "mine_funksjoner.il"            ; laster inn et skill script
min_funksjon()                            ; kaller opp en funksjon uten parametere
min_funksjon(a b)                       ; kaller opp en funksjon med parameterene a og b
min_funksjon(a ?farve "hvit")     ; kaller opp en funksjon med en nødvendig parameter a og en valgfri parameter ?farve som her angis som "hvit"
Dersom man har lyst er det også lov å gjøre funksjonskall i SKILL på en litt uvanlig måte:
(min_funksjon a ?farve "hvit")    ; kaller opp min_funksjon på samme måte som rett over.

Innebygd hjelp i SKILL
Hva kan SKILL brukes til ?
Gjennom SKILL kan man utføre en rekke mer spesialiserte funksjoner, som man i svært stor grad kan definere selv. Noen eksempler kan være:

Monte-Carlo simulering i Analog Artist (med AMS Hit-Kit v3.51)

Det kan være lurt å teste dette på en test-celle før man prøver dette på et virkelig prosjekt. Vi tar deg super-kjapt gjennom et eksempel på hvordan du kan sette opp en DC simulering av en inverter: Lag en inverter. Kontroller inn-spenningen ved å sette en variabel på en vdc spenningskilde og velg å sweepe denne design-variabelen når du setter opp dc-simuleringen. Se bilde under. Hint 1: Design-variable kommer først opp i Analog Artist hvis man velger Variables->Copy from cellview.
mc step 1
Steg mot en monte carlo simulering: (Et skjermbilde lenger ned vil oppsumere hvordan det skal/kan se ut.)
mc step 2
cross( (v("/Out" ?result 'dc)-v("In" ?result 'dc)) 0 1 'either)
  • Vi skal forklare denne kommandoen litt nærmere under.
  • Velg automatisk plotting og klikk så på 'Add'.
  • Kjør en ny simulering. Ett plott med ett histogram skal dukke opp. For de som lurer er mu selvfølgelig gjennomsnitt, sd er standardavvik og N er antall simuleringer.
  • Du kan nå lage andre variasjoner av plott fra menyvalg under "Results->Plot..."

Ekstraksjon av parametere og verdier fra simuleringsresultater

Ved å skrive ocnHelp i icfb-vinduet får man opp en rekke simuleringsrelaterte kommandoer, her har vi laget en tabell med informasjon om de aller nyttigste: Mange av kommandoene har valgfrie parametere betegnet med ? foran. Man kan altså hoppe over å angi disse om man har lyst. For å bruke parameteren skrive man funskjonskall på formen: funksjons_navn(?parameter_navn parameter_verdi)
En nyttig ting å vite om SKILL er at apostrof (') foran noe betyr at interpreteren skal lese inn ett symbol og ikke evaluere en funksjon. Dette brukes for eksempel av ocnHelp.
v("nettnavn")
velger en spenning fra simuleringsresultatene. Bruk ?result 'dc for å velge dc simulering 'ac for ac, osv. En alternativ metode er å først kalle selectResult('dc), men dette virker ikke fra Analog Artist
i("portnavn")
velger en strøm (fungerer på en lignende måte som v, man må imidlertid velge å lagre strømmer i simulator-options først. Det er ikke default)
ocnHelp 'funksjonsNavn
gir hjelp på en ocn-funksjon. ocnHelp uten parametere gir en liste over en rekke simuleringsrelaterte funskjoner
ocnPrint
Skriver ut simuleringsverdier i en tabell i icfb-vinduet eller til en fil. Eksempel for å skrive ut /Out til en fil ved navn "ocnPrint_test":
ocnPrint(?output "ocnPrint_test" ?numberNotation 'scientific v("/Out"))
results
gir en liste over simuleringsresultater du har å velge mellom
selectResult velger bl.a. hvor man plukker simuleringsresultater fra. F.eks. fra 'dc 'ac 'tran 'xf osv..


plot
kommando for å plotte waveforms. Eksempel: plot(v("/Out"))


mange kalkulatorfunksjoner
Skriv ocnHelp for å få en lang liste over disse. Her begrenser vel bare fantasien hvordan man kan transformere simuleringsresultatene.
Noen eksempeler:
  • Anta at du har kjørt en ac simulering:
plot(db20(v("/Ut")))      ; plotter 20*log10(utgangssignalet)
  • Anta at du har kjørt en parametrisk ac simulering:
plot(phaseMargin(v("/Ut")))      ; lager ett plott av fase-margin mot parameteren du varierer

Parametrisk simulering

Her tar vi utganspunkt i en AC-simulering av en inverter: Lag en inverter og sett på en spenningskilde så VDD = 3.3Volt. Kontroller C inn-spenningen ved å sette DC voltage til 1.65 volt og sett AC magnitude til 1. På hver transistor setter du Length = L. Bruk f.eks. W=6u på pmos'en og W=2u på nmos'en. Bruk 'Check and Save'-kommandoen i skjematikkvinduet og velg 'Variables->Copy from cellview' i Analog Artist. Under 'Outputs->Setup...' velg Name=UtDB20 og Expression=db20(v("/Out") ?result 'ac)). Testkjør og se om det virker. Det følgende bilde viser hvordan det kan/skal bli seende ut:
parsim_b1
Man ser av bildet at vi har en DC gain på omkring 11 decibel og en cutoff på rundt 1.2GHz.
  • Legg nå inn et nytt uttrykk i 'Ouputs->Setup...': Name=GBP Expression=gainBwProd(v("/Out" ?result 'ac)) Klikk på 'Add' for å legge den til.
    Dette skal da gi oss gain-bandwith product som er produktet av lav-frekvens gain og -3dB frekvensen.
  • Klikk så på 'Plot outputs' knappen nederst til høyre i Analog Artist-vinduet. Ingen ny bølgeform dukker opp nå, men det skal dukke opp en skalar-verdi i Analog Artist vinduet.
  • Start nå 'Tools->Parametric Analysis..' Sweep parameteren L fra 0.35u til 35u. Velg "Step control"=logarithmic og Total steps=5. Velg 'Analysis->Start' fra menyen.
  • Ett nytt waveform-vindu skal dukke opp. Klikk her på det del-vinduet hvor vårt gainbainwidth produkt har dukket opp. Gå så inn på 'Axes->Y axis..' og velg logaritmisk skala. Man kan da se et skjermbilde som ligner på dette:

  • Du kan nå prøve å legge inn en variabel W for bredden i transtorene også. La denne være W for nmos'en og 3*W for pmos'en. Merk at de to feltene for 'width' og 'width stripe' må være like. Husk check and save.
  • Legg så til den nye variabelen i 'Parametric analysis' vinduet, under 'Setup->Add new variables to top'. Endre verdiene slik at du varierer bredde fra 1u til 10u og lengde fra 0.35u til 3.5u. La step control stå på auto og angi 'Total steps' =4 på begge. Kjør den nye simulering. Du burde da få et vindu som ligner litt på det under, hvor man kan se at GBP øker med bredden og minker med lengden:

Tips:
  • Bruk kommandoen ocnHelp i icfb-vinduet eller kikk litt på funksjonene du har til rådighet i kalkulatoren, for å få hjelp til hva slags funksjoner man kan måle med når man gjør parametriske simuleringer


Testbenker og simuleringsskript

Det er mulig i cadence å lage seg svært avanserte testbenker for simulering av analoge kretser. En måte å teste dette på kan være å bruke funksjonen 'Save script'. Denne finnes i Analog Artist og Monte Carlo-simuleringsverktøyet under 'Session->Save script...', i Parametric Analysis-verktøyet under 'Tools->Save script...' og i Corners verktøyet under 'File->Save Script...'.
Vi tar her et eksempel hvor vi skal simulere en flip-flop og variere klokkefrekvensen, men ønsker kun å simulere i fem klokkeperioder:
  • Vi lager en simuleringscelle med en vpulse kilde hvor period er satt til 1/f. (Merk at variabelnavnet freq ikke kan brukes da denne er reservert internt for simulatoren.)
  • Deretter setter vi opp en transient-simulering i Analog Artist på vanlig måte, og setter variabelen f til 10M. Her kan vi ikke sette simuleringstiden til 5/f, men er nødt til å sette den til et tall, f.eks. 1u. Vi kommer tilbake til hvordan man kan endre dette senere.
  • Vi setter så opp en parametrisk simulering og velger å variere f fra 1M til 10M logaritmisk.
  • Deretter klikker vi på 'Tool -> Save script... ', Vi blir så bedt om å finne på et filnavn vi kan lagre til. Finn på et navn og lagre. Normalt skal så emacs eller din default editor åpne seg med det lagrede scriptet. Hvis ikke kan man åpne filen manuelt det man får se bør ligne på dette scriptet:
simulator( 'spectre )
design(     "/ifi/fenris/h10/hansbe/st90/Sim/test_ff/spectre/schematic/netlist/netlist")
resultsDir( "/ifi/fenris/h10/hansbe/st90/Sim/test_ff/spectre/schematic" )
definitionFile(
    "models"
)

analysis('tran ?stop "1u"  )
desVar(      "f" 10M    )
temp( 27 )
paramAnalysis("f" ?values '(1000000 2154434.690031884 4641588.833612779 10000000 ))
paramRun()
selectResult( 'tran )
plot(getData("/net1") getData("/net2") getData("/O") )

Nå kan vi gjøre en svært enkel modifikasjon av scriptet for å få simulatoren til å simulere i fem klokke-perioder.
Bytt ut linjen: analysis('tran ?stop "1u"  )
Med linjen: analysis('tran ?stop "5/f"  )
  • Du kan nå kjøre skriptet ved å klippe og lime teksten til icfb-vinduet, eller du kan lagre det og skrive load "filnavnetDuFantPå"
  • Øverst til høyre på skjermbildet under ser du hva som ble resultatet.

Dette var en meget enkel innføring i hvordan man kan begynne å sette opp en testbenk i cadence med Analog Artist, og vi viste hvordan vi kan komme rundt en svakhet i GUI'et til Analog Artist, som forhindrer oss i å sette opp en variabel lengde på transientsimuleringen. Det er i bunn og grunn bare fantasien (og eventuelle tidsfrister) som setter begrensninger på hva man kan benytte slike testbenker til. Som et eksempel gir vi en link til et white paper fra cadence som tar for seg en svært avansert testbenk til bruk for analyse av differensielle forsterkere (Cadence White Paper).

Å lage en tekstfil som man kan lese inn i andre programmer (f.eks. MATLAB)

Metode 1 (ofte den enkleste):
Eksempel
I de nyere versjonen av Cadence på Linux har det blitt enklere å få ekstrahert data fra Waveform-vinduet til til programmer slik som MATLAB. Måten man gjør dette på er å velge en (eller flere) kurver i waveform-vinduet og deretter gå på menyvalget 'Tools->Table...'. For å velge flere kurver holde man skift-tasten nede når man velger, eller man kan gå på menyvalget 'Trace->Select All' om man vil ha alle sammen.
Når man åpner Table-vinduet kan man Etter at man har åpnet Table-vinduet kan man der velge 'File->Save as CSV...'. Her lagrer man da en tekstfil som er en liste med kommaseparerte verdier. Øverst i tekstfilen er det satt av en linje til navnet på bølgeformene. For å lese inn verdiene i matlab kan man f.eks. bruke kommandoen:
M = csvread('filnavnet_du_lagret_som.csv',1,0);
Tallene 1,0 angir at csvread begynner å lese inn verdier etter linja med navn på waveformene.
'Tools->Table' er også tilgjengelig gjennom kalkulator-vinduet i Cadence. Men her er der litt vanskeligere å håndtere flere bølgeformer på en gang, men  bussutrykk som "/nett_navn<0:3>" fungerer greit.

Metode 2: En annen måte å få simuleringsdata ut til et annet program på er f.eks. v.h.a. kommandoen ocnPrint:
ocnPrint(gainBwProd((v "/Out" ?result 'ac)) ?numberNotation 'scientific)
Kommandoen over lager og skriver ut til icfb-vinduet en tabell over måleresultat gjort på waveformen til signalet /Out. Kommandoen ber i tilleggg om at den skal bruke scientific-notasjon, som er leselig for de fleste innlesningsrutiner og gir en grei nøyaktighet slev om dataene går over flere ordener. Hvis man vil skrive ut til en fil istedet skriver man:
ocnPrint(gainBwProd((v "/Out" ?result 'ac)) ?numberNotation 'scientific ?output "mitt_filnavn")
Ønsker man alle/hele waveformene direkte kan man skrive:
ocnPrint((v "/Out" ?result 'ac) ?numberNotation 'scientific ?output "mitt_filnavn")
Hvis det er mange waveforms her, og særlig hvis man har kjørt en parametrisk simulering hvor man har variert to parametere samtidig, kan kommandoen splitte opp output'en så det ikke blir så lett leselig for andre program.
Bruk ocnHelp 'ocnPrint for å få mer hjelp på denne kommandoen.
Å lese inn tekstfilen i MATLAB:
Anta at man har fått laget følgende fil ved å skrive inn ocnPrint(value(deriv(i("/M1/d" ?result 'dc )) 500m) ?numberNotation 'scientific ?output "testdata.txt")
(Kommandoen angir at man vil ha printet ut verdien av den deriverte til strømmen i drain på transistoren M1 ved dc-simuleringen når sweep-variabelen var 500m. Videre skal utdata ha scientific notasjon og legges i "testdata.txt":
L                 value(deriv(i(    value(deriv(i(    value(deriv(i(    value(deriv(i(
W                 2.000e-01         8.000e-01         1.400e+00         2.000e+00        
 1.00000e-01       1.51824e-04       5.49240e-04       9.76026e-04       1.40206e-03     
 3.40000e+00       5.69143e-06       2.03260e-05       3.95191e-05       5.86385e-05     
 6.70000e+00       2.90735e-06       1.03699e-05       2.01589e-05       2.99106e-05     
 1.00000e+01       1.95103e-06       6.95525e-06       1.35179e-05       2.00554e-05     
Man kan nå skrive edit "testdata.txt" og emacs (eller default editor'en) vil åpne seg. Ta så bort første linje og bytt ut W med 0 og lagre slik at fila ser slik ut:
0                 2.000e-01         8.000e-01         1.400e+00         2.000e+00        
 1.00000e-01       1.51824e-04       5.49240e-04       9.76026e-04       1.40206e-03     
 3.40000e+00       5.69143e-06       2.03260e-05       3.95191e-05       5.86385e-05     
 6.70000e+00       2.90735e-06       1.03699e-05       2.01589e-05       2.99106e-05     
 1.00000e+01       1.95103e-06       6.95525e-06       1.35179e-05       2.00554e-05     
Åpner du så matlab, skifter arbeidsdirectory til der hvor du lagret og skriver følgende, får du det under som resultat:
M = dlmread('testdata.txt','',[0 0 4 4])
M =

         0    0.2000    0.8000    1.4000    2.0000
    0.1000    0.0002    0.0005    0.0010    0.0014
    3.4000    0.0000    0.0000    0.0000    0.0001
    6.7000    0.0000    0.0000    0.0000    0.0000
   10.0000    0.0000    0.0000    0.0000    0.0000
% Lager nå ett overflateplott i Matlab:
surf(M(1,2:5),M(2:5,1),log(M(2:5,2:5)))



Layout-tips

Her skal vi vise noen lure ting man kan benytte seg av når man tegner utlegg.
All SKILL-koden i boksene er det mulig å klippe og lime til icfb-vinduet for å teste hvordan den virker, eller man kan lagre den til en fil og laste den inn med load "filnavn". Noe av det kan man rett inn i .cdsinit fila hvis man vil det, så kjører den hver gang man starter cadence.
Egne bindkeys i Layout-editoren
Vi begynner med å vise et eksempel på hvordan man kan lage egendefinerte funskjoner som man kan ha tilgjengelig v.h.a. av tastetrykk i cadence. Det første vi gjør er å definere endel tastetrykk slik at man kan velge fort hvilke lag som skal være synlige. (e.g. ofte vil man kanskje kun se metall 1 og 2 og viaene mellom disse, mens alt annet er uinteressant. Denne koden er beregnet på cmos090 prosessen, dersom du skal benytte den i en annen prosess må du bytte ut navnene på lagene.

; hiSetBindKeys brukes til å sette bindkeys, mens "Layout" forteller funksjonen at det er bind-keys til layouteditoren vi ønsker å definere
hiSetBindKeys( "Layout" list(
        list("<Key>1" "leSetEntryLayer(\"M1\")")    ; leSetEntryLayer sier hvilket lag som er aktivt/vi skal tegne med
        list("<Key>2" "leSetEntryLayer(\"M2\")")
        list("<Key>3" "leSetEntryLayer(\"M3\")")
        list("<Key>4" "leSetEntryLayer(\"PO\")")
        list("<Key>5" "leSetEntryLayer(\"OD\")")
        list("<Key>6" "leSetEntryLayer(\"NP\")")
        list("<Key>7" "leSetEntryLayer(\"PP\")")
        list("<Key>8" "leSetEntryLayer(\"NW\")")
        list("<Key>9" "leSetEntryLayer(\"TMP\")")
        list("Ctrl<Key>1" "leSetAllLayerVisible(t), hiRedraw()")  ; Ctrl-1 setter alle lag synlige og tegner på nytt, se under printf-kommandoen lenger ned for å se hva de andre tastetrykkene gjør.
        list("Ctrl<Key>2" "leSetAllLayerVisible(nil), leSetLayerVisible(\"TMP\" t), leSetLayerVisible(\"align\" t), hiRedraw()")
        list("Ctrl<Key>3" "leSetAllLayerSelectable(t)")
        list("Ctrl<Key>4" "leSetAllLayerSelectable(nil), leSetLayerSelectable(leGetEntryLayer() t)")
        list("Ctrl<Key>5" "leSetAllLayerVisible(nil), leSetLayerVisible(\"TMP\" t), leSetLayerVisible(\"align\" t), leSetLayerVisible(\"OD\" t), leSetLayerVisible(\"CO\" t), leSetLayerVisible(\"PO\" t), leSetLayerVisible(\"NP\" t), leSetLayerVisible(\"PP\" t), leSetLayerVisible(\"NW\" t), hiRedraw()")
        list("Ctrl<Key>6" "leSetAllLayerVisible(nil), leSetLayerVisible(\"TMP\" t), leSetLayerVisible(\"align\" t), leSetLayerVisible(\"OD\" t), leSetLayerVisible(\"CO\" t), leSetLayerVisible(\"PO\" t), leSetLayerVisible(\"M1\" t), hiRedraw()")
        list("Ctrl<Key>7" "leSetAllLayerVisible(nil), leSetLayerVisible(\"TMP\" t), leSetLayerVisible(\"align\" t), leSetLayerVisible(\"M1\", t), leSetLayerVisible(\"VIA1\", t), leSetLayerVisible(\"M2\", t), hiRedraw()")
        list("Ctrl<Key>8" "leSetAllLayerVisible(nil), leSetLayerVisible(\"TMP\" t), leSetLayerVisible(\"align\" t), leSetLayerVisible(\"M2\", t), leSetLayerVisible(\"VIA2\", t), leSetLayerVisible(\"M3\", t), hiRedraw()")
        list("Ctrl<Key>9" "leSetAllLayerVisible(nil), leSetLayerVisible(\"TMP\" t), leSetLayerVisible(\"align\" t), leSetLayerVisible(\"M3\", t), leSetLayerVisible(\"VIA3\", t), leSetLayerVisible(\"M4\", t), hiRedraw()")
))
printf("Added custom bindkeys for the layout editor:\n"),
printf("Key: 1 Set layer \"M1\"\n"),
printf("Key: 2 Set layer \"M2\"\n"),
printf("Key: 3 Set layer \"M3\"\n"),
printf("Key: 4 Set layer \"PO\"\n"),
printf("Key: 5 Set layer \"OD\"\n"),
printf("Key: 6 Set layer \"NP\"\n"),
printf("Key: 7 Set layer \"PP\"\n"),
printf("Key: 8 Set layer \"NW\"\n"),
printf("Key: 9 Set layer \"align\" (for notes, alignment shapes, etc.)\n"),
printf("Key: 0 Set layer \"TMP\" (for notes, alignment shapes, etc.)\n"),
printf("Fot the following visibility commands layers TMP and align are always visible."
printf("Key: Ctrl-1 Sets all layers visible."),
printf("Key: Ctrl-2 Sets none but active layer visible. \n"),
printf("Key: Ctrl-3 Sets all layers selectable. \n"),
printf("Key: Ctrl-4 Sets none but current layer (and instances) selectable. \n")
printf("Keys Ctrl-5 to Ctrl-9 sets the following sets of layers visible\n in addition to the current drawing layer:\n"),
printf("Ctrl-5 : OD, CO, PO, NP, PP and NW\n"),
printf("Ctrl-6 : OD, CO, PO and M1\n"),
printf("Ctrl-7 : M1, VIA1 and M2\n"),
printf("Ctrl-8 : M2, VIA2 and M3\n"),
printf("Ctrl-9 : M3, VIA3 and M4\n")
En annen nyttig ting man kan ha på tastaturet er bytting mellom forskjellig snap modi. Her lager vi først en funksjon som leter gjennom en liste av aktuelle modi og setter neste eller første modus fra denne listen.
procedure( leToggleSnapMode(l_validModes) ; her definerer vi prosedyren og antall parametere/navn på disse
prog((i next curMode)   ; i next og curMode er lokale variable

curMode = envGetVal("layout" "segSnapMode")     ; ved hjelp envGetVal finner vi hvilken modus som er valgt nå
next = 0   ; vi skal lagre i next hvilken modus som skal velgse neste gang
; vi traverserer så input listen og sjekker om det forrige elementet var et som var angitt i listen
for(i 1 length(l_validModes)
 if((curMode==nth(i-1 l_validModes)) then
        next = i
        )
 )
; hvis det elementet vi fant var det siste i listen starter vi fra begynnelsen igjen
if((next==length(l_validModes)) then
   next = 0
   )
; setter så snap mode:
leSetFormSnapMode(nth(next l_validModes))
; må også si fra med envSetVal
envSetVal("layout" "segSnapMode" 'string nth(next l_validModes))
; utskrift til icfb-vinduet?
; printf("Set snap to: %s" nth(next l_validModes))
)
)

; kan nå definere tastetrykk som kan benytte denne fnuskjonen:

hiSetBindKeys( "Layout" list(
        list("Shift<Key>1" "leToggleSnapMode(list(\"diagonal\" \"anyAngle\"))")
        list("Shift<Key>2" "leToggleSnapMode(list(\"orthogonal\" \"diagonal\"))")
        list("Shift<Key>3" "leToggleSnapMode(list(\"L90XFirst\" \"L90YFirst\"))")
))

; Hva tastene da gjør:
; Shift-1veksler mellom diagonal snap mode og anyAngle (fri flytting)
; Shift-2veksler mellom ortogonal (loddrett og vannrett) og diagonal
; Shift-1veksler mellom L90XFirst og L90YFirst, som er uvurderlige når man ønsker å tegne paths med 90 graders vinkler

Custom layouts:
Her følger et lite SKILL script som kan generere et utlegg utfra en kommando linje. Hensikten med dette scriptet er å automatiske tegne overgangen mellom en buss med et definert mellomrom på den ene siden og et annet definert mellomrom på den andre siden.

procedure( pitch_projection_centered( num_elms pitch1 pitch2 t_layer mpw mps output_library)
  ;; mpw ~ metal path width
  ;; mps ~ metal path spacing
  ;; pitch1 ~ input leaves spacing
  ;; pitch2 ~ output leaves spacing
  ;; t_layer ~ layer text ex. "M1"
  ;; Example of use:
  ;; pitch_projection_new(16 1 0.4 "M3" 0.14 0.26 "test_st90")
 
  prog( (num_elms_str pitch1_str pitch2_str cell_name i base2 voff vxtra vsp view_name)

    min_sp = 0.005    ;; the minimum spacing of points

    sprintf(num_elms_str "%d" num_elms)
    sprintf(pitch1_str "%d_%d" fix(pitch1) fix(1000*(pitch1-fix(pitch1))))
    sprintf(pitch2_str "%d_%d" fix(pitch2) fix(1000*(pitch2-fix(pitch2))))
    cell_name="pitch_projection_layouts"
    view_name=strcat("pplocc_" t_layer "_" num_elms_str "_" pitch1_str "_to_" pitch2_str)
; åpner et cellview
    dbcv=dbOpenCellViewByType( output_library cell_name view_name "maskLayout" "w")
; obs "w" mode overskriver det man har lagret i et cellview.
    if((pitch1<pitch2) then
      i=pitch2
      pitch2=pitch1
      pitch1=i
    );if
 
    vsp  = (num_elms/2)*(mpw + mps)
    vsp  = floor(vsp/min_sp)*min_sp
    voff = vsp
    vxtra = ceiling((0.5*mpw+mps)/min_sp)*min_sp
    direction = -1
    base2 = ((num_elms-1)*(pitch1-pitch2))/2
    base2 = floor(base2/min_sp)*min_sp

    for( i 0 num_elms-1
      dbCreatePath(dbcv t_layer list(0:i*pitch1 voff:i*pitch1 voff:base2+i*pitch2 vsp+vxtra:base2+i*pitch2) mpw )
; dbCreatePath tar som input det cellview man vil tegne i, hvilket lag man skal tegne i, en liste med punkter og en bredde på banen man vil tegne.
      if(((i+1)>num_elms/2) then
        direction=1
      );if
      if(((i+1)==(num_elms/2)) then
        direction=1
      else
        voff=voff+direction*(met_path_width+met_path_spacing)      
      );

    );for
    dbSave(dbcv)
  ); prog
); procedure

Ved testkjøring av kommandoen over genereres følgende utlegg (rotert 90 grader her):

Layout XL

I layout XL finnes det mange nyttige ekstra funskjoner som kan assistere deg på veien fra skjematikk til utlegg. Blant annet kan layout xl holde rede på hvor du skal koble sammen nettene dine eller f.eks. holder den fortløpende rede på om du bryter endel vanlige DRC regler. For eksempel minimum avstand mellom metall-baner. (For å skru på dette må man inn på 'Options->DRD edit...' og skru på notify eller enforce). På denne måten går det mye raskere å tegne et korrekt utlegg.  Det finnes også mye avansert funskjonalitet for automatisk ruting o.l., men denne krever nok at man leser dokumentasjonen først.
For å komme i gang med Layout XL kan du f.eks. lage en ny skjematikk, der du f.eks. bruker celler fra GATES-biblioteket (AMS 0.35um) eller fra standardcellebiblioteket til ST90cm prosessen. Deretter åpner du et nytt layout for cellen og velger 'Tools->Layout XL' fra menyen. Dette fører til at du får endel ekstra menyer i layoutvinduet ditt og begynnelsen av vindustittelen blir nå byttet om til Virtuoso XL. Velg så fra menyen 'Design->Gen. from source' du får da opp en form med noen valg for å generere/instansiere komponenter og pinner slik som du har angitt i skjematikken din:
Her er det vanligvis nødvendig å endre på options for generering av pinner i utlegget. Du kan merke av flere/alle pinnene for å gjøre samme operasjon på alle. Bruk samme typen opsjoner for pinnene som du vanligvis gjør når du tegner utlegg. For AMS0.35um kan dette for eksempel være "Pin Type = Symbolic", "Layer / Master = MET1_T", "Width = 0.5", "Height = 0.5". Pin Label Shape kan her være Label og under 'Pin Label Options...' kan man angi å legge den i tekstlaget for AMS0.35, eller i pinnelaget for ST90nm. Trykk så på OK:

Du kan i vinduet over se at transistor-primitivene fra GATES biblioteket automatisk har blitt lagt inn i utlegget. Dersom du har skjematikkvinduet ditt ved siden av utleggsvinduet kan du se både i skjematikken og i utlegget hvilken komponent du jobber med ved å merke/klikke på den. Hvis du prøver å flytte på en komponent vil du normalt få se gule linjer som viser deg hvordan du skal koble sammen kompontene for å få det som i skjematikken. Gå nå inn på 'Options->DRD Edit...':

DRD er en daemon som passer på at DRC-reglene (ikke alle, men typiske) blir fulgt. Sett i options vinduet interaktiv-mode til 'Enforce' og hierarki-dybden til minimum 1 for at daemonen skal kunne lese inne i transistorene som har blitt instansiert. Du kan nå prøve å flytte to transistor som skal ha samme source/drain område. De vil nesten automatisk falle på plass. For å få til dette pass på at snap modus er satt til 'anyAngle' (gjøres ved å trykke F3 mens du bruker move-kommandoen), og at de gule strekene du ser i Layout-vinduet passer sammen (roter transistor med høyre museknapp mens du bruker move-kommandoen). Programmet gjør nemlig forskjell på source/drain terminalen akkurat her, og den tror det blir kortslutning om man ikke kobler riktig. I figuren under er et eksempel hvor to transistorer deler source. Der er forresten også et eksempel på hva man kan lage med 'Create->Guard Ring' funksjonen.
   
Over kan man se en masse gule linjer som angir DRC-feil ifølge Design Rule daemonen. Som oftest er det overenstemmelse mellom DRD og DRC, men ikke alltid! For eksempel følger ikke DRD med på avstanden mellom forskjellige lag slik som mellom n-brønn og n+diffusjon, så her er man selv ansvarlig for om det blir riktig. 
Prøv nå å lage en metall-bane i vinkel med 'path' kommandoen. Lag så en metall-bane til og se hva som skjer når du prøver å tegne mot metallbanen du allerede tegnet. Du vil se at Layout XL 'holder igjen' så du ikke får lov å bryte design-reglene så lett. Dersom du ønsker å kortslutte de to banene får du allikevel lov til det, gitt at dette ikke strider mot koblingskjemaet i skjematikken. Snapmodus 'L90XFirst' og 'L90YFirst'  er det også kjekt å benytte seg av i sammenheng med dette. Bruk gjerne 'Enter'-tasten for å avslutte en path istedet for å måtte klikke to ganger med musa. Noen ganger flytter man ørlitegrann på musa mens man gjør dette, og man lager ekstraarbeid for seg selv.






På denne siden vil det etterhvert komme fler tips.
Har du noen gode tips til bruk av cadence du synes burde være med her send mail til Hans Kristian eller Håvard.