parallella データ共有 後編

前回までのあらすじ : どうやら0x8f000000 にアクセスすると、eLinkに出るらしい。


さて、アドレスの下位20bit(1MB) が、コア内のアドレスを示すということは、上位12bitが、各コアのIDを示すということになる。この12bitを半分に割って、上位6bitがrow、下位6bitがcolとなる。

つまり、0x8f000000 は、(row,col)=(16,15) となる。

では、この(16,15)は、Parallella ボードに付いている、E16G301から見て、East側に見えるのかどうか…だが、今のE16G301の row、col はいくらだろうか?

https://github.com/adapteva/epiphany-libs/blob/2014.11/bsps/parallella_E16G3_1GB/parallella_E16G3_1GB.hdf

の、ハードウェア定義ファイルらしきテキストを見ると、(row,col) = (32,8)、となっている。が、この値は実際には、ハードウェアの設定には影響を与えず、ソフトウェア的にこれを使うだけである。

ハードウェア設定は、
http://www.adapteva.com/docs/e16g301_datasheet.pdf
の、2.1 に、Reset_N のエッジ立ち上がり時の、ROWID、COLIDピンの値によって決められると書かれている。


ROWID、COLIDは、

COLID[0] F12
COLID[1] E8
COLID[2] F10
COLID[3] E7
ROWID[0] E10
ROWID[1] E11
ROWID[2] E9
ROWID[3] F9

と対応すると、http://www.adapteva.com/docs/e16g301_datasheet.pdf の 6.5 Complete Package Pin-out に書かれていて、

http://parallella.org/docs/parallella_schematic.pdf を見ると、E8、F9が、DNI で、H に上げられてて、

COLID[0] 0
COLID[1] 1
COLID[2] 0
COLID[3] 0
ROWID[0] 0
ROWID[1] 0
ROWID[2] 0
ROWID[3] 1

となることがわかる。で、これが4コア単位の値なので、col=2x4=8, row=8x4=32、より、parallella ボードの座標設定は、(32,8) となる。

http://www.adapteva.com/docs/epiphany_arch_ref.pdf

の、Table 3を見ると、

Address-Row Tag Address-Column Tag Routing Direction
Greater Than Mesh-Node Column Don’t Care East
Less Than Mesh-Node Column Don’t Care West
Matches Mesh-Node Column Less Than Mesh-Node Row North
Matches Mesh-Node Column Greater Than Mesh-Node Row South
Matches Mesh-Node Column Matches Mesh-Node Row Into Mesh Node

と、なっていて、まず、Column アドレスで比較して、ルーティング方向を決めていることがわかる。(16,15) は、Columnが(32,8)より大きいので、このアクセスはEast側へ流れていくわけである。


まとめると、

  • 0x8f000000 へのアクセスは、(row,col) = (16,15) へアクセスしていると解釈される
  • 自分のチップの座標は、(32,8) なので、0x8f000000 へのアクセスは、Columnが大きい方へルーティングする必要があると判断される
  • Columnの大きいほう、つまり、East 側へアクセスが流れていく
  • (16,15) は、E16G301 の外にあるので、East側のeLinkを経由して、アクセスが外へ流れていく

というわけである。


Zynq による DDR3アクセス

というわけで、0x8f000000 へのアクセスは、eLinkを経由して、外へ流れていくことがわかった。これを受け取っているのが、Zynqになる。


まずはeLinkだが、

http://www.adapteva.com/docs/e16g301_datasheet.pdf

を見ると、「詳細は https://github.com/parallella/parallella-hw を見てね」とか書いてあるので、Zynqの中身を理解するにしても、eLinkを理解するにしても、

https://github.com/parallella/parallella-hw/tree/master/fpga/hdl/elink

を読むしかないようだ。


まあVerilog読めないので、以下はあんまり信用しないで流し読みしてもらうと良いのだが、

E16G301側から、0x8f000000 へアクセスすると、なんかしら経由でこのアドレスが、dstaddr として流れてくる。

それが
https://github.com/parallella/parallella-hw/blob/master/fpga/hdl/elink/ewrapper_link_rxi.v#L310
https://github.com/parallella/parallella-hw/blob/master/fpga/hdl/elink/ewrapper_link_rxi.v#L62

