← Volver al sitio
SKILL · Cadence Virtuoso

mismatch_pelgrom.il
;; =========================================================================
;; PLUGIN: MISMATCH (PELGROM)
;; Requiere haber cargado antes extractor_gmid_saturacion.il
;;
;; Al pulsar Mismatch se abre primero el popup de destino (test y vista de
;; esquematico) y despues el de transistores. Se generan siempre Vgs,
;; Overdrive (Vgs-Vth), ErrorMismatch, ActualCurrent, ExpectedCurrent y los
;; limites Min/MaxCurrentError, solo en los tests elegidos.
;;
;; Cada transistor tiene SU PROPIO PelgromValue_<transistor> (no se
;; comparte entre transistores): como el Vgs real difiere de uno a otro,
;; el coeficiente de Pelgrom que toca de la tabla del datasheet tambien
;; puede diferir. Se crea con un valor de partida (gmidPelgromPe/Ne segun
;; la polaridad detectada, o 0.0 si es desconocida) y luego se ajusta a
;; mano en Maestro, por transistor, mirando su Overdrive_<transistor>.
;;
;; ActualCurrent usa OP(path "id") (corriente de drenador del punto de
;; operacion del transistor), no IDC() sobre el nodo: IDC() depende de un
;; probe de corriente de red que puede no estar disponible o fallar segun
;; el tipo de analisis (p.ej. AC_OP_NOISE), mientras que OP(...) siempre
;; esta disponible si el punto de operacion DC esta guardado.
;; =========================================================================

