Pon un Lauterbach en tu vida (O una historia de éxito)

Os voy a contar una historia que me ha estado pasando a lo largo de estos últimos meses, vacaciones excluidas, que creo debe ser pública, porque eso de criticar está bien, pero alabar es mejor. Y no, no es una entrada patrocinada, ni recomendada por nadie. Es, simplemente, un reflejo de cómo deben ser las empresas pero que la mayoría de veces no son. Vamos allá.

Qué es un JTAG

Todos sabéis que una parte de mi trabajo consiste en desarrollar con placas embebidas, generalmente sin sistema operativo como tal, ni siquiera un RTOS, que significa Sistema Operativo de Tiempo Real, más que nada porque si le metemos algo de eso a los procesadores que suelo usar, pues no queda sitio para nada más, o casi. A veces, como el caso que nos ocupa, suelo utilizar una pequeña capa de abstracción respecto al hardware, que es lo primero que suelo hacer si el fabricante no me la da.

¿Cómo se depura este tipo de placas? Normalmente los fabricantes de los procesadores suelen habilitar un sistema especial, dentro de sus chips, para conectar externamente un aparato y poder así controlar el chip de forma remota.

Es decir, hay algunas patas de los micros, muchas veces compartidas con otros propósitos, destinadas a dicha conexión. Es tarea de quien diseñe una placa base dejar un conector para dicho puerto, conector que en las versiones finales de las placas se suele eliminar, así como los componentes necesarios para que funcione, aunque lo más común es que simplemente no se monten y queden las pistas (para alegría de muchos aficionados, que vuelven a soldar lo necesario y pueden meterle mano a su cacharrín).

A ese conector se enchufa un aparato que a su vez va conectado al PC y que nos permite controlar el chip desde un software en nuestro ordenador. Las conexiones con el PC suelen ser de lo más variopintas, desde un puerto serie hasta un Ethernet pasando por un paralelo o un USB, aunque lo más común es este último tipo de conexión.

Al aparato se lo suele conocer como JTAG, aunque las siglas comprendan todo el estándar y el protocolo… Como habréis supuesto, y si no os lo digo yo, cada fabricante de procesadores usa su propia variante, e incluso varían entre una misma familia. Es decir, a veces no es lo mismo un JTAG para un cf5206 que para un m68340 (que por cierto para estas familias se llama BDM), que son dos procesadores de Motorola, ahora conocido como Freescale. Y, evidentemente, necesitamos un aparato diferente para cada tipo y familia. También hay varios fabricantes de JTAG, entre los que destaca Lauterbach y P&E Micro.

El software en el PC nos puede permitir hacer muchas cosas, algunas de ellas opcionales respecto a lo que estemos dispuestos a pagar y a las limitaciones del propio sistema, que comprenden al aparato, al microprocesador y al propio software. Lo más común es poder cargar nuestro programa a través de él, poder ejecutar paso a paso y ver variables, aunque no nos confundamos: yo tengo un sistema que sólo me permite cargar a velocidad tortuga y ejecutar (frente a tener que estar sacando la Compact Flash de la placa, meterla en el PC y grabar el programa en ella, volver a meterla en la placa y encenderla a ver qué pasa).

Existen soluciones completas, como las de Atmel o Microchip, en las que en un mismo entorno es posible hacerlo todo: editar el código fuente, compilar, grabar en remoto y depurar. En general estos sistemas tan completos suelen pecar de ser bastante limitados en los aspectos de editar y compilar, así que muchas veces es mejor hacerlo desde fuera y usarlos solo para la carga y el depurado. En mi caso suelo usar un Visual Studio para estos procesos. Me creo un proyecto de línea de comandos vacío y lo excluyo de la compilación. No es que pase nada si lo mandas compilar, pero te va a generar una buena espuerta de errores. Allí añado todos los ficheros que necesito y, gracias al Visual Assist X, tengo IntelliSense y ayuda sensible al contexto. Luego uso las otras herramientas para depurar.

