¿Cuáles son los detalles sobre los archivos de enlace y el código de inicio que uno necesita saber para escribir un sistema operativo para un uC? [cerrado]

Sé que algunas personas dirán que no necesito escribir un sistema operativo porque hay muchas opciones disponibles. Pero no estoy interesado en usar un sistema operativo para resolver un problema específico. Quiero escribir uno por mí mismo para aprender a hacerlo.

Sé muchas cosas sobre la teoría de los Sistemas Operativos y también sé programar microcontroladores en bare metal. Pero sé muy poco sobre los detalles de configuración que no están directamente relacionados con la programación. Estoy hablando de archivos de comando de enlace, código de inicio, etc.

Mi pregunta es: ¿qué necesito saber sobre estas cosas relacionadas con la "configuración" para poner en funcionamiento el sistema operativo? Por favor, vincule algún contenido externo sobre esas cosas, si es posible.

Es absolutamente excelente que quieras asumir un proyecto como este. Así es exactamente como los ingenieros aprenden y se expanden, ya sea que solo se conviertan en planes e ideas exploradas o que lleguen hasta el software terminado. Sin embargo, no es una pregunta de diseño electrónico, por lo que está en el foro equivocado. Pero un proyecto y un objetivo brillante :-)
@TonyM Gracias por tu comentario. Publico la pregunta aquí porque este es el foro de stackexchange que está más cerca del software integrado, RTOS, sistema operativo integrado, microcontroladores. Si hago esta pregunta en stackoverflow, la gente dirá que también estoy en el foro equivocado. :D
Esta es una pregunta bastante importante, y puede considerarse demasiado amplia para responderse en una sola. Recomiendo consultar la documentación de freeRTOS junto con los proyectos de ejemplo que proporcionan, o un sistema operativo integrado similar para ver cómo lo hacen.
@DanielGiesbrecht ¡Sí! Ya estoy echando un vistazo a freeRTOS y ChibiOS. ¡Gracias!
¡Un sistema operativo es solo un programa básico! Al intentar escribir un sistema operativo en una PC, debe pasar por todo tipo de obstáculos para que se ejecute su código completo. Pero en un microcontrolador ya tiene esa capacidad por diseño, ¡no hay aros! Solo debería necesitar exactamente los mismos scripts de vinculación, etc. que necesitaría para cualquier otro código bare metal (que con suerte no es ninguno de ellos).
Puede ser extraño pensarlo de esta manera, pero su sistema operativo es solo un programa y los "programas que se ejecutan en su sistema operativo" son en realidad solo bibliotecas para su programa de sistema operativo.
De hecho, puede escribir un sistema operativo sin cambiar de tarea, si todos sus programas están controlados por eventos y regresan rápidamente de los controladores de eventos. Como creador del sistema operativo, puede decidir cómo se estructuran sus programas, por lo que este podría ser un punto de partida interesante. Pero no transferirá ningún programa existente a su sistema operativo de esta manera. (De hecho, puede escribir este sistema operativo sin una sola línea de ensamblaje)

Respuestas (3)

