0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

低レベルな操作によるArmのLPI+ITS割込みの発生方法

Last updated at Posted at 2021-08-10

#はじめに
Arm Cortex-Aシリーズの割込みコントローラであるGICの機能に、PCIなどの割込みに使用されるLPI(Locality-specific Peripheral Interrupts)と、ITS(Interrupt Translation Service)という機能があります。これらLPIとITSの理解を深めるために、u-boot上で動作するプログラムを作成して、LPI+ITS割込みを発生させたときの備忘録です。

#環境

  • CPU:NXP社製 LS2088A(Cortex-A72, GIC500)
  • 評価ボード:NXP社製 LS2088ARDB
  • u-boot:U-Boot 2016.092.0+ga06b209

#LPIとは
PCIなどで使用されるMSI(Message Signaled Interrupts)のための機能で、SDRAMなどのメモリ領域に割り当てたLPIテーブルに値を書き込むことで、8192番以降の割込みを発生させる機能です。
LPIはGIC内にコアの個数分(LS2088Aでは8個)存在します。
LS2088Aでは1コアあたり最大(65536-8192)個の割込みを登録することができます。

##LPIテーブル
LPIテーブルには以下の2つのテーブルがあります。
・LPI構成テーブル(LPI Configuration tables)
・LPI保留テーブル(LPI Pending tables)

#####LPI構成テーブル
各割込みの優先度と有効/無効を設定するためのテーブルです。1つの割込みエントリあたり8bitで構成され(下図参照)、割込みの個数分配列構造になっています。
LPI_config_enrty2.png

#####LPI保留テーブル
各割込みの保留状態を示すテーブルです。1つの割込みエントリあたり1bitで構成され、
割込みの個数分並べられた構造になっています。ただし、テーブルの先頭から1Kbyte分は未使用領域とされており、0クリアしておく必要があります。

#ITSとは
LPIを利用しやすいように補足された機能で、このデバイスのこのイベントが発生したという表現でLPI割込みを発生させられるように、DeviceIDおよびEventIDという識別子と発生させたいLPI割込みを関連付ける機能です。
ITSはGIC内に1つ存在します。

##ITSテーブル
この記事で登場するテーブルとして以下の3つのテーブルがあります。
・デバイステーブル(Device table)
・ITT(Interrupt Translation table)
・コマンドテーブル(Command Queue)

この他にCollection tableや仮想化で使用するテーブルがありますが、この記事では設定していません。

#####デバイステーブル
デバイス毎にITTが存在するため、DeviceIDと対応するITTのベースアドレスを関連付けるためのテーブルです。テーブルの1エントリのサイズは、GITS_BASERのEntry_Sizeから読み取ることができ、エントリの個数分を掛け合わせることで、テーブルに必要なサイズを求めることができます。
LS2088AではGITS_BASERのEntry_Sizeは7という値が設定されており、GICの資料(参考資料1)のGITS_BASERの説明からEntry_Sizeはマイナス1された値を示すので、エントリサイズは8byteであることが分かります。

#####ITT
デバイスに複数のイベントを持たせ、イベント毎にLPI割込みの番号を設定できます。ITTはEventIDと対応するLPI割込み番号を関連付けるためのテーブルです。テーブルの1エントリのサイズは、GITS_TYPERのITT_entry_sizeから読み取ることができ、エントリの個数分を掛け合わせることで、テーブルに必要なサイズを求めることができます。
LS2088AではGITS_TYPERのITT_entry_sizeは3という値が設定されており、GICの資料(参考資料1)のGITS_TYPERの説明からITT_entry_sizeはマイナス1された値を示すので、エントリサイズは4byteであることが分かります。

#####コマンドテーブル
ITSテーブルに値を設定していくためのコマンド群を記述するテーブルです。ITSコマンドを実行させることでITSテーブルを設定していきます。GICの資料(参考資料1)ではCommand queueと記述されていますが、ここではコマンドテーブルと記述しています。

#メモリマップ
以下のメモリマップでテーブルを配置して、割込み発生の実験を行いました。

テーブル アドレス
LPI構成テーブル 0xa0000000-0xa000ffff
LPI保留テーブル 0xa0010000-0xa0011fff
デバイステーブル 0xa1000000-0xa1000fff
ITT 0xa1001000-0xa100103f
コマンドテーブル 0xa1010000-0xa1010fff

