Compilando código para ejecutar desde RAM externa

13

Estoy considerando diseños para un sistema de juego minimalista basado en un PIC18F85J5. Parte de mi el diseño es que los juegos se pueden cargar desde una tarjeta SD sin necesidad de reprogramar el chip o actualizar la memoria del programa. Elegí ese chip porque tiene una interfaz de memoria externa que me permitirá ejecutar código desde una SRAM externa.

La idea básica es que la memoria interna del programa contendrá una interfaz para navegar por la tarjeta sd, y una vez que el usuario seleccione un programa, copiará un archivo hexadecimal de la tarjeta sd al ram externo, y luego saltará la ejecución al espacio externo del ariete.

La memoria interna del programa también tendrá varias bibliotecas para gráficos, entrada de controlador y otras diversas utilidades.

Tengo bastante confianza en que sé cómo hacer que las partes internas del firmware funcionen bien. El problema es crear programas para ejecutar desde la memoria RAM externa. No parece lo mismo que apuntar a una imagen normal, y necesita conocer las funciones de la biblioteca que están disponibles en la memoria interna, pero no las debe recompilar, solo vincularlas. También debe comenzar a usar direcciones justo después de los 32k de flash interno, no en cero. ¿Hay una buena manera de compilar un programa usando estos tipos de restricciones?

Estoy utilizando el IDE de MPLab, pero no estoy muy familiarizado con él o con este tipo de personalización.

    
pregunta captncraig

2 respuestas

8

Tienes dos problemas separados:

  1. Construyendo el código para el rango de direcciones de RAM externa.

    Esto es realmente muy fácil. Todo lo que tiene que hacer es crear un archivo enlazador que contenga solo los rangos de direcciones que desea que ocupe el código. Tenga en cuenta que no solo necesita reservar un rango de direcciones de memoria de programa particular para estas aplicaciones externas, sino también algo de espacio de RAM. Este espacio de RAM, al igual que las direcciones de memoria del programa, debe ser fijo y conocido. Simplemente haga que esos rangos de direcciones fijas y conocidas estén disponibles para su uso en el archivo del vinculador. No olvide hacer que NO estén disponibles en el archivo enlazador de código base.

    Después de que el código base carga una nueva aplicación en la memoria externa, debe saber cómo ejecutarla. Lo más fácil es que la ejecución comience en la primera ubicación de la memoria RAM externa. Esto significa que su código necesitará una sección de CÓDIGO en esa dirección de inicio absoluta. Este contiene un GOTO a la etiqueta correcta en el resto del código, que se podrá reubicar.

  2. Vincular aplicaciones a rutinas de biblioteca en el código base.

    No hay una manera sencilla e inmediata de hacer esto con las herramientas de Microchip existentes, pero tampoco es tan malo.

    Un problema mucho más grande es cómo desea lidiar con los cambios en el código base. La estrategia simplista es construir su código base, ejecutar un programa sobre el archivo de mapa resultante para recopilar direcciones globales, y luego hacer que escriba un archivo de importación con sentencias EQU para todos los símbolos definidos globalmente. Este archivo de importación se incluiría en todo el código de la aplicación. No hay nada que vincular, ya que el código fuente de la aplicación esencialmente contiene las referencias de direcciones fijas a los puntos de entrada del código base.

    Es fácil de hacer y funcionará, pero considera lo que sucede cuando cambias el código base. Incluso una pequeña corrección de errores podría hacer que todas las direcciones se muevan, y entonces todo el código de aplicación existente no sería bueno y habría que reconstruirlo. Si nunca tiene previsto proporcionar actualizaciones de código base sin actualizar todas las aplicaciones, entonces quizás pueda salirse con la suya, pero creo que es una mala idea.

    Una mejor manera es tener un área de interfaz definida en una dirección conocida fija elegida en el código base. Habría un GOTO para cada subrutina al que el código de la aplicación pueda llamar. Estos GOTO se ubicarían en direcciones conocidas fijas, y las aplicaciones externas solo llamarían a esas ubicaciones, que luego saltarían a donde sea que la subrutina terminara en esa compilación del código base. Esto cuesta 2 palabras de memoria de programa por subrutina exportada y dos ciclos adicionales en tiempo de ejecución, pero creo que vale la pena.

    Para hacer esto correctamente, necesita automatizar el proceso de generación de los GOTO y el archivo de exportación resultante que las aplicaciones externas importarán para obtener las direcciones de la subrutina (en realidad el redirector de GOTO). Es posible que pueda hacer algo con un uso inteligente de las macros MPASM, pero si estuviera haciendo esto definitivamente usaría mi preprocesador, ya que puede escribir en un archivo externo en el momento del preprocesamiento. Puede escribir una macro de preprocesador para que cada redirector pueda ser definido por una sola línea de código fuente. La macro hace todas las cosas desagradables bajo el capó, que es generar el GOTO, la referencia externa a la rutina objetivo real, y agregar la línea apropiada al archivo de exportación con la dirección constante conocida de esa rutina, todas con nombres apropiados. Tal vez la macro solo crea un grupo de variables de preprocesador con nombres regulares (algo así como una matriz expandible en tiempo de ejecución), y luego el archivo de exportación se escribe una vez después de todas las llamadas de macro. Una de las muchas cosas que mi preprocesador puede hacer que las macros MPASM no pueden es hacer la manipulación de cadenas para crear nuevos nombres de símbolos a partir de otros nombres.

    Mi preprocesador y un montón de otras cosas relacionadas están disponibles de forma gratuita en www.embedinc.com/pic/dload.htm .

