2015年7月29日水曜日

ZYNQのMIO GPIOをLinuxから使う

ZYNQのMIOピンは、わりと制限事項があって割り振り自由度もそう高くないので、だんだんPL部が高機能になってくると最終的にはMIOのどうでもいいピンがデバッグ用に残ったりします。

ZYBOだとMIO7にLEDと、MIO50,51にスイッチが着いてます。

で、このMIO7のLEDをGPIOとして光らそうと思ってえらく苦労したのでメモを残します。



まず、日本人ならほとんどの zynq ユーザーが参考にしてそうな marsee さんの以下の記事
http://marsee101.blog19.fc2.com/blog-entry-2203.html

によると、ベースアドレス0xE000A000以降に必要なレジスタが割り振ってあって、方向指定、アウトイネーブルを1にしたあと出力レジスタのビットを1にすれば良さそうです。
んが、この通りにやっても反応がありませんでした。読み出し方向も全く反応が無く、全レジスタ0が読めるだけです。

Linuxからだと上位1Gの空間にアクセスするのに制限があるのか? とか勘違い迷宮にはまり込んで、LEDドライバを流用したカーネル空間からあれこれトライするも、やっぱりダメです。



ベアメタルでMIOのGPIOを使うサンプルはxilinxにもいろいろあるので、ためしにFSBLのカスタマイズ部分  ( fsbl_hooks.c の FsblHookBeforeHandoff の中 ) に、marseeさんと同じ書き方で
volatile unsigned int* mio_base;
mio_base = 0xe000a000;
*(mio_base + 0x81) = 0x80;
*(mio_base + 0x82) = 0x80;
*(mio_base + 0x10) = 0x80;
と書いてみると、みごとにMIO7のLEDが光ります。


つまりPLのコンフィギュレーションやレジスタの使い方は合っていて、Linuxが起動しちゃうと動作しないわけですから、上記のレジスタ以外の上位概念のせいで動作しなくなっているようです。

テクニカルマニュアル
http://japan.xilinx.com/support/documentation/user_guides/j_ug585-Zynq-7000-TRM.pdf
のgpioセクションをきれいな心で読み返してみると、上位概念は
  • モジュール単位のリセット
  • モジュール単位のクロック
  • 出力ピンのトライステート指定
の3つがあるようです。

今困ってる現象では読み出しもまったく機能せずレジスタが0に見えているので、Linuxがリセットかクロックのどちらかを操作してGPIOモジュールを止めたままにしてるんではないかと思います。



強制的に slcr を操作してモジュールを動かすこともできるかもしれませんが、Linux流のやりかたはLinuxにちゃんと用意されてる gpio-zynq ドライバで初期化する、という方法だと思います。


xilinxドライバ参考資料
http://www.wiki.xilinx.com/Linux+GPIO+Driver
これに加えてxilinx ZYNQ用のGPIOドライバ( gpio-zynq.c )をmenuConfigで有効にしておきます。

dtsにはzynq用ものなら最初からgpioについて書かれてるのでそのままでOKです。


起動すると、特にdmesgにgpioドライバのメッセージはありませんが、なんとなく起動してるっぽくて /sys/class/gpioを見ると gpiochip906 というフォルダが増えました。

が、使い方がよくわからないまま、xilinxの参考資料のとおりに
  >echo 240 > /sys/class/gpio/export
とかやっても特に制御用のgpio204フォルダが出現するわけでもなく、正しく動いてるのかどうかわかりません。

まあ、ドライバは起動してるんだから行けるかな、と思ってここで冒頭のmarseeさんの記事にあったコードを (MIO10ピンを意味する0x400はMIO7相当の0x80に変更して) 実行しても、やっぱり無反応のまま…というところでまた途方にくれていました。



が! その後しばらくぐぐっていたら、xilinxフォーラムに
http://forums.xilinx.com/t5/Embedded-Linux/Zynq-Petalinux-hangs-on-GPIO-access/td-p/645008
という記事があり、一番下の方のデバッグ出力を見ると
GPIOs 906-1023, platform/e000a000.gpio, zynq_gpio:
という箇所があるのを見つけました。906って、さっき出現したgpiochip906と同じです。
つーことは、zynqのmio/emio用gpioモジュールは、ドライバ内ではピン番号906~にマップされてる、てことみたいですね。


それなら! というわけで、MIO7相当の
  >echo 913 > /sys/class/gpio/export
とやってみると、見事にgpio913というフォルダが出現し、その中のdirectionやvalueに1を書き込むとMIO7のLEDが点灯しました。


また、一度このexport操作をすると、後はmarseeさんのコードの通りにレジスタを操作する方法でもLEDのON/OFFが可能でした。

つまり、906以降のどこかのピンに対して最初にexportをしたときにgpioモジュールのクロック供給が開始されてる、てことみたいです。

これでやっと、MIOピンを無駄にせずLinuxからIOとして使えるようになりました。

2015年7月12日日曜日

X1 Carbon SSD交換

新職場で高級ノートのThinkpad X1 carbon (3rd Gen. 2015) を買ってもらったのは良かったのですが、固定資産にならない縛りのためにSSDは増量できず128Gの最低容量でした。

これが、zynqやらVS2013やらやりだすともう足りないのなんの。
速いUSBメモリのextremeにubuntuマシンのHDDイメージを入れたりしてなんとか耐えていましたが、ディスク開き要領3Gとかで常時使うことになってしまって精神衛生上良くないです。ウェアレベリングがあるので壊れはしないと思いますが…

調べてみると、128GモデルはSSDがPCIEx接続じゃないので遅いとか書いてありました。体感ではちょー速いのでさっぱりわかりませんが。