Lauterbach

En estos momentos estoy usando unas placas basadas en los ColdFire de Freescale, en concreto una con el 5206e y otra con el 5475. Ambas leen de Compact Flash e incluso tienen puerto Ethernet e IDE, y cuentan con hasta 128 MB de memoria principal y la misma para vídeo, toda una gozada para este tipo de desarrollos (en el proyecto en el que estoy, un juego completo, he usado unos 2MB de memoria de vídeo y apenas una miseria de la principal).

Para estos micros hay al menos dos fabricantes de JTAG (o BDM), pero sólo voy a hablar de Lauterbach. Los JTAG de este fabricante son modulares y funcionan para una increíble gama de procesadores, incluidos los x86 y pasando por prácticamente cualquier procesador moderno, incluyendo los multicore y placas multiprocesador.

Existe un módulo base que es el núcleo del sistema, cuya tarea es la de hacer de interfaz entre el PC y el otro lado. Este módulo vale para depurar procesadores de 1 core, pero si necesitas más, existen los módulos adecuados para ampliar el número de núcleos.

Luego está lo que yo llamo “la punta”, que es única para cada familia e incluso a veces procesador. En mi caso la misma punta vale para las dos placas descritas, y en general es válida para cualquier otro ColdFire, pero si en algún momento necesitara usar otro procesador, únicamente tendría que cambiar dicho módulo. (No nos engañemos, no es una cuestión de ahorrar pasta, un módulo de Lauterbach puede valer más que un debugger completo de otro fabricante, pero os aseguro que vale la pena).

Aparte existe la opción de traza, que nos posibilita añadir un analizador lógico al módulo y poder ver cómo reacciona el programa en conjunción con el hardware, viendo cómo cambian las señales a la vez que se ejecuta nuestro programa.

El software que acompaña al producto es, hablando en plata, grandioso. El tiempo de carga del programa sobre el procesador es rapidísimo, casi instantáneo (hablamos de ejecutables de más de 300K, que no es poco para un firmware). Lo que en otros programas suele tardar veinte segundos en este tarda menos de uno (y es que sólo carga lo que ha variado). Entiende todos los formatos de ejecutable y de depuración habidos y por haber, así como sus variantes. Por mor enumerativo, nombraremos COFF, ELF, DWARF, DWARF2, HEX (que tiene una espuerta de versiones), BIN…

La depuración es un placer. Aparte de lo típico como es ir hasta el cursor, ejecutar un paso, un paso sin entrar, ir hasta el final de la función, etc., cuenta con otras maravillas, como una ventana que te muestra las variables que tienen relación con el bloque de código que estás depurando. Sí, ya sé, eso lo hace Visual Studio en un PC, pero es que no es lo mismo: creo que es único fabricante de JTAG que tiene una opción así. Depurar firmware NO es depurar un programa de PC.

Puedes poner puntos de interrupción en variables (del tipo que sean, locales o globales), que saltarán cuando el valor se lea, o se escriba, a nuestra elección (y no os podéis asegurar el ahorra tiempo que significa poder hacer eso). Por supuesto los breakpoints pueden ser condicionales, con condiciones todo lo complejas que queramos, incluso relacionadas con el estado de otros elementos o registros internos o trazas si tenemos la opción.

Pero lo más valioso de todo es que cuenta con lenguaje de script propio tan potente, que el mismo programa está construido con él. Es decir, el programa no es más que el motor del script que ejecuta una serie de scripts que hacen que funcione como lo hace. Imaginaros las posibilidades. Aparte de eso tiene una línea de comandos siempre activa, para poder ir tecleando lo que queramos en tiempo real (aparte de poder ejecutar scripts, claro).

Eso sí, el sistema vale casi 7 cifras de las antiguas pesetas, que no es poco. Pero os aseguro que lo vale.

La primera historia

