Motor de diseño de la neurona LIF: leyes, solver, generador de layout y verificación - #27
Open
carloscl03 wants to merge 68 commits into
Open
Motor de diseño de la neurona LIF: leyes, solver, generador de layout y verificación#27carloscl03 wants to merge 68 commits into
carloscl03 wants to merge 68 commits into
Conversation
Barridos de Iex, Cm, L_M5 y W de salida con medicion multi-ciclo, mas validacion cruzada de cada ecuacion en configuraciones distintas al nominal. Resultados en results/lif_knowledge_base.md. Ecuaciones validadas: - Iex = 169.1*(2.571-Vin)^2 robusta (<0.03% entre configs) - I_drive = 85*W robusta (<1%) - f = (363.6/L_M5 - 2.08)*Iex general, reemplaza la del nominal - Vth = 2.893*L_M5/Cm - 21.28/Cm + 1.2606 - Cm_min = 2.166*L_M5 + 52.06 limite por overshoot capacitivo Se descarto f ~ 1/L_M5: con 6 puntos el producto f*L varia 31%, la dependencia es indirecta via el threshold.
W_M5 resulta parametro de primer orden (nunca se habia variado). A Iex=100nA, Cm=150f, L_M5=50u: W de 0.5 a 5u cambia la frecuencia de 1359 a 278 kHz y el threshold de 1.68 a 3.25 V (este ultimo casi en Vdd). Hallazgos: - Vth = 0.3387*W_M5 + 1.6114 (R2=0.981, a L_M5=50u) - f ~ 1/W_M5 (R2=0.972) pero satura arriba de 2.5u - W_M5 tambien mueve el limite de Cm: la ley Cm_min=2.166*L_M5+52 esta incompleta, le falta el termino de W - El parametro NO es W/L: mismo ratio 0.025 da 545 vs 2531 kHz segun las dimensiones absolutas. Domina el acoplamiento capacitivo (area W*L), no la resistencia de fuga - Rango util W_M5 <= 1.25u; el nominal esta en el borde
Barrido 4x4 (W 0.5-2.5u, L 25-50u) a Cm=200f. Solo 11 de 16 puntos en regimen valido: con W y L grandes 200f ya no evita el overshoot. - freq ~ 1/(W*L) R2=0.978; ley de potencia W^-1.008 * L^-1.304 R2=0.981. Los modelos lineales en (W,L) fallan (R2<=0.83): la relacion es inversa. - Vth = -0.11481*W +0.00280*L +0.008966*W*L +1.3001 R2=0.994, LOO=0.025V. El termino de interaccion W*L es imprescindible (sin el, R2<=0.92). - El area no predice el overshoot: W=2.5/L=25 (area 62.5) queda OK mientras W=1.75/L=41 (area 71.8) falla. Para el overshoot L pesa mas que W. - La ley Cm_min=2.166*L+52 subestima el limite para W>1.25u.
Barrido de 30 puntos (6 combinaciones W,L x 5 valores de Cm) interpolando donde Vm_min cruza cero. La ley anterior Cm_min=2.166*L+52 solo acierta en W=1.25u (su punto de medicion). Fuera de ahi subestima hasta 105 fF, lo que llevaria a disenos en regimen anomalo. Modelo aproximado: Cm_min = 6.230*(W*L) - 208.7 (R2=0.826) Con reservas: solo 4 puntos tienen frontera medible (los de W=0.5u caen por debajo de la grilla) y el LOO RMSE es 42 fF. Modelos con mas terminos dan R2=1.0 pero es sobreajuste trivial (4 parametros, 4 puntos). Usar como estimacion gruesa y verificar por simulacion el punto concreto.
Cm_min = 8.94 * W_M5^1.038 * L_M5^0.700 R2=0.980, LOO=15.4 fF Busqueda binaria del limite (rango 30-500 fF) en 12 combinaciones, lo que permitio capturar las fronteras por debajo de 100 fF que la grilla anterior no alcanzaba. Errores dentro de +-11%, la mayoria bajo 5%. Validada por extrapolacion: W=2.0u/L=33u predicho 207 fF vs 206 medido (0.6%); W=2.5u/L=41u predicho 301 vs 337 (10.7%). Nota metodologica: con los primeros 9 puntos (todos W<=1.5u) el modelo W^2*L parecia el mejor (LOO 10.3 fF), pero al anadir W=1.75u fallo por 31% y su LOO subio a 28.3. Un LOO bajo no protege contra extrapolacion cuando el rango de datos esta sesgado.
Se fusionaron las 85 mediciones de Vm_min de los 4 barridos y se ajusto el modelo fisico Vm_min = A - k*W^a*L^b/Cm (R2=0.837). Despejando el limite da Cm_min = 3.48*W^1.453*L^0.847, con RMSE 33.6 fF contra las fronteras medidas, frente a 11.5 fF de la ley ajustada directamente a ellas. La razon: las 85 mediciones cubren todo el rango de Vm_min, la mayoria lejos de cero. El ajuste global optimiza el error medio en todo el espacio y pierde precision justo en Vm_min~0, que es lo que define el limite. Se conserva la ley de las 12 fronteras.
El paso .tran 20n sobreestima la frecuencia +41% en promedio y hasta +193%: el integrador salta ciclos y los cuenta como disparos. test_tstep.sh lo verifica re-simulando 5 configuraciones a 20/5/1 ns. Consecuencias: - el jitter de 30-55% que atribuiamos al circuito era resolucion temporal, desaparece con paso fino (<0.5% real). No hay frontera de jitter. - Cm no controla la frecuencia: varia <4% al triplicarse. Lo que veiamos como +79% era el artefacto. Leyes nuevas sobre 32 puntos, todos convergidos: f = 24837*W^-1.076*L^-0.940 LOO 3.1% swing = 4.114*W^0.951*L^1.065*Cm^-1.006 LOO 12 mV Vth = 1.2792 + (-16.83W+0.4884L+1.766WL)/Cm LOO 16 mV Como Cm sale de la ecuacion de frecuencia, el algoritmo de diseno deja de necesitar iteracion: W,L fijan f y luego Cm ajusta Vth sin realimentar. Los scripts nuevos marcan NOCONV los puntos con jitter >2% para que un dato mal convergido no entre en un ajuste sin avisar.
Puntos nuevos W en {0.75,1.4,2.1} x L en {33,50}: ninguna combinacion esta
en la grilla con que se ajustaron las leyes, y L=50 queda fuera del rango
ajustado (25-41), asi que ademas prueba extrapolacion.
f error medio -0.00%, RMS 1.23%
Vth error medio -0.63%, RMS 0.90%
swing error medio -0.58%, RMS 1.11%
Extrapolando a L=50 el error se mantiene ~1%. El LOO de 3.1% resulto
conservador.
Iex medida a paso 1n: RMS 0.07%, y el reajuste da k=169.19 vs 169.1 (+0.05%).
Queda comprobado por medicion lo que antes solo se argumentaba.
Limites de validez que faltaban:
Cm >= 50f bajo eso Vth diverge (a 25f predice 5.83V, imposible con
VDD=3.3V). Todo diseno debe comprobar Vth < 3.3.
W <= 2.5u sobre eso f satura (a 5u predice 111 kHz, medido 278)
Iex >= 50nA para que f sea lineal en Iex (a 23nA el error es +7.4%)
test_tstop.sh: acortar el transitorio de 100u a 30u da resultados identicos
a 4 cifras, 3.3x mas rapido.
bench_par.sh: ngspice ya usa 7.3 de 8 nucleos con un solo proceso, asi que
correr varios en paralelo no aporta y degrada por contencion de cache. Los
barridos van en serie.
36 puntos en los bordes del espacio de diseno. Los estados no-OK son el dato: marcan donde deja de funcionar el circuito. Iex minimo ~12.5 nA (Vin>=2.3 no oscila) L_M5 maximo 50-60 um (L=60 no converge) W_M5 maximo ~3.8 um a Cm=400f, depende de Cm Cm minimo 80-100 f para W=1,L=41 Correccion: se documentaba que sobre W=2.5um la ley fallaba porque f satura. Es falso. El barrido fino muestra que la membrana se sale del riel: W=3.5 da Vm_min +0.087 (OK) y W=4.0 da -0.058 (ANOMALO). El punto de W=5um que se uso como referencia de -60% era anomalo. Es la misma frontera Cm_min(W,L) vista desde el eje de W. Cm_min resulta conservadora en +34%: predice 120f donde la frontera real esta entre 80 y 100f. Del lado seguro, pero desperdicia area. Reajuste con los 69 puntos limpios (13 niveles de W, 8 de L): las leyes no cambian. f pasa de RMS 2.07% a 2.03%, Vth y swing igual. Se conservan las actuales; cambiar coeficientes por 0.04 pp invalidaria la validacion externa a cambio de nada. Nota metodologica: un corte a W=1.0 fijo daba exponente de L -0.871 frente al -0.940 de la ley, y parecia un error del 7.3%. Con los 69 puntos el exponente global es -0.9285. Un exponente de una seccion 1-D no es comparable con el de un ajuste multivariable. La ley pierde precision bajo L=25um: 5-7% frente al ~1% habitual.
La celda usa entrada de corriente (se conecta a distintas etapas), asi que se
rehizo el tb sin depender de Vin. Comprobado que la impedancia de M6 era lo
bastante alta como para no afectar: 501 vs 494 kHz, 1.4%. Las leyes previas
siguen valiendo.
Ganancia de modulacion (45 pts, 9 configuraciones):
k = 1063*W^-0.936*L^-0.940 kHz/nA LOO 4.4%
f = k*Iex + f0, lineal con R2 0.998-0.9999
Cm no interviene: anadirlo empeora el ajuste.
Iex es ortogonal a Vth: <1.2% de variacion con la corriente x16.
Carga de salida (24 pts):
C_load_max ~= 600*W_M7M8 fF (criterio tf <= 5ns)
La carga NO afecta la frecuencia: <0.7% con C_load de 0 a 1600 fF.
Impedancia de fuente (28 pts) -- el requisito mas restrictivo:
ro >= 190/Iex[nA] GOhm para 1% de error.
A 100 nA son 1.9 GOhm. Un espejo simple da 1-10 MOhm y desvia la
frecuencia 20-200%. Con 3 MOhm el circuito deja de oscilar.
Ventana de Iex:
techo f_max ~4500 kHz en 3 configuraciones con corrientes distintas
(350/500/600 nA) -> es limite de periodo, no de corriente.
piso NO EXISTE. Verificado hasta 5 nA con k y swing constantes.
El piso que se habia reportado antes era artefacto: con tstop=30us una
neurona a 15 kHz (periodo 67us) no completa un ciclo y el criterio lo
marcaba NO_OSCILA. Segundo artefacto instrumental de la serie, tras el del
paso de .tran.
Resuelve intencion -> parametros (W_M5, L_M5, Cm, W_M7M8) con las leyes medidas. Herramienta para que la use una IA: entrada determinista y salida estructurada, incluido el detalle de por que algo no se puede. Todo por despeje cerrado, sin iteracion: las leyes son potencias o lineales en 1/Cm, y como Cm no interviene en la frecuencia la cadena es unidireccional (W,L -> f ; W,L,Cm -> Vth) y no hay punto fijo. 149 us por diseño completo. Lo unico no analitico es elegir sobre la curva de iso-frecuencia cuando W y L quedan libres, y ahi es un barrido de 60 pasos con criterio explicito. Solo stdlib (math, dataclasses, enum). Sin numpy: son ~15 pow() por diseño y la importacion costaria 100 ms para un calculo de 50 us. Politica de prioridades: los objetivos mandan sobre las dimensiones fijadas. Si contradicen, se ajusta la dimension con WARNING; si no hay salida, ERROR con la cadena causal. Nunca lanza excepcion. Orden de ajuste por coste de cambio: Cm, luego L_M5, luego W_M5. W_M7M8 queda fuera de la cadena porque depende solo del fan-out y la carga no afecta a f. La ganancia se reajusto al probar el motor: la constante estaba transcrita como 1063 cuando el ajuste da 280.22, y los exponentes tambien estaban desviados. Queda k = 280.22*W^-1.0447*L^-0.9923 con RMS 2.18%. Derivarla de f(100)/100 daria RMS 7.9% con sesgo +6% porque la recta tiene intercepto.
verify.py genera el netlist del diseño, lo simula y compara lo medido con lo predicho. En primera instancia el return del sistema es SPICE; cuando gLayout avance sera el layout y esto quedara como verificacion previa. Probado en tres diseños, dos de ellos con geometrias que el sistema invento y que no estaban en ninguna grilla de caracterizacion: nominal W=1.25 L=50.0 Cm=150 f 494 -> 501 -1.4% rapida W=0.602 L=35.42 Cm=77 f 1500 -> 1492 +0.5% lenta W=2.835 L=40.42 Cm=422 f 250 -> 253 -1.0% Peor error de los tres: 2.5%. Valida la cadena completa: leyes, despejes, eleccion sobre la curva de iso-frecuencia, netlist y simulacion. Dos salvaguardas contra los artefactos que ya nos costaron caro: el paso queda fijo en 1 ns (con 20n la frecuencia se infla hasta +193%) y el transitorio se calcula por diseño para dejar 8 ciclos utiles tras el arranque (con tstop fijo una neurona lenta no completa ni un ciclo). f0 resulta ser CERO. El 14-144 kHz que salia de ajustar rectas sobre Iex 25-400 nA era artefacto: un intercepto lejos del origen absorbe la curvatura de la zona alta. Midiendo a 5 y 10 nA sale f0=+0.57 kHz. Extrapolar la recta con intercepto hacia abajo falla +56%; el anclaje proporcional da -3.4%. Se aborto el barrido de f0 (habria sido 18 h) porque los 2 primeros puntos ya respondian la pregunta. Queda documentado un sesgo real: k tiene curvatura y a corriente baja la pendiente es ~11.6% mayor que la ley.
Hay dos testbenches y nada explicaba la diferencia. El vigente es tb_charac_isrc.spice (entrada de corriente, sin M6); tb_charac.spice queda como historico. El README de tb/ recoge las dos trampas que costaron caro durante la caracterizacion: el paso de .tran (con 20n la frecuencia se infla hasta +193%) y el transitorio corto (una neurona a 15 kHz no completa un ciclo en 30us y parece que no oscila). Se borra verify_laws_par.sh: bench_par.sh midio que ngspice ya usa 7.3 de 8 nucleos con un solo proceso, asi que la version paralela no aplica. .gitignore cubre ahora netlists_*/ y los verify_*.spice que genera el modulo de verificacion.
La documentacion tecnica del repo esta en ingles (README principal, stdp), asi que estos tres archivos se alinean. Los mensajes de commit siguen en español como el resto del equipo. La knowledge base habia crecido por acumulacion: 1141 lineas donde las leyes vigentes estaban en la linea 26 y luego venian 700 lineas de secciones historicas con ecuaciones ya invalidadas, y un segundo resumen en la 1049. Un lector no podia saber que creer. Queda en 320 lineas con estructura por temas: las 5 leyes, la estructura del espacio de diseño, los limites de operacion, las correcciones a conclusiones previas, la validacion y la metodologia. No se pierde informacion: lo que se fue eran ecuaciones sustituidas y tablas duplicadas.
example.py es ejecutable y cubre los seis casos: default, rangos, hibrido, conflicto resoluble, imposible fisico y verificacion por simulacion. Al escribirlo salio un bug: con una sola dimension fija (W o L) y un objetivo de frecuencia, si la otra se salia de rango el motor la acotaba y devolvia una frecuencia distinta a la pedida (+26% en el ejemplo). Solo entraba a ajustar ambas cuando las dos estaban fijas. Ahora libera tambien la que el usuario habia fijado, que es la politica acordada: los objetivos mandan. Verificado en cuatro casos, la frecuencia resultante queda a <=0.1% de la pedida.
Antes habia que hacer sys.path.insert con una ruta relativa distinta segun
desde donde se ejecutara el script. Con pyproject.toml basta:
cd sch/lif && pip install -e .
y luego 'from design import NeuronSpec, design' funciona desde cualquier ruta.
Modo editable, asi que editar laws.py se refleja sin reinstalar.
Sin dependencias: solo stdlib.
.gitignore cubre ahora los *.egg-info/ que deja pip install -e.
El motor vivia en sch/lif/design/, dentro de una carpeta que se creo para
trabajo personal ("creando espacios para que cada quien trabaje"). Lo que
se entrega es designs/, asi que ahi debe estar.
designs/scripts/lif_design/ leyes, spec y solver
designs/libs/tb_analog/tb_lif/ testbench, siguiendo tb_ota_5t
verify.py pasa a ser el fixture.py del testbench: hace lo mismo que el de
tb_ota_5t -- componer netlist, correr ngspice, medir -- y ahora se invoca
con pytest como el resto.
De paso, freq_range e iex_range aceptan un escalar ademas de un par:
freq_range=500 es lo mismo que freq_range=(500, 500). Pedir ambos fijos
antes reventaba con ZeroDivisionError al calcular la ganancia (0/0);
ahora se resuelve como punto de operacion.
plan_row() calcula la separacion entre bloques a partir de las rutas que
cruzan cada hueco, en vez de un factor fijo. El orden de los bloques se
respeta tal cual se da; esto solo decide cuanto separarlos.
Sobre la neurona de layout/lif/neurona.ipynb, los tres huecos dejan de ser
iguales: 0.58 / 1.16 / 0.58 um segun crucen una o dos rutas.
OVERLAP_NOTES.md recoge lo que se midio para llegar ahi:
* un fet de glayout lleva ~2.6 um de bbox vacio por lado, asi que solapar
los bbox no es de por si un error -- el limite util es 5.5 um, donde las
difusiones se acercan a 0.20 um y salta DF.3a
* el DRC no avisa de esto: a 6 um las dos difusiones se fusionan y los dos
transistores pasan a ser uno, y el deck lo da por bueno
* with_tie/with_dummy/with_substrate_tap explican las 31 violaciones de la
celda actual. Con los defaults de glayout el DRC queda limpio, pero el
area pasa de 1168 a 4204 um2
* fingers mueve la frecuencia <=0.40% (dentro del ruido de la propia
simulacion) pero el ancho del bloque +85% con 2 dedos y +255% con 4:
es decision de layout, no del motor
Ojo con el deck: el que trae glayout en src/glayout/pdk/gf180_mapped/ es el
que evalua. El de $PDK_ROOT/libs.tech/klayout/tech/drc/ devuelve un reporte
vacio incluso para un GDS con violaciones conocidas.
place.py calcula donde va cada bloque. Ninguna distancia es un factor de ajuste: salen de las reglas del pdk y de las capas que dos bloques comparten. Lee tamano, pozo y capas ocupadas del componente ya generado, via evaluate_bbox y los puertos well_*, asi que no hay que intuir nada del generador. El pitch entre inversores le sale 5.840 um contra 5.820 medidos en la celda de Abrahan, sin barrer. Las rutas no entran en el hueco: cruzan por encima de los bloques, no entre ellos. clearances() dice por que capa vuela cada red, o si no le queda ninguna y toca rodear. build.py convierte el plan en geometria. Los rieles se dibujan aqui y no se dejan al llamante: una celda que expone VDD y VSS y no los conecta parece terminada y no lo esta, que es como la neurona LIF llego a DRC limpio sin alimentacion. Los rieles van una capa por encima de los bloques. Un fet de glayout ocupa met2 hasta su anillo de tie y el puerto de source cae dentro del anillo, asi que una bajada en met2 lo cruza al salir y cortocircuita los dos. En met3 la pletina pasa por encima y solo baja, con via, sobre el source. preview.py dibuja la celda con sus violaciones marcadas. Los numeros dicen que regla fallo; el dibujo dice que lado del dispositivo ya esta ocupado. Barrido de 10 casos: topologia correcta en los diez y DRC 0 en nueve. Multipliers > 1 avisa: conecta bien pero deja las bajadas a 0.10 um del ruteo interno.
La fila unica desperdiciaba el 45% del area: la altura la fijaba el inversor mientras M5, que mide 24-54 um de largo, dejaba vacio todo lo que tenia encima y debajo. En bandas -- pfets arriba, M5 cruzando, nfets y condensadores abajo -- el hueco de M5 se llena y la celda pasa de 84x14 a 54x21, con la forma que el equipo ya usaba en su layout. place.py gana Band y plan_bands. El hueco entre bandas sale de la regla de pozo, y son distintos por eso: 1.4 um entre dos pwell, 0.4 entre pwell y nwell. Los rieles se calculan sobre el conjunto ya planificado, asi que redimensionar los condensadores los aparta solo. Rails.above pasa a mirar toda la fila, no un bloque: el que decide la CAPA no es el que decide la ALTURA. En la LIF el inversor es lo mas alto pero el mimcap es lo unico que llega a met3, asi que mirando solo al mas alto los rieles salian en met3, atravesando el cap. build.py gana lif_cell. Dos cosas costaron seis intentos y conviene dejarlas escritas: Puerta y drenador no pueden bajar por la misma columna. gate_S y drain_S estan los dos en x=0 del dispositivo, asi que rutear ambos desde ahi los superpone y deja cada inversor en diodo -- con puerta y drenador en una sola red, que a un verificador descuidado le sigue pareciendo un par bien formado. Y no se puede suponer donde esta el metal. La columna met3 del drenador no cae sobre su puerto, porque c_route sale al este antes de subir; su x se toma del bbox de la ruta. Sobre la puerta si coincide, porque esa es una recta por el centro del bloque. Verificado por extraccion en 5 juegos de dimensiones -- inversores 4.5x mas anchos, M5 con la mitad de longitud, M5 casi 3x mas ancho y condensadores un 60% mayores: DRC 0 y topologia correcta en los cinco. Los condensadores se comprueban por geometria, no por extraccion: el techfile de magic situa el MIM entre metal4 y metal5 y nuestro stack lo tiene en met2/met3, asi que lo que reporta de ellos no corresponde. Falta la realimentacion de inv1 a la puerta de M5 y los rieles.
Las fuentes van al anillo que tienen justo al lado y el anillo baja al riel, en vez de tirar cada fuente al riel cruzando la celda entera, que es lo que se llevaba por delante las pistas de fan-out y membrana. Rieles y bajadas van gruesos porque por ahi pasa la corriente. M5 baja al VSS por el hueco libre que dejen los caps, calculado, y si no hay hueco salta al anillo del nfet que tiene debajo. La realimentacion a la puerta de M5 sale con un solo via: la columna met3 del drenador de inv1 ya cruza la tira de puerta. Anade check.py y netcheck.py. El extractor de magic no sirve aqui porque su techfile pone el MIM en metal4/metal5 y el nuestro esta en met2/met3, asi que la conectividad se camina sobre el metal. Con eso salta que el deck se saltaba todas las reglas MIM (mim_option por defecto es "Nan") y que faltaba MIM.1 en el placement: 1.2 um de la placa inferior a cualquier met2, no la separacion normal. Seis casos de dimensiones, DRC 0 y las seis redes del LIF exactas.
from_design() toma lo que devuelve el solver y saca la celda. Faltaban dos piezas: convertir Cm a geometria, y poder dimensionar el inversor de salida aparte, que es el unico que el solver dimensiona por carga. La conversion no es area por densidad. El modelo del PDK es C = c_cox*area + c_capsw*perimetro, y a nuestro tamaño el perimetro pesa un 21%. Ver mim.py, con los parametros copiados de sm141064.ngspice. Al dejar mandar a la caracterizacion salieron dos cosas que los casos de dimensiones fijas no tocaban: MIM.8a no deja un FuseTop de menos de 25 um2, o sea 5 um de lado, asi que el numero de MIM baja solo cuando la membrana es pequeña; y el salto anillo a anillo topaba con los dos anillos en vez de solaparlos y el redondeo dejaba 7 nm de hueco. Elegida la opcion A con receta 2f0: A deja libres las capas altas para el ruteo entre neuronas, y de las tres recetas la densa es la que menos area pide, entre 8% y 25% de celda. Falta confirmarlo con el equipo, que es una decision de todo el chip. check.py corre ahora tambien desde la especificacion. Diez casos, DRC 0 con reglas MIM y las seis redes exactas.
Las dos capas por separado, las dos verificaciones y las dos tandas de robustez, con las salidas dentro para poder leerlo sin ejecutar nada. Deja escritas las dos trampas que nos costaron dias: el deck se salta todas las reglas MIM si no le pasas mim_option, y el extractor de magic no vale aqui porque su techfile pone el MIM en otras capas. check.py se vuelve portable de paso. Importaba un nombre de paquete que solo existia en mi contenedor y tenia /tmp escrito a mano, asi que ahora resuelve el paquete por donde esta el fichero y toma el deck y la salida del entorno.
La imagen ya trae python 3.12, gdstk y klayout, asi que basta un venv y el glayout de la rama. Sin descargas grandes. Hace falta GLAYOUT_BACKEND=gdstk y no es opcional: el gdsfactory 9.40 de la imagen ya no expone component_reference, asi que glayout de upstream ni arranca aqui, cae a un DummyPdk. Con nuestro backend si. Instala desde capimagics-base, no desde PyPI: comprueba con un assert que capmet apunte a fusetop, porque si apunta al marcador los MIM salen con las placas en corto y eso no da error en ningun sitio. Verificado corriendo el notebook entero en un contenedor limpio: mismos resultados que en el de desarrollo.
…y PR Junto a los otros dos y con su misma forma, pero desde capimagics-base y sobre el python de la imagen. Sin conda ni pyenv, sin descargas grandes. Los dos que hay llevan a un glayout con tres problemas: no arranca siquiera en esta imagen (cae a un DummyPdk sin fallar del todo), el mimcap de gf180 sale con las placas en corto, y los fets estrechos no se construyen. run_GL_conda.sh instala de PyPI y run_GL_pyenv.sh instala libs/gLayout, que es un clon de upstream main, asi que los dos caminos acaban ahi. Este no toca sus entornos ni su kernel: registra uno aparte llamado 'lif'. Y si el puerto esta ocupado avisa en vez de lanzar un segundo Jupyter. Comprobado en un contenedor limpio, con asserts que revientan si el glayout instalado generaria los MIM en corto. Sustituye a setup_container.sh, que estaba escondido dentro de lif_design.
Los notebooks declaran ese kernel, asi que en un contenedor recien creado abren sin kernel valido. Y si uno elige el equivocado, el error que da no apunta al kernel por ningun lado: sale un ValueError dentro de pmos, que es el bug de los fets estrechos del glayout sin parchear. Solo lo crea si no existe. Si ya hay uno es porque lo puso run_GL_conda.sh o run_GL_pyenv.sh, y ese no se toca: avisa de que hay que cambiar a mano.
C_in = 0.945 + 0.865*W_M5, la capacidad que la fuente ve en el nodo aparte de Cm. Medida como C_total - Cm con C_total = Iex/(dV/dt) sobre la rampa: la propia fuente cargando el nodo es la medida, sin necesidad de .ac (el circuito oscila y no hay punto de operacion que linealizar). LOO 0.67% RMS, validacion externa 0.56% RMS sobre rejilla disjunta, con dos puntos en W=0.30 y W=0.22, por debajo del borde de ajuste. Afin y no potencia, por la misma razon que Vth: el 0.945 son las puertas de M1/M2, que cuelgan del nodo aunque M5 sea minimo. Una potencia pura pasa por el origen y falla -21% en W=0.22. Entra como salida predicha, nunca como objetivo: depende solo de W_M5, y 1.1-4.0 fF en todo el envolvente es un rango demasiado estrecho para restringir nada. c_in_max va en contexto y se comprueba en _validate; no compite por W_M5, que es el ultimo eslabon de la cadena de ajuste. NO se suma a freq(): la ley de frecuencia se ajusto sobre simulaciones del circuito completo que ya la contienen.
Cm era la unica incoherencia viva de la convencion: mayuscula en el spec y en las claves de predicted, minuscula como parametro en laws.py y en los bancos de prueba. La regla es que el guion bajo es el subindice del simbolo del esquematico (W_M5, c_load, C_in), y Cm va pegado porque asi viene del esquematico y del paper. Arrastra cm_min -> Cm_min y solve_cm_for_vth -> solve_Cm_for_vth. Mayuscula en medio de un nombre de funcion incomoda a PEP 8, pero laws.py ya lo rompe a proposito con solve_L_for_freq y solve_W_for_freq, asi que esto es consistente dentro del fichero en vez de abrir una excepcion nueva. Renombrado mecanico: ningun llamador pasaba cm= por palabra clave.
La suite pasa de 6+4 a 7+7 casos de layout. Los tres nuevos de especificacion entran por rutas distintas Y salen con geometrias que ninguna de las anteriores producia, que es el criterio para estar ahi porque cada caso cuesta un DRC completo: umbral 2.0V Vth arrastra Cm, o sea el numero de MIM Iex 5 nA saca W_M5 a 0.222, casi el minimo; la celda mas estrecha carga 800fF unica ruta que dimensiona W_M7M8 (1.333) Un objetivo de ganancia NO esta: recorre codigo distinto en el solver pero resuelve por (f_hi, iex_hi), asi que da el mismo GDS que el caso de 800 kHz. Comprobado antes de descartarlo, y anotado para que no vuelva a colarse. En CASOS entra "todo minimo" (W_M5=0.22 con L_M5=20), la esquina donde el anillo de M5 se queda mas estrecho que el paso de las pistas. El notebook gana la seccion de C_in -- colocada junto a source_ro, porque las dos son el interfaz con la fuente: una es lo que le exigimos y la otra lo que le ofrecemos -- y "el solver nunca lanza", ocho entradas hostiles de las que dos hacen saltar ValueError dentro de las leyes. Ese contrato importa porque quien llama a design() es un generador: si lanzara, un barrido de mil neuronas se caeria en la primera absurda. Los 14 casos de layout pasan con DRC 0 y las seis redes.
…ierta Seis leyes en vez de cinco. C_in entra con su derivacion, la fila de validacion externa (0.56% RMS) y la salvedad de que ese numero mide la ley sobre la superficie Cm = 2*Cm_min, donde viven las dos rejillas; fuera de ella se desvia hasta el 15%. En "Not characterized" queda la pista que el dato pide y que no modelamos a proposito. A geometria fija el exponente de Cm sale +0.098 en W=0.5 y -0.023 en W=2.5: signos opuestos, asi que no hay forma separable. Ordenando los diez puntos por excursion en vez de por geometria colapsan sobre una sola curva, y ahi se ve que lo que parecian dependencias de Cm y de L son una sola variable vista dos veces, porque swing lleva las dos dentro. Predijo dos puntos nuevos a 0.1% y 0.2%. Uno de los diez se desvia un 26% y queda sin explicar; esta marcado, no escondido. No se afina mas porque el efecto se traduce en menos de 0.15% de desviacion en frecuencia sobre todo el espacio que el solver alcanza -- ocho veces por debajo del 1.23% RMS de la propia ley de frecuencia. Afinar una correccion muy por debajo del error de lo que corrige no compra nada.
El equipo se mueve a opcion B: glayout fusiono el PR 106, que reescribio
mimcap() y movio capmet a met4/met5. A y B no pueden convivir en el mismo
proceso -- la DRM lo dice y hay una sola capa FuseTop, asi que un poligono
no puede declarar a que altura pertenece -- de modo que es una decision de
chip, no por celda.
NO ESTA TERMINADO. La celda construye y encoge de 44.53x25.64 a
40.73x24.14, pero las seis redes NO salen: la membrana queda en corto con
VSS y las placas inferiores sin conectar. Ver abajo.
Lo que cambia, y el hilo es siempre el mismo: dejar de suponer las capas
y preguntarselas al PDK.
_cap_glayers() lee capmettop/capmetbottom del PDK. Suponerlas deja el
ruteo dos niveles por debajo de la placa, sin via que lo
salve y sin error: el condensador flota en silencio.
_CAP_TOP/_CAP_BOT el mimcap reescrito ya no expone bottom_met_*. La placa
inferior sale por la extension sur, que la sube a la capa
de la superior.
la membrana corre por capmettop, asi que llega al banco y se funde con las
placas sin una sola via -- que es lo que hacia opcion A cuando esa capa era
met3. De paso sale de met2, el carril mas poblado, y la red sensible de la
celda pierde vecinos que le acoplen.
drop() sabe bajar ademas de subir: con el MIM arriba la placa inferior
queda por encima del riel y la pila se invierte. Y no desplaza "hacia
dentro del metal" en el mimcap: alli eso empuja la correa contra la placa
superior, que esta a 0.6 um.
check.py deriva la opcion del deck del PDK. Con la equivocada, mim_virtual
sale vacio y MIM.3 acusa al condensador de no tener placa -- un falso
positivo que parece un fallo de layout. Y sus sondas leen las capas de las
placas en vez de llevar 42 y 36 escritos.
DRC: 176 violaciones. De esas, ~90 son de mimcap() de glayout: la primitiva
sola, sin nada alrededor, ya da 30 (MIMTM.1, MIMTM.3, MIMTM.10). Reportarlo
aguas arriba antes de seguir puliendo las nuestras, porque si cambian la
geometria de la primitiva parte de este ajuste se rehace.
Lo que falta: la correa de la placa inferior a VSS. Entre el pad de la
extension y la placa superior hay 0.6 um y hay que entrar ahi sin tocar
arriba ni quedarse corto abajo.
Dos errores de esta migracion, y el primero es del instrumento.
netcheck.py tenia la pila cableada hasta met4:
STACK = [met1, via1, met2, via2, met3, via3, met4]
Con el MIM en met4/met5 eso deja INVISIBLES la placa superior, el puente de
membrana y las correas de VSS. El verificador reportaba las tres conexiones
del condensador como abiertas mientras el layout las tenia. Varias rondas de
depuracion se fueron persiguiendo eso: el mismo error que ya nos costo un dia
con magic y su techfile, medir con una herramienta que busca el condensador
donde ya no esta.
Ahora la pila llega a met5 y la via del MIM se DETECTA en vez de fijarse:
se parte por FuseTop la capa de via que de verdad lo solapa, y se suelda con
el metal de arriba que le corresponda. Partir la que no es deja las placas
soldadas y las conexiones reales fuera del grafo.
El segundo era mio. Habia metido un margen vertical para que la pista de
membrana guardase MIM.1 a la placa inferior, y eso la empujo de 8.4 a 9.7 --
justo encima del anillo de met2 de M5, que es su tap de bulk, o sea VSS. La
pila de la esquina lo tocaba. El margen sobraba: la pista corre por la capa
de la placa SUPERIOR y MIM.1 se mide contra la INFERIOR, que es otra capa.
Quien tiene que apartarse es la pila, y por eso va donde no hay cap debajo.
Estado: VSS y las placas de arriba y abajo conectan. Queda UN corto, entre
membrana y salida, y esta medido: la pila de la esquina no cabe en el canal
entre el ultimo inversor y el banco.
columna met3 del inversor, borde 15.64
+ separacion met3 + medio pad x >= 16.19
cap met4 empieza 17.11
- MIM.1 - medio pad x <= 15.66
No hay x valido: faltan 0.53 um de canal. Toca en floorplan.py, que ya
dimensiona cada hueco por las redes que lo cruzan -- hay que decirle que ese
ademas aloja una pila con margen de MIM.1.
DRC 172, de las que ~90 son de mimcap() de glayout.
Las seis redes pasan. La migracion a opcion B funciona. Dos restos de un estado anterior del codigo, y los dos hacian daño: La esquina del camino de membrana llevaba un via_stack de met2 a met5. Venia de cuando el horizontal era met2 y el vertical met3, y necesitaba una. Con los dos tramos en la capa de la placa la esquina es un doblez y nada mas; la pila solo servia para atravesar met1..met4 y tocar la columna del ultimo inversor. De ahi el corto entre membrana y salida. Y la bajada se habia ido al lado del banco para que esa pila no cayera sobre el FuseTop -- el deck de LVS descarta de la conectividad las vias que lo solapan. Sin pila esa razon desaparece: la bajada vuelve a caer sobre el primer condensador y se funde con la placa, que es lo que hacia opcion A cuando la placa era met3. Ni una via en todo el tramo del cap. Cuesta 9 violaciones mas que rodear por el lado (180 contra 171), todas MIMTM.1 y MIMTM.3 sobre los condensadores: nuestro met5 se funde con la placa -- que es justo lo que se busca -- y la placa superior efectiva crece mas alla del FuseTop, asi que el met4 deja de envolverla los 0.6 um. Se prefiere el camino directo porque es mas corto y porque ~90 de las violaciones ya son de mimcap() de glayout: hasta que esa geometria se arregle no sabemos si esas reglas mediran lo mismo. Caja 40.73 x 24.14 contra 44.53 x 25.64 en opcion A: la celda encoge un 9% de ancho porque el MIM deja de competir por met2/met3.
El deck del PDK asume 5LM si no se le dice, pero la copia que trae glayout
asume 6LM. Con 6LM el deck cree que la cima de la pila es metaltop, asi que
`topmin1_via` pasa a ser via4 y `topmin1_metal` met5 -- que es justo el
sandwich del MIM en opcion B. MIMTM.10 entonces acusa a las vias del propio
condensador de ser vias prohibidas dentro de si mismo.
antes 180 MIMTM.1: 11 MIMTM.3: 21 MIMTM.10: 148
ahora 3 MIMTM.1: 3
Y el mimcap de glayout, que yo habia dado por roto, da CERO en aislamiento.
Las 30 que le atribui eran del mismo error. Estuve a punto de reportarle
aguas arriba un fallo que no existe.
Es la tercera vez en esta migracion que el instrumento estaba configurado
para otro proceso: el deck de LVS con mim_option, netcheck con la pila hasta
met4, y ahora el DRC con metal_level. Las tres se ven igual -- el layout
parece roto y lo que esta mal es lo que mide. Por eso los tres se derivan
ahora del PDK en vez de escribirse.
Quedan 3 violaciones, una por condensador, y estan medidas: la pila de la
correa de VSS crea un pad de met4 al subir de met3 a met5, y queda a 0.85 um
de la placa inferior cuando MIMTM.1 pide 1.2. Faltan 0.35 um entre el banco
y el riel.
Estado: caja 40.73 x 24.14, las seis redes OK, DRC 3.
Migracion a opcion B terminada: DRC 0 y las seis redes.
Rails.minimum calculaba la separacion con la regla de la capa del riel, y
eso basta mientras lo unico que se le acerque sea metal de esa capa. No basta
cuando algo aterriza en el riel viniendo de arriba: la pila deja un pad en
cada capa que atraviesa, y uno de esos puede caer bajo una regla mas dura.
Aqui la correa de la placa inferior baja desde met5 y su pila deja un pad de
met4 justo bajo el banco. Contra la placa, que tambien es met4, manda MIM.1
con 1.2 um -- cuatro veces la separacion de met3, que era la que se estaba
usando. De ahi una violacion por condensador.
`clearance` es ahora un argumento de Rails.minimum, y build.py lo eleva a
MIM.1 cuando hay caps y su placa inferior no comparte capa con el riel.
caja 40.73 x 25.95 (opcion A: 44.53 x 25.64)
area 1057 um2 contra 1141: 7% menos
DRC 0
redes las seis
La holgura cuesta 1.8 um de alto. Sale a cuenta igual, y la celda sigue
siendo mas pequeña que en opcion A pese a pagarla.
…tomatica
check.py pasaba rail_layer=RIEL, que sin la variable de entorno vale None y
pide la eleccion automatica. Esa mira la capa mas alta de la fila y sube un
piso: con el MIM en met4/met5 la mas alta es la placa superior y no queda
piso encima, asi que levanta ValueError y el barrido entero no arranca.
La regla es conservadora, no incorrecta -- los rieles corren por los extremos
y no cruzan el banco en ningun punto -- pero no lo sabe. Sin la variable no
se pasa rail_layer y vale el defecto del paquete, que es met3.
Barrido completo en opcion B, 14 casos:
7 juegos de dimensiones a mano DRC 0 en todos
200 kHz 1 M5.2a
300 kHz 0
800 kHz 0
2000 kHz 0
umbral 2.0V 48 MIMTM.4
Iex 5 nA 0
carga 800fF 0
Topologia correcta en LOS CATORCE: las seis redes salen en todo el rango.
Las 48 de MIMTM.4 no son nuestras. El mimcap solo, sin nada alrededor, da 24
al tamaño que pide ese caso (5.047 um) y CERO a 5.0, 5.1, 5.5, 6.0, 6.5 y
7.0. La regla pide que el FuseTop solape la via 0.4 um, y #106 monta la
matriz con minus1=False -- "maximum via coverage" -- asi que la fila del
borde queda pegada al canto cuando la division del tamaño cae de cierta
forma. Antes de #106 se usaba minus1=True, que dejaba ese margen. Va aguas
arriba.
Queda una M5.2a, separacion de met5, en el caso de 200 kHz. Esa si es
nuestra.
Rails.above elegia la capa subiendo un piso sobre el bloque mas alto de la fila. Con el MIM en met4/met5 el mas alto es la placa superior del mimcap, met5, y no queda piso: levantaba ValueError y la eleccion automatica quedaba inservible en opcion B. La regla no puede aplicarse a un bloque que ya esta en la cima -- no hay sitio al que subir -- asi que ese bloque no participa. Solo levanta si NINGUNO deja capa libre. Excluirlo traslada una responsabilidad al llamante: los rieles no pueden cruzar ese bloque. En la celda LIF se cumple, los rieles corren por los extremos y el banco esta dentro, y queda escrito en el docstring para que quien reutilice esto lo sepa. Y `clearance` llega ahora tambien por ese camino, via plan_bands. Sin eso el automatico daba las 3 violaciones de MIMTM.1 que el forzado ya no tiene: la holgura del riel se calculaba bien en una rama y no en la otra. Los dos caminos coinciden: 40.73 x 25.95, riel met3, DRC 0, las seis redes. Y coinciden porque la regla lo deduce, no porque el defecto este escrito dos veces.
Con Cm grande el banco crece mas que la banda de nfet. El caso de 200 kHz pide 511 fF, la placa superior sube hasta 10.52 y la pista de membrana caia a 10.65: 0.13 um cuando M5.2a pide 0.28. Son la misma red -- las dos son la membrana -- asi que no habia corto. Pero la separacion se mide igual entre poligonos del mismo nodo, y el suelo de la ventana de pistas salia solo de los anillos de los nfet. Ahora sale del mas alto de los dos. Es el mismo patron que ya aparecio en la holgura del riel y en la eleccion de capa: una parte del floorplan que no sabe hasta donde llega el condensador. Con el MIM en met2/met3 no importaba porque nunca era el bloque mas alto; con el en met4/met5 lo es a menudo. Barrido completo, 14 casos: 13 con DRC 0 y topologia correcta en los 14. La que falla es umbral 2.0V con 48 MIMTM.4, y esas son de mimcap() -- la primitiva sola da 24 al tamaño que pide ese caso.
El equipo sigue lo que dicta glayout, y glayout esta en opcion B desde el PR 106. Con el MIM en met4/met5 la placa inferior ocupa met4 y la superior met5, asi que tres comentarios que justificaban decisiones "para dejar met4 libre al ruteo entre neuronas" describian un plano de capas que ya no existe. met3 sigue siendo la capa de los rieles, pero por otro motivo: es la mas alta que los transistores dejan libre, no un hueco que reservamos. Fuera de la huella del banco met4 y met5 siguen disponibles para el ruteo entre celdas -- el banco solo las ocupa donde esta. Y el comentario sobre Rails.above ya no aplica: decia que la eleccion automatica sube un piso sobre el mimcap y por eso habia que forzar met3. Ahora el mimcap esta en la cima y no participa en esa regla, asi que la eleccion automatica llega a met3 sola.
mim.py llevaba PAR_METALES = "m2m3" fijo. Con la celda construida en met4/met5 eso emitiria cap_mim_2f0_m2m3_noshield: el VALOR seria correcto --los coeficientes son identicos en las cuatro parejas, c_cox 1.99e-3 y c_capsw 2.383e-10, porque el sandwich es el mismo nitruro y solo cambia a que altura se inserta-- pero el netlist declararia un dispositivo de otro apilamiento, y el LVS lo compararia contra una extraccion que no coincide. Ahora par_metales() y drc_option() lo derivan de donde el PDK ponga capmet. Con opcion B sale cap_mim_2f0_m4m5_noshield, que existe en el PDK. Es la quinta capa escrita a mano que aparece en esta migracion, despues del mim_option del deck de LVS, la pila de netcheck, el metal_level del DRC y la holgura del riel. Todas se veian igual: algo parecia roto y lo que estaba mal era lo que lo describia. Y confirma que la migracion fue de layout y no de caracterizacion: Cm no se recalcula, asi que las cinco leyes y C_in siguen en pie sin tocar nada. Celda verificada tras el cambio: DRC 0 y las seis redes.
Era el hueco declarado del flujo desde hace semanas: el motor generaba layout pero no esquematico, asi que no habia contra que comparar y el LVS nunca se habia ejecutado sobre nuestra celda. netlist.de_diseño() lo emite desde la misma NeuronDesign que produce el layout. Eso cambia lo que significa un fallo de LVS: en un flujo a mano es un DESCUBRIMIENTO -- dos artefactos independientes que pueden divergir -- y aqui es una COMPROBACION, porque las dos salidas vienen de la misma descripcion. "Alguien tecleo distinto en dos sitios" deja de ser posible. Y el LVS, la primera vez que corrio, encontro algo que netcheck no puede ver: la marca de pin de Iin estaba en met3 escrita a mano. Con el MIM en met4/met5 la placa superior es met5, asi que la marca caia sobre una capa vacia; el extractor no encuentra conductor bajo el texto y la red sale sin nombre. netcheck camina el metal y no mira etiquetas, asi que daba las seis redes por buenas. Sexta capa escrita a mano de esta migracion. Estado: el LVS ejecuta y falla con "extra top-level pin(s): Iin|spike_neg", o sea que klayout ve las dos etiquetas sobre la MISMA red. netcheck dice que estan separadas, y las etiquetas caen en capas distintas a 21 um una de otra. Los dos instrumentos discrepan y hay que averiguar cual miente antes de tocar el layout -- hoy ya me ha pasado cuatro veces que el roto fuera el medidor.
LVS was failing on three counts, all of them the netlist describing something slightly different from the GDS: - the MIM device class. The deck extracts cap_mim_2f0fF, without the metal-pair suffix that the simulation model carries. modelo_lvs() now gives that name and mim.modelo() documents that it is not it. - the drawn dimensions. The GDS is written on a 0.005 um grid and both the MIM plate and a FET channel are centred, so a 5.864 um plate comes out 5.87 and a 1.671 um M5 comes out 1.68. en_rejilla() rounds each dimension up to that grid before building, and handles['dims'] carries what was drawn so the netlist stops declaring what was asked for. - the capacitor parameters. The gf180 reader translates W*L*M into area and (W+L)*M*2 into perimeter; c_width/c_length are ignored in silence and the capacitor enters with zero area. Netlists match.
157 commits desde el 30 de julio. Sin conflictos: el equipo no ha tocado designs/scripts/lif_design, que es todo lo nuestro. Entra el submodulo designs/libs/gLayout, fijado por el equipo en 4662db4. Ese pin es upstream main pelado y NO lleva el arreglo de MIMTM.4 en mimcap ni la opcion de MIM del runner de LVS, asi que la celda no pasa DRC ni LVS construida contra el. Se deja donde el equipo lo puso; moverlo es una decision que hay que hablar con ellos.
La regla se escribio cuando designs/libs/gLayout era un clon manual para probar. Desde que el equipo lo fijo como submodulo esa ruta si forma parte del proyecto, y la regla dice lo contrario.
Todo lo escrito describia la opcion A, que es donde estabamos hasta la migracion. Cuatro pasajes decian lo contrario de la realidad: - el DRC pasaba `mim_option=A` a mano; ahora `check` lo deriva del PDK, y con el sale B: met4 / FuseTop / met5, contacto por via4. De paso se cuenta la segunda trampa del deck, el METAL_LEVEL a 6LM que hace que MIMTM.10 acuse al condensador de sus propias vias. - netcheck modelaba via2 sobre FuseTop; es via4. - "met4 queda entero libre para el ruteo" -- la placa inferior vive ahi. - "esta celda no pasa LVS, no hay netlist de referencia" -- lo hay, y pasa. Entra una seccion de LVS con los dos hallazgos que costaron: los dos nombres del condensador (cap_mim_2f0fF al extraer, cap_mim_2f0_m4m5_noshield al simular) y el ajuste a rejilla, que hacia que lo declarado no fuese lo dibujado. En "lo que falta", primero la pregunta que no tiene respuesta escrita en ningun sitio: quien fija la opcion de MIM para la oblea. Las dos dibujan la misma capa FuseTop, asi que el proceso solo puede tener una, y a nosotros B nos llego por el defecto de mimcap() desde el PR #106 de glayout, no porque nadie lo decidiera. Salidas regeneradas: 14/14 casos con DRC 0.
…pio script La rama por defecto seguia siendo capimagics-base, anterior a la migracion. El mimcap de esa rama no crea los puertos de la extension, asi que `_rails_bands` pide `bot_via_S_top_met_S` y el motor muere con un KeyError en cuanto se construye una celda. Y como el script hace `reset --hard`, deshacia cualquier checkout manual y devolvia el fallo. Dos cosas mas que impedian que corriese tal cual: - La comprobacion de arranque exigia `capmet == "fusetop"`. Desde el PR #106 de glayout el valor correcto es CAP_MK -- es el marcador -- y el dielectrico lo define el FuseTop que dibuja el propio mimcap. La asercion abortaba por una condicion que ya no aplica. Ahora se comprueba el resultado: se construye un MIM y se mira que la capa (75,0) este dibujada, que vale para las dos opciones. - `git fetch origin <rama>` no creaba `origin/<rama>`: el clon es --depth 1 de una sola rama y su refspec no cubre las demas, asi que el commit se quedaba en FETCH_HEAD y el reset moria con "ambiguous argument". Con el destino escrito a mano la referencia existe siempre. Afectaba a cualquier rama distinta de la clonada, tambien via GLAYOUT_RAMA. Corrido de punta a punta: las dos comprobaciones pasan y la celda se construye, 40.73 x 25.95 um.
Un macro se integra por su LEF, que declara un origen. Si la geometria
vive en (0, -2.21) y el LEF dice (0, 0), la herramienta coloca el bloque
creyendo una cosa y el metal aparece desplazado: pistas que no conectan y
violaciones en el borde, y no al colocar sino despues de rutear.
glayout compone centrado en el origen, que es lo natural para disenar. La
convencion cambia cuando la celda deja de ser un diseno suelto y pasa a
ser un bloque que LibreLane coloca, y el equipo la acaba de fijar para
todas las celdas del D14_topcell.
Va despues de los rieles y antes de los puertos y las etiquetas. Las
referencias arrastran sus puertos al moverse, pero `rails_y` lleva
coordenadas absolutas en float que no se enteran, asi que se corrigen a
mano: sin eso las sondas de netcheck pinchan 2.21um por debajo de los
rieles y reportan redes rotas que no lo estan.
antes (0.000, -2.210) -> (40.735, 23.740)
ahora (0.000, 0.000) -> (40.735, 25.950)
Verificado: los 14 casos de la suite con DRC 0 y topologia ok, y el LVS
sigue en Netlists match.
Las dos vistas que le faltaban a la celda para entrar en LibreLane como un bloque, siguiendo la convencion que el equipo acaba de fijar para todas las celdas del D14_topcell. El contorno va en la capa (0,0), que no se fabrica: es la marca que la herramienta lee para saber donde acaba el bloque, porque no lo deduce mirando el metal. Se dibuja despues de llevar la celda al origen, asi que arranca en (0,0) y coincide al micrometro con lo que declarara el LEF -- al reves quedaria desplazado respecto al contenido. El .lib es un stub: sin tiempos ni potencia, solo la declaracion de los cinco pines con su direccion. Los nombres y las direcciones salen de la misma fuente que el netlist de referencia, para que un pin no pueda llamarse de una forma en el LVS y de otra en la integracion. Lleva ademas `pg_type : primary_power` y `primary_ground` en Vdd y Vss, sin lo cual la herramienta las trata como senales en vez de como red de potencia. Verificado: los 14 casos de la suite siguen con DRC 0 -- la capa (0,0) no dispara ninguna regla -- y el contorno mide 0,0 -> 40.735,25.950, igual que la celda. PENDIENTE de hablar con el equipo: el anillo de PDN, que ellos han puesto en neurona, STDP y current_mirror y aqui son rieles horizontales, y si sus nombres avdd/avss tienen que sustituir a Vdd/Vss para que el top_module.v conecte.
La celda que construye `lif_demo` heredaba `d`, que para ese punto ya habia
pasado por siete reasignaciones: la ultima, del barrido de c_in, la dejaba
en 1500 kHz con tope de capacidad de entrada. Asi que el dibujo era esa
celda -- un solo MIM, 982 um2 -- mientras el texto describe el ejemplo
canonico de 500 kHz, que lleva tres MIM y mide 1057.
No fallaba: dibujaba una celda correcta que no era la que decia el titulo,
y cualquier conclusion que se sacara de la imagen -- numero de
condensadores, area, disposicion -- quedaba mal sin que nada lo indicase.
Ahora resuelve su propio diseno en vez de heredarlo.
Imprime tambien la esquina inferior izquierda, para que el (0,0) se vea en
la salida y no haya que fiarse.
Salidas regeneradas con el codigo de hoy, que ya lleva el origen y el
contorno:
caja: 40.73 x 25.95 um = 1057 um2
esquina inferior izquierda: (0.000, 0.000)
bloques: nfets 3, pfets 3, caps 3, m5 1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motor de diseño de la neurona LIF en GF180MCU: se le pide un comportamiento eléctrico y devuelve las dimensiones, el layout y el netlist de referencia, contando qué tuvo que cambiar y por qué.
Qué trae
designs/scripts/lif_design/— el paquete: leyes empíricas, solver, generador de layout con gLayout, y la capa de verificación (DRC, comprobación de redes, netlist de referencia para LVS).designs/libs/tb_analog/tb_lif/— los testbenches de ngspice con los que se ajustaron las leyes.designs/notebooks/3_test_lif_engine.ipynb— recorrido completo del motor, con los casos de robustez.Las seis leyes salen de barridos de ngspice (
.tran 1n), no de fórmulas: frecuencia 2.03% RMS, ganancia 2.18%, umbral 1.32%, excursión 1.68%, yC_in = 0.945 + 0.865·Wal 0.67%. Validadas fuera de la rejilla de ajuste en 18 puntos: −0.00% de media, 1.23% RMS.Estado
Sobre el diseño de 500 kHz a 100 nA, celda de 40.73 × 25.95 µm:
mim_option=B,metal_level=5LMNetlists matchcontra el netlist que emite el propio motorEl netlist de referencia se emite desde el mismo
NeuronDesignque genera el layout, así que un fallo de LVS solo puede significar un bug del generador. No existe la clase de fallo "alguien tecleó distinto en dos sitios".Qué NO toca
88 ficheros nuevos, 0 borrados, y solo dos
.gitignoremodificados (reglas para artefactos de simulación propios). El pin del submódulodesigns/libs/gLayoutse queda en4662db4, exactamente donde está enmain. Ninguna celda, script ni flujo existente cambia.Requisitos — importante para probarlo
El motor no construye contra el gLayout que el submódulo fija hoy. Le faltan arreglos que están en revisión en upstream. Medido sobre
4662db4:via_array, dispositivos estrechosmimcapcon extensionesPor eso van dos lanzadores:
run_GL.shpara el flujo oficial del equipo yrun_GL_tmp.shapuntando a un gLayout con esos PRs aplicados. Si se prueba con el oficial van a salir 2583 violaciones, y no son de la celda: es el pin.De esos PRs, el #104 lleva aprobado desde el 14 de agosto y el #100 abierto desde el 3. En cuanto entren, el pin del submódulo puede avanzar y esto corre con el flujo normal.
Nota sobre el resto del repo
Aprovecho para dejar dicho, por si es útil: construyendo vuestro
integratory vuestroencodercontra ese mismo pin, el integrator no compila (Component.add: unsupported type Component, lo arregla el #102) y el encoder da 2893 violaciones de rejilla que el #104 elimina. No he tocado nada de eso — solo lo medí al comprobar que lo mío no os rompiera nada.