電気回路/zynq/DMA処理 のバックアップの現在との差分(No.5)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

#contents

* 概要 [#f674dcb8]

Zynq で DMA 処理するために勉強中の内容です。
Zynq で簡単な DMA 処理をするための手順をまとめました。

まだ動かせていないので間違ったことも多いと思います。
Web上で探すといろんなドライバに関する情報が交錯していて、
正解にたどり着くまでに非常に苦労します。

* axi_dma のドキュメント [#e7f3007e]
- Xilinx のベアメタルドライバ
-- Linux とはコンパチビリティのないドライバ
- Xilinx の Linux ドライバ 
-- Linux 上で他のデバイスドライバから使われることを想定したドライバ
-- ユーザーアプリケーションから使うものではない
- サードパーティーの bperez77/xilinx_axidma
-- Xilinx の Linux ドライバの上に構築された汎用ドライバ
-- /dev/axidma 経由で簡単に DMA 転送が行える

デザインダイアグラム上で axi_dma IP を右クリックして
[IP Documentations] から各種ドキュメントが出る。
のようにいろいろあるのですが、お手軽に使うには
bperez77/xilinx_axidma が良さそうです。

https://www.xilinx.com/products/intellectual-property/axi_dma.html#overview
サードパーティー製のドライバは他にもいくるかあるようですが、
少なくとも上記のものは固定サイズパケットの単純転送で割り込み通知を使うだけなら
非常に簡単に使えるのでおすすめです。

など。
Scatter Gather 機能を使いたければもう少し苦労が必要なのかもしれませんが、
割り込みハンドラのスレッドを分けてしまえば、
次のパケットを CPU で受け取るというのも、
そんなに悪くない設計に思えます。

* 後で読む [#te8e7275]
パケットサイズを可変にしたいときにどうするべきか、
についてはまだ調査中です。
* bperez77/xilinx_axidma の使い方を調べる [#h7b14a1c]

axidma.c.golden driver というのがある?~
https://forums.xilinx.com/t5/Embedded-Linux/AXI-DMA-in-Linux-Timing-Out/m-p/555968/highlight/true#M11610
これが使えるとかなり楽ができそう。

official xilinx_axidma.c driver included in the petalinux kernel というのもある?~
https://github.com/Digilent/linux-Digilent-Dev/blob/master/drivers/dma/xilinx/xilinx_axidma.c
https://github.com/bperez77/xilinx_axidma

AXI DMA with Zynq Running Linux Accepted Solution [Solved] ~
https://forums.xilinx.com/t5/Embedded-Linux/AXI-DMA-with-Zynq-Running-Linux/td-p/522755 ~
** カーネルオプションを確認 [#c50efd43]

Zynq DMA 用 Linux ドライバ:~
https://github.com/bmartini/zynq-xdma
ドライバに書かれている条件を満たしているか、
z-turn 付属のカーネル設定を調べます。

その使用例?:~
https://tobygomersallwordpresscom.wordpress.com/2016/02/11/xilinx-zynq-using-dma-to-transfer-data-to-the-linux-userspace/
- CONFIG_CMA=y
- CONFIG_DMA_CMA=y
- CONFIG_XILINX_DMAENGINES=y
- CONFIG_XILINX_AXIDMA=y
- CONFIG_XILINX_AXIVDMA=y
- CONFIG_DMA_SHARED_BUFFER=y

上手に動かさないと速度が出ない:~
https://github.com/bmartini/zynq-xdma/issues/6
のうち、CONFIG_XILINX_DMAENGINES が無かったが、代わりに 

Zynq DMA 用 Linux ドライバ2:~
https://github.com/bmartini/zynq-axis.git
- CONFIG_XILINX_DMA_ENGINES=y

[stackoverflow] Linux DMAドライバのインスタンス化および利用方法~
http://qiita.com/wstone/items/ab4d47a3690bb1a9bf4c ~
があったので、Xilinx 製のドライバは初めから入っているみたい。

UIO(User space IO)の割り込みの使い方の例~
独自実装の DMA IP を Linux から割り込み駆動で使う例が出ています~
http://qiita.com/ikwzm/items/b22592c31cdbb9ab6cf7

** Device Tree [#ia885cc1]

[[電気回路/HDL/VivadoでAXIバスを利用#sf57016a]] で作ったデバイスは、

- address = 0x40400000
-- tx は +0x0000
-- rx は +0x0030
- irq = GIC の F2P[4]
- s2mm のみ
- データ幅 32

DTS における割り込み番号の設定方法は、
http://qiita.com/ikwzm/items/b22592c31cdbb9ab6cf7 によれば、

> interrupts=<0x0 0x1d 0x4> の2番目のパラメータの0x1d(十進数で29)が割り込み番号を指定しています。DMAコントローラーの割り込みはPS部のIRQ_F2P[0]に接続しています。ZYNQのテクニカルリファレンスマニュアルによれば、IRQ_F2P[0]は汎用割り込みコントローラー(GIC)内の共有ペリフェラル割り込み(SPI)の61番を通じて割り込みがかかります。このパラメータには61から32引いた値を設定するようです。
>
>interrupts=<0x0 0x1d 0x4>の1番目のパラメータはGIC内部の割り込みの種類を指定します。GIC内部には共有ペリフェラル割り込み(SPI)の他にCPUプライベートペリフェラル割り込み(PPI)とソフトウェア生成割り込み(SGI)があります。共有ペリフェラル割り込み(SPI)を使う場合は0を指定します。
>
>interrupts=<0x0 0x1d 0x4>の3番目のパラメータは割り込み信号がエッジトリガーかレベルトリガーかを指定します。1を指定するとエッジトリガー、4を指定するとレベルトリガーになります。

とのことなので、今の場合は
- 0 = 共有ペリフェラル割り込み(SPI)
- 33 = 61 + 4 - 32 = 33
- 4 = レベルトリガー

インクルードファイルを使わない場合 &clkc = 1, &intc = 3 として、

 axidma_chrdev: axidma_chrdev@0 {
     compatible = "xlnx,axidma-chrdev";
     dmas = <&axi_dma_0 0>;
     dma-names = "rx_channel";
 };
 
 axidma0: axidma0@40400000 {
     compatible = "xlnx,axi-dma", "xlnx,axi-dma-6.03.a", "xlnx,axi-dma-1.00.a";
     reg = <0x40400000 0x10000>;
     xlnx,include-sg;
     #dma-cells = <1>;
 
     dma-s2mm-channel@40400030 {
         compatible = "xlnx,axi-dma-s2mm-channel";
         dma-channels = <1>;
         xlnx,datawidth = <32>;
         xlnx,device-id = <0x0>;
         clocks = <1 15>;
         clock-names = "axis";
         interrupt-parent = <3>;
         interrupts = <0 33 4>;
     };
 };

となりそうです?

http://qiita.com/ikwzm/items/aae3dab578e28c13bc65 によると
clocks = 15 は fclk0 のことのようなのですが、どこで調べられるのかよくわかっていません。。。

始め dma-s2mm-channel のアドレスを 40400000 としていて
うまく行かずはまりました。40400000 は mm2s 用のレジスタのために確保されているので、
mm2s を実装しているかどうかにかかわらず、s2mm のレジスタは 40400030 から始まります。
** axidma デバイスが登録された [#nb7fbf42]

上記 devicetree をインストールしたところ、起動メッセージに、

 xilinx-dma 40400000.axidma0: Probing xilinx axi dma engine...Successful

が現れ、

 LANG:console
 $ ls /sys/class/dma/dma1chan0/device/driver/
  40400000.axidma0  bind  uevent  unbind

となっているので、まずは xilinx の axidma が登録されたことになります。
これ自体はユーザーアプリケーションから直接使えるものではないので、
この上に構築された汎用ドライバである bperez77/xilinx_axidma をかぶせて使うことになります。


** bperez77/xilinx_axidma ドライバのビルド [#i70128a3]

 LANG:console
 $ git clone https://github.com/bperez77/xilinx_axidma.git
 $ cd xilinx_axidma
 $ make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm KBUILD_DIR=/home/osamu/linux-zturn
  make -C /home/osamu/linux-zturn M=/home/osamu/xilinx_axidma/driver modules
  make[1]: Entering directory '/home/osamu/linux-zturn'
    CC [M]  /home/osamu/xilinx_axidma/driver/axi_dma.o
    CC [M]  /home/osamu/xilinx_axidma/driver/axidma_dma.o
  In file included from /home/osamu/xilinx_axidma/driver/axidma_dma.c:25:0:
  /home/osamu/xilinx_axidma/driver/version_portability.h: In function ‘axidma_setup_vdma_config’:
  /home/osamu/xilinx_axidma/driver/version_portability.h:133:15: error: ‘struct xilinx_vdma_config’ has no member named ‘vsize’
       dma_config->vsize = height;         // Height of the image (in lines)
                 ^
  /home/osamu/xilinx_axidma/driver/version_portability.h:134:15: error: ‘struct xilinx_vdma_config’ has no member named ‘hsize’
       dma_config->hsize = width * depth;  // Width of the image (in bytes)
                 ^
  /home/osamu/xilinx_axidma/driver/version_portability.h:135:15: error: ‘struct xilinx_vdma_config’ has no member named ‘stride’
       dma_config->stride = width * depth; // Number of bytes to process per line
                 ^
  /home/osamu/xilinx_axidma/driver/version_portability.h: In function ‘axidma_to_xilinx_type’:
  /home/osamu/xilinx_axidma/driver/version_portability.h:154:59: error: ‘XILINX_DMA_IP_VDMA’ undeclared (first use in this function)
       return (dma_type == AXIDMA_DMA) ? XILINX_DMA_IP_DMA : XILINX_DMA_IP_VDMA;
                                                             ^
  /home/osamu/xilinx_axidma/driver/version_portability.h:154:59: note: each undeclared identifier is reported only once for each function it appears in
  /home/osamu/xilinx_axidma/driver/axidma_dma.c: In function ‘axidma_prep_transfer’:
  /home/osamu/xilinx_axidma/driver/axidma_dma.c:198:32: error: ‘DMA_COMPL_SKIP_DEST_UNMAP’ undeclared (first use in this function)
       dma_flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT;
                                  ^
  /home/osamu/xilinx_axidma/driver/axidma_dma.c: In function ‘axidma_start_transfer’:
  /home/osamu/xilinx_axidma/driver/axidma_dma.c:271:30: error: ‘DMA_SUCCESS’ undeclared (first use in this function)
           } else if (status != DMA_SUCCESS) {
                                ^
  In file included from /home/osamu/xilinx_axidma/driver/axidma_dma.c:25:0:
  /home/osamu/xilinx_axidma/driver/version_portability.h: In function ‘axidma_to_xilinx_type’:
 /home/osamu/xilinx_axidma/driver/version_portability.h:155:1: error: control reaches end of non-void function [-Werror=return-type]
   }
   ^
  cc1: all warnings being treated as errors
  scripts/Makefile.build:318: recipe for target '/home/osamu/xilinx_axidma/driver/axidma_dma.o' failed
  make[2]: *** [/home/osamu/xilinx_axidma/driver/axidma_dma.o] Error 1
  Makefile:1310: recipe for target '_module_/home/osamu/xilinx_axidma/driver' failed
  make[1]: *** [_module_/home/osamu/xilinx_axidma/driver] Error 2
  make[1]: Leaving directory '/home/osamu/linux-zturn'
  driver/Makefile:83: recipe for target 'driver/axidma.ko' failed
  make: *** [driver/axidma.ko] Error 2
 $ grep -A11 "struct xilinx_vdma_config {" ../linux-zturn/include/linux/amba/xilinx_dma.h
  struct xilinx_vdma_config {
          int frm_dly;
          int gen_lock;
          int master;
          int frm_cnt_en;
          int park;
          int park_frm;
          int coalesc;
          int delay;
          int reset;
          int ext_fsync;
  };
 $ head -3 ../linux-zturn/Makefile
  VERSION = 3
  PATCHLEVEL = 15
  SUBLEVEL = 0
 $ # https://github.com/bperez77/xilinx_axidma/issues/14
 $ jed driver/version_portability.h
  + #if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
  +     dma_config->vsize = height;         // Height of the image (in lines)
  +     dma_config->hsize = width * depth;  // Width of the image (in bytes)
  +     dma_config->stride = width * depth; // Number of bytes to process per line
  + #endif
    ...
  
  + #ifdef XILINX_DMA_IP_VDMA
  +     BUG_ON(dma_type != AXIDMA_DMA && dma_type != AXIDMA_VDMA);
  +     return (dma_type == AXIDMA_DMA) ? XILINX_DMA_IP_DMA : XILINX_DMA_IP_VDMA;
  + #else
  +     BUG_ON(dma_type != AXIDMA_DMA);
  +     return XILINX_DMA_IP_DMA;
  + #endif
    ...
  
  + #ifndef DMA_SUCCESS
  + // DMA_SUCCESS was renamed to DMA_COMPLETE (indicates a DMA transaction is done)
  + #define DMA_SUCCESS                 DMA_COMPLETE
  + #endif
  + 
  + #ifndef DMA_COMPL_SKIP_DEST_UNMAP
  + // The skip destination unmap DMA control option was removed in between 3.0 and 3.15 ?
  + #define DMA_COMPL_SKIP_DEST_UNMAP   0
  + #endif
 $ make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm KBUILD_DIR=/home/osamu/linux-zturn
 $ ls outputs/
  axidma.ko  axidma_benchmark  axidma_display_image  axidma_transfer  libaxidma.so

4.x 向けに対策されている内容の一部が先取りして入っているようで、
3.15 に対して同様の対策が必要でした。

上記のように無理矢理書き換えたところ、コンパイルが通りました。

** デバイスのインストール [#sa8cbf2c]

axidma.ko と libaxidma.so を z-turn 上の Linux へコピーして、

 LANG:console
 $ sudo chown root:root axidma.ko libaxidma.so
 $ sudo cp libaxidma.so /usr/local/lib/
 $ sudo insmod axidma.ko
 $ dmesg | tail
  [  469.438795] axidma: axidma_dma.c: axidma_dma_init: 674: DMA: Found 0 transmit channels and 1 receive channels.
  [  469.447790] axidma: axidma_dma.c: axidma_dma_init: 676: VDMA: Found 0 transmit channels and 0 receive channels.
 $ ls /dev/axidma
  /dev/axidma

正しく入ったみたい。

** bootargs の設定 [#q00cefa6]

ドライバの説明書きには

uEnv.txt の bootargs 行に cma=25M などと加えれば良いように書かれていましたが、

設定しないときのブート時のメッセージを見ると、

 cma: CMA: reserved 128 MiB at 27800000

となっていて、始めから十分な大きさが確保されているようでした。






** 使ってみる [#wda13c3c]

ドライバには、2種類の API があるようで、それぞれの使い方は
対応するヘッダファイルに詳しく書かれています。

- /dev/axidma への ioctrl で制御する方法 ([[include/axidma_ioctl.h>https://github.com/bperez77/xilinx_axidma/blob/master/include/axidma_ioctl.h]])
- ioctrl をラップして使いやすくするシェアドライブラリ ([[include/libaxidma.h>https://github.com/bperez77/xilinx_axidma/blob/master/include/libaxidma.h]])

後者はとても簡単に使えるようになっているので、こちらを使うことにします。

どちらを使った場合にも、Scatter Gather 機能のサポートは無いようです。

 LANG:c
 #include <stdio.h>
 #include <stdbool.h>
 #include "libaxidma.h"
 
 #define MYCHAN 0
 #define BUFFER_SIZE 1024
 #define TRANSFER_LEN 16
 
 void dma_int_handler(int channel_id, void *data)
 {
   if(channel_id != MYCHAN)
     printf("ERROR: unexpected channel id %d\n", channel_id);
 
   (*(int*)data) = 1; // done
 }
 
 int array_contains(int target, int *array, int length)
 {
   int i;
   for (i=0; i<length; i++)
     if ( array[i] == target ) return 1;
   return 0;
 }
 
 
 void display_result(void *buffer)
 {
   int i, *p = (int*)buffer;
   for(i=0; i<TRANSFER_LEN; i++) {
     printf("%08x ", *(p++));
     if (i % 8 == 7)
       printf("\n");
   }
 }
 
 void main()
 {
   // デバイスハンドルを得る
   axidma_dev_t hdma = axidma_init();
 
   // 使用するチャンネルが rx 機能を持っていることを確認
   int nrxchan, *rxchans = axidma_get_dma_rx(hdma, &nrxchan);
   if(!array_contains(MYCHAN, rxchans, nrxchan)) {
     printf("チャンネル %d は読み込みをサポートしません", MYCHAN);
     goto exit;
   }
 
   // バッファーを確保
   void *buffer = axidma_malloc(hdma, BUFFER_SIZE);
 
   // 割り込みハンドラを設定
   volatile int done = 0;
   axidma_set_callback(hdma, MYCHAN, dma_int_handler, (void*)&done);
 
   // 非同期転送開始
   printf("転送開始\n");
   int ret = axidma_oneway_transfer(hdma, AXIDMA_READ, MYCHAN, buffer, TRANSFER_LEN, false);
 
   // 終了待ち
   while (!done) {
     ;
     // 途中で止める場合
     // axidma_stop_transfer(hdma, MYCHAN, enum axidma_dir dir);
   }
 
   display_result(buffer);
 
   // バッファーを解放
   axidma_free(hdma, buffer, BUFFER_SIZE);
 
 exit:
 
   // デバイスハンドルを解放する
   axidma_destroy(hdma);
 }

** リンクできない? → 解決 [#bdbfbc73]

ごく普通にコンパイルしようとしたところ、

 LANG:console
 $ gcc main.c -L /usr/local/lib -l libaxidma
  /usr/bin/ld: -llibaxidma が見つかりません
 $ file /usr/local/lib/libaxidma.so
  /usr/local/lib/libaxidma.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=b533a8f1de3cf2fd0ca9faf9b7e1424b5dd8c776, not stripped

so ファイルはあるにもかかわらずリンクできないという症状に悩まされました。

理由がわからずかなり悩みましたが、
xilinx_axidma の make は exmples のコードもリンクできているのだから、
それをまねすればいいはず!と気づいて(実はデバイスドライバの説明にも「まねしろ」と書かれてました orz)、まずはどうしているか確認。

 LANG:console
 $ make -n CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm KBUILD_DIR=/home/osamu/linux-zturn
  ...
 
  arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -std=gnu99 -O3 -I include \
     examples/axidma_transfer.c examples/dma_util.c examples/util.c \
    -o examples/axidma_transfer -L outputs -l axidma -Wl,-rpath,'$ORIGIN'
  ...

怪しいオプションがあるのでこれをまねすると、

 LANG:console
 $ gcc main.c -L /usr/local/lib -l axidma -Wl,-rpath,'$ORIGIN'
 $ ls a.out
  a.out

リンクできました。

http://www.kotha.net/ghcguide_ja/7.0.4/using-shared-libs.html によれば、
この指定はアプリケーションプログラムと同じディレクトリに .so ファイルを
置くという宣言になるので、ちょっと変更して

 LANG:console
 $ cat > Makefile
  axidmatest: main.c
          gcc -o axidmatest main.c -L /usr/local/lib -l axidma -Wl,-rpath,'/usr/local/lib'
 $ make
  gcc -o axidmatest main.c -L /usr/local/lib -l axidma -Wl,-rpath,'/usr/local/lib'

としました。

** エラーが出る [#x4301f01]

 LANG:console
 $ cat /sys/class/dma/dma1chan0/memcpy_count
  0
 $ cat /sys/class/dma/dma1chan0/bytes_transferred
  0
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
  00000004 00000005 00000006 00000007 00000000 00000000 00000000 00000000
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
 $ cat /sys/class/dma/dma1chan0/memcpy_count
  0
 $ cat /sys/class/dma/dma1chan0/bytes_transferred
  0
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
  00000033 00000034 00000035 00000036 00000000 00000000 00000000 00000000
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
  00000062 00000063 00000064 00000065 00000000 00000000 00000000 00000000
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
  00000091 00000092 00000093 00000094 00000000 00000000 00000000 00000000
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
  000000c0 000000c1 000000c2 000000c3 00000000 00000000 00000000 00000000
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
  000000ef 000000f0 000000f1 000000f2 00000000 00000000 00000000 00000000
  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

おかしいところ:
- エラーが出ている xilinx-dma 40400000.axidma0: Channel e6f61950 has errors 11, cdr 0 tdr 0
- 4ワードしか更新されていない
- ゼロから始まっていない
- 等間隔 (0x2f ごと) に値が増えている

エラーを出しているのは xilinx の xilinx_dma.c のここで、~
https://github.com/Xilinx/linux-xlnx/blob/f6b354241c0b0eebd8aa9bff1411c24301e1e50f/drivers/dma/xilinx/xilinx_dma.c#L1474

axi_dma のステータスレジスタのエラービットをそのまま表示したのが 0x11 ということになる。

axi_dma のリファレンスによれば、~
https://www.xilinx.com/support/documentation/ip_documentation/axi_dma/v7_1/pg021_axi_dma.pdf

&ref(dmasr_register.png,,66%);

0x11 = DMAIntErr + Halted なので、問題は DMAIntErr だ。

このエラーが出る理由は、Stream から送られたデータ量が読み込み側が期待するより大きいとき、
とのことで、、、マジカッカカカ。

読み取り時に指定するパケットサイズを送信パケットサイズに合わせなければならないのだそうです。

** 受信できました [#md9f68cd]

パケットサイズをハードウェアに合わせて 256ワード = 1024バイト にする。

あと、TRANSFER_LEN をワード単位と勘違いしていたので、
display_result を修正して、

 LANG:c
 #define BUFFER_SIZE 2048
 #define TRANSFER_LEN 1024
 ...
 void display_result(void *buffer)
 {
   int i, *p = (int*)buffer;
   for(i=0; i<TRANSFER_LEN/4; i++) {
 ...

としたところ、正しく動作しました。

 LANG:console
 $ sudo ./axidmatest
  転送開始
  xilinx_dma_start_transfer::simple DMA mode
  00000800 00000801 00000802 00000803 00000804 00000805 00000806 00000807
  00000808 00000809 0000080a 0000080b 0000080c 0000080d 0000080e 0000080f
  ...
  000008f8 000008f9 000008fa 000008fb 000008fc 000008fd 000008fe 000008ff
 $ cat /sys/class/dma/dma1chan0/memcpy_count
  0 
 $ cat /sys/class/dma/dma1chan0/bytes_transferred
  0

/sys/class/dma/dma1chan0/ 以下の値が変化しないのはそういうものなのかどうか。

** 事前にパケットサイズがわかっていないと受け取れない? [#eead2d62]

このインタフェースを用いる限り、
パケットサイズが不明なデータを
DMA を使って受け取るのは難しいみたいですね。

axi_dma 自体の説明によればタイムアウトで割り込みを送出する
機能もあるようなので、どうにかうまく使えないか、後で調べたいと思います。

* 以下、もっと突っ込んだ話を勉強しなければならなくなった時用の資料 [#n06c1de4]

というか、上記の簡単な解にたどり着くまでに苦労した経緯だったりもします。

** 後で読むリスト [#dea41291]

Linux DMA from User Space~
https://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10693/1/Linux%20DMA%20from%20User%20Space-public.pdf

Linux DMA in Device Drivers~
https://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10658/1/drivers-session4-dma-4public.pdf

Introduction to Linux Device Drivers~
https://forums.xilinx.com/xlnx/attachments/xlnx/ELINUX/10669/1/drivers-sessions1-2-public.pdf

* axi_dma のドキュメント [#e7f3007e]

デザインダイアグラム上で axi_dma IP を右クリックして
[IP Documentations] から各種ドキュメントが出る。

https://www.xilinx.com/products/intellectual-property/axi_dma.html#overview

など。

* キャッシュについて [#g64cc687]

** ACP について [#uf2e1786]

accelerator coherency port を使うとキャッシュコヒーレンシーを
アプリケーション側で制御しなくても良くなるみたい?

http://marsee101.blog19.fc2.com/blog-entry-2297.html

* udmabuf [#o0e80ea9]

DMA バッファとして使うメモリ領域に Linux のユーザーアプリケーションから
アクセスするには udmabuf を使うのが良いらしい?

http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea

* Xilinx の XAxiDma ドライバ [#b6740db8]

自分で Linux ドライバを作るときやベアメタルでは
Xilinx の低レベルドライバを使うことになる?

C:\Xilinx\SDK\2016.4\data\embeddedsw\XilinxProcessorIPLib\drivers\axidma_v9_3\src

というようなディレクトリにソースコードが、

C:\Xilinx\SDK\2016.4\data\embeddedsw\XilinxProcessorIPLib\drivers\axidma_v9_3\doc/html/api/index.html

にドキュメントがある。[File List] から各ヘッダファイル *.h 
をクリックして関数一覧を見るのがわかりやすそう


** ドキュメントトップの日本語訳 [#z33ce603]

以下は AXI DMA エンジンドライバの API です。

DMA 機能の全詳細はハードウェアのスペックを参照してください。
このドライバでは次の機能を利用できます。

- Scatter-Gather DMA (SGDMA)
- Simple DMA
- Interrupts
- Programmable interrupt coalescing for SGDMA
- APIs to manage Buffer Descriptors (BD) movement to and from the SGDMA engine

*** Simple DMA [#b0d504bd]

Simple DMA API を使うと DMA とデバイスとの間の単一の転送処理を設定できます。
2つのチャンネルを利用可能で、1つは DMA からデバイスへ、もう1つはデバイスから DMA 
へのチャンネルになります。転送処理を開始するには対応するチャンネルに対応する
バッファーアドレスとバッファー長さの2つのフィールドを設定する必要があります。

*** 転送処理 [#lef55eb5]

転送処理を記述するのに使われる構造体は Buffer Descriptor (BD) と呼ばれます。
ユーザーアプリケーションは BD を malloc し、転送処理に用いるバッファーアドレス、
転送長、制御情報を設定します。制御情報は SOF や EOF を含みます。
これらの定数は xaxidma_hw.h に定義されています。

*** Scatter-Gather DMA (SGDMA) [#q9fc2618]

(訳注:axi_dma では IP のカスタマイズで [Enable Scatter Gather Engine] 
をオンにしないとこの機能は使えない)

SGDMA ではユーザーアプリケーションがメモリ上に次善に準備した一連の転送処理設定を、
ハードウェアが順に処理します。この処理中にユーザーアプリケーションが介在する
必要はありません。また、処理中に新たな処理内容を追加することも可能であり、
ハードウェアを常に働かせておくことができます。

ユーザーはハードウェアをポーリングする、あるいは割り込みを使うことで
転送処理の完了を知ることができます。

SGDMA はすべてのパケットを処理します。パケットは一続きのバイトデータで、1つの
メッセージを表します。SGDMA を使えば1つのパケットを複数の転送処理に分割できます。
例えば、14バイトのヘッダーと、それに続く1バイト以上のペイロードからなるIP
パケットを思い浮かべてください。SGDMA を使えばアプリケーションはヘッダーに対応する
BD と、ペイロードに対応する BD とを指定して、それらを1つのメッセージとして転送処理
することができます。この方法を使うと TCP/IP スタックをより効率的に作成できます。
なぜならパケットヘッダーとデータを異なるメモリ領域においたまま処理できるからです。
そうでなければパケットを一つの連続するメモリブロック上に組み立て直さなければなりません。

*** BD リングの管理 [#r0a72f92]

BD リングはソフトとハードとで共用されます。

ハードウェアは BD が連結リストの形で提供されることを期待します。DMA ハードウェアは
1つの BD の処理が終わるごとにその next pointer フィールドの値をたどることでリストを
次々に処理します。そして、ハードウェアの Tail Ptr レジスタに指定された BD を処理したら
停止します。

BD リングでは最終 BD は最初の BD へリンクされています(ので、処理は自動では停止しません)。

BD の管理はすべてドライバ内で行われます。ユーザーアプリケーションは BD のフィールドを
直接変更すべきではありません。BD フィールドの変更は常に対応する API 関数を通じて
行うようにしてください。

BD リング中ではドライバーは4つの BD グループを管理します。それぞれのグループは
0 個以上の連続する BD から構成されます。

:Free|
アプリケーションが XAxiDma_BdRingAlloc() で確保可能な(現在使われていない)BD
:Pre-process|
XAxiDma_BdRingAlloc() で確保済みの BD。
これらの BD はアプリケーションの管理下にあります。
アプリケーションはドライバ API を用いて BD の内容を編集し、DMA 転送の準備をします。
:Hardware|
XAxiDma_BdRingToHw() でハードウェアに渡され、処理待ちになっている BD。
これらの BD はハードウェアの管理下にあり、処理を待ち、処理中、処理済み、
のいずれかの状態にあります。
この状態の BD をアプリケーションが変更することはできません。
変更した場合にはデータが壊れたり、システムが不安定になったりしかねません。
:Post-process|
XAxiDma_BdRingFromHw() により Hardware グループから取り出された処理済みの BD。
これらの BD はアプリケーションの管理下にあります。アプリケーションは BD 
の転送処理結果を確認することができます。アプリケーションは XAxiDma_BdRingFree()
することで BD を Free グループに戻せます。

連続して転送処理を行うには、BD は下記のような状態変化をすることが期待されています。

&uml(
[*]-r->Free
Free: under Driver control
Free: to be alloc for use
Free-r->Preprocess : XAxiDma_BdRingAlloc() 
state "Pre-process" as Preprocess
Preprocess: under Application control
Preprocess: setup for Transaction
Preprocess-d->Free :  XAxiDma_BdRingUnAlloc()
Preprocess-d->Hardware :  XAxiDma_BdRingToHw()
state Hardware {
[*]-r->Queuing
Queuing-->InProcess
InProcess-->Processed
Processed-l->[*]
}
Hardware-l->PostProcess :  XAxiDma_BdRingFromHw()
state "Post-process" as PostProcess
PostProcess: under Application control
PostProcess: Transaction status can be checked
PostProcess-u->Free :  XAxiDma_BdRingFree()
);

Pre-process 段階で、DMA 転送をハードウェアのキューに入れる前にキャンセルするには、
アプリケーションは XAxiDma_BdRingUnAlloc() を呼んで BD を Free に戻すことができます。

API は BD リストを渡り歩くため、以下の関数を提供しています。
- XAxiDma_BdRingNext()
- XAxiDma_BdRingPrev()

これらの関数はどこでグループが終了し、次のグループが始まるかを認識しないため、
使う際には注意が必要です。

*** SGDMA デスクリプタリングの作成 [#lbc8babe]

BD リングは XAxiDma_BdRingCreate() で作成されます。BD リングのためのメモリは
アプリケーションが割り当てます。この領域は連続していなければなりません。
BD リングの設定には物理アドレスが必要となります。

アプリケーションは XAxiDma_BdRingMemCalc() で所望の数の BD を用意するのに必要な
メモリの大きさを計算できます。逆に、XAxiDma_BdRingCntCalc() により指定のメモリサイズに
何個の BD を格納可能か計算できます。

XAxiDma_BdRingClone() というヘルパ関数を使うことで、同じタイプの指示、例えば
SOF や EOF を持つ、複数の BD を含む BD リングを高速に準備できます。
XAxiDma_BdRingClone() した後、アプリケーションはバッファアドレスと転送長のみ
設定すれば良いことになります。1つのパケットのうちいくつかの BD、例えば
最初と最後のものは、特別な制御情報を設定する必要があるかもしれません。

*** デスクリプタリングのステートマシン [#c4c82e49]

BD リングには2つの状態があります。

- HALTED (H) : ハードウェアは停止中です。
- NOT HALTED (NH) : ハードウェアは動作中です。

DMA エンジンの状態遷移は次のようになります。

&uml(
[*]-->HALTED
HALTED-r->NOT_HALTED : StartBdRingHw() or BdRingStart() or Resume() 
HALTED: 停止中
NOT_HALTED-l->HALTED : Pause() or Reset()
NOT_HALTED: 動作中
);

*** 割り込みの併合 [#u6ae311d]

SGDMA は割り込み頻度を制御するために割り込みの併合機能を提供します。
DMA エンジンは割り込み併合を最適化するために2種類の方法を提供しています。

- パケット臨界値カウンタ:
エンジンにより指定された数のパケットが処理されるごとに1回割り込みを発生させます。
- パケット遅延時間カウンタ:
最後のパケットが処理された後、新しいパケットが処理されないまま指定の時間が
経過したら1回割り込みを発生させます。最低1つのパケットが処理されないと
割り込みは発生しないことに注意してください。

*** 割り込み [#c3a4d1c3]

割り込みはユーザーアプリケーションで処理されます。
DMA チャンネルごとに割り込みIDを持っています。
ドライバは割り込みを許可・拒否し、また、パケット処理の頻度に応じて割り込み頻度を
最適化するための API を提供します。

*** ソフトウェア初期化 [#y0e186bc]

Simple mode DMA エンジンを使い転送を行うには、次の設定手順に従います。

+ XAxiDma_CfgInitialize() により DMA を初期化します。
これにより指定された DMA エンジンに対するドライバインスタンスを初期化し、
エンジンをリセットします。
+ 割り込みモードを使う場合、割り込みを許可します。
割り込みシステムを適切に設定するのはアプリケーションの責任です。
少なくとも、エンジンが実際に割り込みを発行する前に割り込みハンドラを用意し、
適切に登録することが必要です。
+ 該当のチャンネルのバッファアドレスと転送長フィールドを設定し、DMA 転送を開始します。

SG モードで DMA エンジンを使い転送処理を行うには、次の手順に従います。

- XAxiDma_CfgInitialize() にて DMA を初期化します。
- BD リングを作成します。DMA チャンネルごとに XAxiDma_BdRingCreate().
により BD リングを作る必要があります。
- 割り込みモードを使うのであれば割り込みを許可します。
- DMA 転送を開始します: 一番始め、あるいはリセット後には XAxiDma_BdRingStart() 
により転送を開始します。チャンネルがすでに転送を開始しているなら、
XAxiDma_BdRingToHw() を使います。DMA チャンネルが動作していないときに
XAxiDma_BdRingToHw() しても BD はハードウェアに送られません。かわりに、
後に DMA チャンネルが XAxiDma_BdRingStart() で開始されてから処理されます。

*** DMA 転送処理の開始方法 [#c337e9e1]

ユーザーアプリケーションは XAxiDma_BdRingToHw() により BD をハードウェアに送り、
DMA 転送を開始します。

どちら向きのチャンネルにおいても、DMA エンジンが(XAxiDma_Pause() により)
現在停止中であれば、新たに追加された BD は受理されるものの、
XAxiDma_BdRingStart() により DMA エンジンが転送を開始する、あるいは
XAxiDma_Resume() により転送を再開するまで、処理されることはありません。

*** DMA 転送が終了した後の処理 [#e1700e04]

割り込みが設定され、許可されたなら、DMA チャンネルは
転送終了をソフトウェアへ通知するのために割り込みを行います。
割り込みを使わない場合、ユーザーアプリケーションは
BD の完了を XAxiDma_BdRingFromHw() あるいは 
XAxiDma_BdHwCompleted() によりポーリングすることもできます。

- BD が DMA チャンネルにより処理済みとなったら、
アプリケーションはまずそれらを XAxiDma_BdRingFromHw() 
により回収する必要があります。
- TX 側では、その時点でアプリケーションはその BD に関連づけられていた
データバッファーを解放することができます。なぜならバッファー中のデータは
すでに転送されたからです。
- RX 側では、その時点でアプリケーションは BD に関連づけられたデータ
バッファーのデータを利用可能になります。
- どちらのチャンネルにおいても、処理済みの BD は XAxiDma_BdRingFree()
により Free 状態に戻してやる必要があります。そうすることで以降の
転送処理で再利用することが可能になります。
- RX 側では、アプリケーションは常にいずれかの DB がデータ受け入れ
可能な状態にあるよう手配する責任があります。そうでないと、その RX 
チャンネルはデータを受け入れられなくなってしまいます。

*** 使用例 [#sbf3c318]

ドライバ API の使用方法を示すため、5つの例が挙げられています。
- SG 割り込みモード (xaxidma_example_sg_intr.c)~
複数の BD/パケットを転送します
- SB ポーリングモード (xaxidma_example_sg_poll.c) ~
単一 の BD を転送をします
- SB ポーリングモード (xaxidma_poll_multi_pkts.c) ~
複数の BD/パケットを転送します
- Simple ポーリングモード (xaxidma_example_simple_poll.c)
- Simple 割り込みモード (xaxidma_example_simple_intr.c)

*** アドレス変換 [#af60eac5]

ハードウェアに与えるすべてのバッファーアドレスおよび BD アドレスは物理アドレスです。

*** キャッシュの整合性 [#g5401c99]

このドライバは BD で指定されるすべてのアプリケーションバッファーは
キャッシュと整合性の取れたメモリ空間にあると仮定しています。
システムでキャッシュが使われている場合、データの送信元として
用いるバッファメモリはドライバに BD を渡す前にキャッシュを
フラッシュ(メモリへの書き戻し)する必要があります。
また、データの受信先として用いるバッファメモリは、受信された
データにアクセスする前にキャッシュを無効化しなければなりません。

*** データアラインメント [#s54707ff]

BD に対して:~
アラインメントの最小単位は XAXIDMA_BD_MINIMUM_ALIGNMENT 定数で
定義されています。

これはハードウェアとソフトウェアの両方が正しく動作するために
必要な最小アラインメントです。

デスクリプタリングがキャッシュメモリに置かれる場合、
アラインメントは最低限プロセッサーのキャッシュライン
サイズでなければなりません。
キャッシュサイズより大きいアラインメントを使う場合、
キャッシュラインの整数倍のアラインメントが必要です。
(訳注:XAXIDMA_BD_MINIMUM_ALIGNMENT とキャッシュライン
サイズの両方の倍数となるように取れということ)

デスクリプタリングを最初に作るとき(XAxiDma_BdRingCreate()
を参照)以外、BD のアラインメントが正しいかどうかランタイムの
チェックは働きません。

アプリケーションデータバッファについて:~
ハードウェアに DRE が実装されている限り、アプリケーション
データバッファは任意のアラインメントに基づくもので構いません。
そうでなければアプリケーションデータバッファはワードアラインメント
でなければなりません。ワードサイズは送信用には
XPAR_AXIDMA_0_M_AXIS_MM2S_TDATA_WIDTH で、受信用には
XPAR_AXIDMA_0_S_AXIS_S2MM_TDATA_WIDTH で定義されています。

複数の BD からなる BD チェインに対する SG 転送では、
それぞれの BD 転送長もワード単位でなければなりません。
さもないとハードウェア内で内部エラーが生じます。

*** エラーの取り扱い [#v52ea731]

DMA エンジンは任意のエラーに対して停止します。
ソフトウェアは新しい転送要求を開始できるようにするために
リセットをかける必要があります。

*** 停止後の再開 [#cc34cfe0]

DMA エンジンがリセットや、エラー後にリセットにより停止状態に
あるとき、ソフトウェアはリセットがかかった時点の DB ポインタを確認し、
XAxiDma_BdRingStart() により BD の処理を再開することができます。

*** 制限事項 [#oc640014]

このドライバは同時利用の排他処理機能を持っていません。
この種の保護はアプリケーション側で行う必要があります。

*** ハードウェアの初期設定と独占的使用 [#yc4fdc18]

初期化後、あるいはリセット後には DMA エンジンは以下のモードに初期設定されます。

- すべての割り込みは禁止されます
- 割り込み併合カウンタは1になります
- DMA エンジンは停止状態になります。
それぞれの DMA チャンネルは独立して処理を開始します。
それには、転送に BD が設定されていない状態であれば 
XAxiDma_StartBdRingHw() を使って、そうでなければ
XAxiDma_BdRingStart() を使います。

ドライバはレジスタや BD を独占的に使用します。
レジスタや BD へのすべてのアクセスはドライバインターフェースを
介して行われるべきです。

*** デバッグ表示 [#ec635106]

ドライバに対するデバッグ用メッセージを表示するには、
コンパイル時に -DDEBUG フラグを付けて下さい。
さらに、xdebug.h の中の "#undef DEBUG" という行をコメントアウトして下さい。

** API メモ [#od119c43]

*** xaxidma.h [#ef2e999d]

- enum Direction { XAXIDMA_DMA_TO_DEVICE, XAXIDMA_DEVICE_TO_DMA };
- XAxiDma_BdRing * XAxiDma_GetTxRing(XAxiDma * InstancePtr);
- XAxiDma_BdRing * XAxiDma_GetRxRing(XAxiDma * InstancePtr);
- XAxiDma_BdRing * XAxiDma_GetRxIndexRing(XAxiDma * InstancePtr, int RingIndex);
- bool XAxiDma_HasSg(XAxiDma * InstancePtr);
- void XAxiDma_IntrEnable(XAxiDma * InstancePtr, uint Mask, int Direction);
- uint XAxiDma_IntrGetEnabled(XAxiDma * InstancePtr, int Direction);
- void XAxiDma_IntrDisable(XAxiDma * InstancePtr, uint Mask, int Direction);
- uint XAxiDma_IntrGetIrq(XAxiDma * InstancePtr, int Direction);
- void XAxiDma_IntrAckIrq(XAxiDma * InstancePtr, uint Mask, int Direction);
- XAxiDma_Config *XAxiDma_LookupConfig(u32 DeviceId);
- int  XAxiDma_CfgInitialize(XAxiDma * InstancePtr, XAxiDma_Config *Config);
- void XAxiDma_Reset(XAxiDma * InstancePtr);
- int  XAxiDma_ResetIsDone(XAxiDma * InstancePtr);
- int  XAxiDma_Pause(XAxiDma * InstancePtr);
- int  XAxiDma_Resume(XAxiDma * InstancePtr);
- u32  XAxiDma_Busy(XAxiDma *InstancePtr,int Direction);
- u32  XAxiDma_SimpleTransfer(XAxiDma *InstancePtr, UINTPTR BuffAddr, u32 Length,int Direction);
- int  XAxiDma_SelectKeyHole(XAxiDma *InstancePtr, int Direction, int Select);
- int  XAxiDma_SelectCyclicMode(XAxiDma *InstancePtr, int Direction, int Select);
- int  XAxiDma_Selftest(XAxiDma * InstancePtr);

*** xaxidma_bdring.h [#tb54f8b9]

- int XAxiDma_BdRingCntCalc(u32 Alignment, u32 Bytes);
- int XAxiDma_BdRingMemCalc(u32 Alignment, u32 NumBd)
- int XAxiDma_BdRingGetCnt(XAxiDma_BdRing* RingPtr)
- int XAxiDma_BdRingGetFreeCnt(XAxiDma_BdRing* RingPtr)
- void XAxiDma_BdRingSnapShotCurrBd(XAxiDma_BdRing* RingPtr)
-- RingPtr->IsRxChannel, RingPtr->ChanBase, RingPtr->RingIndex を設定してから呼び出す
-- ハードウェアから現在値を読み出した値により RingPtr->BdaRestart, RingPtr->ChanBase 
がセットされる
- XAxiDma_Bd * XAxiDma_BdRingGetCurrBd(XAxiDma_BdRing* RingPtr)
- XAxiDma_Bd *XAxiDma_BdRingNext(XAxiDma_BdRing* RingPtr, XAxiDma_Bd *BdPtr)
-- 配列上で次の XAxiDma_Bd を返す。連結リストの Next ではない
-- Ring なので最後の要素の Next は最初の要素になる
- XAxiDma_Bd *XAxiDma_BdRingPrev(XAxiDma_BdRing* RingPtr, XAxiDma_Bd *BdPtr)
-- 配列上で前の XAxiDma_Bd を返す。連結リストの Prev ではない
-- Ring なので最初の要素の Prev は最後の要素になる
- u32 XAxiDma_BdRingGetSr(XAxiDma_BdRing* RingPtr)
-- Sr はステータスレジスタ
- u32 XAxiDma_BdRingGetError(XAxiDma_BdRing* RingPtr)
-- ステータスレジスタにエラービットのマスクをかけて返す
-- 内容を見るには XAXIDMA_ERR_*_MASK と & を取る (xaxidma_hw.h)
- int XAxiDma_BdRingHwIsStarted(XAxiDma_BdRing* RingPtr)
-- Halt か Not Halt かを見る
- int XAxiDma_BdRingBusy(XAxiDma_BdRing* RingPtr)
-- 実際に転送中かを見る
- void XAxiDma_BdRingIntEnable(XAxiDma_BdRing* RingPtr, u32 Mask)
-- XAXIDMA_CR_OFFSET レジスタを設定する
- u32 XAxiDma_BdRingIntGetEnabled(XAxiDma_BdRing* RingPtr)
-- XAXIDMA_CR_OFFSET レジスタを読む
- void XAxiDma_BdRingIntDisable(XAxiDma_BdRing* RingPtr, u32 Mask)
-- XAXIDMA_CR_OFFSET レジスタを設定する
- u32 XAxiDma_BdRingGetIrq(XAxiDma_BdRing* RingPtr)
-- IRQ_OFFSET レジスタを読む
-- アプリケーションは XAXIDMA_IRQ_*** 定数と & を取って解釈する
- void XAxiDma_BdRingAckIrq(XAxiDma_BdRing* RingPtr)
- int XAxiDma_StartBdRingHw(XAxiDma_BdRing* RingPtr);
- int XAxiDma_UpdateBdRingCDesc(XAxiDma_BdRing* RingPtr);
- u32 XAxiDma_BdRingCreate(XAxiDma_BdRing * RingPtr, UINTPTR PhysAddr, UINTPTR VirtAddr, u32 Alignment, int BdCount);
- int XAxiDma_BdRingClone(XAxiDma_BdRing * RingPtr, XAxiDma_Bd * SrcBdPtr);
- int XAxiDma_BdRingAlloc(XAxiDma_BdRing * RingPtr, int NumBd, XAxiDma_Bd ** BdSetPtr);
- int XAxiDma_BdRingUnAlloc(XAxiDma_BdRing * RingPtr, int NumBd, XAxiDma_Bd * BdSetPtr);
- int XAxiDma_BdRingToHw(XAxiDma_BdRing * RingPtr, int NumBd, XAxiDma_Bd * BdSetPtr);
- int XAxiDma_BdRingFromHw(XAxiDma_BdRing * RingPtr, int BdLimit, XAxiDma_Bd ** BdSetPtr);
- int XAxiDma_BdRingFree(XAxiDma_BdRing * RingPtr, int NumBd, XAxiDma_Bd * BdSetPtr);
- int XAxiDma_BdRingStart(XAxiDma_BdRing * RingPtr);
- int XAxiDma_BdRingSetCoalesce(XAxiDma_BdRing * RingPtr, u32 Counter, u32 Timer);
- void XAxiDma_BdRingGetCoalesce(XAxiDma_BdRing * RingPtr, u32 *CounterPtr, u32 *TimerPtr);

*** xaxidma_bd.h [#ac50fffc]

BD は

typedef u32 XAxiDma_Bd[XAXIDMA_BD_NUM_WORDS];

のように構造体じゃなく u32 の配列として定義されているのだけれど、

 LANG:c
 typedef struct {
   XAxiDma_Bd *Next;           // 64bit  次の BD
   void *Buffer;               // 64bit  バッファーアドレス
   struct {
     u8 TDest;                 // 8bit 
     u8 TReserved1;            // 8bit 
     u8 TId;                   // 8bit 
     u8 TReserved2;            // 8bit 
     u8 TUser;                 // 8bit 
     u8 TReserved3;            // 8bit 
     u8 ARCache;               // 8bit 
     u8 ARUser;                // 8bit 
   } MultiChannelControl;      // 32bit  MCCTL
   u16  Stride;                // 16bit
   u3   Reserved0;             //  3bit
   u13  VSize;                 // 13bit
   u16  Control;               // 16bit
   u16  Length;                // 16bit  転送長
   struct {
     u26 ;
     struct {
       u1 RXEOF;               //  1bit Last rx packet
       u1 RXSOF;               //  1bit First rx packet
       struct {
         u1 InternalError;     //  1bit
         u1 SlaveError;        //  1bit
         u1 DecodeError;       //  1bit
       } StatusErrorBits;      //  4bit
       u1 Completed;           //  1bit
     } StatusBits;             //  6bit
   } Status;                   // 32bit  STS
   u32  User0;                 // 32bit
   u32  User1;                 // 32bit
   u32  User2;                 // 32bit
   u32  User3;                 // 32bit
   u32  User4;                 // 32bit
   u32  Id;                    // 32bit
   u32  HasSTSCNTRL;           // 32bit
   u32  HasDRE;                // 32bit
 } XAxiDma_Bd;

と考えればいいみたい。

個々のフィールドにアクセスするために

- XAxiDma_BdClear(BdPtr) // ゼロフィルする
- XAxiDma_BdGetCtrl(BdPtr) // Control
- XAxiDma_BdSetCtrl(BdPtr, Data); // Control
- XAxiDma_BdGetSts(BdPtr) // Status
- XAxiDma_BdGetLength(BdPtr, LengthMask) // 設定した転送長
- XAxiDma_BdSetLength(BdPtr, LenBytes, LengthMask); // 転送長
- XAxiDma_BdSetId(BdPtr, u32 Id) // アプリケーションが付ける任意の ID
- XAxiDma_BdGetId(BdPtr) // アプリケーションが付けた任意の ID
- XAxiDma_BdGetBufAddr(BdPtr)
- XAxiDma_BdSetBufAddr(BdPtr, Addr);
- XAxiDma_BdSetBufAddrMicroMode(BdPtr, Addr);
- XAxiDma_BdHwCompleted(BdPtr) // Status の Completed ビットを読む
- XAxiDma_BdGetActualLength(BdPtr, LengthMask) // Status の転送されたデータ長を取り出す
- XAxiDma_BdSetTId(BdPtr, TId) // MCCTL の TId フィールド
- XAxiDma_BdGetTId(BdPtr) // MCCTL の TId フィールド
- XAxiDma_BdSetTDest(BdPtr, TDest) // MCCTL の TDest フィールド
- XAxiDma_BdGetTDest(BdPtr) // MCCTL の TDest フィールド
- XAxiDma_BdSetTUser(BdPtr, TUser) // MCCTL の TUser フィールド
- XAxiDma_BdGetTUser(BdPtr) // MCCTL の TUser フィールド
- XAxiDma_BdSetARCache(BdPtr, ARCache) // MCCTL の ARCache フィールド
- XAxiDma_BdGetARCache(BdPtr) // MCCTL の ARCache フィールド
- XAxiDma_BdSetARUser(BdPtr, ARUser) // MCCTL の ARUser フィールド
- XAxiDma_BdGetARUser(BdPtr) // MCCTL の ARUser フィールド
- XAxiDma_BdSetStride(BdPtr, Stride)
- XAxiDma_BdGetStride(BdPtr)
- XAxiDma_BdSetVSize(BdPtr, VSize)
- XAxiDma_BdGetVSize(BdPtr)
- XAxiDma_BdGetAppWord(BdPtr, Offset, int *Valid);
- XAxiDma_BdSetAppWord(BdPtr, Offset, Word);

* Xilinx の XAxiDma ドライバのサンプルを読む [#e3026669]
** Xilinx の XAxiDma ドライバのサンプルを読む [#e3026669]

エラー処理を飛ばして読んでみます。

割り込みについて XPAR_INTC_0_DEVICE_ID による #ifdef があるけれど、
Zynq は GIC を持っているので、XPAR_INTC_0_DEVICE_ID をセットしない、
xscugic.h ベースのコードを追えばいいはず?

Zynq での割り込み使用例:~
https://japan.xilinx.com/support/answers/50572.html

** xaxidma_example_simple_intr.c [#l75492f1]
*** xaxidma_example_simple_intr.c [#l75492f1]

+ XAxiDma_LookupConfig(DeviceId) で DeviceId に対するハードウェア情報を得る (xaxidma_sinit.c)
+ XAxiDma_CfgInitialize(&AxiDma, CfgPtr); によりハードウェア情報を元に AxiDma を設定する
-- 内部で呼ばれる XAxiDma_Reset がタイムアウトすると失敗する (AxiDma.Initialized == 0 となる)
+ XAxiDma_HasSg(&AxiDma) で SG モードかどうかを判別 (AxiDma.HasSg, CfgPtr->HasSg に対応)
-- SG モード対応だと Simple モードでは動作しない???
+ SetupIntrSystem 内で割り込みを設定
++ XScuGic_LookupConfig(INTC_DEVICE_ID); でハードウェア情報を得る
++ XScuGic_CfgInitialize(IntcPtr,IntcConfig,IntcConfig->CpuBaseAddress); で Intc を設定
++ XScuGic_SetPriorityTriggerType で Priority = 0xA0, TriggerType = 0x3 にする
--- Priority は小さいほど高い。8 の倍数が有効。
--- TriggerType = 3 は Rising edge sensitive
++ XScuGic_Connect(IntcPtr, XxIntrId, XxIntrHandler, AxiDmaPtr); で割り込みハンドラを設定
++ XScuGic_Enable(IntcPtr, XxIntrId); で割り込みを許可
++ Xil_ExceptionInit(); これ、実際には何もしない。 (lib\bsp\standalone_vX.X\src\arm\common\xil_exception.c)
++ Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, INTC_HANDLER, IntcPtr); 割り込みハンドラを登録
--- INTC_HANDLER は xscugic_intr.c の XScuGic_InterruptHandler を指しており、
そこから GIC へ登録したハンドラが呼ばれる
++ Xil_ExceptionEnable(); で cpsr の XIL_EXCEPTION_IRQ で指定したビットをクリアする
+ XAxiDma_IntrDisable で一旦 DMA からの割り込み信号送出を禁止する
+ XAxiDma_IntrEnable で DMA からの割り込み信号送出を許可する
+ Xil_DCacheFlushRange で送信バッファのキャッシュをメモリに書き出す
+ XAxiDma_SimpleTransfer で送受信の要求
+ 割り込みハンドラで
++ 引数から AxiDma へのポインタを得る
++ XAxiDma_IntrGetIrq により対応する IRQ 番号を得る
++ IrqStatus & XAXIDMA_IRQ_ALL_MASK で割り込みが生じたか確かめる
--- いくつかの割り込み信号線をまとめている場合にも対応できる
++ XAxiDma_IntrAckIrq により対応する IRQ に Acknowledge を送る
++ IrqStatus & XAXIDMA_IRQ_ERROR_MASK ならエラーで割り込みが生じた
--- XAxiDma_Reset して HALT 状態から戻す
--- アプリケーションにエラーを通知する
++ IrqStatus & XAXIDMA_IRQ_IOC_MASK なら正常終了
+ Xil_DCacheInvalidateRange で受信バッファのキャッシュ内容を無効化
+ DisableIntrSystem 内で割り込みハンドラを解除する
++ XScuGic_Disconnect で GIC から割り込みハンドラを解除する

 LANG:c
 #include "xscugic.h"
 
 void main(void)
 {
    int Status;
    XAxiDma_Config *Config;
    int Tries = NUMBER_OF_TRANSFERS;
    int Index;
    u8 *TxBufferPtr;
    u8 *RxBufferPtr;
    u8 Value;
 
    TxBufferPtr = (u8 *)TX_BUFFER_BASE ;
    RxBufferPtr = (u8 *)RX_BUFFER_BASE;
 
    Config = XAxiDma_LookupConfig(DMA_DEV_ID);
    XAxiDma_CfgInitialize(&AxiDma, Config);
 
    assert(!XAxiDma_HasSg(&AxiDma));
 
    // Set up Interrupt system
    SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
 
    // Disable all interrupts before setup
    XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
 
    // Enable all interrupts
    XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
 
    /* Initialize flags before start transfer test  */
    TxDone = 0;
    RxDone = 0;
    Error = 0;
 
    InitializeTxBuffer(TxBuffer);
 
    // Flush the SrcBuffer before the DMA transfer, in case the Data Cache is enabled
    Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
 
    // Send a packet
    for(Index = 0; Index < Tries; Index ++) {
 
        XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
                    MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
        XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,
                    MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
 
        // Wait TX done and RX done
        while (!TxDone && !RxDone && !Error) {
                // NOP
        }
        assert(!Error);
 
        // Transfer finished, check data
	Xil_DCacheInvalidateRange((u32)RxPacket, Length);
        Status = CheckData(MAX_PKT_LEN, 0xC);
        assert(Status == XST_SUCCESS);
    }
 
    // Disable TX and RX Ring interrupts and return success
    DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);
 }
 
 static void TxIntrHandler(void *Callback)
 {
    u32 IrqStatus;
    int TimeOut;
    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
 
    // Read and acknowledge pending interrupts
    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
 
    // If no interrupt is asserted, we do not do anything
    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK))
        return;
 
    // If error interrupt is asserted, raise error flag, reset the
    // hardware to recover from the error, and return with no further
    // processing.
    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
        Error = 1;
 
        // Reset should never fail for transmit channel
        XAxiDma_Reset(AxiDmaInst);
 
        TimeOut = RESET_TIMEOUT_COUNTER;
        while (TimeOut) {
            if (XAxiDma_ResetIsDone(AxiDmaInst))
                break;
            TimeOut -= 1;
        }
        return;
    }
 
    // If Completion interrupt is asserted, then set the TxDone flag
    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))
        TxDone = 1;
 }
 
 static void RxIntrHandler(void *Callback)
 {
    u32 IrqStatus;
    int TimeOut;
    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
 
    // Read and acknowledge pending interrupts
    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
 
    // If no interrupt is asserted, we do not do anything
    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK))
        return;
 
    // If error interrupt is asserted, raise error flag, reset the
    // hardware to recover from the error, and return with no further
    // processing.
    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
        Error = 1;
 
        // Reset could fail and hang
        // NEED a way to handle this or do not call it??
        XAxiDma_Reset(AxiDmaInst);
 
        TimeOut = RESET_TIMEOUT_COUNTER;
        while (TimeOut) {
            if(XAxiDma_ResetIsDone(AxiDmaInst))
                break;
            TimeOut -= 1;
        }
        return;
    }
 
    // If completion interrupt is asserted, then set RxDone flag
    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK))
        RxDone = 1;
 }
 
 static int SetupIntrSystem(INTC * IntcInstancePtr,
               XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
 {
    int Status;
 
    XScuGic_Config *IntcConfig;
 
    // Initialize the interrupt controller driver so that it is ready to use.
    XScuGic_LookupConfig(INTC_DEVICE_ID);
    XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
                    IntcConfig->CpuBaseAddress);
 
    XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
 
     // 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.
    XScuGic_Connect(IntcInstancePtr, TxIntrId,
                (Xil_InterruptHandler)TxIntrHandler, AxiDmaPtr);
 
    XScuGic_Connect(IntcInstancePtr, RxIntrId,
                (Xil_InterruptHandler)RxIntrHandler, AxiDmaPtr);
 
    XScuGic_Enable(IntcInstancePtr, TxIntrId);
    XScuGic_Enable(IntcInstancePtr, RxIntrId);
 
    // Enable interrupts from the hardware
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler)INTC_HANDLER,
            (void *)IntcInstancePtr);
 
    Xil_ExceptionEnable();
 
    return XST_SUCCESS;
 }
 
 static void DisableIntrSystem(INTC * IntcInstancePtr,
                    u16 TxIntrId, u16 RxIntrId)
 {
    XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
    XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
 }

* zynq-xdma のソースを読んでみる [#qabc22a4]
*** Xilinx の XAxiDma ドライバ から直接叩いてみる [#a1f5bcfa]

#これじゃなくて xilinx 純正のドライバを使うべきなのかも
ベアメタルで試します

https://github.com/bmartini/zynq-xdma/blob/master/dev/xdma.c
* コメント・質問 [#m05abd7d]

事前情報として、使い方の基本を見る。
#article_kcaptcha

- xdma-demo.c
++ fd = open("/dev/xdma", O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0600);
++ map = mmap(0, BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
++ map[i] = data; のようにしてデータを書き込める
++ ioctl(fd, XDMA_GET_NUM_DEVICES, &num_devices) で num_devices を得る
++ xdma_dev dev に dev.device_id = num_devices - 1; として、
ioctl(fd, XDMA_GET_DEV_INFO, &dev) で情報を得る
--- rx, tx 別に、irq 番号とハンドラへ渡すポインタ値を読む
++ xdma_chan_cfg rx_config, tx_config を作って、
ioctl(fd, XDMA_DEVICE_CONTROL, &xx_config) で設定
--- irq, 転送方向, 割込併合カウンタ, delay, reset を設定
++ xdma_buf_info rx_buf, tx_buf を作って、
ioctl(fd, XDMA_PREP_BUF, &xx_buf) で設定
--- irq, ハンドラへ渡すポインタ値, cookie, map先頭からのオフセット, 転送長, 方向 を設定
++ xdma_transfer rx_trans, tx_trans を作って、
ioctl(fd, XDMA_START_TRANSFER, &xx_trans) で設定
--- irq, ハンドラへ渡すポインタ値, cookie, wait を設定
++ munmap(map, FILESIZE) でバッファを解放
++ close(fd)

何かいくつもの項目で irq やら何やら重複する設定を繰り返さなければならないのがいやな感じ

コード中で割り込み処理をしていないのはどういうこと?

時間のかかる転送だったらこれでは困るはず。

以下、ドライバのソースを読みやすくしながら抜粋:

xdma.h
 LANG:c
 #define MODULE_NAME	"xdma"
 #define DMA_LENGTH	(32*1024*1024)
 #define MAX_DEVICES     4
 
 #define XDMA_IOCTL_BASE	'W'
 #define XDMA_GET_NUM_DEVICES	_IO(XDMA_IOCTL_BASE, 0)
 #define XDMA_GET_DEV_INFO	_IO(XDMA_IOCTL_BASE, 1)
 #define XDMA_DEVICE_CONTROL	_IO(XDMA_IOCTL_BASE, 2)
 #define XDMA_PREP_BUF		_IO(XDMA_IOCTL_BASE, 3)
 #define XDMA_START_TRANSFER	_IO(XDMA_IOCTL_BASE, 4)
 #define XDMA_STOP_TRANSFER	_IO(XDMA_IOCTL_BASE, 5)
 #define XDMA_TEST_TRANSFER	_IO(XDMA_IOCTL_BASE, 6)

xdma.c
 LANG:c
 module_init(xdma_init);
 module_exit(xdma_exit);
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Wrapper Driver For A Xilinx DMA Engine");
 
 u32 num_devices;
 static dev_t dev_num;       // Global variable for the device number
 
 static int __init xdma_init(void)
 {
     num_devices = 0;
 
     /* device constructor */
     alloc_chrdev_region(&dev_num, 0, 1, MODULE_NAME);
     cl = class_create(THIS_MODULE, MODULE_NAME);
     device_create(cl, NULL, dev_num, NULL, MODULE_NAME);
 
     cdev_init(&c_dev, &fops);
     cdev_add(&c_dev, dev_num, 1);
 
     /* allocate mmap area */
     xdma_addr = dma_zalloc_coherent(NULL, DMA_LENGTH, &xdma_handle, GFP_KERNEL);
 
     /* hardware setup */
     xdma_probe();
 
     return 0;
 }
 
 static void __exit xdma_exit(void)
 {
     cdev_del(&c_dev);
     device_destroy(cl, dev_num);
     class_destroy(cl);
     unregister_chrdev_region(dev_num, 1);
 
     /* hardware shutdown */
     xdma_remove();
 
     /* free mmap area */
     dma_free_coherent(NULL, DMA_LENGTH, xdma_addr, xdma_handle);
 }
 
 static void xdma_probe(void)
 {
     dma_cap_mask_t mask;
     u32 match_tx, match_rx;
     struct dma_chan *tx_chan, *rx_chan;
 
     dma_cap_zero(mask);
     dma_cap_set(DMA_SLAVE | DMA_PRIVATE, mask);
 
     // https://github.com/Xilinx/linux-xlnx/blob/xlnx_3.14/include/linux/amba/xilinx_dma.h
     // によれば XILINX_DMA_DEVICE_ID_SHIFT は 28
     for (;;) {
         match_tx = (DMA_MEM_TO_DEV & 0xFF) | XILINX_DMA_IP_DMA |
             (num_devices << XILINX_DMA_DEVICE_ID_SHIFT);
         tx_chan = dma_request_channel(mask, xdma_filter, (void *)&match_tx);
 
         match_rx = (DMA_DEV_TO_MEM & 0xFF) | XILINX_DMA_IP_DMA |
             (num_devices << XILINX_DMA_DEVICE_ID_SHIFT);
         rx_chan = dma_request_channel(mask, xdma_filter, (void *)&match_rx);
 
         if (!tx_chan && !rx_chan) break;
 
         // この中で num_devices がインクリメントされるという驚きの設計
         xdma_add_dev_info(tx_chan, rx_chan);
     }
 }
 
 static bool xdma_filter(struct dma_chan *chan, void *param)
 {
     if (*((int *)chan->private) == *(int *)param)
         return true;
     return false;
 }
 
 static void xdma_add_dev_info(struct dma_chan *tx_chan,
                   struct dma_chan *rx_chan)
 {
     struct completion *tx_cmp, *rx_cmp;
 
     tx_cmp = (struct completion *)
         kzalloc(sizeof(struct completion), GFP_KERNEL);
 
     rx_cmp = (struct completion *)
         kzalloc(sizeof(struct completion), GFP_KERNEL);
 
     xdma_dev_info[num_devices] = (struct xdma_dev *)
         kzalloc(sizeof(struct xdma_dev), GFP_KERNEL);
 
     xdma_dev_info[num_devices]->tx_chan = (u32) tx_chan;
     xdma_dev_info[num_devices]->tx_cmp = (u32) tx_cmp;
 
     xdma_dev_info[num_devices]->rx_chan = (u32) rx_chan;
     xdma_dev_info[num_devices]->rx_cmp = (u32) rx_cmp;
 
     xdma_dev_info[num_devices]->device_id = num_devices;
     num_devices++;
 }
 
 static void xdma_remove(void)
 {
     int i;
 
     for (i = 0; i < MAX_DEVICES; i++) {
         if (xdma_dev_info[i]) {
             if (xdma_dev_info[i]->tx_chan)
                 dma_release_channel((struct dma_chan *)
                             xdma_dev_info[i]->tx_chan);
 
             if (xdma_dev_info[i]->tx_cmp)
                 kfree((struct completion *)
                       xdma_dev_info[i]->tx_cmp);
 
             if (xdma_dev_info[i]->rx_chan)
                 dma_release_channel((struct dma_chan *)
                             xdma_dev_info[i]->rx_chan);
 
             if (xdma_dev_info[i]->rx_cmp)
                 kfree((struct completion *)
                       xdma_dev_info[i]->rx_cmp);
 
         }
     }
 }

* xilinx_axidma の使い方を調べる [#h7b14a1c]

https://github.com/Xilinx/linux-xlnx/blob/xlnx_3.17/drivers/dma/xilinx/xilinx_axidma.c

http://qiita.com/wstone/items/ab4d47a3690bb1a9bf4c

http://zedboard.org/content/axi-dma-driver-linux

これが使えるとかなり楽そう。~
https://github.com/bperez77/xilinx_axidma

** カーネルオプションを確認 [#c50efd43]

z-turn 付属のカーネルにおいて

- CONFIG_CMA=y
- CONFIG_DMA_CMA=y
- CONFIG_XILINX_DMAENGINES=y
- CONFIG_XILINX_AXIDMA=y
- CONFIG_XILINX_AXIVDMA=y
- CONFIG_DMA_SHARED_BUFFER=y

のうち、CONFIG_XILINX_DMAENGINES が無かったが、代わりに 

- CONFIG_XILINX_DMA_ENGINES=y

があったので、必要なものは初めから揃っているっぽい。

** Device Tree [#ia885cc1]

[[電気回路/HDL/VivadoでAXIバスを利用#sf57016a]] で作ったデバイスは、

- address = 0x40400000
- irq = GIC の F2P[4]
- s2mm のみ
- データ幅 32

DTS における割り込み番号の設定方法は、
http://qiita.com/ikwzm/items/b22592c31cdbb9ab6cf7 によれば、

> interrupts=<0x0 0x1d 0x4> の2番目のパラメータの0x1d(十進数で29)が割り込み番号を指定しています。DMAコントローラーの割り込みはPS部のIRQ_F2P[0]に接続しています。ZYNQのテクニカルリファレンスマニュアルによれば、IRQ_F2P[0]は汎用割り込みコントローラー(GIC)内の共有ペリフェラル割り込み(SPI)の61番を通じて割り込みがかかります。このパラメータには61から32引いた値を設定するようです。
>
>interrupts=<0x0 0x1d 0x4>の1番目のパラメータはGIC内部の割り込みの種類を指定します。GIC内部には共有ペリフェラル割り込み(SPI)の他にCPUプライベートペリフェラル割り込み(PPI)とソフトウェア生成割り込み(SGI)があります。共有ペリフェラル割り込み(SPI)を使う場合は0を指定します。
>
>interrupts=<0x0 0x1d 0x4>の3番目のパラメータは割り込み信号がエッジトリガーかレベルトリガーかを指定します。1を指定するとエッジトリガー、4を指定するとレベルトリガーになります。

とのことなので、今の場合は
- 0 = 共有ペリフェラル割り込み(SPI)
- 33 = 61 + 4 - 32 = 33
- 4 = レベルトリガー

 axidma_chrdev: axidma_chrdev@0 {
     compatible = "xlnx,axidma-chrdev";
     dmas = <&axi_dma_0 0>;
     dma-names = "rx_channel";
 };
 
 axi_dma_0: axidma0@40400000 {
     compatible = "xlnx,axi-dma", "xlnx,axi-dma-6.03.a", "xlnx,axi-dma-1.00.a";
     reg = <0x40400000 0x10000>;
     xlnx,include-sg;
     #dma-cells = <1>;
 
     dma-s2mm-channel@40400000 {
         compatible = "xlnx,axi-dma-s2mm-channel";
         dma-channels = <1>;
         xlnx,datawidth = <32>;
         xlnx,device-id = <0x0>;
         clocks = <&clkc 15>;
         clock-names = "axis";
         interrupt-parent = <&intc>;
         interrupts = <0 33 4>;
     };
 };

となりそうです?


Counter: 27674 (from 2010/06/03), today: 9, yesterday: 0