unless( boundp('gmidPelgromPe) gmidPelgromPe = 0.0 )
unless( boundp('gmidPelgromNe) gmidPelgromNe = 0.0 )
unless( boundp('gmidExpectedCurrentDefault) gmidExpectedCurrentDefault = 1.0e-05 )

;; Lista de tuplas (fullPath polaridad w l m), en el MISMO orden que ?choices
;; del toggle field. El valor devuelto por el campo (~>value) es una lista
;; de t/nil posicional, no de nombres, asi que hay que casar por posicion.
gmidMismatchOrderedInfo = nil
gmidMismatchForm = nil

procedure( gmidMismatchLabel(par)
    sprintf(nil "%s%s" car(par)
        cond(
            ( eq(cadr(par) 'n) " [NMOS]" )
            ( eq(cadr(par) 'p) " [PMOS]" )
            ( t " [?]" )
        )
    )
)

;; Entrada de menu: pide destino y despues abre el popup de transistores
procedure( MismatchConDestino()
    gmidPedirDestino("AbrirPopupMismatchGmId()")
)

procedure( AbrirPopupMismatchGmId()
    let( (cv xtors choices toggleField i sym)
        cv = geGetEditCellView()
        when( cv && cv~>cellViewType == "schematic"
            gmidMosCache = nil
            xtors = RecorrerJerarquiaGmId(cv "" 1 gmidMaxDepth)
            gmidMismatchOrderedInfo = nil
            choices = nil
            i = 0
            foreach( par xtors
                sym = concat('gmidTx i)
                choices = cons(list(sym gmidMismatchLabel(par)) choices)
                gmidMismatchOrderedInfo = cons(par gmidMismatchOrderedInfo)
                i = i + 1
            )
            toggleField = hiCreateToggleField(
                ?name 'gmidMismatchToggle
                ?choices choices
                ?numSelect length(choices)
                ?prompt "Transistores a analizar"
                ?value nil
            )
            gmidMismatchForm = hiCreateAppForm(
                ?name 'gmidMismatchForm
                ?formTitle "Mismatch (Pelgrom)"
                ?fields list(
                    toggleField
                    hiCreateFloatField(
                        ?name 'gmidExpectedCurrent
                        ?prompt "Corriente esperada (A)"
                        ?value gmidExpectedCurrentDefault
                    )
                )
                ?buttonLayout 'OKCancel
                ?callback "CalcularMismatchGmId()"
            )
            hiDisplayForm(gmidMismatchForm)
        )
    )
)

procedure( CalcularMismatchGmId()
    let( (cv sess tests seleccion expCur selList infoList seleccionados
          pathInfo path pol w l m nameSafe pelgromDefault
          n0 n1 n2 n3 n4 n5 n6 n7 r0 r1 r2 r3 r4 r5 r6 r7
          nSel nErr)
        cv = geGetEditCellView()
        sess = car(maeGetSessions())
        unless( sess
            sess = maeOpenSetup(cv~>libName cv~>cellName "maestro" ?mode "a"))

        ;; Solo los tests elegidos en el popup de destino (nil = ninguno)
        tests = gmidTestsElegidos(sess)

        seleccion = (hiGetCurrentForm()->gmidMismatchToggle)~>value
        expCur = (hiGetCurrentForm()->gmidExpectedCurrent)~>value

        seleccionados = nil
        selList = seleccion
        infoList = gmidMismatchOrderedInfo
        while( selList
            when( car(selList)
                seleccionados = cons(car(infoList) seleccionados)
            )
            selList = cdr(selList)
            infoList = cdr(infoList)
        )

        nSel = length(seleccionados)
        nErr = 0

        unless( tests
            printf("Ningun test elegido.\n")
        )

        foreach( tst tests
            foreach( pathInfo seleccionados
                path = car(pathInfo)
                pol  = cadr(pathInfo)
                w    = caddr(pathInfo)
                l    = cadddr(pathInfo)
                m    = car(cddddr(pathInfo))
                nameSafe = gmidSafeName(path)
                pelgromDefault = cond(
                    ( eq(pol 'n) gmidPelgromNe )
                    ( eq(pol 'p) gmidPelgromPe )
                    ( t 0.0 )
                )

                ;; PelgromValue propio de este transistor: punto de partida
                ;; segun su polaridad, editalo despues en Maestro segun su
                ;; Overdrive_<transistor> real.
                n0 = sprintf(nil "PelgromValue_%s" nameSafe)
                n1 = sprintf(nil "ErrorMismatch_%s" nameSafe)
                n2 = sprintf(nil "ActualCurrent_%s" nameSafe)
                n3 = sprintf(nil "ExpectedCurrent_%s" nameSafe)
                n4 = sprintf(nil "MinCurrentError_%s" nameSafe)
                n5 = sprintf(nil "MaxCurrentError_%s" nameSafe)
                n6 = sprintf(nil "Vgs_%s" nameSafe)
                n7 = sprintf(nil "Overdrive_%s" nameSafe)
                errset(maeDeleteOutput(n0 tst ?session sess))
                errset(maeDeleteOutput(n1 tst ?session sess))
                errset(maeDeleteOutput(n2 tst ?session sess))
                errset(maeDeleteOutput(n3 tst ?session sess))
                errset(maeDeleteOutput(n4 tst ?session sess))
                errset(maeDeleteOutput(n5 tst ?session sess))
                errset(maeDeleteOutput(n6 tst ?session sess))
                errset(maeDeleteOutput(n7 tst ?session sess))

                r0 = maeAddOutput(n0 tst ?outputType "point"
                    ?expr sprintf(nil "%g" pelgromDefault)
                    ?session sess)

                r6 = maeAddOutput(n6 tst ?outputType "point"
                    ?expr sprintf(nil "OP(\"/%s\" \"vgs\")" path)
                    ?session sess)
                r7 = maeAddOutput(n7 tst ?outputType "point"
                    ?expr sprintf(nil "%s - OP(\"/%s\" \"vth\")" n6 path)
                    ?session sess)

                if( !w || !l || !m then
                    nErr = nErr + 1
                    printf("Faltan W/L/m para %s; solo Vgs/Overdrive/PelgromValue\n" path)
                else
                    r1 = maeAddOutput(n1 tst ?outputType "point"
                        ?expr sprintf(nil "%s / sqrt((%g * %g * %g))" n0 w l m)
                        ?session sess)
                    r2 = maeAddOutput(n2 tst ?outputType "point"
                        ?expr sprintf(nil "abs(OP(\"/%s\" \"id\"))" path)
                        ?session sess)
                    r3 = maeAddOutput(n3 tst ?outputType "point"
                        ?expr sprintf(nil "%g" expCur)
                        ?session sess)
                    r4 = maeAddOutput(n4 tst ?outputType "point"
                        ?expr sprintf(nil "(%s * (1 - (%s / 100)))" n3 n1)
                        ?session sess)
                    r5 = maeAddOutput(n5 tst ?outputType "point"
                        ?expr sprintf(nil "(%s * (1 + (%s / 100)))" n3 n1)
                        ?session sess)
                    errset(maeSetSpec(n4 tst ?lt sprintf(nil "value(calcVal(\"%s\"))" n2) ?session sess))
                    errset(maeSetSpec(n5 tst ?gt sprintf(nil "value(calcVal(\"%s\"))" n2) ?session sess))
                    unless( r0 && r1 && r2 && r3 && r4 && r5 && r6 && r7
                        nErr = nErr + 1
                        printf("Fallo generando mismatch para %s en %s\n" path tst))
                )
            )
        )
        maeSaveSetup(?session sess)
        printf("Mismatch Pelgrom: %d transistores seleccionados | tests %L | %d avisos/fallos. Cada transistor tiene su propio PelgromValue_<nombre>: ajustalo en Maestro segun su Overdrive real.\n"
            nSel tests nErr)
    )
)

;; -------------------------------------------------------------------------
;; Enganche al menu "Crear expresiones" del otro archivo
;; -------------------------------------------------------------------------
unless( boundp('gmidExtraMenuItems) gmidExtraMenuItems = nil )

itemMismatch = hiCreateMenuItem(
    ?name 'ItemMismatch
    ?itemText "Mismatch"
    ?callback "MismatchConDestino()"
)

gmidExtraMenuItems = append(
    setof(it gmidExtraMenuItems it~>itemText != "Mismatch")
    list(itemMismatch)
)

printf("Mismatch enganchado al menu. Cierra y vuelve a abrir cualquier esquematico ya abierto para verlo.\n")