Después del rollo macabeo introductorio os cuento el motivo de esta entrada en el blog. Imaginaros que se compra el producto, se prueba en una placa y funciona de cojones, pero en la otra no funciona del todo bien. O bien se corta la comunicación con el procesador, o bien un paso se convierte en una ejecución directa o bien, simplemente, el TRACE32 (así se llama el programa) te devuelve un error críptico y no hace nada…

Puestos al habla con el distribuidor en España a quien se lo compramos, Captura Electrónica, o CAPEL, toda la ayuda que nos suministran se ve insuficiente para solventar el problema… Básicamente, el producto no funciona en un 5475. Y punto.

Pasan unos días sin que en CAPEL den señales de vida, yo ya me veía usándolo sólo para cargar y lanzar remotamente (o incluso devolverlo), cuando de repente me llaman por teléfono, para preguntarme si tenía tiempo para hacer unas pruebas de forma remota con mi Lauterbach. Yo, sorprendido a más no poder, y seguro que me lo notaron, porque el chaval como que tampoco las tenía consigo con mis respuestas…

Pues bien, se activa el escritorio remoto con un programa similar al Team Viewer (ahora no recuerdo el nombre), les monto todo el sistema y fue cosa de un par de horas. Al otro lado estaba el programador principal del TRACE32. Tocó aquí, tocó allí, cambió parámetros, scripts, activó modos ocultos y no documentados, revisó logs… Al final se retiró… ¡Y al rato me envió una versión casi plenamente funcional!

¡¡Toma Moreno!! ¡¡Chúpate esa!! Eso es estilo, y lo demás son tonterías. Es decir, el programador principal, desde Alemania, se había conectado a mi ordenador, había visto cuál era el problema en su software, y lo había arreglado en un tris. Bueno, en un tris no, porque a lo largo de la semana siguiente fueron enviándome versiones más y más funcionales hasta la que al final fue bien del todo.

(Resulta triste tener que contar el buen hacer de una empresa como si fuera algo extraordinario, en oposición a lo habitual, que es ir acumulando miles y miles de bugs sin solucionar, de dejar de lado al usuario sacando versión tras versión, etc.)

La segunda historia

Bueno, ya en posesión del Lauterbach, y tras haberme familiarizado con él, llegué a un punto del proyecto en el que tenía un problema bastante serio con unos gráficos: no se pintaban bien en la pantalla (hablamos de una placa embebida, no un PC). Tras volverme poco menos que loco, descubro el problema: la función que devuelve el tamaño, en bytes, de la imagen cargada de disco, está mal. Es decir, funciona bien casi siempre, pero cuando un bitmap pasa de cierto tamaño, devuelve un valor incorrecto, siempre menor al real.

Cuando uno carga gráficos desde disco, los va apilando en la memoria de vídeo libre, uno detrás de otro sin más acción que anotarse su dirección base en la memoria gráfica, su altura y su anchura (recordemos que trabajamos con hardware puro). Es decir, tras cargar los gráficos, por un lado tenemos un array de estructuras, situado en la memoria principal, que contiene la dirección base y el tamaño de cada mapa de bits cargado, que se almacena en la memoria de vídeo de forma secuencial y sin separación. Luego será el motor gráfico el que irá copiando dichos bitmaps en las áreas de la memoria de vídeo que representan la pantalla.

Pues bien, como la función estaba devolviendo el valor de tamaño incorrecto y menor al real, el siguiente bitmap sobreescribía parte del anterior, y al copiarlo a pantalla copiábamos la parte inicial del mismo y parte del siguiente o siguientes…

Todo el tema estaba en que la función que devolvía el tamaño del bitmap retornaba un entero sin signo de 16 bits, limitando el tamaño a un valor no superior a 65535 bytes. Dado que había bitmaps de mayor tamaño, aunque no muchos, eran estos los que fallaban…

