Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
arm_interrupt_controllers [2023/05/16 23:03]
cnigri Configuracion IRQ codigo ensamblador
arm_interrupt_controllers [2024/06/03 20:16] (actual)
cnigri Fix Incorporacion de registro de control de la interfaz con el CPU
Línea 4: Línea 4:
 Antes de comenzar vale la pena recordar dos conceptos elementales: Antes de comenzar vale la pena recordar dos conceptos elementales:
   * Un controlador de interrupción es un módulo (IC, ASIC, FPGA, etc) que se encuentran entre las fuentes de interrupción (periféricos) y **los núcleos** ARM,  el cual decide cómo gestionar (priorizar, habilitar, direccionar) las interrupciones.   * Un controlador de interrupción es un módulo (IC, ASIC, FPGA, etc) que se encuentran entre las fuentes de interrupción (periféricos) y **los núcleos** ARM,  el cual decide cómo gestionar (priorizar, habilitar, direccionar) las interrupciones.
-  * El núcleo ARM acepta solo dos señales de entrada no programadas que manejan interrupciones de los sistemas externos: nIRQ y nFIQ. Como se debería saber si se recibe IRQ, el sistema ingresa al modo IRQ o FIQsi se recibe una FIQ.+  * El núcleo ARM acepta solo **dos señales** de entrada no programadas que manejan interrupciones de los sistemas externos: **nIRQ** y nFIQ. Como se debería saber el sistema ingresa al modo IRQ si se recibe nIRQ (activación por nivel bajo 0), en modo FIQ si se recibe nFIQ (activación por nivel bajo 0).
  
 Si bien dependiendo de las licencias ARM, es decir del fabricante de SoC, se decide el mecanismo para direccionar las señales de interrupción al núcleo, en este artículo se tratará el método estandarizado y más frecuentemente empleado, el [[https://developer.arm.com/documentation/198123/0302/What-is-a-Generic-Interrupt-Controller- | Global Interrupt Controller]]. Es importante aclarar que no se pretende brindar una explicación detallada del **//GIC//**, ya que para ello se encuentra disponible la [[https://developer.arm.com/documentation/ihi0069/h/?lang=en | especificación]] correspondiente, sino su aplicación para el caso de uso de la Cátedra. Si bien dependiendo de las licencias ARM, es decir del fabricante de SoC, se decide el mecanismo para direccionar las señales de interrupción al núcleo, en este artículo se tratará el método estandarizado y más frecuentemente empleado, el [[https://developer.arm.com/documentation/198123/0302/What-is-a-Generic-Interrupt-Controller- | Global Interrupt Controller]]. Es importante aclarar que no se pretende brindar una explicación detallada del **//GIC//**, ya que para ello se encuentra disponible la [[https://developer.arm.com/documentation/ihi0069/h/?lang=en | especificación]] correspondiente, sino su aplicación para el caso de uso de la Cátedra.
Línea 13: Línea 13:
 {{ :arm_gic_partitioning.png?800 |GIC logical partitioning}}  {{ :arm_gic_partitioning.png?800 |GIC logical partitioning}} 
  
-Si bien el diagrama presenta la arquitectura para múltiples núcleos (processors), en nuestro caso de uso (cortex A8) solo basta con reducir el esquema a un solo procesador. +Si bien el diagrama presenta la arquitectura para múltiples núcleos (processors), en nuestro caso de uso (inicialización mono procesador) solo basta con reducir el esquema a un solo procesador. 
  
 Según la fuente, hay tres tipos principales de interrupciones definidas. Según la fuente, hay tres tipos principales de interrupciones definidas.
Línea 56: Línea 56:
     CPU Interface   base address CPUBASE = PERIPHBASE + 0x0000.     CPU Interface   base address CPUBASE = PERIPHBASE + 0x0000.
  
-<code c>+El valor especifico de la dirección base se brinda en la documentación especifica de cada fabricante ([[https://developer.arm.com/documentation/dui0417/d/programmer-s-reference/generic-interrupt-controller--gic/generic-interrupt-controller-registers|PB-A8]], [[https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/CPU-Private-Bus-Registers|Zynq-7000]]) 
 +<code java>
 #include <stddef.h> #include <stddef.h>
-/*GIC Register Definitions +/*GIC Register Definitions*/ 
-#define ICCICR      *((uint32_t *) 0x1E000000)         //CPU Interface Control Register +#if defined(__PB_A8__) 
-#define ICDDCR      *((uint32_t *) 0x1E001000         //Distributor Control Register +#define GIC0_CPU_BASE           0x1E000000 
-#define ICDICTR     *((uint32_t *) 0x1E001004         //Interrupt Controller Type Register +#define GIC0_DISTRIBUTOR_BASE   0x1E001000 
-#define ICDISER(n)  *(((uint32_t *) 0x1E000100) + n)    //Interrupt n Set-Enable Registers +#elif defined(i386) 
-#define ICDICER(n)  *(((uint32_t *) 0x1E000180) + n)    //Interrupt Clear-Enable Registers +#define GIC0_CPU_BASE           0xF8F00100 
-#define ICDIPR(n)   *(((uint32_t *) 0x1E000400) + n) //Interrupt Priority Registers +#define GIC0_DISTRIBUTOR_BASE   0xF8F01000 
-#define ICDIPTR(n)  *(((uint32_t *) 0x1E000800) + n)    //Interrupt Processor Targets Registers +#endif 
-#define ICDICFR(n)  *(((uint32_t *) 0x1E000C00) + n) //Interrupt Configuration Registers+ 
 +#define ICCICR      *((uint32_t *) GIC0_CPU_BASE + 0x00)         //CPU Interface Control Register 
 +#define ICCPMR      *((uint32_t *) GIC0_CPU_BASE + 0x04)         //Interrupt Priority Mask Register 
 +#define ICCBPR      *((uint32_t *) GIC0_CPU_BASE + 0x08)         //Binary Point Register 
 +#define ICCIAR      *((uint32_t *) GIC0_CPU_BASE + 0x0C)         //Interrupt Acknowledge 
 +#define ICCEOIR     *((uint32_t *) GIC0_CPU_BASE + 0x10)         //End of Interrupt Register 
 +#define ICDDCR      *((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0x000      //Distributor Control Register 
 +#define ICDICTR     *((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0x004      //Interrupt Controller Type Register 
 +#define ICDISER(n)  *(((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0x100) + n) //Interrupt n Set-Enable Registers 
 +#define ICDICER(n)  *(((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0x180) + n) //Interrupt Clear-Enable Registers 
 +#define ICDIPR(n)   *(((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0x400) + n) //Interrupt Priority Registers 
 +#define ICDIPTR(n)  *(((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0x800) + n) //Interrupt Processor Targets Registers 
 +#define ICDICFR(n)  *(((uint32_t *) GIC0_DISTRIBUTOR_BASE + 0xC00) + n) //Interrupt Configuration Registers
 </code> </code>
  
Línea 97: Línea 110:
    ICDDCR = 1);    ICDDCR = 1);
    }    }
-</code> +</code> 
 + 
 + 
 +===== Gestión de las interrupciones en el Core ===== 
 +En los párrafos precedentes se brindó una referencia básica sobre la operación y configuración del //GIC//, es decir sobre el sistema de gestión de interrupciones externo al/los núcleos, a continuación el artículo se centrará sobre la contra-parte situada en el propio núcleo. 
 +En esta instancia es bien sabido que cada [[https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers/Registers | modo de operación]] debe contar con su propia pila, a fin de preservar el contexto de ejecución, lo cual debe realizarse en la etapa [[https://sge.frba.utn.edu.ar/wiki/td3/lib/exe/detail.php?id=inicializacion_de_procesador_cortex-a8&media=initialization_soc.png | stack_configure]] de la secuencia de inicialización del procesador 
 + 
 +<code asm> 
 +stack_setup_irq: 
 +    cps #0xd2           @Change Processor Status a IRQ 
 +  
 +    ldr r0, #CONFIG_PILA_LIMITE_DIRECCION 
 +    sub sp, r0, #(CONFIG_PILA_IRQ_LARGO + CONFIG_PILA_FIQ_LARGO) 
 + 
 +    cpsie if, #0x13     @Change Processor Status a SVC con IRQ habilitada 
 + 
 +    mov pc, lr          @retorna 
 +</code> 
 + 
 +Una vez que el //GIC// a señalizado al núcleo respecto a una interrupción pendiente, este debe realizar una [[https://developer.arm.com/documentation/den0013/d/Interrupt-Handling/External-interrupt-requests/Simplistic-interrupt-handling | secuencia de pasos mínima (descripta en el manual)]] para gestionar la interrupción, la cual se ejemplifica mediante el siguiente código 
 + 
 +<code asm> 
 +irq_handler: 
 +    sub lr, lr, #4                      @retornar a la instruccion en ejecucion al momento de la interrupcion 
 +    stmfd sp!, {r0-r12, lr}             @resguarda contexto en pila de IRQ 
 + 
 +    mrs r11, spsr                       @reguarda Saved Processor Status Register 
 +    push {r11} 
 + 
 +    @bl handler_dispatch                @Gestor generico para identificar y derivar al controlador especifico 
 +    bl timer0_handler                   @Nuestro caso de uso (simple) solo presenta una IRQ 
 + 
 +    @bl data_synchronization_barrier    @requerido en SoC multi core (dsb, dmb segun systema) 
 + 
 +    pop {r11} 
 +    msr spsr, r11                       @reguarda Saved Processor Status Register 
 + 
 +    ldmfd sp!, {r0-r12, pc}^            @restaura contexto y retorna 
 +</code> 
 ===== Caso de uso PB-A8 ===== ===== Caso de uso PB-A8 =====
 A los fines de la placa empleada, para la resolución de la guía de trabajos prácticos del primer cuatrimestre, el [[https://developer.arm.com/documentation/dui0417/d/programmer-s-reference/generic-interrupt-controller--gic?lang=en | GIC a configurar para el uso de IRQ es el 0]] el cual tiene asociado el [[https://developer.arm.com/documentation/dui0417/d/programmer-s-reference/generic-interrupt-controller--gic/interrupt-signals?lang=en | ID#36 para el Timer0]] A los fines de la placa empleada, para la resolución de la guía de trabajos prácticos del primer cuatrimestre, el [[https://developer.arm.com/documentation/dui0417/d/programmer-s-reference/generic-interrupt-controller--gic?lang=en | GIC a configurar para el uso de IRQ es el 0]] el cual tiene asociado el [[https://developer.arm.com/documentation/dui0417/d/programmer-s-reference/generic-interrupt-controller--gic/interrupt-signals?lang=en | ID#36 para el Timer0]]
  
-En forma complementaría al código referenciado anteriormente para la configuración del GIC, en C, a continuación se brinda una referencia básica en ensamblador+En forma complementaría al código, en C, presentado anteriormente para la configuración del GIC, a continuación se brinda a modo de referencia básica el equivalente en ensamblador
 <code asm> <code asm>
 +.global GIC_CFG
 +
 +GIC_CFG:
 +    push {r0-r1, lr}
 +
 +    mov r0, #0
 +    ldr r1, =0x1E001000             @ICDDCR
 +    str r0, [r1]                    @Deshabilita CPU Interface y Distributor
 +
 +    mov r0, #36                     @IRQ ID 36 para Timer0
 +    mov r1, #1                      @Sistema mono procesador
 +    bl _IRQ_CFG                     @Llamada a rutina con argumentos r0, r1
 +
 +    ldr r1, =0x1E000004             @Interrupt Priority Mask Register (ICCPMR)
 +    ldr r0, [r1]                    @Carga el valor actual
 +    orr r0, r0, #0xF
 +    bic r0, r0, #1                  @BIt Clear -> r0 = r0 and 0xFFFF FFFE
 +    str r0, [r1]                    @Habilita todas las prioridades
 +
 +    mov r0, #1
 +    ldr r1, =0x1E000000             @ICCICR
 +    str r0, [r1]                    @Habilita señalizar las interrupciones
 +
 +    mov r0, #1
 +    ldr r1, =0x1E001000             @ICDDCR
 +    str r0, [r1]                    @Habilita CPU Interface y Distributor
 +
 +    pop {r0-r1, pc}
 +
 @ r0 = Interrupt ID, N @ r0 = Interrupt ID, N
 @ r1 = target CPU @ r1 = target CPU
 @ Asume que los valores por defecto de los registros no configurados @ Asume que los valores por defecto de los registros no configurados
 @ son correctos @ son correctos
-IRQ_CFG:+_IRQ_CFG:
     push {r2-r5, lr}     push {r2-r5, lr}
     mov r3, #1                      @32(N+1) maximo, solo necesito hastala 36     mov r3, #1                      @32(N+1) maximo, solo necesito hastala 36
Línea 117: Línea 198:
      * value = 1 << (N mod 32) */        * value = 1 << (N mod 32) */  
     lsr r4, r0, #3                  @ Logical Shift Right = INT(N/(2³)) = reg_offset     lsr r4, r0, #3                  @ Logical Shift Right = INT(N/(2³)) = reg_offset
-    bic r4, r4, #3                  @ BIt Clear -> r4 and 0b0000 0011+    bic r4, r4, #3                  @ BIt Clear -> r4 and 0b1111 1100
     ldr r2, =0x1E000100             @ ICDISER registro 0     ldr r2, =0x1E000100             @ ICDISER registro 0
     add r4, r2, r4                  @ r4=ICDISER sobre el que se debe operar     add r4, r2, r4                  @ r4=ICDISER sobre el que se debe operar
Línea 132: Línea 213:
      * reg_offset = integer_div(N / 4) * 4      * reg_offset = integer_div(N / 4) * 4
      * index = N mod 4 */      * index = N mod 4 */
-    bic r4, r0, #3                  @ r4=r0 and 0b0000 0011=reg_offset+    bic r4, r0, #3                  @ r4=r0 and 0b1111 1100=reg_offset
     ldr r2, =0x1E000800             @ ICDIPTR registro 0     ldr r2, =0x1E000800             @ ICDIPTR registro 0
     add r4, r2, r4                  @ r4= ICDIPTR sobre el que se debe operar     add r4, r2, r4                  @ r4= ICDIPTR sobre el que se debe operar
Línea 145: Línea 226:
 {{ :td3:nueva_seccion.jpg?400 |}} {{ :td3:nueva_seccion.jpg?400 |}}
  
- --- //[[cnigri@frba.utn.edu.ar|ChristiaN]] 2023/05/16 00:17//+ --- //[[cnigri@frba.utn.edu.ar|ChristiaN]] 2023/06/22 20:29//