¿Existen plataformas en las que la desactivación / restauración de las interrupciones de ISR se realice de forma diferente a la de un contexto que no sea ISR?

4

Estoy familiarizado con varios kernels en tiempo real: AVIX , FreeRTOS , TNKernel , y en todos tenemos 2 versiones de casi todas las funciones: una para llamar desde la tarea y la segunda para llamar desde ISR.

Por supuesto, tiene sentido para las funciones que podrían cambiar de contexto y / o dormir: obviamente, ISR no puede dormir, y el cambio de contexto se debe hacer de manera diferente. Pero hay varias funciones que no cambian de contexto ni de suspensión: digamos, puede devolver el conteo de tics del sistema, o configurar el temporizador del software, etc.

Ahora, estoy implementando mi propio kernel: TNeoKernel , que tiene un código bien formado y se ha probado cuidadosamente, y Estoy considerando inventar funciones "universales" a veces: las que se pueden llamar desde cualquier tarea o contexto ISR. Pero como los tres núcleos mencionados utilizan funciones separadas, me temo que voy a hacer algo mal.

Por ejemplo, en el contexto de tareas e ISR, TNKernel usa diferentes rutinas para deshabilitar / restaurar interrupciones, pero por lo que veo, la única diferencia posible es que las funciones de ISR pueden "compilarse" como una optimización si la plataforma de destino no lo hace. No soporta interrupciones anidadas. Pero si la plataforma de destino admite interrupciones anidadas, la desactivación / restauración de las interrupciones parece absolutamente lo mismo para la tarea y el contexto ISR.

Por lo tanto, mi pregunta es : ¿existen plataformas en las que las interrupciones de deshabilitación / restauración de ISR se deben hacer de manera diferente a las del contexto que no es ISR?

Si no hay tales plataformas, preferiría ir con funciones "universales". Si tiene comentarios sobre este enfoque, son muy apreciados.

UPD: no me gusta tener dos conjuntos de funciones porque conducen a una duplicación y complicación notables del código. Por ejemplo, necesito proporcionar una función que debería iniciar el temporizador del software. Aquí está lo que parece:

enum TN_RCode _tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout)
{
   /* ... real job is done here ... */
}

/*
 * Function to be called from task
 */
enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_Timeout timeout)
{
   TN_INTSAVE_DATA;  //-- define the variable to store interrupt status,
                     //   it is used by TN_INT_DIS_SAVE()
                     //   and TN_INT_RESTORE()
   enum TN_RCode rc = TN_RC_OK;

   //-- check that function is called from right context
   if (!tn_is_task_context()){
      rc = TN_RC_WCONTEXT;
      goto out;
   }

   //-- disable interrupts
   TN_INT_DIS_SAVE();

   //-- perform real job, after all
   rc = _tn_timer_start(timer, timeout);

   //-- restore interrupts state
   TN_INT_RESTORE();

out:
   return rc;
}

/*
 * Function to be called from ISR
 */
enum TN_RCode tn_timer_istart(struct TN_Timer *timer, TN_Timeout timeout)
{
   TN_INTSAVE_DATA_INT;    //-- define the variable to store interrupt status, 
                           //   it is used by TN_INT_DIS_SAVE()                
                           //   and TN_INT_RESTORE()                           
   enum TN_RCode rc = TN_RC_OK;

   //-- check that function is called from right context
   if (!tn_is_isr_context()){
      rc = TN_RC_WCONTEXT;
      goto out;
   }

   //-- disable interrupts
   TN_INT_IDIS_SAVE();

   //-- perform real job, after all
   rc = _tn_timer_start(timer, timeout);

   //-- restore interrupts state
   TN_INT_IRESTORE();

out:
   return rc;
}

Por lo tanto, necesitamos envoltorios como los de arriba para casi todas las funciones del sistema. Este es un tipo de inconveniente, tanto para mí como desarrollador del kernel como para los usuarios del kernel.

La única diferencia es que se utilizan macros diferentes: para la tarea, estas son TN_INTSAVE_DATA , TN_INT_DIS_SAVE() , TN_INT_RESTORE() ; para las interrupciones, estos son TN_INTSAVE_DATA_INT , TN_INT_IDIS_SAVE() , TN_INT_IRESTORE() .

Para las plataformas que admiten interrupciones anidadas (ARM, PIC32), estas macros son idénticas. Para otras plataformas que no admiten interrupciones anidadas, TN_INTSAVE_DATA_INT , TN_INT_IDIS_SAVE() y TN_INT_IRESTORE() se expanden a nada. Por lo tanto, es un poco de optimización de rendimiento, pero el costo es demasiado alto en mi opinión: es más difícil de mantener, no es tan cómodo de usar y el tamaño del código aumenta.

    
pregunta Dmitry Frank