https://github.com/parallella/parallella-hw/blob/master/fpga/hdl/elink/ewrapper_link_receiver.v

とかに繋がって、

https://github.com/parallella/parallella-hw/blob/master/fpga/hdl/parallella-I/parallella.v#L514

で、0x80000000 かつ、AXI内部(0x81000000)でない場合に、外部メモリへ流れていきそうな雰囲気がある(が、こっから先は追いかけてないのでよくわからない)。


こうやって、Epiphany が、0x8XXXXXXXへアクセスすると、0x3XXXXXXXに置いてあるDDR3へアクセスするようになっている。

(Epiphany編終わり)

ARM による DDR3 アクセス

次は、ARMだが、こちらは、そんなに難しくない。Linux には /dev/mem というデバイスがあって、これを使うと、ユーザー空間から、直接物理アドレスにアクセスすることが可能である。


例えば、ACPI 対応の PC/AT の場合、0x000e0000 - 0x000fffff の間に、RSD PTR という文字列が埋まっている可能性が高いのだが、

#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>
#include <stdio.h>

int
main()
{
    int fd = open("/dev/mem", O_RDONLY);

    if (fd == -1) {
        perror("/dev/mem");
        return 1;
    }

    char *addr = (char*)mmap(0, 0x20000, PROT_READ, MAP_PRIVATE, fd, 0x000e0000);

    if (addr == MAP_FAILED) {
        perror("mmap");
    }

    for (int i=0; i<0x20000; i++) {
        if (strncmp("RSD PTR", addr+i, 7) == 0) {
            printf("found @ %p\n", (uintptr_t)(0x000e0000+i));
            return 0;
        }
    }

    puts("not found");
}

とかみたいなコードを書くと、物理アドレス 0x000e0000 - 0x000fffff のメモリを読むことができる。


Parallella の場合、Epiphany と共有しているメモリは、0x3e000000 〜 0x3fffffff の32MBなので、/dev/mem 経由で、これをmmap すればよい。

それをやっているのが、
https://github.com/adapteva/epiphany-libs/blob/esdk.5.13.09.10/src/e-hal/src/epiphany-hal.c#L584
このe_alloc関数である。


Hello World サンプルでも、
https://github.com/adapteva/epiphany-examples/blob/2014.05/apps/hello-world/src/hello_world.c#L59
で、この、e_allocを呼んでいることが確認できる。

Epiphany メモリから結果を読んでいる、e_read関数も、

実体は、
https://github.com/adapteva/epiphany-libs/blob/esdk.5.13.09.10/src/e-hal/src/epiphany-hal.c#L715
このmmap したアドレスに memcpy しているだけである。

なお、0x3e000000 〜 0x3fffffff の領域は、どっかでLinuxのメモリから除外されていて、

/proc/iomem を見ると、

$ cat /proc/iomem 
00000000-3dffffff : System RAM
  00008000-008dd26f : Kernel code
  00910000-00ea00c7 : Kernel data
66000000-6600ffff : /fpga-axi@0/axi-clkgen@66000000
6c000000-6c00ffff : /fpga-axi@0/axi_hdmi@6C000000
75c00000-75c00fff : /fpga-axi@0/axi-spdif-tx@0x75c00000
e0001000-e0001ffe : xuartps
e0004000-e0004fff : /i2c@e0004000
e000a000-e000afff : /amba@0/gpio@e000a000
e000b000-e000bfff : /amba@0/eth@e000b000
e000d000-e000dfff : /amba@0/qspi@e000d000
e0101000-e0101fff : mmc0
f8003000-f8003fff : /amba@0/ps7-dma@f8003000
  f8003000-f8003fff : /amba@0/ps7-dma@f8003000
f8005000-f80050ff : /amba@0/swdt@f8005000
f8007000-f80070ff : /amba@0/devcfg@f8007000
f8007100-f800711f : /amba@0/xadc@f8007100

System RAM 領域は0x3dffffff までになっていることが確認できる。

まとめ

0x3e000000 に置いてあるメモリはよく見える。