El problema para solucionar esto está en que la función pertenece a la biblioteca que acompaña a la placa, de la que ni tengo el código fuente ni el fabricante suele hacerte mucho caso (lo opuesto a CAPEL, vamos). Y en caso de reconocer el bug (esa es otra, no sabéis lo cabezones que son), tendrían que pasar, como poco, semanas hasta que lo corrigieran. Y nada de ver el código fuente para imaginarme cómo solucionarlo temporalmente…

El primer acercamiento fue multiplicar la anchura por la altura por el número de bytes de cada pixel, pero dado que deben existir alineaciones y otras zarandajas, el tamaño obtenido así es aproximado, por lo que tampoco nos vale.

Y es aquí donde entra de lleno el Lauterbach. Punto de interrupción en la llamada a la función que obtiene el tamaño. Step Into. El Lauterbach tiene la insidiosa manía de agrupar las instrucciones máquina como si fueran en grupos… semejando al código fuente inexistente, quizás deducido por alguna información de depuración o por conocimiento del compilador… El tema es que estaba clarísimo: la función devolvía el contenido de un desplazamiento fijo sobre una dirección de memoria almacenada en un registro del procesador…

Me voy a dicho registro, con el botón derecho le doy a inspeccionar la dirección de memoria y… ¡tachaaaaaan! Me muestra una variable global en código fuente, una variable que el fabricante dice que debes dejar y no tocar si quieres que todo funcione (bueno, realmente no lo dice, porque la documentación brilla por su ausencia, pero si la tocas se desbarajusta todo)…

Así que el valor está precalculado en una estructura tras la carga de la imagen desde disco… ¿Será también de 16 bits? Probemos. Cambiemos el código para que el cálculo del tamaño se haga con el contenido de esa variable más el offset fijo obtenido al desensamblar la función original (¿para qué están los punteros si no?). Lanzamos el test case (que es el propio programa funcionando de forma automática), a riesgo de que se nos cuelgue nada más iniciar, y… ¡¡Funciona sin corrupciones de pantalla!!

Es decir, la variable estaba almacenada como un entero sin signo de 32 bits, pero la función devolvía uno de 16… Capón al canto y ole los cohones del Lauterbach.

Lo dicho, pon uno en tu vida. Vale lo que cuesta.

5 comentarios en “Pon un Lauterbach en tu vida (O una historia de éxito)”

  1. Vaya rollo de trabajo Rafael…. Yo hace unos años estaba también estancado en algo así(era software para cajeros automáticos, de los antiguos sin so) y a todo el mundo que me preguntaba le decía….no, si es que a mo me gusta esto…me divierte y lo prefiero. Realmente me estaba aunque decía eso y para
    muchos el ensamblador era un rollo para no era lo mas cómodo …, yo conocía de estar siempre
    trasteando otros lenguajes y frameworks pero
    no era experto en ninguno. Llego un momento que decidí que yo no era menos que nadie y empece a interesarme en asp.net, ruby,
    Mvc..pero a nivel experto. Solo puedo decir que soy feliz y no me hace falta autoafirnarme delante de los demás..te animo a evolucionar.

  2. Hola. Comentarte que he leído la entrada y aunque programo a veces con cosas de bajo nivel (tripas) nunca he tenido que lidiar con firmware.
    Gracias por acercarnos al mundo del hierro.
    Escribes muy bien y te animo a seguir haciéndolo.
    Saludos.

  3. Púes yo a este hombre lo veo un poco justito.

    Ha escrito un tocho tremendo y así parece que sabe algo pero si te fijas en los detalles y todo lo que le ha llevado sacar un error de tipo mal asignado……Pero bueno, sí a vosotros os vale perfecto.

  4. Es un gusto leer artículos así, aparte de informar (aunque sea una pena que haya que informar de un buen servicio) son muy amenos. Lo repetiré hasta la saciedad, tienes una forma de escribir que hace que cualquier cosa sea entretenida. Enhorabuena 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *