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 はいくらだろうか?
の、ハードウェア定義ファイルらしきテキストを見ると、(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 に置いてあるメモリはよく見える。
訂正
図とか修正するの面倒なので放置してますが、0x8e000000 は (32,14)、0x8f000000 は(32,15) です。