ZYBOで遊ぶ01:簡易タイマーIPの自作(4)

ZYBOで遊ぶ01:簡易タイマーIPの自作(3) - ThuruThuruToru’s blog の続き


ZYBOで遊ぶ01:簡易タイマーIPの自作(1) - ThuruThuruToru’s blog
ZYBOで遊ぶ01:簡易タイマーIPの自作(2) - ThuruThuruToru’s blog
ZYBOで遊ぶ01:簡易タイマーIPの自作(3) - ThuruThuruToru’s blog
ZYBOで遊ぶ01:簡易タイマーIPの自作(4) - ThuruThuruToru’s blog



10. 割り込み処理の記述

自作したタイマーが割り込みを上げたときの割り込み処理の記述と割り込みハンドラの登録処理を書いていく.
ちゃんとマニュアルを読めば手順が書いてあるのだと思うが,横着をして以下のサンプルから必要そうな部分を取り出して動かしてみた.

SDK/2014.4/data/embeddedsw/XilinxProcessorIPLib/drivers/
axidma_v8_0/examples/xaxidma_example_simple_intr.c

10.1. 割り込みハンドラの記述

割り込みが上がったときにCPUにやってもらう仕事を書く.
今回は,

  • [割り込みステータス]を確認し,割り込みクリア
  • タイマーを再実行

という処理を書いてみた.
今回は使っていないが,関数の引数には割り込みハンドラ登録時に任意のポインタを渡すようにできる.

void mytimer_int_handler(void *callback)
{
    u32 read_data;

    print("\n\n\rmytimer_int_handler--------------\n\r");
    read_data = read_and_print(MYTIMER_INT_STAT_ADDR, "[INT_STAT]");
    if (read_data == 1) {
        print("Clear Interrupt\n\r");
        Xil_Out32(MYTIMER_INT_STAT_ADDR, 0x01);

        print("Rerun Timer\n\r");
        Xil_Out32(MYTIMER_STAT_CTRL_ADDR, 0x01);
    }
    print("---------------------------------\n\n\r");
}

10.2. 割り込みハンドラの登録

以下の関数を順番に実行していけば良さそう(後でちゃんとマニュアルを読みたい).
作成した割り込みハンドラを登録・有効化するのは,XScuGic_ConnectとXScuGic_Enableなので,この部分だけ書き換えればその他の部分はこの先もそのまま使いまわせそう.
割り込みをあげるIPが複数あるケースでは,XSuGic_ConnectとXScuGic_Enableを複数書けば良い.

  • XScuGic_LookupConfig
  • XSuGic_CfgInitialize
  • XScuGic_Connect
  • XScuGic_Enable
  • Xil_ExceptionInit
  • XilExceptionRegisterHandler
  • Xil_ExceptionEnable

XPAR_FABRIC_MYTIMER_0_INTERRUPT_INTR という定数は,xparameters.h で自動で定義されているmyTimerの割り込み番号. XScuGic_Connectの最後の引数にはNULLを指定しているので,割り込みハンドラには何もデータを渡していない.
この引数に変数のポインタとかを指定すれば,割り込みハンドラ側でデータを参照できる.

割り込みハンドラの登録・有効化は以下の様になった.

#include "xscugic.h"
#include "xil_exception.h"
int SetupIntrSystem(XScuGic *IntcInstancePtr)
{
    int Status;

    XScuGic_Config *IntcConfig;


    /*
     * Initialize the interrupt controller driver so that it is ready to
     * use.
     */
    IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
    if (IntcConfig == NULL) {
        print("Failed XScuGic_LookupConfig\n\r");
        return XST_FAILURE;
    }

    Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
                                   IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) {
        print("Failed XScuGic_CfgInitialize\n\r");
        return XST_FAILURE;
    }

    /*
     * Connect the device driver handler that will be called when an
     * interrupt for the device occurs, the handler defined above performs
     * the specific interrupt processing for the device.
     */
    Status = XScuGic_Connect(IntcInstancePtr,
                         XPAR_FABRIC_MYTIMER_0_INTERRUPT_INTR,
                             (Xil_InterruptHandler)mytimer_int_handler,
                             NULL);
    if (Status != XST_SUCCESS) {
        print("Failed XScuGic_Connect\n\r");
        return XST_FAILURE;
    }

    XScuGic_Enable(IntcInstancePtr,
                   XPAR_FABRIC_MYTIMER_0_INTERRUPT_INTR);

    /* Enable interrupts from the hardware */
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 (void *)IntcInstancePtr);
    Xil_ExceptionEnable();

    return XST_SUCCESS;
}

