Diferencias
Muestra las diferencias entre dos versiones de la página.
Próxima revisión | Revisión previa | ||
arm_interrupt_controllers [2023/05/16 00:23] cnigri Version inicial |
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 FIQ, si se recibe | + | * El núcleo ARM acepta solo **dos señales** de entrada no programadas que manejan interrupciones de los sistemas externos: |
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:// | 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:// | ||
Línea 13: | Línea 13: | ||
{{ : | {{ : | ||
- | Si bien el diagrama presenta la arquitectura para múltiples núcleos (processors), | + | Si bien el diagrama presenta la arquitectura para múltiples núcleos (processors), |
Según la fuente, hay tres tipos principales de interrupciones definidas. | Según la fuente, hay tres tipos principales de interrupciones definidas. | ||
Línea 49: | Línea 49: | ||
===== Interfaz de programación ===== | ===== Interfaz de programación ===== | ||
+ | [[https:// | ||
+ | A fin de brindar una referencia genérica agnóstica de la implementación SoC específica, | ||
+ | |||
+ | GIC Distributor base address DISTBASE = PERIPHBASE + 0x1000 | ||
+ | CPU Interface | ||
+ | |||
+ | El valor especifico de la dirección base se brinda en la documentación especifica de cada fabricante ([[https:// | ||
+ | <code java> | ||
+ | #include < | ||
+ | /*GIC Register Definitions*/ | ||
+ | #if defined(__PB_A8__) | ||
+ | #define GIC0_CPU_BASE | ||
+ | #define GIC0_DISTRIBUTOR_BASE | ||
+ | #elif defined(i386) | ||
+ | #define GIC0_CPU_BASE | ||
+ | #define GIC0_DISTRIBUTOR_BASE | ||
+ | #endif | ||
+ | |||
+ | #define ICCICR | ||
+ | #define ICCPMR | ||
+ | #define ICCBPR | ||
+ | #define ICCIAR | ||
+ | #define ICCEOIR | ||
+ | #define ICDDCR | ||
+ | #define ICDICTR | ||
+ | #define ICDISER(n) | ||
+ | #define ICDICER(n) | ||
+ | #define ICDIPR(n) | ||
+ | #define ICDIPTR(n) | ||
+ | #define ICDICFR(n) | ||
+ | </ | ||
+ | |||
+ | Teniendo presente las direcciones de acceso, se puede establecer una secuencia de inicialización básica compuesta de los siguientes pasos | ||
+ | * Deshabilitar el // | ||
+ | <code c> | ||
+ | void gic_init(void){ | ||
+ | | ||
+ | </ | ||
+ | * Configurar el número máximo de interrupciones que serán gestionadas por el GIC mediante el ICDICTR. Siendo el número máximo de interrupciones 32(N+1) donde N es el valor a cargar en el registro. | ||
+ | <code c> | ||
+ | | ||
+ | </ | ||
+ | * Habilitar el número de interrupción asociada al periférico que se desee atender. ICDISER representa la dirección base y su rango máximo es 0x7C (127) por lo que si se quiere habilitar la IRQ36 se debe establecer en 1 el bit 4 del ICDISER(1), ya que este registro opera como ICDISERn = 0x100 + (4*M), donde M = N/32 (division entera). | ||
+ | <code c> | ||
+ | | ||
+ | </ | ||
+ | * Configurar el procesador al cual la interrupción debe ser direccionada. El conjunto de registros ICDIPTRn debe establecer la parte baja de cada byte en 0x1 para sistemas de un único núcleo | ||
+ | <code c> | ||
+ | for i=0; i++; i<24 | ||
+ | | ||
+ | </ | ||
+ | {{ : | ||
+ | * Configurar si la detección de la interrupción es por nivel o flanco. Solo es aplicable a PPI | ||
+ | {{ : | ||
+ | * Configurar la prioridad. | ||
+ | {{ : | ||
+ | * Habilitar el // | ||
+ | <code c> | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== 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:// | ||
+ | |||
+ | <code asm> | ||
+ | stack_setup_irq: | ||
+ | cps #0xd2 | ||
+ | ldr r0, # | ||
+ | sub sp, r0, # | ||
+ | |||
+ | cpsie if, #0x13 | ||
+ | |||
+ | mov pc, lr @retorna | ||
+ | </ | ||
+ | |||
+ | Una vez que el //GIC// a señalizado al núcleo respecto a una interrupción pendiente, este debe realizar una [[https:// | ||
+ | |||
+ | <code asm> | ||
+ | irq_handler: | ||
+ | sub lr, lr, #4 @retornar a la instruccion en ejecucion al momento de la interrupcion | ||
+ | stmfd sp!, {r0-r12, lr} | ||
+ | |||
+ | mrs r11, spsr | ||
+ | push {r11} | ||
+ | |||
+ | @bl handler_dispatch | ||
+ | bl timer0_handler | ||
+ | |||
+ | @bl data_synchronization_barrier | ||
+ | |||
+ | pop {r11} | ||
+ | msr spsr, r11 | ||
+ | |||
+ | ldmfd sp!, {r0-r12, pc}^ @restaura contexto y retorna | ||
+ | </ | ||
+ | |||
===== 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, | A los fines de la placa empleada, para la resolución de la guía de trabajos prácticos del primer cuatrimestre, | ||
- | --- // | + | 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> | ||
+ | .global GIC_CFG | ||
+ | |||
+ | GIC_CFG: | ||
+ | push {r0-r1, lr} | ||
+ | |||
+ | mov r0, #0 | ||
+ | ldr r1, =0x1E001000 | ||
+ | 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 | ||
+ | |||
+ | ldr r1, =0x1E000004 | ||
+ | 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 | ||
+ | str r0, [r1] @Habilita señalizar las interrupciones | ||
+ | |||
+ | mov r0, #1 | ||
+ | ldr r1, =0x1E001000 | ||
+ | str r0, [r1] @Habilita CPU Interface y Distributor | ||
+ | |||
+ | pop {r0-r1, pc} | ||
+ | |||
+ | @ r0 = Interrupt ID, N | ||
+ | @ r1 = target CPU | ||
+ | @ Asume que los valores por defecto de los registros no configurados | ||
+ | @ son correctos | ||
+ | _IRQ_CFG: | ||
+ | push {r2-r5, lr} | ||
+ | mov r3, #1 @32(N+1) maximo, solo necesito hastala 36 | ||
+ | ldr r2, =0x1E001004 | ||
+ | ldr [r2], r3 | ||
+ | |||
+ | /*Interrupt Set-Enable Registers (ICDISERn) | ||
+ | * reg_offset = (integer_div(N / 32) * 4 | ||
+ | * value = 1 << (N mod 32) */ | ||
+ | lsr r4, r0, #3 @ Logical Shift Right = INT(N/ | ||
+ | bic r4, r4, #3 @ BIt Clear -> r4 and 0b1111 1100 | ||
+ | ldr r2, =0x1E000100 | ||
+ | add r4, r2, r4 @ r4=ICDISER sobre el que se debe operar | ||
+ | |||
+ | and r2, r0, #0x1F @N mod 32 | ||
+ | mov r3, #1 | ||
+ | lsl r2, r3, r2 @Logical Shift Left = 1 << (N mod 32) = r2 | ||
+ | |||
+ | ldr r5, [r4] @Carga actual valor de ICDISERn | ||
+ | orr r5, r5, r2 @Modifica segun r2 | ||
+ | str r5, [r4] @Carga nuevo valor a ICDISERn | ||
+ | |||
+ | /* Interrupt Processor Targets Register (ICDIPTRn) | ||
+ | * reg_offset = integer_div(N / 4) * 4 | ||
+ | * index = N mod 4 */ | ||
+ | bic r4, r0, #3 @ r4=r0 and 0b1111 1100=reg_offset | ||
+ | ldr r2, =0x1E000800 | ||
+ | add r4, r2, r4 @ r4= ICDIPTR sobre el que se debe operar | ||
+ | and r2, r0, #0x3 @N mod 4 | ||
+ | add r4, r2, r4 @Byte asociado al CPU en ICDIPTR | ||
+ | |||
+ | strb r1, [R4] @Solo modifica el byte asociado al CPU | ||
+ | |||
+ | pop {r2-r5, pc} | ||
+ | </ | ||
+ | ===== Caso de uso Beagle Bone Black===== | ||
+ | {{ : | ||
+ | |||
+ | --- // | ||