✓
schematic_A
✓schematic_B
schematic_C
OKCancelHelp
extractor_gmid_saturacion.il
;; =========================================================================
;; PLUGIN: DETECTOR DE TRANSISTORES, GM/ID Y SATURACION
;; Recorre jerarquia (hasta gmidMaxDepth niveles) desde el esquematico activo
;;
;; Deteccion automatica de transistores MOS: una celda hoja (sin vista
;; schematic*) cuyo simbolo tiene pines D, G y S. La polaridad se deduce del
;; nombre de la celda y del parametro "model". "Tipos de transistores" solo
;; hace falta para forzar celdas concretas.
;; =========================================================================
unless( boundp('gmidTests) gmidTests = nil )
unless( boundp('gmidSatMargin) gmidSatMargin = "0" )
unless( boundp('gmidMaxDepth) gmidMaxDepth = 3 )
;; t = detectar transistores por sus pines; nil = usar solo las listas
unless( boundp('gmidAutoDetect) gmidAutoDetect = t )
;; Celdas forzadas como transistor (nombre de celda en minusculas). Tienen
;; prioridad sobre la deteccion automatica y fijan la polaridad.
unless( boundp('gmidNmosCells) gmidNmosCells = '("ne" "nmos" "nch" "nfet" "nelna") )
unless( boundp('gmidPmosCells) gmidPmosCells = '("pe" "pmos" "pch" "pfet" "pe3lna") )
;; Archivo de configuracion persistente
gmidConfigFile = strcat(getShellEnvVar("HOME") "/.gmid_tipos_transistores.il")
when( isFile(gmidConfigFile)
load(gmidConfigFile)
)
;; Lista de (cellName vista). Se rellena desde el popup de destino.
unless( boundp('gmidViewOverrides) gmidViewOverrides = nil )
;; Ultimo cellview de esquematico valido visto (se actualiza cada vez que
;; gmidCvTrabajo() consigue uno de verdad). Sirve de respaldo cuando una
;; funcion se ejecuta desde el callback de un popup (OK de "Destino de las
;; expresiones", de "Mismatch", etc.): en ese momento la ventana activa es
;; el propio formulario, no el esquematico, y geGetEditCellView() directo
;; falla con *WARNING* (GE-2067).
unless( boundp('gmidCvActual) gmidCvActual = nil )
;; Otros archivos (p.ej. mismatch_pelgrom.il) pueden anadir aqui sus propios
;; items para que aparezcan en el mismo menu "Crear expresiones".
unless( boundp('gmidExtraMenuItems) gmidExtraMenuItems = nil )
gmidAccionPendiente = nil
gmidDestTestsOrdered = nil
gmidDestCeldas = nil
gmidCeldasAcc = nil
gmidHojasAcc = nil
gmidDestForm = nil
gmidTiposForm = nil
gmidMosCache = nil
;; -------------------------------------------------------------------------
;; DETECCION DE TRANSISTORES
;; -------------------------------------------------------------------------
;; Nombres de los pines del simbolo de una instancia
procedure( gmidTerminalesMaster(inst)
let( (res)
res = errset(inst~>master~>terminals~>name)
if( res then car(res) else nil)
)
)
;; Nombres de las vistas que empiezan por "schematic" de una celda
procedure( gmidVistasSchematic(libName cellName)
let( (objRes obj nombres)
nombres = nil
objRes = errset(ddGetObj(libName cellName))
obj = if( objRes then car(objRes) else nil)
when( obj
nombres = setof(n obj~>views~>name rexMatchp("^schematic" n))
)
nombres
)
)
;; Deteccion por pines: D, G y S, y sin vista schematic* (primitiva)
procedure( gmidDetectarMos(inst)
let( (nombres l hasD hasG hasS sinSchem)
if( gmidAutoDetect then
nombres = gmidTerminalesMaster(inst)
l = mapcar(lambda((x) if(stringp(x) lowerCase(x) "")) nombres)
hasD = member("d" l) || member("drain" l)
hasG = member("g" l) || member("gate" l)
hasS = member("s" l) || member("source" l)
sinSchem = !gmidVistasSchematic(inst~>libName inst~>cellName)
if( hasD && hasG && hasS && sinSchem then t else nil)
else
nil
)
)
)
;; Es transistor si esta en las listas forzadas o lo detecta por pines.
;; El resultado se guarda en cache por lib/celda (gmidMosCache).
procedure( gmidEsMos(inst)
let( (key ent res cn)
key = sprintf(nil "%s/%s" inst~>libName inst~>cellName)
ent = assoc(key gmidMosCache)
if( ent then
res = eq(cadr(ent) 'yes)
else
cn = lowerCase(or(inst~>cellName ""))
if( member(cn gmidNmosCells) || member(cn gmidPmosCells) then
res = t
else
res = gmidDetectarMos(inst)
)
gmidMosCache = cons(list(key if(res 'yes 'no)) gmidMosCache)
)
res
)
)
;; Polaridad segun un texto (nombre de celda o modelo): 'n, 'p o nil
procedure( gmidPolaridadStr(s)
if( stringp(s) then
cond(
( rexMatchp("pmos" s) 'p )
( rexMatchp("pfet" s) 'p )
( rexMatchp("pch" s) 'p )
( rexMatchp("nmos" s) 'n )
( rexMatchp("nfet" s) 'n )
( rexMatchp("nch" s) 'n )
( rexMatchp("^p" s) 'p )
( rexMatchp("^n" s) 'n )
( t nil )
)
else nil)
)
;; Polaridad de una instancia: 'n, 'p o 'u (desconocida)
procedure( gmidPolaridad(inst)
let( (cn val mdl)
cn = lowerCase(or(inst~>cellName ""))
cond(
( member(cn gmidNmosCells) 'n )
( member(cn gmidPmosCells) 'p )
( t
val = gmidGetInstParam(inst "model")
mdl = if( stringp(val) then lowerCase(val) else "")
or(gmidPolaridadStr(cn) gmidPolaridadStr(mdl) 'u)
)
)
)
)
;; Vista a usar para una celda: la elegida en el popup; si no hay eleccion,
;; "schematic" si existe, o la primera vista schematic* que tenga.
;; Devuelve nil si la celda no tiene ninguna vista schematic* (primitiva):
;; en ese caso no se intenta abrir.
procedure( gmidResolverVista(libName cellName)
let( (ov vistas)
ov = assoc(cellName gmidViewOverrides)
vistas = if( ov then nil else gmidVistasSchematic(libName cellName))
cond(
( ov cadr(ov) )
( member("schematic" vistas) "schematic" )
( vistas car(vistas) )
( t nil )
)
)
)
;; Evalua de forma segura un string como expresion SKILL (para numeros o
;; variables de diseno). Si la variable no existe o la evaluacion falla
;; por cualquier motivo, devuelve nil en vez de lanzar un error que
;; interrumpiria todo el recorrido de la jerarquia.
procedure( gmidSafeEvalNum(s)
let( (r)
r = errset(evalstring(s))
if( r && numberp(car(r)) then car(r) else nil)
)
)
;; Dice si una ventana es un entorno grafico editable (esquematico/layout)
;; sin llamar a geGetEditCellView(w) directamente: esa llamada, sobre una
;; ventana que no lo es (CIW, log, un popup), imprime el *WARNING* (GE-2067)
;; aunque este dentro de un errset. Comprobar la propiedad editCellView del
;; objeto ventana evita el aviso.
procedure( gmidEsVentanaGrafica(w)
let( (r)
r = errset(w~>editCellView)
r != nil && car(r) != nil
)
)
;; cv de trabajo robusto frente al popup "Destino de las expresiones" (y al
;; de Mismatch): cuando una funcion se ejecuta desde el callback OK de un
;; formulario, la ventana activa es el formulario, no el esquematico, y
;; geGetEditCellView() directo falla con *WARNING* (GE-2067) porque esa
;; ventana no es un entorno grafico editable. Si la llamada directa
;; funciona (p.ej. invocado desde el menu del esquematico o desde el CIW
;; con el esquematico como ultima ventana activa de verdad), se usa y
;; ademas se guarda en gmidCvActual; si falla, se devuelve el ultimo cv
;; bueno conocido.
procedure( gmidCvTrabajo()
let( (wRes w r cv)
wRes = errset(hiGetCurrentWindow())
w = if( wRes then car(wRes) else nil)
cv = if( w && windowp(w) && gmidEsVentanaGrafica(w) then
r = errset(geGetEditCellView(w))
if( r then car(r) else nil)
else nil)
if( cv then
gmidCvActual = cv
cv
else
gmidCvActual
)
)
)
;; Parseo de valores tipo "40u" / "180.0n" / "2u" a micras (float).
;; Si el valor es una variable de diseno parametrizada (p.ej. "MULT_DIFF_PAIR",
;; sin parte numerica reconocible, o que no se puede evaluar), devuelve nil
;; en vez de fallar: el transistor queda sin W/L conocido para este calculo.
procedure( gmidParseLenToUm(str)
let( (n i numStr suf mult numVal)
n = strlen(str)
i = n
while( i > 0 &&
member(substring(str i 1) '("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "." "e" "E" "+" "-"))
i = i - 1
)
numStr = substring(str 1 i)
suf = lowerCase(if( i < n then substring(str i+1 n-i) else ""))
mult = case( suf
("" 1.0)
("f" 1.0e-15) ("p" 1.0e-12) ("n" 1.0e-9)
("u" 1.0e-6) ("m" 1.0e-3)
("k" 1.0e3) ("meg" 1.0e6) ("g" 1.0e9)
(t nil)
)
numVal = gmidSafeEvalNum(numStr)
if( mult == nil || numVal == nil then nil else (numVal * mult * 1.0e6))
)
)
;; Lee un parametro (w/l/m/model) del CDF resuelto de una instancia
procedure( gmidGetInstParam(inst paramName)
let( (cdfRes cdfId p val)
val = nil
cdfRes = errset(cdfGetInstCDF(inst))
cdfId = if( cdfRes then car(cdfRes) else nil)
when( cdfId
foreach( p cdfId~>parameters
when( p~>name == paramName val = p~>value )
)
)
val
)
)
;; Devuelve list(w_um l_um m) para una instancia de transistor. Si W, L o m
;; estan parametrizados con una variable que no se puede evaluar a numero,
;; ese campo queda en nil (no se interrumpe el calculo de los demas).
procedure( gmidGetWLM(inst)
let( (wStr lStr mStr w l m mVal)
wStr = gmidGetInstParam(inst "w")
lStr = gmidGetInstParam(inst "l")
mStr = gmidGetInstParam(inst "m")
w = if( wStr then gmidParseLenToUm(wStr) else nil)
l = if( lStr then gmidParseLenToUm(lStr) else nil)
mVal = if( mStr then gmidSafeEvalNum(mStr) else nil)
m = if( mVal then (mVal * 1.0) else if( mStr then nil else 1.0))
list(w l m)
)
)
;; Devuelve lista de (fullPath polaridad w_um l_um m), con polaridad 'n 'p 'u
procedure( RecorrerJerarquiaGmId(cv path depth maxDepth)
let( (resultado fullPath subCvRes subCv vName wlm)
resultado = nil
when( cv && cv~>instances && depth <= maxDepth
foreach( inst cv~>instances
fullPath = if( path == "" then inst~>name else sprintf(nil "%s/%s" path inst~>name))
if( gmidEsMos(inst) then
wlm = gmidGetWLM(inst)
resultado = cons(list(fullPath gmidPolaridad(inst) car(wlm) cadr(wlm) caddr(wlm)) resultado)
else
vName = gmidResolverVista(inst~>libName inst~>cellName)
when( vName
subCvRes = errset(dbOpenCellViewByType(inst~>libName inst~>cellName vName nil "r"))
subCv = if( subCvRes then car(subCvRes) else nil)
when( subCv
resultado = append(RecorrerJerarquiaGmId(subCv fullPath depth+1 maxDepth) resultado)
dbClose(subCv)
)
)
)
)
)
resultado
)
)
;; Recorre la jerarquia y acumula en gmidCeldasAcc las celdas que tienen
;; alguna vista schematic*: lista de (cellName listaDeVistas)
procedure( gmidDescubrirCeldas(cv depth maxDepth)
let( (vistas vName subCvRes subCv)
when( cv && cv~>instances && depth <= maxDepth
foreach( inst cv~>instances
unless( gmidEsMos(inst)
vistas = gmidVistasSchematic(inst~>libName inst~>cellName)
when( vistas
unless( assoc(inst~>cellName gmidCeldasAcc)
gmidCeldasAcc = cons(list(inst~>cellName vistas) gmidCeldasAcc)
vName = gmidResolverVista(inst~>libName inst~>cellName)
when( vName
subCvRes = errset(dbOpenCellViewByType(inst~>libName inst~>cellName vName nil "r"))
subCv = if( subCvRes then car(subCvRes) else nil)
when( subCv
gmidDescubrirCeldas(subCv depth+1 maxDepth)
dbClose(subCv)
)
)
)
)
)
)
)
t
)
)
;; Diagnostico: acumula las celdas hoja (sin vista schematic*) que NO se han
;; detectado como transistor, con sus pines: lista de (lib/celda pines)
procedure( gmidRecogerHojas(cv depth maxDepth)
let( (vName subCvRes subCv clave nombres pines)
when( cv && cv~>instances && depth <= maxDepth
foreach( inst cv~>instances
unless( gmidEsMos(inst)
vName = gmidResolverVista(inst~>libName inst~>cellName)
if( vName then
subCvRes = errset(dbOpenCellViewByType(inst~>libName inst~>cellName vName nil "r"))
subCv = if( subCvRes then car(subCvRes) else nil)
when( subCv
gmidRecogerHojas(subCv depth+1 maxDepth)
dbClose(subCv)
)
else
clave = sprintf(nil "%s/%s" inst~>libName inst~>cellName)
unless( assoc(clave gmidHojasAcc)
nombres = gmidTerminalesMaster(inst)
pines = if( nombres then buildString(nombres ",") else "?")
gmidHojasAcc = cons(list(clave pines) gmidHojasAcc)
)
)
)
)
)
t
)
)
;; Diagnostico: celdas hoja que NO se cuentan como transistor, con sus pines.
;; Si ves algun transistor en la lista, anadelo desde "Tipos de transistores".
procedure( ListarHojasGmId()
let( (cv)
cv = gmidCvTrabajo()
gmidMosCache = nil
gmidHojasAcc = nil
gmidRecogerHojas(cv 1 gmidMaxDepth)
printf("Celdas hoja no detectadas como transistor:\n")
foreach( h gmidHojasAcc
printf(" %s pines: %s\n" car(h) cadr(h))
)
t
)
)
;; Diagnostico: transistores detectados, con polaridad y W/L/m
procedure( ListarTransistoresGmId()
let( (cv xt)
cv = gmidCvTrabajo()
gmidMosCache = nil
xt = RecorrerJerarquiaGmId(cv "" 1 gmidMaxDepth)
printf("Transistores detectados: %d\n" length(xt))
foreach( par xt
printf(" %s polaridad=%L W=%L L=%L m=%L\n"
car(par) cadr(par) caddr(par) cadddr(par) car(cddddr(par)))
)
t
)
)
;; -------------------------------------------------------------------------
;; TIPOS DE TRANSISTORES (opcional): fuerza celdas y polaridad. Se guarda en
;; disco y se carga solo en cada sesion.
;; -------------------------------------------------------------------------
;; "a b, c" -> ("a" "b" "c") en minusculas
procedure( gmidParseCeldas(str)
let( (res)
res = nil
when( str && str != ""
foreach( p parseString(str " ,;")
when( p != ""
res = cons(lowerCase(p) res)
)
)
)
reverse(res)
)
)
;; Escribe la configuracion actual en el archivo persistente
procedure( gmidGuardarTipos()
let( (fid)
fid = outfile(gmidConfigFile)
when( fid
fprintf(fid ";; Configuracion de tipos de transistores (generado automaticamente)\n")
fprintf(fid "gmidNmosCells = '%L\n" gmidNmosCells)
fprintf(fid "gmidPmosCells = '%L\n" gmidPmosCells)
close(fid)
printf("Tipos de transistores guardados en %s\n" gmidConfigFile)
)
unless( fid
printf("No se pudo escribir %s\n" gmidConfigFile)
)
t
)
)
procedure( TiposTransistoresGmId()
let( (nStr pStr)
nStr = if( gmidNmosCells then buildString(gmidNmosCells " ") else "")
pStr = if( gmidPmosCells then buildString(gmidPmosCells " ") else "")
gmidTiposForm = hiCreateAppForm(
?name 'gmidTiposForm
?formTitle "Tipos de transistores (opcional)"
?fields list(
hiCreateStringField(
?name 'gmidTiposNmos
?prompt "Celdas NMOS forzadas (separadas por espacios)"
?value nStr
)
hiCreateStringField(
?name 'gmidTiposPmos
?prompt "Celdas PMOS forzadas (separadas por espacios)"
?value pStr
)
)
?buttonLayout 'OKCancel
?callback "GuardarTiposGmId()"
)
hiDisplayForm(gmidTiposForm)
)
)
procedure( GuardarTiposGmId()
let( (f nStr pStr)
f = hiGetCurrentForm()
nStr = (f->gmidTiposNmos)~>value
pStr = (f->gmidTiposPmos)~>value
gmidNmosCells = gmidParseCeldas(nStr)
gmidPmosCells = gmidParseCeldas(pStr)
gmidMosCache = nil
printf("NMOS: %L | PMOS: %L\n" gmidNmosCells gmidPmosCells)
gmidGuardarTipos()
)
)
;; Devuelve los tests elegidos (que existan en la sesion). nil si no hay
;; ninguno elegido: en ese caso no se crea nada.
procedure( gmidTestsElegidos(sess)
if( gmidTests
then setof(tn maeGetSetup(?session sess) member(tn gmidTests))
else nil)
)
;; Resuelve sesion, tests y transistores para el esquematico activo
procedure( gmidPrepare(cv)
let( (sess tests xtors)
gmidMosCache = nil
sess = car(maeGetSessions())
unless( sess
sess = maeOpenSetup(cv~>libName cv~>cellName "maestro" ?mode "a"))
tests = gmidTestsElegidos(sess)
xtors = RecorrerJerarquiaGmId(cv "" 1 gmidMaxDepth)
list(sess tests xtors)
)
)
;; Lee el valor (lista posicional t/nil) del selector de vista numero idx
;; (campos gmidDestView0 ... gmidDestView7). Devuelve nil si no existe.
procedure( gmidLeerVistaField(f idx)
let( (r)
r = cond(
( idx == 0 errset((f->gmidDestView0)~>value) )
( idx == 1 errset((f->gmidDestView1)~>value) )
( idx == 2 errset((f->gmidDestView2)~>value) )
( idx == 3 errset((f->gmidDestView3)~>value) )
( idx == 4 errset((f->gmidDestView4)~>value) )
( idx == 5 errset((f->gmidDestView5)~>value) )
( idx == 6 errset((f->gmidDestView6)~>value) )
( idx == 7 errset((f->gmidDestView7)~>value) )
( t nil )
)
if( r then car(r) else nil)
)
)
;; -------------------------------------------------------------------------
;; POPUP DE DESTINO: test(s) + vista de esquematico. Se abre cada vez y, al
;; pulsar OK, ejecuta la accion pedida (string con la llamada, p.ej.
;; "GenerarGmId()").
;; -------------------------------------------------------------------------
procedure( gmidPedirDestino(accion)
let( (cv sess tests choices i sym fields c vchoices vnames j vsym nVw)
cv = gmidCvTrabajo()
sess = car(maeGetSessions())
unless( sess
printf("Abre primero el Maestro (ADE Assembler) de este esquematico.\n")
)
when( sess && cv && cv~>cellViewType == "schematic"
gmidAccionPendiente = accion
gmidMosCache = nil
;; Campo de tests
tests = maeGetSetup(?session sess)
gmidDestTestsOrdered = nil
choices = nil
i = 0
foreach( tn tests
sym = concat('gmidTst i)
choices = cons(list(sym tn) choices)
gmidDestTestsOrdered = cons(tn gmidDestTestsOrdered)
i = i + 1
)
fields = list(
hiCreateToggleField(
?name 'gmidDestTests
?choices choices
?numSelect length(choices)
?prompt "Test(s) donde crear las expresiones"
?value nil
)
)
;; Un selector de vista por cada celda con varias vistas schematic*
;; (maximo 8)
gmidCeldasAcc = nil
gmidDescubrirCeldas(cv 1 gmidMaxDepth)
gmidDestCeldas = nil
nVw = 0
foreach( c reverse(gmidCeldasAcc)
when( length(cadr(c)) > 1 && nVw < 8
vchoices = nil
vnames = nil
j = 0
foreach( vn cadr(c)
vsym = concat('gmidVw nVw 'x j)
vchoices = cons(list(vsym vn) vchoices)
vnames = cons(vn vnames)
j = j + 1
)
fields = append(fields list(
hiCreateToggleField(
?name concat('gmidDestView nVw)
?choices vchoices
?numSelect 1
?prompt sprintf(nil "Vista de %s" car(c))
?value nil
)
))
gmidDestCeldas = cons(list(car(c) vnames nVw) gmidDestCeldas)
nVw = nVw + 1
)
)
gmidDestForm = hiCreateAppForm(
?name 'gmidDestForm
?formTitle "Destino de las expresiones"
?fields fields
?buttonLayout 'OKCancel
?callback "GuardarDestinoGmId()"
)
hiDisplayForm(gmidDestForm)
)
)
)
procedure( GuardarDestinoGmId()
let( (f selT selList nameList elegidos valores entry idx vistaElegida nuevo kept)
f = hiGetCurrentForm()
;; Tests elegidos (lista posicional t/nil)
selT = (f->gmidDestTests)~>value
elegidos = nil
selList = selT
nameList = gmidDestTestsOrdered
while( selList
when( car(selList)
elegidos = cons(car(nameList) elegidos)
)
selList = cdr(selList)
nameList = cdr(nameList)
)
gmidTests = elegidos
;; Vistas elegidas por celda (si no eliges ninguna, se conserva la anterior)
nuevo = nil
foreach( entry gmidDestCeldas
idx = caddr(entry)
valores = gmidLeerVistaField(f idx)
vistaElegida = nil
selList = valores
nameList = cadr(entry)
while( selList
when( car(selList)
vistaElegida = car(nameList)
)
selList = cdr(selList)
nameList = cdr(nameList)
)
when( vistaElegida
nuevo = cons(list(car(entry) vistaElegida) nuevo)
)
)
kept = setof(o gmidViewOverrides !assoc(car(o) nuevo))
gmidViewOverrides = append(nuevo kept)
printf("Destino: tests %L | vistas %L\n" gmidTests gmidViewOverrides)
if( gmidTests
then evalstring(gmidAccionPendiente)
else printf("No has elegido ningun test: no se crea nada.\n"))
)
)
;; Entradas de menu: piden el destino y luego ejecutan la accion
procedure( GmIdConDestino()
gmidPedirDestino("GenerarGmId()")
)
procedure( SaturacionConDestino()
gmidPedirDestino("GenerarSaturacion()")
)
;; -------------------------------------------------------------------------
;; GM/ID
;; -------------------------------------------------------------------------
procedure( GenerarGmId()
let( (cv prep sess tests xtors nN nP nU unk n1 r1 nErr path)
cv = gmidCvTrabajo()
when( cv && cv~>cellViewType == "schematic"
prep = gmidPrepare(cv)
sess = car(prep)
tests = cadr(prep)
xtors = caddr(prep)
nN = 0
nP = 0
nU = 0
unk = nil
foreach( par xtors
cond(
( eq(cadr(par) 'n) nN = nN + 1 )
( eq(cadr(par) 'p) nP = nP + 1 )
( t nU = nU + 1 unk = cons(car(par) unk) )
)
)
nErr = 0
foreach( tst tests
foreach( par xtors
path = car(par)
n1 = sprintf(nil "gmid_%s" gmidSafeName(path))
errset(maeDeleteOutput(n1 tst ?session sess))
r1 = maeAddOutput(n1 tst ?outputType "point"
?expr sprintf(nil "OP(\"/%s\" \"gm\") / abs(OP(\"/%s\" \"id\"))" path path)
?session sess)
unless( r1
nErr = nErr + 1
printf("Fallo gm/Id %s en %s\n" path tst))
)
)
if( tests then
maeSaveSetup(?session sess)
printf("gm/Id: %d NMOS, %d PMOS, %d sin polaridad | tests %L | %d fallos\n"
nN nP nU tests nErr)
when( nU > 0
printf("Aviso: polaridad desconocida en %L. gm/Id no la necesita, pero Mismatch si: anade su celda en Tipos de transistores.\n" unk)
)
else
printf("Ningun test elegido.\n")
)
)
)
)
;; -------------------------------------------------------------------------
;; SATURACION
;;
;; vdsmargin = abs(vds) - abs(vdsat). Guia de interpretacion:
;; > ~50-100 mV -> bien saturado
;; 0-50 mV -> al limite
;; negativo -> en triodo
;; -------------------------------------------------------------------------
procedure( GenerarSaturacion()
let( (cv prep sess tests xtors n2 r2 r3 nErr path)
cv = gmidCvTrabajo()
when( cv && cv~>cellViewType == "schematic"
prep = gmidPrepare(cv)
sess = car(prep)
tests = cadr(prep)
xtors = caddr(prep)
nErr = 0
foreach( tst tests
foreach( par xtors
path = car(par)
n2 = sprintf(nil "vdsmargin_%s" gmidSafeName(path))
errset(maeDeleteOutput(n2 tst ?session sess))
r2 = maeAddOutput(n2 tst ?outputType "point"
?expr sprintf(nil "abs(OP(\"/%s\" \"vds\")) - abs(OP(\"/%s\" \"vdsat\"))" path path)
?session sess)
r3 = maeSetSpec(n2 tst ?gt gmidSatMargin ?session sess)
unless( r2 && r3
nErr = nErr + 1
printf("Fallo saturacion %s en %s\n" path tst))
)
)
if( tests then
maeSaveSetup(?session sess)
printf("Saturacion: %d transistores | tests %L | spec > %s | %d fallos\n"
length(xtors) tests gmidSatMargin nErr)
else
printf("Ningun test elegido.\n")
)
)
)
)
;; -------------------------------------------------------------------------
;; MENU (admite items extra anadidos por otros archivos via gmidExtraMenuItems)
;; -------------------------------------------------------------------------
procedure( RegistrarMiMenuGmId(args)
let( (itemGmid itemSat itemTipos menuCustom win allItems)
win = if( windowp(args) then args else args->window )
when( win && windowp(win)
itemGmid = hiCreateMenuItem(
?name 'ItemGmId
?itemText "gm/Id"
?callback "GmIdConDestino()"
)
itemSat = hiCreateMenuItem(
?name 'ItemSat
?itemText "Saturación"
?callback "SaturacionConDestino()"
)
itemTipos = hiCreateMenuItem(
?name 'ItemTipos
?itemText "Tipos de transistores"
?callback "TiposTransistoresGmId()"
)
allItems = append(list(itemGmid itemSat itemTipos) gmidExtraMenuItems)
menuCustom = hiCreatePulldownMenu(
'MenuGmId
"Crear expresiones"
allItems
)
hiInsertBannerMenu(win menuCustom length(hiGetBannerMenus(win)))
)
)
)
;; Quita el menu "Crear expresiones" de todas las ventanas donde ya este
;; puesto. ChequearMenusGmId() lo vuelve a anadir en su siguiente ciclo
;; (hasta 1s despues) con la lista de items actualizada: hace falta cuando
;; se carga otro archivo (p.ej. mismatch_pelgrom.il) que anade su propio
;; item a gmidExtraMenuItems y quiere verlo sin esperar o sin reabrir el
;; esquematico.
procedure( gmidQuitarMenus()
foreach( w hiGetWindowList()
when( windowp(w) && member('MenuGmId hiGetBannerMenus(w))
errset(hiRemoveBannerMenu(w 'MenuGmId))
)
)
t
)
procedure( ChequearMenusGmId()
errset(
foreach( w hiGetWindowList()
errset(
when( windowp(w) && gmidEsVentanaGrafica(w) &&
!member('MenuGmId hiGetBannerMenus(w))
RegistrarMiMenuGmId(list(nil 'window w))
)
)
)
)
hiRegTimer("ChequearMenusGmId()" 1000)
)
ChequearMenusGmId()