11. 完成したFW

main関数では,

  • 割り込み設定
  • タイマー起動
  • 'q'をプレスされると終了するループ

を行い,割り込みハンドラでは,割り込みをクリアし,タイマーを再起動するようにした.

#include <stdio.h>
#include "platform.h"
#include "xil_io.h"
#include "xscugic.h"
#include "xil_exception.h"


#define MAX_SIZE 1024
#define MYTIMER_INT_STAT_ADDR  XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x0
#define MYTIMER_STAT_CTRL_ADDR XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x4
#define MYTIMER_COUNTER_ADDR   XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0x8
#define MYTIMER_DEBUG0_ADDR    XPAR_MYTIMER_0_S00_AXI_LITE_BASEADDR+0xC

u32 read_and_print(u32 Addr, char *head_str)
{
    u32 read_data;
    char str[MAX_SIZE];

    read_data = Xil_In32(Addr);
    snprintf(str, MAX_SIZE, "%s = %lx\n\r", head_str, read_data);
    print(str);

    return read_data;
}

void mytimer_int_handler(void *callback)
{
    u32 read_data;

    print("\n\n\rmytimer_int_handler--------------\n\r");
    read_data = read_and_print(MYTIMER_INT_STAT_ADDR, "[INT_STAT]");
    if (read_data == 1) {
        print("Clear Interrupt\n\r");
        Xil_Out32(MYTIMER_INT_STAT_ADDR, 0x01);

        print("Rerun Timer\n\r");
        Xil_Out32(MYTIMER_STAT_CTRL_ADDR, 0x01);
    }
    print("---------------------------------\n\n\r");
}

int SetupIntrSystem(XScuGic *IntcInstancePtr)
{
    int Status;

    XScuGic_Config *IntcConfig;


    /*
     * Initialize the interrupt controller driver so that it is ready to
     * use.
     */
    IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
    if (IntcConfig == NULL) {
        print("Failed XScuGic_LookupConfig\n\r");
        return XST_FAILURE;
    }

    Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
                                   IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) {
        print("Failed XScuGic_CfgInitialize\n\r");
        return XST_FAILURE;
    }

    /*
     * Connect the device driver handler that will be called when an
     * interrupt for the device occurs, the handler defined above performs
     * the specific interrupt processing for the device.
     */
    Status = XScuGic_Connect(IntcInstancePtr,
                             XPAR_FABRIC_MYTIMER_0_INTERRUPT_INTR,
                             (Xil_InterruptHandler)mytimer_int_handler,
                             NULL);
    if (Status != XST_SUCCESS) {
        print("Failed XScuGic_Connect\n\r");
        return XST_FAILURE;
    }

    XScuGic_Enable(IntcInstancePtr,
                   XPAR_FABRIC_MYTIMER_0_INTERRUPT_INTR);

    /* Enable interrupts from the hardware */
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 (void *)IntcInstancePtr);
    Xil_ExceptionEnable();

    return XST_SUCCESS;
}

int main()
{
    int status;
    int c;
    char str[MAX_SIZE];
    XScuGic Intc;

    init_platform();

    status = SetupIntrSystem(&Intc);
    if (status != XST_SUCCESS) {
        return status;
    }

    // run timer
    print("Run Timer------------------------------------------\n\r");
    Xil_Out32(MYTIMER_COUNTER_ADDR, 0x10000000);
    Xil_Out32(MYTIMER_STAT_CTRL_ADDR, 0x00000001);

    // wait for 'q' is pressed
    while (1) {
        c = inbyte();
        snprintf(str, MAX_SIZE, "%c\n\r", c);
        print(str);
        if (c == 'q')
            break;
    }

    print("\n\n\rquit\n\r");

    cleanup_platform();
    return 0;
}

11. まとめ

簡易タイマーIPを自作し,PS部から自作IPを操作するFWを書いた.
CPUからIPの制御レジスタアクセス,割り込みハンドラの登録ができるようになった.
これで,自作IPを制御できるようになったので,次はPS部からDMACを起動しデータ転送を行ってみる予定.