respondido por el Olin Lathrop
5

Opción 1: lenguajes interpretados

Esto no directamente responde la pregunta (que es una pregunta excelente, por cierto, y espero aprender de una respuesta que la aborde directamente), pero es muy común cuando se realizan proyectos que Puede cargar programas externos para escribir los programas externos en un lenguaje interpretado. Si los recursos son limitados (para lo que estarán en este procesador, ¿ha pensado utilizar un procesador PIC32 o ARM pequeño para esto?), Es común restringir el lenguaje a un subconjunto de la especificación completa. Aún más abajo en la cadena hay lenguajes específicos de dominio que solo hacen algunas cosas.

Por ejemplo, el elua project es un ejemplo de lenguaje interpretado de bajo recurso (64 kB RAM). Puede reducir esto a 32k de RAM si elimina algunas características (Nota: no funcionará en su procesador actual, que es una arquitectura de 8 bits. El uso de RAM externa probablemente sea demasiado lento para los gráficos). Proporciona un lenguaje rápido y flexible en el que los nuevos usuarios podrían programar juegos fácilmente si proporciona una API mínima. Hay mucha documentación disponible para el idioma en línea. Hay otros idiomas (como Forth y Basic) que podrías usar de una manera similar, pero creo que Lua es la mejor opción en este momento.

De forma similar, puedes crear tu propio lenguaje específico de dominio. Tendría que proporcionar una API más completa y documentación externa, pero si los juegos fueran todos similares, entonces esto no sería demasiado difícil.

En cualquier caso, el PIC18 probablemente no sea el procesador que usaría para algo que involucra programación / scripting y gráficos personalizados. Puede estar familiarizado con esta clase de procesadores, pero sugeriría que este sería un buen momento para usar algo con un controlador de pantalla y más memoria.

Opción 2: solo reprograma todo

Sin embargo, si ya estás planeando programar todos los juegos en C, entonces no te molestes en cargar solo la lógica del juego desde la tarjeta SD. Tiene solo 32kB de Flash para reprogramar, y fácilmente podría obtener una tarjeta microSD de 4 GB para esto. (Nota: las tarjetas más grandes suelen ser SDHC, que es más difícil de conectar). Suponiendo que usa cada último byte de sus 32 kB, eso deja espacio en la tarjeta SD para 131,072 copias de su firmware con cualquier lógica de juego que necesite.

Hay muchas notas para escribir cargadores de arranque para PIC, como AN851 . Necesitará diseñar su gestor de arranque para que ocupe una región específica de la memoria (probablemente la parte superior de la región de memoria, lo especificaría en el enlazador) y especificar que los proyectos de firmware completo no llegan a esta región. El apéndice explica esto con más detalle. Simplemente reemplace la "Sección de arranque del PIC18F452" con la "Sección de arranque que especifico en el enlazador" y todo tendrá sentido.

Luego, su gestor de arranque solo necesita permitir que el usuario seleccione un programa para ejecutarse desde la tarjeta SD, y copiar todo. Una IU podría ser que el usuario tenga que mantener presionado un botón para ingresar al modo de selección. Por lo general, el gestor de arranque solo verificará el estado de este botón al reiniciarlo y, si no se mantiene presionado, iniciará el juego. Si se mantiene presionada, debería permitir al usuario elegir un archivo en la tarjeta SD, copiar el programa y continuar para iniciar el juego [nuevo].

Esta es mi recomendación actual.

Opción 3: Magia profunda que implica almacenar solo una parte del archivo hexadecimal

El problema con su mecanismo previsto es que el procesador no se ocupa de las API y las llamadas a funciones, se trata de números : direcciones a las que el puntero de instrucción puede saltar y esperar que haya un código que ejecuta una llamada de función de acuerdo con una especificación de API. Si intenta compilar solo una parte del programa, el vinculador no sabrá qué hacer cuando llama a check_button_status() o toggle_led() . Es posible que sepa que esas funciones existen en el archivo hex en el procesador, pero necesita saber con precisión en qué dirección residen.

El enlazador ya divide tu código en varias secciones; teóricamente podría dividir esto en secciones adicionales con algunos conjuros de -section y #pragma . Nunca he hecho esto, y no sé cómo. Hasta que los dos métodos anteriores me fallan (o alguien publica una respuesta asombrosa aquí), probablemente no aprenderé este mecanismo y, por lo tanto, no puedo enseñárselo.

    
respondido por el Kevin Vermeer

Lea otras preguntas en las etiquetas