✓
schematic_A
✓schematic_B
schematic_C
OKCancelHelp
✓X0/M1 [NMOS]
✓X0/M2 [NMOS]
X0/M3 [PMOS]
X0/M4 [PMOS]
X1/M1 [NMOS]
X1/M2 [NMOS]
1e-05
OKCancelHelp
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")