パソコン新調したので、Linuxのカスタマイズ

新しいパソコンにUbuntuを入れた関係で、環境構築に励んでいます。

デフォルトでnautilusというファイルマネージャが入っているのだと思いますが、 使い勝手はいまいちです。ちょっと調べてみて、rangerというのが便利そうでした。 (参考:CLIファイルマネージャrangerの基本設定) ただ、gnome-terminal(“端末”)だと、どうしても画像プレビューができない様子でした。 それで、rxvt-unicodeをapt-getでインストールしました。 デフォルトだとフォントがあまりにもひどいので、最低限の設定として、以下を.Xresourcesとして作成しました。 (rxvtの設定参考情報:RXVT Customization with ~/.Xresources, urxvtことrxvt-unicodeを使うことのメモ)

URxvt*background: Black
URxvt*foreground: White
URxvt*visualBell: true
URxvt.fading: 40
URxvt.letterSpace: -1
URxvt*font: xft:Takaoゴシック

また、w3mimgdisplayはパスが通っていないので、.bashrcに以下の行を追加しました。

export W3MIMGDISPLAY_PATH=/usr/lib/w3m/w3mimgdisplay

そして、~/.config/ranger/rc.confのpreview_imagesの行を次のようにしました。

set preview_images true

これらの作業で、画像が端末中でプレビューできるようになりました。

Zynq上でiperf3による帯域テスト

前々から気になっていた、Zynqでのイーサネット帯域を測定しました。

Zynqで使用しているカーネルは3.15.0-xilinx(z-turn board付属)です。 通信相手は、仮想マシンで動作しているUbuntu(カーネルは3.13.0-32-generic)です。 Windows上でのiperfと通信させても、それほど大きな違いはありませんでした。

Zynq側iperfのコンソール出力を以下に示します。

root@ubuntu-armhf:/home/ubuntu/iperf-3.1.3/src# ./iperf3 -s
warning: this system does not seem to support IPv6 - trying IPv4
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 192.168.0.100, port 33239
[  5] local 192.168.0.90 port 5201 connected to 192.168.0.100 port 33240
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-1.00   sec  59.0 MBytes   495 Mbits/sec
[  5]   1.00-2.00   sec  61.8 MBytes   516 Mbits/sec
[  5]   2.00-3.00   sec  61.9 MBytes   519 Mbits/sec
[  5]   3.00-4.00   sec  61.8 MBytes   518 Mbits/sec
[  5]   4.00-5.00   sec  61.9 MBytes   518 Mbits/sec
[  5]   5.00-6.00   sec  61.6 MBytes   519 Mbits/sec
[  5]   6.00-7.00   sec  61.6 MBytes   517 Mbits/sec
[  5]   7.00-8.00   sec  61.8 MBytes   518 Mbits/sec
[  5]   8.00-9.00   sec  61.6 MBytes   515 Mbits/sec
[  5]   9.00-10.00  sec  61.6 MBytes   518 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-10.00  sec  0.00 Bytes  0.00 bits/sec                  sender
[  5]   0.00-10.00  sec   615 MBytes   515 Mbits/sec                  receiver
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
^Ciperf3: interrupt - the server has terminated
root@ubuntu-armhf:/home/ubuntu/iperf-3.1.3/src# ./iperf3 -c 192.168.0.100
Connecting to host 192.168.0.100, port 5201
[  4] local 192.168.0.90 port 33104 connected to 192.168.0.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.01   sec  50.0 MBytes   416 Mbits/sec    0    233 KBytes
[  4]   1.01-2.02   sec  49.7 MBytes   412 Mbits/sec    0    298 KBytes
[  4]   2.02-3.02   sec  48.9 MBytes   409 Mbits/sec    1    242 KBytes
[  4]   3.02-4.00   sec  47.5 MBytes   405 Mbits/sec    0    247 KBytes
[  4]   4.00-5.02   sec  50.0 MBytes   412 Mbits/sec    0    247 KBytes
[  4]   5.02-6.02   sec  48.8 MBytes   409 Mbits/sec    0    266 KBytes
[  4]   6.02-7.02   sec  48.8 MBytes   411 Mbits/sec    0    267 KBytes
[  4]   7.02-8.00   sec  48.5 MBytes   413 Mbits/sec    0    281 KBytes
[  4]   8.00-9.02   sec  49.0 MBytes   404 Mbits/sec    0    293 KBytes
[  4]   9.02-10.00  sec  48.5 MBytes   414 Mbits/sec    0    303 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec   490 MBytes   411 Mbits/sec    1             sender
[  4]   0.00-10.00  sec   489 MBytes   410 Mbits/sec                  receiver