とりあえず開けてみる。スロット部分だけ取れる蓋とか無いですが、簡単に底面がぜんぶ外れるので超簡単。
M2スロットがいっぱいあって、電話用のスロットが空いてるけど当然ここにSSDを挿しても機能しないらしいです。

SSDはSAMSUNGのPM851とかいうシリーズで、型番 MZNTE128HMGR でした。
SATA規格で繋がってるとのことで、500MB/secが最高らしいですが、ベンチマーク結果は
やっぱり速いです。容量さえあれば文句ないのに。



というわけで、交換しちゃいましょう。

X1 carbonの3rd Gen.は、上位モデルだとSM951というさらに速いのを積んでるらしいですが、出たばかりで512Gモデルは簡単には手にはいらなそうです。

しょうがないので、少し前のモデルだけどXP941というシリーズの512Gモジュールを買ってきました。5万円くらい。
両面実装ですが、X1 carbon側のスロットもちゃんと上下とも空間が空いてるので問題なく挿せます。


交換してベンチマーク。

はや! シーケンシャルアクセスは倍超え!
4KQ32リードが遅くなってるように見えますがここはやるたびにかなり前後するので正直よくわかりません。

ちなみに体感では、特に違いはわかりませんでした。とにかく広いのでうれしい。

ZYNQで追加のUARTポートを使う(3) 16550互換IP

uartliteはドライバに手をいれることで特に問題なく動きました。

けど、なんかとても不安ですよね。こんな状態で配布されてるって、どんだけzynqでこれを使ってる人が少ないのだろうかと。


実績を重んじる立場からすると、多少IPフットプリントが大きくてもドライバに実績がある16550互換IPを使ったほうがいい、と思うケースもあると思います。

こちらはすんなり動く・・・かと思ったのですが、少し罠があったので紹介しておきます。


まずPL側は、IPを配置して線を繋ぎます。AXIバスの他に、TX/RXに相当するsout/sinと、割り込み線だけは繋いでください。他は全て無結線でも大丈夫です。

次にDTSは適当にぐぐってきたのを参考に書きます。割り込みはuartliteの時と同様に61-32=29、アドレスは43C00000にしました。
axi_uart0: serial@43C00000 {
   current-speed = <115200>;
   clock-frequency = <100000000>;
   compatible = "ns16550a";
   interrupt-parent = <&ps7_scugic_0>;
   interrupts = < 0 29 4>;
   reg = < 0x43C00000 0x10000 >;
   reg-offset = <0x1000>;
   reg-shift = <2>;
   xlnx,family = "zynq";
   xlnx,has-external-rclk = <0x0>;
   xlnx,has-external-xin = <0x0>;
   xlnx,is-a-16550 = <0x1>;
   xlnx,s-axi-aclk-freq-hz = <0x5F5E100>;
   xlnx,use-modem-ports = <0x0>;
   xlnx,use-user-ports = <0x0>;
   xlnx,instance = "axi_uart16550_0";
};
特にドライバに手を加える必要は無いですが、compatibleの参考とかで見たいときはファイル名8250.c を探してください。


あとはLinuxカーネルのビルド時に2箇所、有効にしなきゃいけないチェックがあります。
一つは当然
8250/16550 and compatible serial support
さらに、DTSファイルにシリアルポートの初期化を記述する前提となる機能
Serial port on Open Firmware platform bus
も有効にしないとIPを認識してドライバが起動してくれません。


これで16550互換シリアルが/dev/ttyS0 とかで動作するはずです。

ZYNQで追加のUARTポートを使う(2) Linux側

だいぶ日付が開いてるけど前回の続き

PL側にIPが配置できて割り込みを61番につないだところからスタートです。


まずはDTSファイルにポート情報の記述。PL側で自動割付したレジスタアドレスと割り込み番号を反映させます。
uartlite_1@42C00000 {
   compatible = "xlnx,xps-uartlite-1.00.a";
   reg = <0x42C00000 0x10000>;
   device_type = "serial";
   interrupt-parent = <&ps7_scugic_0>;
   interrupts = <0 29 4>;
   clock = <100000000>;
};
この例だとアドレスは0x42C00000で、割り込み番号は61から32を引いて29と記述してます。


次にLinuxドライバですが、xilinx / digilent配布のものにソースは入っています。標準ツリーにも入っているようです。ただし、menuconfigでこれが選択肢に出るにはARCHとCROSS_COMPILEをそれっぽく指定してmenuconfigする必要があります。
make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- menuconfig 
これで出たメニューで、driver → character → serialの中に、Xilinx uartlite serial port supportという項目があるのでチェックを付けて再ビルドします。

なお、ドライバのソースuartlite.c内に書かれているデバイス名候補と、DTSに記述したcompatible項目"xlnx,xps-uartlite-1.00.a"が対応していないとデバイスが認識されないのですが、最新のソースを見てもここは上記の名前ともう一つくらいしか記述がありません。

ネットをぐぐるとDTS内にuartlite-1.02を記述してる人がいたり、そもそもvivadoに入ってるIPのバージョンはuartlite2.00になってるのですが、そういう名称は自分でuartlite.cに追加しない限り認識されないので、新しいほうがいいかもとか悩まずに上記の古い名前を記述してください。どうせIP自体は1.00のころから完全互換です。


これでブートすると、/dev/ttyUL0というデバイスが出点して、echoでAとか送ると文字が送信されます。

が! 割り込みが機能してなくて、3文字以上を送ると固まるし、受信も全く反応無しです。



いろいろ調べたところ、ドライバソース uartlite.c 内のirqハンドラ生成のところをレベルではなく立ち上がりエッジ指定にしなきゃいけないようです。
ret = request_irq(port->irq, ulite_isr, IRQF_TRIGGER_RISING, "uartlite", port);

これでカーネルを最ビルドして、やっとuartliteが動作しました。