Aquí hay algunas cosas que vienen a la mente de inmediato.

  1. Debe conocer todos los detalles de lo que sucede cuando el procesador sale de un reinicio de encendido. Habrá una serie de registros de control y estos tienen valores predeterminados. Necesitas saber esto, frío.
  2. Debe conocer todos los detalles de la arquitectura de la CPU. Todos los registros, modos especiales usándolos, etc. Estas cosas le darán pistas sobre cómo organizar los registros para llamar y regresar de funciones, etc. Si está usando C, o quiere ser compatible con C, tendrá para leer acerca de las elecciones realizadas por varios proveedores de compiladores de C (no siempre las mismas opciones).
  3. Debe conocer todos los detalles relacionados con varias unidades funcionales y el código de la biblioteca que permitirá que se vinculen. En particular, si admitirá la preferencia. En algunos casos, no puede permitir la preferencia ya que no hay forma de guardar el estado de algunas operaciones que están en progreso (el multiplicador MSP430 es así) a pesar de que un temporizador de intervalo puede interrumpir ese proceso. En algunos casos, con respecto a las bibliotecas, tampoco se pueden interrumpir ya que tienen un estado estático que se corrompería si un hilo separado accediera al mismo código. Etc.
  4. Su código de inicio tendrá que estar vinculado a la dirección de reinicio de encendido para la MCU. Su trabajo es hacer cualquier inicialización prometida. Esto podría ser tabular áreas de memoria, inicializar algo de memoria y cualquier otra cosa que se espere en algún estado conocido antes de la ejecución del primer hilo/proceso. Si C está involucrado, esto significa inicializar todas las variables estáticas inicializadas según sus valores apropiados e inicializar todas las demás variables estáticas a su equivalente semántico de 0, cualquiera que sea.
  5. Debe conocer todas las formas en que pueden ocurrir excepciones y cómo diferenciar, por ejemplo, entre un evento de temporizador de vigilancia y un evento de reinicio de encendido.
  6. El archivo de entrada del enlazador simplemente establece las áreas donde se pueden colocar el código y los datos inicializados y qué tan grandes son. También utiliza nombres para que el proceso de vinculación pueda asignar un código con nombre o datos inicializados con nombre a los lugares apropiados como se indica en el archivo de entrada del vinculador. Esto no es complicado. Pero los detalles tienen que ser correctos. Si está utilizando un compilador de C, ese compilador de C probablemente hará todo tipo de suposiciones sobre los nombres de las secciones (segmentos) y tendrá que obedecerlos o escribir una herramienta que modifique el código del objeto antes de vincular.
  7. Es posible que deba escribir una herramienta especial para vincular, de todos modos. Esto puede suceder si tiene que admitir varios compiladores de C que generan diferentes segmentos con nombre en sus archivos de objetos; o una salida del ensamblador que también usa diferentes nombres; etc. A veces, los prólogos y epílogos de rutina no hacen las mismas suposiciones, incluso, y es posible que deba parchear esas cosas antes de vincular. Es posible que también necesite escribir una herramienta de parcheo, de todos modos, para parchear o "arreglar" las direcciones que se especifican en los archivos de objetos antes de vincular. (Tuve que hacer esto con el código del procesador 6502/65816 destinado a la producción de archivos ROM con mapeadores de memoria extraños que son diferentes y también incompatibles entre sí).

Y esa es solo una lista muy corta que fluye de mis dedos sin pensarlo. Estoy seguro de que si dedicara otros 5 minutos, podría duplicar esta lista. (Por ejemplo, ni siquiera he abordado nada sobre lo que un depurador podría querer en términos de información y/o código modificado o insertado para respaldar sus operaciones. Tampoco he discutido las diferencias involucradas en los sistemas de memoria Harvard vs von Neumann. Tampoco he Discutí el "modelo de programa" estándar de organización [código; constantes; datos de inicio; datos de unidad; montón; pila, etc.])

Recomiendo tomar esto en pasos lentos. Dado que afirma que ya sabe sobre programación bare metal y también sobre sistemas operativos, permítame simplemente recomendarle que lea el primer libro XINU de Douglas Comer (es alrededor de 1984, tiene una portada roja y no hay volumen 2 , etc.) Luego vea si puede improvisar un cambio cooperativoSistema operativo. Esto significa SIN APROPIACIÓN. Significa simplemente HILOS, no procesos completos y separados, sino hilos que comparten el mismo espacio de código, constantes, datos estáticos y montón; con la única diferencia de que tienen pilas separadas. Admite una llamada switch() para hacer que este cambio de hilo cooperativo funcione. También debe admitir eventos de controlador de hardware sin desbordar accidentalmente la pila de algún subproceso en el proceso. Debe diseñar cuidadosamente cómo maneja los eventos de hardware y su código de controlador. (Divido esto en código de respuesta de hardware de bajo nivel + código accesible por subprocesos de alto nivel separados por búferes para desacoplar los dos entre sí para que puedan operar de forma independiente, al mismo tiempo que admiten el hardware por completo).