#LPIの設定と有効化
LPIテーブルの初期化およびLPI関連のレジスタを設定して、LPIを有効化します。
実験で使用したLPI設定のソースの一部を以下に示します。(gic_write、gic_write64関数はGICのレジスタに値を書き込む関数)

 1 #define LPI_CFG_TABLE       0x00000000a0000000ULL
 2 #define LPI_CFG_TABLE_SZ    (65536 * 1)
 3 #define LPI_PEND_TABLE      0x00000000a0010000ULL
 4 #define LPI_PEND_TABLE_SZ   (65536 / 8)
 5
 6 /* LPI構成テーブル(64Kbyte)の0クリア */
 7 adr = (volatile uint64_t *)LPI_CFG_TABLE;
 8 for(i = 0; i < LPI_CFG_TABLE_SZ/8; i++){
 9     *(adr + i) = 0;
10 }
11 /* 8194番目の割込みを優先度0xa0で有効化 */
12 *adr = 0x0000000000a30000;
13
14 /* LPI保留テーブル(8Kbyte)の0クリア */
15 adr = (volatile uint64_t *)LPI_PEND_TABLE;
16 for(i = 0; i < LPI_PEND_TABLE_SZ/8; i++){
17     *(adr + i) = 0;
18 }
19
20 /* LPI構成テーブルのGICR_PROPBASERへの登録 */
21 gic_write64(GICR_PROPBASER, 0x000000000000038f + LPI_CFG_TABLE);
22 /* LPI保留テーブルのGICR_PENDBASERへの登録 */
23 gic_write64(GICR_PENDBASER, 0x0000000000000380 + LPI_PEND_TABLE);
24
25 /* LPI有効 */
26 gic_write(GICR_CTLR, 0x00000001);

6-12行目でLPI構成テーブルを65536個分クリアして、実験で使用する8194番目の割込みのみを優先度0xa0で有効化します。

14-18行目でLPI保留テーブルを65536個分クリアします。

21行目でGICR_PROPBASERにLPI構成テーブルのアドレス(LPI_CFG_TABLE)とキャッシュ有効なノーマルメモリ属性を設定します。また、IDbitsにLPI割込みの個数が65536個になるように設定します。GICの資料(参考資料1)のGICR_PROPBASERの説明から、65536個の割込みは2の16乗なので、16から1引いた値をIDbitsに設定します。以下にGICR_PROPBASERの構成を示します。
GICR_PROPBASER2.png

23行目でGICR_PENDBASERにLPI保留テーブルのアドレス(LPI_PEND_TABLE)とキャッシュ有効なノーマルメモリ属性を設定しています。以下にGICR_PENDBASERの構成を示します。
GICR_PENDBASER2.png

26行目でLPIテーブルの設定が終わったので、GICR_CTLRのEnable_LPIsを有効にして、LPI機能を有効にします。実験ではコア0用のLPIのみ有効にします。

#ITSの設定と有効化
ITSテーブルの初期化およびITS関連のレジスタを設定して、ITSを有効化します。
実験で使用したITS設定のソースの一部を以下に示します。

 1 #define DEVICE_TABLE        0x00000000a1000000ULL
 2 #define DEVICE_TABLE_SZ     (4096 * 1)
 3 #define ITT_ADDRESS         0x00000000a1001000ULL
 4 #define ITT_SZ              (16 * 4)
 5 #define CMD_QUEUE           0x00000000a1010000ULL
 6 #define CMD_QUEUE_SZ        (4096 * 1)
 7
 8 /* デバイステーブル(4Kbyte * 1block)の0クリア */
 9 adr = (volatile uint64_t *)DEVICE_TABLE;
10 for(i = 0; i < DEVICE_TABLE_SZ/8; i++){
11     *(adr + i) = 0;
12 }
13
14 /* ITT(16entry * 4byte)の0クリア */
15 adr = (volatile uint64_t *)ITT_ADDRESS;
16 for(i = 0; i < ITT_SZ/8; i++){
17     *(adr + i) = 0;
18 }
19
20 /* デバイステーブルのGITS_BASERへの登録 */
20 gic_write64(GITS_BASER, 0xb800000000000000 + DEVICE_TABLE);
21
22 /* コマンドテーブル(4Kbyte * 1block)の0クリア */
23 adr = (volatile uint64_t *)CMD_QUEUE;
24 for(i = 0; i < CMD_QUEUE_SZ/8; i++){
25     *(adr + i) = 0;
26 }
27
28 /* コマンドテーブルのGITS_CBASERへの登録 */
29 gic_write64(GITS_CBASER, 0xb800000000000000 + CMD_QUEUE);
30 /* 書き込み位置を0クリア */
30 gic_write64(GITS_CWRITER, 0x0000000000000000);
31
32 /* ITS有効 */
33 gic_write(GITS_CTLR, 0x00000001);