iperf Done.

通信方向によって、かなり帯域に違いがあります。サーバモードで動作しているときの方が高速のようです。

カーネルを新しくしたりすることで高速化するのかもしれませんが、思ったよりパフォーマンスが悪い印象です。

ADCデータキャプチャのためのLinuxとFPGAの設計

現在Elecrowにて製作中のADC基板ができてきた折に、Z-turnボードと接続してイーサネットにてADCデータを取得するためのファームウェア周りを作成しています。

FPGAについて

Zynqに接続されているDDRメモリにADCデータを保存します。 DMAマスタをRTLで記述するのは割と面倒(とにかくAXIはポートが多いので、どうしても行数は増える)なので、できるだけ楽をしたいと思います。 そういうわけで、Vivado HLS一択です。DMAコントローラのコードは次のように至極単純です。

ちなみに、AXI DMAを使えばいいじゃないか、という意見もありそうです。 しかしながら、AXI DMAは一回の転送で最大8MBしか転送できないという理解不能な制限があるので、今回はわざわざ作成しました。 SGDMAを駆使すればできると思いますが、とりあえずデータを取り込むことが目的なので、そのあたりは別途余裕ができたところで検討したいと思います。

#include "hls_stream.h"
#include "ap_int.h"
#include "axi_dma.h"

void axi_dma(ap_uint<64>* addr, ap_uint<32> offset, ap_uint<32> len, hls::stream<ap_uint<64> > &in){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE s_axilite port=offset
#pragma HLS INTERFACE s_axilite port=len
#pragma HLS INTERFACE m_axi depth=4096 port=addr
#pragma HLS INTERFACE axis port=in
    ap_uint<32> i;
    
    for (i = 0; i < len; i++){
#pragma HLS PIPELINE
    	addr[offset+i] = in.read();
    }
}

ん?Hugoでc++のシンタックスハイライトが効かない…

AXI Liteインタフェースでoffsetアドレス、転送長をレジスタで設定します。また、モジュールの開始終了もAXI Lite経由で操作できるようになっています。

テストベンチは次のようになりました。

#include "hls_stream.h"
#include "ap_int.h"
#include "axi_dma.h"

int main(){
	ap_uint<64> buf[4096];
	hls::stream<ap_uint<64> > fifo;

	for (int i = 0; i < 1024; i++){
		fifo.write(i);
	}

	axi_dma(buf, 16, 1024, fifo);	// 16*8=128bytes offset

	for (int i = 0; i < 1024; i++){
		if (buf[16+i] != i)
			return 1;
	}

	return 0;
}

一応オフセットも動作しているか確認するようになっています。 シミュレーション後、上記のモジュールをaxi_dmaという名前でIPにして、Vivado IP Integratorでインスタンスを置きました。

IPI画面

DMA IPはInterconnectと100MHzでインタフェースし、IPの前にAXI Stream Data FIFOを置いてクロック変換を行います。 ADCのデータは40MHzで入力されるので、FIFOの入力側は40MHzで駆動します。

ちなみに、FPGA実機にて32bitのカウンタを作成し、DMAを行ったときの開始部分をキャプチャした画像が以下になります。 100MHzで駆動しています。

