Introducción: El costo de un crash en el mundo real
Hola estimados lectores de este blog ingenieril, quise hacer un artículo de Toyota y un error grave que cometió hace algunos años en uno de sus autos, pero como el tema es complejo, primero quise hacer este artículo explicando qué es la tolerancia a fallos, el software crítico y cómo los sistemas deben ser seguros y con máxima confiabilidad. Pero primero hagamos un ejercicio de imaginación…
En el software tradicional, un error común (los errores son también llamados bugs) suele terminar en un log de excepciones, un código de estado HTTP 500 o una alerta en tu sistema de monitoreo, es decir que sólo los nerds de tecnología como nosotros nos enteraríamos jaja.
Si el error es un poco más crítico como que falle el Yape (conocida aplicación fintech de Perú) o se cae tu API de pagos como MercadoPago durante unos minutos, es un problema molesto y potencialmente problemático, sí, pero se puede solucionar.
Sin embargo ahora imagina lo siguiente: lo que falla no es una API de pagos sino el código que gestiona el control de estabilidad de un automóvil, es decir pierdes el control de tu auto durante tres minutos… asu que miedo no? tu conduciendo tu auto en la panamericana a 80km/h y de pronto te falla el acelerador o los frenos de la nada por un error en el sistema... ahí sí que ya valiste jajaja. Suena a meme pero es anécdota.
En la ingeniería de sistemas críticos (safety-critical systems), asumimos una premisa fundamental: el hardware fallará, la memoria se corromperá y el entorno físico interferirá.
Por ello, la meta del desarrollo embebido crítico no es solo escribir código "sin bugs", sino diseñar arquitecturas tolerantes a fallos capaces de detectar sus propios errores y volver a un estado seguro antes de que ocurra un accidente.
Advertencia para el lector: este post entra en detalles técnicos profundos. Traté de masticar los conceptos lo más posible, pero si encuentras palabras raras, no te detengas, al contrario mira este artículo como tu punto de partida para tu fabulosa carrera de ingeniería, un ingeniero cuando no entiende algo, lo investiga, igual puedes hacerlo tú, una búsqueda rápida en internet o un prompt a tu IA de confianza te darán el contexto de toda palabra o concepto que no entiendas aquí.
P.D. Por cierto, si tu nombre es Esther, este párrafo va para ti mana: Sí, sigo metiéndote presión sutil para que le agarres el gusto a la tecnología🤣. Es bromita (pero si quieres no es broma).
Ahora sí cracks, entremos en detalles.
1. El enemigo invisible: Interferencias y Bit Flips
A diferencia de un servidor en un centro de datos con temperatura controlada, un microcontrolador automotriz o industrial opera en un entorno hostil: vibración, cambios drásticos de temperatura e interferencias electromagnéticas (EMI).
Un pulso electromagnético generado por el motor de arranque o el encendido de un motor puede inducir pequeñas variaciones de voltaje en las líneas de la memoria RAM. Esto provoca lo que en la industria se conoce como un SEU (Single Event Upset) o simplemente un Bit Flip: un 0 se convierte en 1 (o viceversa) en la memoria física del chip.
Memoria RAM Normal: 0 0 0 0 0 0 0 0 (Valor: 0 - Acelerador Cerrado)
Bit Flip por radiación/EMI: 0 0 0 0 0 0 0 1 (Valor: 1 - Acelerador Abierto)
Si ese bit alterado pertenecía a una variable global que indicaba si el pedal de aceleración estaba presionado, el software actuará en consecuencia, creyendo legítimamente que el usuario pisó el pedal.
¿Cómo nos defendemos?
- Memoria ECC (Error-Correcting Code): El hardware incluye bits adicionales de paridad para detectar y corregir automáticamente alteraciones en la RAM.
- Redundancia por Software: Guardar variables críticas por duplicado o triplicado (o almacenar su complemento XOR) y verificar que coincidan antes de tomar una decisión física.
2. El perro guardián: Watchdog Timers (WDT)
Un Watchdog Timer es un temporizador por hardware, independiente del procesador principal, cuyo único trabajo es contar hacia atrás desde un valor determinado (por ejemplo, 100 milisegundos).
Si el contador llega a cero, el hardware del Watchdog fuerza un reset eléctrico del microcontrolador para reiniciar el sistema.
Para evitar que el sistema se reinicie, el software debe "alimentar al perro" (kick the watchdog) continuamente, reiniciando el contador antes de que expire.
El peligro de los Watchdogs ingenuos
Un error de diseño muy común es colocar la instrucción de "alimentar al watchdog" dentro de una interrupción periódica por timer o en una tarea secundaria con prioridad baja.
Si la tarea principal (por ejemplo, la que calcula la trayectoria o lee los sensores) se congela o muere por un desbordamiento de memoria, la interrupción del timer puede seguir ejecutándose alegremente y continuar alimentando al watchdog.
El procesador seguirá vivo, el watchdog creerá que todo está bien, pero el sistema estará "zombie" e incapaz de responder. Un verdadero Task-Level Watchdog exige que todas las tareas críticas confirmen que están vivas y saludables antes de resetear el temporizador de hardware.
3. Fail-Safe y Fail-Operational: La estrategia ante la falla
Cuando el sistema detecta que una variable está corrupta o que un sensor entregó un valor físicamente imposible, entra en juego el concepto de Fail-Safe (Fallo Seguro).
- Fail-Safe: Si ocurre un fallo indebidamente grave, el sistema entra en un estado predecible que minimiza el daño, aunque pierda funcionalidad.
- Ejemplo: Si el software de una caldera detecta una lectura errónea de temperatura, corta el paso de gas por completo y enciende un LED de error.
- Fail-Operational: Se utiliza en aviación o conducción autónoma donde "apagar todo" no es una opción segura. Requiere múltiples canales de procesamiento en paralelo (redundancia modular triple) para votar y continuar operando incluso si un microcontrolador completo falla.
¿Qué significa "votar"?
Es una validación por mayoría. Un circuito compara en milisegundos los datos de los tres procesadores, adopta el resultado idéntico (regla del "2 contra 1") y descarta de inmediato el erróneo.
En automoción, el mecanismo Fail-Safe clásico para el acelerador electrónico es desactivar la corriente del motor del acelerador para que un resorte mecánico fuerce la mariposa a cerrarse por defecto.
4. Estándares de Codificación: El caso de MISRA C
En el desarrollo de software convencional confiamos en linters o guías de estilo para mantener la legibilidad. En la industria automotriz y aeroespacial, los estándares de código existen para prevenir comportamientos indefinidos (Undefined Behavior) en C.
El estándar más famoso es MISRA C (creado por la Motor Industry Software Reliability Association).
MISRA C no es una recomendación estética; es un subconjunto restringido del lenguaje C que prohíbe expresamente características peligrosas:
- Sin asignación dinámica de memoria (
malloc/freeprohibidos): Evita la fragmentación de la RAM y la posibilidad de que un puntero nulo cuelgue el sistema. - Sin recursión: Garantiza que el tamaño máximo del stack se pueda calcular de forma exacta antes de compilar.
- Restricción severa de variables globales: Obliga a encapsular el estado para evitar lecturas/escrituras concurrentes no coordinadas.
- Verificación de valores de retorno: No se permite ignorar el resultado de ninguna función o llamada a registros de hardware.
Conclusión: El lienzo perfecto para el caso Toyota
Dominar estos conceptos nos permite entender la diferencia entre un código que "funciona en el camino feliz" y un código diseñado para no matar a nadie cuando el mundo real falla.
Cuando revisas la teoría, los principios parecen claros:
- Evitar el desbordamiento de pila.
- No depender ciegamente de variables globales compartidas.
- Implementar Watchdogs que monitoreen la salud real de cada hilo.
- Diseñar un hardware con redundancia que imponga un Fail-Safe mecánico.
Sin embargo, a finales de la década de 2000, uno de los fabricantes de automóviles más grandes e importantes del mundo se enfrentó a un problema masivo de aceleración involuntaria. Cuando los peritos forenses de software abrieron el firmware del sistema ETCS-i de Toyota, descubrieron que prácticamente cada una de estas reglas había sido violada.
En el próximo y último artículo de esta serie, analizaremos en detalle el caso Toyota Throttle-gate: el análisis de código fuente que cambió para siempre la historia del software embebido.
Si esta entrada te ha gustado crack, ya sabes compártela!