8-12行目でデバイステーブルを0クリアします。実験では最小単位の4Kbyteを1block割り当てます。

14-18行目でITTを0クリアします。実験では16entryとし、エントリサイズはLS2088Aでは4byteです。

20行目でGITS_BASERにデバイステーブルのアドレス(DEVICE_TABLE)とキャッシュ有効なノーマルメモリ属性を設定します。また、Page_Sizeは4Kbyte(00b)、16Kbyte(01b)、64Kbyte(10b)からの選択で4Kbyteを選択し、その個数を1blockとするために、Sizeにはマイナス1した0を設定します。以下にGITS_BASERの構成を示します。詳細はGICの資料(参考資料1)を参照して下さい。

GITS_BASER2.png

21-26行目でコマンドテーブルを0クリアします。実験では最小単位の4Kbyteを1block割り当てます。

29行目でGITS_CBASERにコマンドテーブルのアドレス(CMD_QUEUE)とキャッシュ有効なノーマルメモリ属性を設定します。また、Sizeは1blockとするため、GICの資料(参考資料1)のGITS_CBASERの説明から、Sizeにはマイナス1した0を設定します。以下にGITS_CBASERの構成を示します。
GITS_CBASER2.png

30行目でGITS_CWRITERに0を書き込み、書き込み位置をコマンドテーブルの先頭にします。

33行目でGITS_CTLRのEnabledを有効にして、ITS機能を有効にします。

#ITSコマンドの実行
LPIテーブルやITSテーブルに設定値を書き込むために、ITSコマンドを実行します。
実験では、DeviceID=1、EventID=2のイベントが発生したとき、コア0に8194番のLPI割込みが発生するように設定します。
実験で使用したITSコマンド実行のソースの一部を以下に示します。

 1 adr = (volatile uint64_t *)CMD_QUEUE;
 2 /* MAPD DeviceID=1, ITT_ADDRESS, size=3+1 */
 3 *adr++ = 0x0000000100000008;
 4 *adr++ = 0x0000000000000003;
 5 *adr++ = 0x8000000000000000 + ITT_ADDRESS;
 6 *adr++ = 0x0000000000000000;
 7 /* MAPTI DeviceID=1, INTNO=0x2002, EventID=2, ICID=0 */
 8 *adr++ = 0x000000010000000a;
 9 *adr++ = 0x0000200200000002;
10 *adr++ = 0x0000000000000000;
11 *adr++ = 0x0000000000000000;
12 /* MAPC RDBase=0, ICID=0 */
13 *adr++ = 0x0000000000000009;
14 *adr++ = 0x0000000000000000;
15 *adr++ = 0x8000000000000000;
16 *adr++ = 0x0000000000000000;
17 /* SYNC RDBase=0 */
18 *adr++ = 0x0000000000000005;
19 *adr++ = 0x0000000000000000;
20 *adr++ = 0x0000000000000000;
21 *adr++ = 0x0000000000000000;
22
23 /* ITSコマンド実行 */
24 gic_write64(GITS_CWRITER, (uint64_t)adr - CMD_QUEUE);
25
26 /* 読み込み位置が書き込み位置に来るまで待つ */
26 val = gic_read64(GITS_CWRITER);
27 while(val != gic_read64(GITS_CREADR));

2-6行目でMAPDコマンドを設定します。デバイステーブルにDeviceID=1のITTのアドレス(ITT_ADDRESS)と、エントリ数を設定します。
実験ではITTのエントリ数を16entryにするため、GICの資料(参考資料1)のMAPDコマンドの説明から2の4乗の4という値からマイナス1した3をSizeに設定します。以下にMAPDコマンドの構成を示します。
MAPD_command2.png
7-11行目でMAPTIコマンドを設定します。ITTにEventID=2の割込み番号は8194番であることを設定します。ICIDには0を設定します。以下にMAPTIコマンドの構成を示します。
MAPTI_command2.png
12-16行目でMAPCコマンドを設定します。ICID=0と関係のあるコア番号(RDBase)を設定します。以下にMAPCコマンドの構成を示します。
MAPC_command2.png
17-21行目でSYNCコマンドを設定します。ITSの設定内容を指定コアのLPIへ通知し同期をとります。以下にSYNCコマンドの構成を示します。
SYNC_command2.png
24行目でGITS_CWRITERを更新して、書き込み位置を移動させ、27行目で読み込み位置が書き込み位置と同じになるのを待つことで、コマンドが実行されたことを確認します。