2 respuestas

2

Creo que hay varias más fuerzas impulsoras para tener dos conjuntos de funciones, que usted ha mencionado.

  1. Esos RTOS están diseñados para ser portátiles a muchos procesadores diferentes Arquitecturas de conjuntos de instrucciones (ISA), y también intentan garantizar Los desarrolladores pueden escribir sistemas que sean portátiles. Entonces ellos tienen que acomodar toda la variabilidad de las ISA del procesador de manera que ocultar las diferencias entre los ISA de los usuarios de RTOS.
  2. Algunas de las ISA utilizadas en un RTOS tienen modo de "usuario", y Modo 'privilegiado'. El modo 'Usuario' no tendrá acceso a todos los sistema, y específicamente no puede ser capaz de bloquear las interrupciones. Además, algunas de las funciones podrían no ejecutarse en 'usuario' modo, y puede "atrapar" a un controlador de excepciones en modo privilegiado para realmente hacer el trabajo Así que tener dos 'espacios de nombres' alineados a los dos Los estados de privilegio de CPU pueden ser útiles para el RTOS y la aplicación desarrolladores.
  3. En mi humilde opinión es mucho más fácil recordar usar una función de una 'espacio de nombres' (por ejemplo, el modo 'usuario') que el de averiguar exactamente Qué función se puede utilizar en cualquier contexto específico. Específicamente, Es mucho más fácil para todas las funciones venir en una 'aplicación de usuario' espacio de nombres y un espacio de nombres 'componente-sistema-priviliado'. Aun asi Si el código es idéntico, es más sencillo para la aplicación. desarrollador para utilizar las funciones en el nombre de 'aplicación' o 'usuario' espacio. Si eso es una ventaja para el desarrollador de aplicaciones, entonces es más útil para el proveedor de RTOS "duplicar" la código, en una función en el otro 'espacio de nombre' que no tenga el función duplicada.

Resumen: IMHO que tiene dos conjuntos de funciones, constituyendo efectivamente dos 'espacios de nombres' diferentes ('aplicación de usuario' y 'componente del sistema'), hace que sea más fácil para el desarrollador de la 'aplicación', y que los desarrolladores agreguen extra 'sistema privilegiado- componente 'a utilizar.

También podría ser más fácil tener dos conjuntos de funciones diferentes para facilitar la gestión de las interrupciones generadas durante el código que se ejecuta en dos estados diferentes del sistema.

Puede ser relativamente sencillo crear las dos alternativas utilizando algunas macros de preprocesador cuidadosamente pensadas, para minimizar la duplicación de código.

Si no pretende que otra persona utilice su sistema operativo, o su sistema tratará las excepciones generadas durante el modo 'usuario' y el modo 'sistema' de manera idéntica, es probable que pueda ignorar la idea.

Sin embargo, si no sabe cómo podría funcionar todo (por ejemplo, esta es la primera vez que escribe un RTOS), o si cree que podría cambiar, es posible que desee piense detenidamente cómo volvería a introducir los dos 'espacios de nombres' en la mitad del desarrollo de su sistema operativo.

    
respondido por el gbulmer
0

Algunas bibliotecas de E / S suministradas por el proveedor tienen sus propias formas de habilitar y deshabilitar las interrupciones. En algunos casos, están escritos de una manera desafortunada que es propensa a un mal funcionamiento, a menos que todo el código que habilita o inhabilite las interrupciones lo haga a través de dichas bibliotecas. Si las rutinas de E / S hacen uso de tales rutinas, a menos que las rutinas puedan parchearse para comportarse de manera razonable, puede ser necesario asegurarse de que todos los demás códigos del sistema se ajusten a las necesidades de dichas bibliotecas.

No estoy seguro de por qué las bibliotecas de proveedores no simplemente proporcionan una función que captura el estado de habilitación de interrupción, desactiva las interrupciones y devuelve el estado capturado valor, junto con uno que restaurará un estado capturado, todo sin usar ningún objeto estático o global. Los módulos que utilizan este enfoque general serán naturalmente compatibles con otros módulos que hacen lo mismo, sin necesidad de que coordinen el uso global de objetos. No obstante, existen bibliotecas mal diseñadas y, a veces, es necesario escribir programas que las acomoden.

    
respondido por el supercat

Lea otras preguntas en las etiquetas