AXI DMA

AWVALIDのタイミングで転送先アドレスを設定し、WVALID, WREADYのハンドシェークでデータを転送しています。 WDATAが0,1,2,…とカウントアップしています(32bitカウンタを64bitに入れているので、1転送で0,1が同時に送られている)。 WREADYがLowになる期間がバーストの間にあるため、最高速が出ているわけではありません。 ただ、入力側はもっと遅い(40MHz)ので、とりあえずこれで良しとします。

Linuxについて

次に、ZynqのARMで動作させるLinuxですが、 U-Boot と Linux Kernel のメインラインで Zynq を動かす を参考に、次のようなboot.tclを作成し、XMDコンソールからsource boot.tclすることでu-bootを起動します。 u-boot, KernelはZ-turnボードに付属しているものを使用します。

connect arm hw
rst -slcr
fpga -f design_1_wrapper.bit
source ps7_init.tcl
ps7_init
ps7_post_config
targets 64
dow -data u-boot.bin 0x04000000
con 0x04000000

u-bootが起動したところでキーボードで介入し、TeraTermの次のマクロを使用してLinuxをNFS rootで起動します。 DTBはTFTPにて取得しています。 LinuxのルートファイルシステムはarmhfのUbuntu Trusty 14.04 LTS (no kernel)を取得して展開して、 Z-turn Boardから参照できるようにしています。

timeout=15
sendln 'setenv ipaddr 192.168.0.90'
sendln 'setenv serverip 192.168.0.100'
sendln 'tftpboot 0x2a00000 zynq-zturn.dtb'
wait 'Bytes transferred'
sendln 'tftpboot 0x3000000 uImage'
wait 'Bytes transferred'
sendln 'setenv bootargs mem=512M console=ttyPS0,115200 root=/dev/nfs rw nfsroot=192.168.0.100:/srv,v3,tcp rootwait ip=192.168.0.90:192.168.0.100:192.168.0.100'
sendln 'bootm 0x3000000 - 0x2a00000'

また、Z-turn Boardに添付されているDTBそのものでは、ZylonのIPにレジスタアクセスするところでカーネルの起動が止まってしまっているようでした。 今回のFPGAデザインには当該IPは存在せず、ADCに必要な最小限のものになっています。 そのため、DTSの記述を修正し、当該IP部を無効にして、DTCにてDTBを再度生成しました。

加えて、単に上記のルートファイルシステムを展開しただけだと、ttyPS0が見つからなくてコンソール出力が得られないので、/dev/ttyPS0を

# mknod -m 600 ttyPS0 c 250 0

にて作成し、/etc/init/ttyPS0.confを下記のように作成しました。

start on stopped rc RUNLEVEL=[2345] and (
            not-container or
            container CONTAINER=lxc or
            container CONTAINER=lxc-libvirt)

stop on runlevel [!2345]

respawn
exec /sbin/getty -8 -a root 115200 ttyPS0

Ubuntuの起動後にMakeやgccをインストールして、クロスコンパイルではなく、ターゲット上でアプリケーションを開発します。

Linux上のアプリケーションについて

Helioボード: LinuxとFPGAでSDRAMをシェアするによれば、 bootargsで指定したmem=512Mにて、DDRの上位側512MBはLinuxからは使用しないようになります。 そういうわけで、[0x2000_0000, 0x4000_0000)の範囲がFPGAからDMAを行う転送先アドレスになります。

次に、UIO(User space IO)の割り込みの使い方の例を参考に、 DTSにUIOを追加し、今回作成したDMAモジュールのレジスタアクセスと割り込みをハンドルできるようにします。

また、/dev/memをmmapすることで、DMAで転送したデータをアプリケーションから参照できるようにし、 データをTCPにて開発機で動作するC#アプリケーションに転送できるようにしました。

次は、FPGA上で適当な周期の正弦波を作って、PC上にてFFTして結果が正しいことを確認したいと思います。

« 2/2