この時点でのデバイステーブルとITTのメモリ内容を、u-bootで見てみると以下のようになっています。

=> md.q 0xa1000000 10
a1000000: 0000000000000000 80030000a1001000    ................
a1000010: 0000000000000000 0000000000000000    ................
a1000020: 0000000000000000 0000000000000000    ................
a1000030: 0000000000000000 0000000000000000    ................
a1000040: 0000000000000000 0000000000000000    ................
a1000050: 0000000000000000 0000000000000000    ................
a1000060: 0000000000000000 0000000000000000    ................
a1000070: 0000000000000000 0000000000000000    ................
=> md.l 0xa1001000 10
a1001000: 00000000 00000000 80002002 00000000    ......... ......
a1001010: 00000000 00000000 00000000 00000000    ................
a1001020: 00000000 00000000 00000000 00000000    ................
a1001030: 00000000 00000000 00000000 00000000    ................
=>

#割込みの発生と確認
LPI割込みを受信できる条件に整えてLPI割込みを発生させ、割込み発生時の割込み番号を確認します。割込み発生時の割込み番号を確認するためにベクタテーブルを書き換えて確認します。
実験で使用した割込み発生のソースの一部を以下に示します。(v8sys_write関数はシステムレジスタに値を書き込む関数)

 1 /* ベクタテーブルを書き換える */
 2 v8sys_write("VBAR_EL3", (uint64_t)vector_table);
 3
 4 /* Non-secure group1割込みを有効にする */
 5 gic_write(GICD_CTLR, 0x00000037);
 6
 7 /* 割込みマスクレベルを0xa0より低くする */
 8 v8sys_write("ICC_PMR", 0xf0);
 9
10 /* CPUの割込みを許可状態にする */
11 v8sys_write("DAIF", 0x0000);
12
13 /* INT DeviceID=1, EventID=2 */
14 *adr++ = 0x0000000100000003;
15 *adr++ = 0x0000000000000002;
16 *adr++ = 0x0000000000000000;
17 *adr++ = 0x0000000000000000;
18 gic_write64(GITS_CWRITER, (uint64_t)adr - CMD_QUEUE);

2行目で割込み発生時の割込み番号を確認するためにベクタテーブルを書き換えます。(u-bootには戻れなくなります)

5行目でLPI割込みはNon-secureのgroup1に属する割込みなので、Non-secure group1の割込みを有効にします。

8行目で割込みの優先度を0xf0に設定します。LPI割込みの優先度(0xa0)より低い優先度にしてLPI割込みを受信できる状態にします。

11行目でCPU側のIRQ割込みを受付許可状態にします。

13-18行目でITSコマンドのINTコマンドを使用して、DeviceID=1、EventID=2の割込みをかけます。以下にINTコマンドの構成を示します。
INT_command2.png
割込みをかければ、vector_tableから0x280(Current Exception level with SP_ELxのIRQ)オフセットした場所に飛び、ICC_IAR1レジスタを確認すると、8194番が記されていることが確認できます。

#####最後に
低レベルな操作でLPI+ITS機能を使用する方は少ないと思いますが、LPI+ITSを理解したいと思っている方に少しでも参考になれば幸いです。

##参考資料
参考資料1: ARM® Generic Interrupt Controller Architecture Specification (GIC architecture version 3.0 and version 4.0) [ARM IHI 0069C]
参考資料2: GICv3 and GICv4 Software Overview [ARM DAI 0492B]
参考資料3: Arm® Architecture Reference Manual (Armv8, for Armv8-A architecture profile) [ARM DDI 0487E.a]
参考資料4: QorIQ LS2088A Reference Manual [Rev.0.1,02/2020]

『各種製品名は、各社の製品名称、商標または登録商標です。本記事に記載されているシステム名、製品名には、必ずしも商標表示((R)、TM)を付記していません。』

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?