DE10-Nano Kitは結構良さそう

つい先日、FPGA評価ボードを購入して、基板まで製作しFPGAによるFMラジオまで製作してしまいましたが、 最近発売されたDE10-Nano Kitも悪くないのではないか?と思いました。 Z-turn Board(7020版)と比較してみると、次のような利点、欠点がありそうです。個人的に重要なポイントは太字で。

Pros:

  • 130USDなので、送料も含めるとZ-turn Boardよりは高くなると思われるが、JTAGケーブルは別途必要ではないので、トータルでは安いと思われる。
  • 多ピンコネクタが2.54mmピッチのようなので、汎用性は高い
  • 搭載FPGAのサイズもZynqの7020より少し大きい。ARMも少しだけ早く動作する(800MHz vs 667MHz)。
  • +5V電源もキットに標準で含まれている(でもこちらも電源SWは無さそう?)。
  • IOもZ-turn Board搭載Zynqの400pinパッケージよりも多い672pinパッケージ。でも実際に使用可能なIOはそれほど変わらない雰囲気です。

Cons:

  • DDR3の動作周波数は遅い(1066MHz vs 800MHz)。
  • ロジックの動作は多分Zynqの方が速い?
  • Alteraは無料で高位合成ツールは提供されていない。ソフトコアCPUも有償。

2017/4/17追記: Terasicの公式ページだとStockが無い様で、 “Contact Us"となっていますが、Mouserだと在庫があるようです。 しかも送料無料です。レート換算してみると、1USD=121.5円なので若干悪いですが、送料無料と思えば適正ではないかと思います。 追記終わり

一長一短という感じですが、用途(と好み)によっては良いのではないでしょうか。ちなみに、 搭載されているCyclone V SE 5CSEBA6U23I7は、digikeyで調べてみると、 1個買いで29,694円(2017/3/28調べ)です。そう考えるとかなりお値段頑張っていると思います。 ただし、Alteraは機能のわりにXilinxより元々少し高いという印象がありますが。

Z-turn Board搭載のZynqは、同じくdigikeyで調べてみると、 13,724円です。これも一個買いするのであれば、それほど変わらない値段でZ-turn Boardが買える計算になります。

ADC基板のBPFのシミュレーション

今回は、LTspiceを使用して、 ADC基板に実装しているBPF(Band Pass Filter)の周波数特性をシミュレーションしてみました。

“Simulation”

実線が左側の軸(振幅)に対応し、点線が右側の軸(位相)に対応しています。

初めて使ったので、少し使い方に迷いましたが、比較的簡単に使えました。 シミュレーションを行う周波数を50M-120Mと入力したらメガヘルツになるかと思ったら、 Mはミリと解釈されるようです。メガを指定するにはmegと入力しないといけません。 LTSpice入門を読んで分かりました。

シミュレーション結果を見てみると、-3dBになるのは大体60MHzと105MHzあたりでしょうか。 70-90MHzの範囲はほぼ0dBで通過しているのが見て取れます。 もう少し急峻にバンド外を落とすかと思っていましたが、わりとゆるやかな特性なのですね。

Wikipedia によると、これはバターワースフィルタのCauer形のようです。ロールオフ特性は緩やか、とはっきり書いてありました。

DDSデータキャプチャとFFT表示

前回、ADCキャプチャのためのFPGAとLinux周りの準備を行いました。

まだ実際のADC基板は完成してきていないので、FPGAにてDDS(Direct Digital Synthesizer: デジタル的に正弦波を生成する仕組み)を使用して、仮想的なデータを作ってみました。

具体的には、FPGAにDDS Compilerをインスタンシエートして、 PCのソフトウェアにてキャプチャ&FFTを実行しました。

DDS Compilerにて作成するIPは、3MHzの正弦波としました。

DDS compiler

12bit出力ですが、AXI Streamポートは16bit幅となっています。どうやら16bitの内、下位12bitにデータが入っている様子でした。符号付きデータです。

この12bitを5データ集めて、かつ4bitのダミービットを付加して64bitとし、前回のAXI DMA IPにてDDRに転送します。 PC側のソフトウェアは、手っ取り早くC#で作成しました。

いろいろ細かいデバッグを行って、現状次のような表示になっています。

ADC_FFT

画面上部にキャプチャした波形そのものを少し表示しています。横軸はサンプル番号です。 マイナスからプラスの範囲でしっかりフルスケールの正弦波となっている様子がわかります。 画面下部はキャプチャしたデータをFFTしてdB表示しています。 どうも使用しているFFTライブラリの正規化の挙動が不明で、0dBを超えてしまっています… 3MHzにびしっと信号があることを確認できます。

FFTライブラリには、C#でマイク音声をFFTするを参考に、 Math.NET NumericsをNugetにてインストールして使用しました。

これで基板が来るまでの準備は大体満足するところまで作成できました。

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して結果が正しいことを確認したいと思います。

ADC基板のRF入力部分

前回、SDRの実験用基板について記載しましたが、 やっとRF入力についての回路図が大体できたと思います。

RF入力回路図

我ながらごちゃごちゃして見にくいです。1枚に詰め込みすぎ。

図面の上半分が、ADCの入力の回路です。 片方は70-90MHzのBPFを通し、RFアンプ、RF balunを通して差動入力されています。こちらはFM用です。 RFアンプのデータシートを見てみると、一般的にはLNA(Low Noise Amplifier)を通してから、 このアンプに信号を入力するようです。ただ、Interfaceの特集記事ではLNAは使用しないで実現していましたので、 FM程度の帯域だったら問題ないのかもしれません。

もう一方の入力は、シングルエンドモードで使用し、かつRFアンプを使用するパスと使用しないパスをジャンパで選択できるようにしています。 ジャンパでRF信号を通すのは少し気が引けますが、こちらはAM帯域をターゲットするので、 まぁそれほど高速性は要求されないから良いでしょう。

ADCの入力インピーダンスは、データシートによると100Ω以下とするように記載されていますが、 どちらの入力も基本的には50Ωに揃えてあるつもりです。 RFアンプは入力も出力もインピーダンスが50Ωとなるデバイスです。

また、SENSEピンはジャンパにて2Vp-pか1Vp-pかを選択します。

図面真ん中左側にあるのは、ADCのクロック生成部です。 TCXOから出力される40MHzをCDCLVS1102で2つに分岐します。片方はADCのCLKA,CLKBに入力され、 もう一方はコネクタを介してFPGAのCCピンに接続します。ADCからのクロック出力は無いためです。

2017/2/20追記:当初予定していたTCXOは2.5ppmのものでしたが、 digikeyでは在庫がなくなっているため、 ちょっと怖いですがMEMSの5ppmのクロックに変更しました。 お値段もそれなりにアップしてしまいました。

図面下側はADCの電源部です。VDDはMax 3.4V、Typical 3.0Vです。LDOで3.3Vから3.0Vを生成し、 かつ生成された3.0Vと3.3Vから使用する電圧をジャンパで設定できるようにしています。 クロックが3.3Vなので、VDD=3.0Vで動作させるのはどうかな、という懸念があるためです。 この回路全体で使用されるのは最大で150mA程度と想定されますので、300mA出力のLDOを選択しています。

この回路を作成していたら、オーディオ出力も載せたくなってきました。 最終的にFPGAでSDRを実装したら、そのまま音を出してみたいですしね。 元々の予定だと、この程度の回路で基板を描くつもりでしたが、 オーディオ部分も回路を作成しようと思います。

回路作成すると、いろいろと欲が出てきます。

« 5/7 »