Hola,

Tenemos un nuevo video en el canal sobre como crear un timer en VHDL y conectarlo a un procesador ARM dentro de una FPGA Zynq. Todo ello desde cero.

Cuando creamos periféricos para conectar un procesador embebido como un Microblaze o un ARM en las Zynq, siempre tendemos a utilizar métodos de polling, es decir, tener un bit en un registro y estar todo el rato preguntando si el periférico ha terminado ya de ejecutar la tarea. Este método es muy sencillo, y si no tenemos que hacer nada mas que hacer mientras esperamos a que termine el periférico es suficiente.

No obstante, muchas veces si que tenemos que atender a varias tareas y en esas ocasiones el polling no es suficiente y es necesario utilizar interrupciones. A lo largo de los años he visto cosas increíbles que hace la gente con tal de no utilizar interrupciones, tienen un halo de misterio y fama de difíciles que hace que la gente las evite a toda costa.

Si que es cierto que en ocasiones si que tenemos un sistema operativo y varias fuentes de interrupciones pueden dar lugar a fallos misteriosos, pero en nuestras aplicaciones bare metal son muy sencillas de utilizar. En el siguiente video veremos cómo crear un sistema completo para la FPGA Zynq con un pequeño periférico que crea interrupciones cada cierto tiempo. El software dentro del ARM nos avisará cada vez que esto ocurra a través del puerto serie.

Os dejamos el video y debajo todo el código utilizado.

Timer en VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity interrupt is
  port(
    clk : in std_logic;
    int_o : out std_logic
  );
end interrupt;

architecture Behavioral of interrupt is

  type contador_t is range 0 to 100000000;
  signal contador : contador_t;

begin

process(clk)
begin
  if (clk'event and clk='1') then
    int_o <= '1';
    if(contador = contador_t'high) then
      contador <= 0;
      int_o <= '1';
    else
      contador <= contador + 1;
    end if;
  end if;
end process;
end Behavioral;

Código fuente del programa para el procesador ARM

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"

#include "xparameters.h"
#include "xscugic.h"
#include "xil_exception.h"

#define INTC_INTERRUPT_ID  61  // IRQ [0]
#define INTC XScuGic
#define INTC_HANDLER XScuGic_InterruptHandler
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID

static INTC Intc;

void PIsr(void *InstancePtr){    // INTERRUPT SERVICE ROUTINE(ISR)
    xil_printf("Received");
}

int SetupInterruptSystem()  {
    int result;
    XScuGic *IntcInstancePtr = &Intc;
    XScuGic_Config *IntcConfig;

    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (IntcConfig == NULL)    {
        return XST_FAILURE;
    }
    result = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);
    if (result != XST_SUCCESS)    {
        return XST_FAILURE;
    }
    /* Connect the interrupt handler */
    result = XScuGic_Connect(IntcInstancePtr, INTC_INTERRUPT_ID, (Xil_ExceptionHandler) PIsr, 0);
    if (result != XST_SUCCESS)    {
        return result;
    }

    XScuGic_SetPriorityTriggerType(IntcInstancePtr, INTC_INTERRUPT_ID, 0xA0, 0x3);// 0x3 is trigger to rising edge

    /* Enable the interrupt for the controller device. */
    XScuGic_Enable(IntcInstancePtr, INTC_INTERRUPT_ID);

    /* Enable interrupts in the ARM*/
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
        (Xil_ExceptionHandler)INTC_HANDLER, IntcInstancePtr);

    Xil_ExceptionEnable();    /* Enable non-critical exceptions */

    return XST_SUCCESS;
}

int main(void)  {
    int status = XST_SUCCESS;

    status = SetupInterruptSystem();
    if (status != XST_SUCCESS)   {
           return XST_FAILURE;
    }

    print("Hola\n");

    return 0;
}