Si desea dar el siguiente paso, agregue mensajes entre subprocesos. Use una palabra simple, solo una palabra, para cada hilo y deje que cualquier otro hilo la escriba. Sobreescribirlo, de hecho. No hagas esto complicado. Vea si puede obtener mensajes de una sola palabra entre procesos.

A continuación, agregue una cola de suspensión y proporcione un temporizador que pueda mover subprocesos de la cola de suspensión a la cola de ejecución.

Luego agregue colas de semáforo.

Si puedes llegar hasta aquí, habrás aprendido mucho.

Y, por cierto, me llevó menos de dos días hábiles (desde el lunes hasta el martes temprano por la tarde) lograr que todo lo anterior funcionara, desde cero y sin una sola línea de código de proyectos anteriores, así que solo tipeé tan rápido como pude. podría pensar, incluida una mezcla completa de ensamblaje y C para manejar eventos de hardware, etc. Tenía colas de ejecución/reposo/semáforo, temporizadores y subprocesos cooperativos junto con el manejo de excepciones por subproceso agregado... en menos de dos días.

Así que esta NO es una tarea particularmente difícil por delante.

Tienen en él.

Ciertamente es una tarea difícil, no lo trivialicemos, y terminará tomando mucho más de dos días, pero es perfectamente realizable, que es el punto que me imagino que estás diciendo. Excelente respuesta detallada, votada. Genial ver tu emoción y satisfacción de escalar esa montaña ardiendo igual de brillante después de los años en tus palabras :-) Todavía atesoro mi primera vez, un motor adelantado Z80 en un 64180.
@TonyM El libro XINU proporciona los conceptos básicos en una forma tan fácil de entender como la que he visto en un libro. ¡Sí, le tomará a una persona nueva mucho más de dos días! Sobre todo, solo quería señalar que, al principio, NO deberían intentar la prevención. Mantenlo simple. Simplemente cree pilas separadas, mantenga una matriz pequeña, proporcione una función switch() muy simple y diviértase. Agregue suspensión cuando esté listo para manejar el inicio y el uso de un temporizador (pero aún así no se adelante... solo mueva los hilos). Luego agregue mensajes (fácil) y más tarde después de algunas colas de semáforos elegantes. Eso va un largo camino de ser útil, creo.
Estoy de acuerdo y es genial ver tu experiencia, aliento y entusiasmo al respecto, muy valioso para el OP. Esperaba el coro habitual de 'compra uno, idiota' de los comerciantes de 'no hagas nada, no aprendas nada, siéntete orgulloso' que contribuyen con estas cosas. Su 'pruébelo' es, con mucho, la actitud correcta y puede tener una fiesta por tan poco costo en estos días.
Ya hice esto en LabView y Javascript y assmebler. LabView facilitó la acumulación de colas prioritarias y habilitaciones y bloqueos de tareas (rama ejecutiva). Luego está el intercambio entre tareas y los disparadores. Cíñete a las reglas de la lógica y funcionará bien.
@TonyM Estaba consultando en una pequeña empresa con 1 programador. Su código era un nido de ratas y, además, no funcionaba bien. Lo convencí de que tratara de escribir un conmutador cooperativo (no me contrataron para hacerlo por él). Así que me senté y le expliqué cómo podía abordarlo. NUNCA había hecho algo como esto antes. Creo que no fue más de dos días después que corrió a mi oficina. ¡¡Lo había hecho!! Y estaba funcionando. ¡Lo miré y estaba feliz! El software de esa empresa mejoró mucho poco después. Tenía experiencia en ASM, por lo que era fácil de enseñar. Simplemente no había estado expuesto a las ideas, eso es todo.

La mayoría de los microcontroladores no son realmente apropiados para esto. Quiere más de un microprocesador de propósito general.

Algunos micros de 32 bits de gama alta como ARM y PIC32 pueden permitirle escribir un sistema operativo razonable. Para un sistema operativo general, necesita un procesador que pueda ejecutar el código de usuario de manera que el código de usuario no pueda dañar el sistema. Esto generalmente se hace teniendo un modo privilegiado y un modo de usuario. La mayoría de los micros no tienen esto.

Otro problema es que la mayoría de los micros solo se ejecutan desde la ROM, no desde la RAM. Eso hace que cargar código de usuario arbitrario y luego ejecutarlo sea difícil o imposible. El espacio total de RAM y ROM también suele ser fijo, y no se puede ampliar externamente. Eso no impide un sistema operativo, pero hará que las aplicaciones que el sistema pueda ejecutar sean bastante limitadas. La mayoría de los micros no tienen MMU que puedan reasignar direcciones de memoria reales a lógicas y causar trampas al intentar acceder a cierta memoria. Eso hace que la paginación sea difícil o imposible.

Mira el PIC32. Puede ejecutarse desde la RAM y tiene al menos una MMU básica. Algunas variantes tienen suficiente memoria para hacer posibles programas útiles en modo de usuario.

En cuanto a los detalles de la vinculación, realmente debe leer los manuales de las herramientas. No hay sustituto para comprender lo que hace el enlazador y cómo controlarlo. Tienes que hacer RTFM. No hay ningún atajo.

* RTFM = Leer el manual fino
Olin Lathrop, mi plataforma objetivo es TI TM4C1294 Connected Launchpad (EK-TM4C1294XL), que se basa en ARM Cortex-M4F (32 bits). El hardware ofrece un buen soporte para un sistema operativo.

Sospecho que podría ser posible implementar un sistema operativo en tiempo real sin ningún conocimiento especial o personalización de la secuencia de comandos de la directiva del enlazador y el código de inicio en tiempo de ejecución de C.

La secuencia de comandos de la directiva del enlazador identifica regiones y secciones de memoria. Si el proveedor de la herramienta proporciona un archivo de script de vinculación predeterminado que funciona para sus aplicaciones completas, también podría funcionar para su aplicación basada en RTOS. Personalizaría el script del enlazador si su aplicación tiene requisitos de memoria especializados. Los ejemplos incluyen una aplicación para una placa personalizada con memorias externas, una aplicación con secciones de memoria especializadas que requieren una inicialización especializada y una aplicación que funciona junto con un programa de carga de arranque. No puedo pensar en una razón por la cual el uso de un RTOS lo obligue a personalizar la secuencia de comandos del vinculador. El conocimiento sobre cómo personalizar el script del enlazador es importante para cualquier aplicación especializada que lo requiera, independientemente de si se usa un RTOS.

El código de inicio inicializa el entorno de tiempo de ejecución de C para su aplicación. Si el código de inicio predeterminado del proveedor de la herramienta es lo suficientemente bueno para su aplicación completa, también podría ser lo suficientemente bueno para su aplicación basada en RTOS. El código de inicio copia los valores de inicialización de la ROM a la RAM y pone a cero los datos no inicializados. (Para C++ llama a los constructores para objetos asignados estáticamente). Personalizaría el código de inicio si tiene secciones de memoria especializadas que requieren una inicialización especial. También puede personalizar el código de inicio si su placa tiene una inicialización de hardware especial que debe realizarse antes de la inicialización del entorno de tiempo de ejecución (o antes main()). No puedo pensar en una razón por la que usar un RTOS forzaríapara personalizar el código de inicio. El RTOS se puede inicializar desde main()(después de que se complete el código de inicio). El conocimiento sobre cómo personalizar el código de inicio es importante para cualquier aplicación especializada que lo requiera, independientemente de si se utiliza un RTOS.

Sin embargo, querrá saber todo acerca de los registros de la CPU y cómo la CPU maneja las interrupciones. Deberá saber qué registros de la CPU se deben guardar/restaurar para cambiar de contexto. Y las interrupciones son una excelente oportunidad para cuando se produce un cambio de contexto RTOS.

Además de la documentación de FreeRTOS, consulte los libros uC/OS-III de Micrium.