« 2017年9月 | トップページ | 2017年11月 »

2017.10.31

新しいMPPCアレイ

特電のアルバイトさんが、新しいMPPCアレイを作ってくれています。

Mppc1

MPPCというのは超高感度の光センサで、フォトンを1個1個数えることができる半導体素子です。

この赤い箱の中は16個の小部屋にわかれていて、各小部屋にプラスチックシンチレータとPPCがついています。

MPPCは下の写真のような小さな基板に実装されていて、上のアンプ基板が微小な信号を増幅します。

Mppc2

そして、フレキのケーブルで箱の外にあるコネクタ&電源基板のところまでひっぱりだされてくるというしくみです。

なお、MPPCは温度やバイアス電圧によって特性が変わってしまうので、このアンプ基板には調整用の穴が開いていて、最適なバイアス電圧がかけられるようになっています。

Mppc3

まだ16chで動かしてはいませんが、1chで見たときにはちゃんと波形が出ていたとのことです。

| | コメント (0)

2017.10.30

ZYNQを搭載しADC/DACなIoT(1)

朝、早起きしたので、アナログまわりの配線が引けました。

Cszmini1030_2

ここまで作ってから出勤。

Cszmini1130

この装置の筐体は、うちのアルバイトさんが3D CADで設計してくれています。

Cszmincase2

かっこよすぎるぞ!

Cszmincase1

| | コメント (0)

2017.10.28

ADC/DAC基板の設計

来月の展示会に出すための基板の設計をはじめました。

まず、使われているLSHM-150-04.0-L-DV-A-S-K-TRと、LSHM-130-04.0-L-DV-A-S-K-TRというコネクタの部品のFootPrintを作ります。

http://suddendocs.samtec.com/prints/lshm-1xx-xx.x-x-dv-a-x-x-tr-footprint.pdf

Footprint

0.5mmピッチで、パッドのサイズは0.3mm×1.5mmで間が2.2mmだとは思いますが、後で実物を見ながら確認することにします。とりあえずは何となくで作ります。

Lshm

ここで、Trenz社のベースボード、TE0703の回路図を見てみると、回路図のコネクタの1番ピンが左になっていますが、実際のコネクタではTopViewで右側になるので注意が必要です。

Te0703sch


コネクタを3個配置した、4×5cmのモジュールを作って基板に乗せてみると、こんな感じです。小さいですね。

Module

現在のラッツネストの状態を示します。

大量のCやRがありますが、ほとんどはOPアンプの周辺のものだったり、パスコンだったりするので、数は多いですが遠くに行く配線ではないので、それほど難しくはないと予想されます。

Rats

試しに、アナログフロントエンド回路を1chだけ乗せてみたところ、こうなりました。20個程度のCRが消費できました。

Opamp

| | コメント (0)

2017.10.26

Trenz社のTE0720を組み込むための情報

Trenz社のTE0720は4cm×5cmの小型なサイズに、DDR3メモリをはじめ、多彩なコンポーネントを搭載しているZYNQボードです。

Te0720

このボードの裏面には100ピン、60ピン、100ピンと、3つのコネクタが出ています。このコネクタを使って拡張して、自社製品の基板に組み込むための方法を考えたいと思います。

Te0720_bot

まずは、外部に接続するコネクタのピンの回路図を図示します。

Te0720_con1

Te0720_con2

B13_**のようなピンはI/Oバンクがそのまま出ているピンなので、使い方の説明は不要だと思います。

おおまかな説明をすると、

  • 電源は3.3VINに、3.3Vを供給すればよい。
  • 1.8Vと1.5Vという端子は、電源出力である
  • Bank13の全ピンがコネクタに出ている。
  • Bank33とBank34、Bank35はほとんどのピンがコネクタに出ている。Bank13,33,34,35はそれぞれ電源が独立していて、コネクタからVCCIOを与えるしくみになっている。4種類のBankには機能的な違いはなさそう
  • USBホストのチップが搭載されているので、コネクタを外付けするだけでよい
  • RTCは、VBAT_INという端子にバッテリを外付けする。ボード上にはバッテリはない。
  • ギガビットイーサのPHYは搭載されているので、PHY_MDI*の8本の信号をRJコネクタに接続するだけでよい

です。

自明でないピンの役割は次のようになっています

  • EN1 ・・・ HならオンボードのDC-DCを使う。使わないならNOSEQ=1にする。
  • NOSEQ・・・1.0Vと1.8Vの電源を常にONにする。通常はオープン
  • PGOOD・・ボード上のすべての電源が正しく動作したらHになる
  • MODE・・・LならばSDカードからブート。電源ONのときのみ有効
  • JTAGMODE・・・4.7kΩの抵抗で3.3Vにプルアップすること。そうしないとJTAGでZYNQがみえない
  • SOUT_N、SOUT_P、SIN_N、SIN_P・・・ギガビットイーサのチップからSGMII。無接続にする。
  • RESIN・・LでPOR_BとPSをリセット

それから、MIOの割り当ては下の図のようになっています。

Mio

コネクタに出ているMIO[40:45]にはSDカードをつなぎます。ただし、これらの端子はLVCMOS1.8規格なので、SDカードインタフェースするにはベースボード上にレベル変換が必要となります。SDカードのWPとCDはEMIOを通じてPL内から接続します。

MIO[10:15]は、

  • MIO[14:15] = UART0
  • MIO[12:13] = UART1
  • MIO[10:11] = I2C0 SDL,SDA

に割り当てられています。これらの端子はLVCMOS33です。用途は自由に決めて構いません。また、MIO0と9は3.3VのGPIOですので自由にして構いません。

なお、ZYNQの7020以下の石では、PLはArtixなのでユーザBankはHighRange仕様です。HighRangeでLVDSを使うには2.5VのVCCIO電源が必要となります。2.5Vでない場合はLVDSの受信はできますが、DIFF_TERMが使えず、またLVDSの送信はできません。

まとめると、

  • メインの電源供給は3.3Vのみ。各バンクのVCCIOは自由に設定してよい。
  • ユーザ用にはBank13,33,34,35が使用できる。
  • SDカードはMIO40~45 (1.8V規格) に、レベル変換ICを通じて供給する
  • EN1=H、PGOOD=Open、MODE=LまたはH、JTAGMODE=L、VBAT_IN=バッテリ、RESIN=プルアップ、NOSEQ=オープン、SOUT_N、SOUT_P、SIN_N、SIN_P=オープン、MIO[10:15]=UARTやI2Cでご自由に
  • USBとPHYにはコネクタをつなげばよい

となります。

| | コメント (0)

2017.10.25

IoTなADC計測装置を迅速に設計したい

新しい計測器を設計することになったのですが、ET/IoTまで3週間弱しかありません。

この短い期間に形になるものを出すには、ZYNQ周りのDDR3の設計をしていては絶対に間に合わなそうなので、TrenzElectronic社のモジュール「TE0720」を使うことにしました。

Te072001top300x243

https://www.trenz.jp/products/te0720-02-1cf/

宣伝じゃないのですが、4cm×5cmのサイズにZYNQに1GBのDDR3メモリと、USBホストのチップ、GbE、RTC、QSPI ROM、eMMCなど、その他諸々の部品が乗っていてかなり安いので、今回のような目的にはピッタリです。

これをタカチのケース「MXA3-11-11」に納めます。基板は、過去に作った似たようなサイズの適当な基板で雰囲気を確かめています。

Czmini6

基板は二階建てになりますが、蓋は無事にしまりそうですね。

Czmini7

このタカチのケース「MXA」は、今までに見たことがない洗練されたデザインをしています。

まず、横部分に厚さ1.6mmのレールのような凹みが切ってあって、そこにプリント基板を差し込みます。

また、上の蓋と下の蓋が同じ形状をしていて、互いが噛み合う部分に2.5mmくらいの四角い隙間ができるようになっていて、そこにタッピングねじを切り込むとい予想だにしなかった構造になっています。

上蓋がないとネジが締まりません。考えた人は天才じゃないでしょうか。

sun

今回のような小型IoT計測に、このTE0720というZYNQモジュールは、サイズ的にも最適だと思います。

Czmini8

いままではこういうZYNQとかDDR3とかの配線を喜んで自分でやっていたのですが、特電の製品としての差別化のポイントは高速ADCと高速DACにあるはずです。今、FPGAまわりの配線を頑張ってもあまりメリットはありません。

今回は、市販の産業用モジュールを使って「他社にない違う製品を迅速に出す」ということ挑戦してみたいと思います。

Czmini5

| | コメント (0)

2017.10.24

Cosmo-ZにSATAをつなげたい

Cosmo-ZでSATAが使えるようにインターンの学生さんが頑張ってくれています。

Cszsata

私が昔、Spartan-6用に作ったSATAコアを、7シリーズで動くようにAXI版にアップグレードしてくれています。

SATAのコマンド等はGPIOで送り、SATAのデータはAXIにしてZYNQのHP1ポートにつないでいるようです。

| | コメント (0)

2017.10.23

新しいIoTタイプのZYNQ&ADC計測装置

特電のアルバイトさんが新しい「ZYNQ搭載の高速ADCボード」の設計をしてくれています。

Czmini4

小型の金属ケースに入ってADC 125MHz 4ch、DACも2ch搭載です。

Czmini1

Ubuntu Linux搭載で、無線LANで計測データを飛ばすIoTな計測器を目指しています。

Czmini2

間に合えばET/IoTに出展予定…いや、きっと間に合わせます!

映らなくても、動かなくてもいいんです。

Czmini3

ちょうど明日、基板屋さんの営業さんが来るから、基板設計を全部お願いしてみようかな。

| | コメント (0)

2017.10.13

XILINXのPCIe XDMAコアによる起動不具合の原因

XILINXのPCIe XDMAコアを入れたFPGAをPCにつないで起動すると、OSが起動する途中にハングアップしてしまったり、再起動を繰り返すという現象があります。

海外のforumを見ても問題は報告されています。

すべてのPCで起きるわけではなく、現象が起きるPCとそうでないPCがあります。

Kintex-7搭載のPCI Expressボード「Cosmo-K」でもこの現象が出るので、ハードの問題なのか、VivadoのIPコアのバグなのか切り分けができませんでした。

この現象は、MIGを入れてDMAとDDR3メモリを連携させたあたりから起き始めた気がするので、シグナルインテグリティの問題だったら・・と思うと量産に踏み切る覚悟ができませんでした。

Acpierr1

私の古いPC(上の写真)では、起動時にリブートしてしまうときに一瞬ブルースクリーンが出るのですが、エラーメッセージに何がかかれているかわからなかったので、画面をビデオに撮ってコマ送りで見てみました。

すると、

Acpierr2

ACPI BIOS ERRORと書かれています。これは根が深そうです。新しいPCでは画面にランダムな点がいっぱいでて再起動してしまいます。

いろいろチャレンジしてみて、ようやく原因がわかりました。

Pcie_dma_async

問題は未接続のAXIのBYPASSが存在するためのようです。

Acpierr6

M_AXI_BYPASSというポートは、BAR2にアクセスされたデータが出てくるポートなのですが、これが未接続だとPCの起動時に問題を起こすようです。

使わないのであれば、XDMAの設定でAXI_BYPASSを無効にしたほうがいいでしょう。

Acpierr4

AXI_BYPASSに適当なBRAMをつないでおくくらいではダメで、全アドレスを処理できなければならないのかもしれません。

こうしてAXI_BYPASSのポートが消えると、いままで起動できなかったPCでも問題なく起動するようになりました。

Acpierr5

いま、PCI Express経由でDDR3メモリ(データレート1600MHz)に6TByte程度くらいReadとWriteを繰り返していますが、エラーは一つも起きていません。

Cosmo-Kのエラー率は10^-13以下といえます。

Acpierr3

これで、Kintex-7搭載のPCI Expressボード「Cosmo-K」の量産に踏み切れます。

Cosmok

Cosmo-Kをどうぞよろしくお願いします。

http://www.tokudenkairo.co.jp/cosmok/

| | コメント (3)

2017.10.12

Cosmo-ZをPythonで遠隔操作してグラフ描画

Cosmo-ZをPythonで遠隔操作して、波形のグラフを描くことができるようになりました。

初めてのプログラムですが、すぐにこんなことができるって、Pythonすごい。

# encoding:utf-8
import sys
import argparse
import datetime

# 定数の設定
convert_xy=1
server="192.168.2.100"
points = int(sys.argv[1])
freq = 100

# 引数の解釈

if(len(sys.argv) <= 1):
	print "Usage: python cszcli.py  capture_length  [freq=FREQUENCY] [server=SERVER_NAME]"
	sys.exit()


# NumPyライブラリのインポート(遅い)

print "import NumPy and MatPlotLib"
import numpy as np
from matplotlib import pyplot
print "done"

# XMLRPCのインポート

print "import XMLRPC lib"
try:
	import xmlrpclib as xmlrpc_client
except ImportError:
	import xmlrpc.client as xmlrpc_client
print "done"


# サーバに接続
print "Connect serer.."
cosmoz = xmlrpc_client.ServerProxy('http://' + server + ':21068')
print "done"

# Cosmo-Zの操作
cosmoz.open()
adc_vrange = cosmoz.adc_vrange()
adc_info = cosmoz.adc_info()

cosmoz.adc_setfreq(freq)

while(1):
	cosmoz.capture(0xff,points,"auto")
	
	print "capture done."
	print "Data transfer start"
	
	# データの取得
	time = np.array(map(int,cosmoz.getdata(0).split(",")))
	ch1 = np.array(map(int,cosmoz.getdata(1).split(",")))
	ch2 = np.array(map(int,cosmoz.getdata(2).split(",")))
	ch3 = np.array(map(int,cosmoz.getdata(3).split(",")))
	ch4 = np.array(map(int,cosmoz.getdata(4).split(",")))
	ch5 = np.array(map(int,cosmoz.getdata(5).split(",")))
	ch6 = np.array(map(int,cosmoz.getdata(6).split(",")))
	ch7 = np.array(map(int,cosmoz.getdata(7).split(",")))
	ch8 = np.array(map(int,cosmoz.getdata(8).split(",")))
	
	print "Data transfer end"
	
	if(convert_xy):
		# 時間へ変換
		adc_freq = adc_info["sampling_freq_mhz"];
		if(adc_freq != 0):
			samp_period = 1 / adc_freq
		else:
			samp_period = 1;
		time = time * samp_period
		
		# 電圧へ変換
		vmax = adc_vrange["max_input_voltage"]
		vmin = adc_vrange["min_input_voltage"]
		resolution = float(adc_vrange["adc_resolution"])
		ch1 = ch1 / resolution *  (vmax - vmin) +  vmin
		ch2 = ch2 / resolution *  (vmax - vmin) +  vmin
		ch3 = ch3 / resolution *  (vmax - vmin) +  vmin
		ch4 = ch4 / resolution *  (vmax - vmin) +  vmin
		ch5 = ch5 / resolution *  (vmax - vmin) +  vmin
		ch6 = ch6 / resolution *  (vmax - vmin) +  vmin
		ch7 = ch7 / resolution *  (vmax - vmin) +  vmin
		ch8 = ch8 / resolution *  (vmax - vmin) +  vmin

	else:
		print time

	
	# 波形の描画
	print "Plot start"
	pyplot.cla()
	pyplot.clf()
#	pyplot.figure()
	pyplot.title('Cosmo-Z ' + " captuer " + str(points) + 'points. ' + str(datetime.datetime.now()))
	pyplot.plot(time, ch1, color='brown',   label='ch1')
	pyplot.plot(time, ch2, color='red',     label='ch2')
	pyplot.plot(time, ch3, color='orange',  label='ch3')
	pyplot.plot(time, ch4, color='yellow',  label='ch4')
	pyplot.plot(time, ch5, color='green',   label='ch5')
	pyplot.plot(time, ch6, color='blue',    label='ch6')
	pyplot.plot(time, ch7, color='fuchsia', label='ch7')
	pyplot.plot(time, ch8, color='gray',    label='ch8')
	pyplot.text(0,max(max(ch1),max(ch2),max(ch3),max(ch4),max(ch5),max(ch6),max(ch7),max(ch8)), "Sampling freq " + str(freq) + "MHz",fontsize=11 , color="skyblue")
	
	if(convert_xy):
		pyplot.ylabel("Voltage [V]")
		pyplot.xlabel("Time [us]")
	else:
		pyplot.ylabel("ADC Value [LSB]")
		pyplot.xlabel("Sampling point")
	pyplot.legend()
	pyplot.pause(.01)
	raw_input('Hit any key to capture next wave')

Cosmo-Zのサーバからは2044,2045,2057・・・みたいなテキストで計測データが送られてくるので、それをカンマで区切って配列に入れて、配列をmapでintに変換して、それをNumPyの配列に変換しています。

そして、NumPyの配列の各要素にADC値→電圧に変換するための係数をかけて、グラフにして表示しています。

20171012_9

上の波形はNaIシンチレータから受信したパルスですが、pyplotはマウスで拡大できて便利です。

20171012_11

実行時のコンソール。

20171013

サーバ側のCosmo-ZのLinuxイメージは明日にはでもリリースしたいと思います。

| | コメント (0)

2017.10.11

Cosmo-Z ライブラリ 2.0のマニュアルを執筆開始

Cosmo-ZライブラリのマニュアルはDoxygenで書くことにしました。

http://www.tokudenkairo.co.jp/cosmoz/apidoc

長久しぶりのDoxygenですが、なんとか書き方を思い出して書いています。

Dox1

Dox2

整ったマニュアルが手軽に生成できるのはいいですね。

Doxygenを使う上でハマった点を書きます。

ファイルの最初に@fileがないとドキュメント化されないようなのですが、

/******************************************************************************
@file  document.h
@brief ライブラリマニュアルのメインページを記述している
******************************************************************************/

のように@briefの前に何もないとだめで、

/******************************************************************************
* @file  document.h
* @brief ライブラリマニュアルのメインページを記述している
******************************************************************************/

と、* が必要なようです。

| | コメント (0)

2017.10.10

1GspsのADCボードのひずみ

開発中の1Gsps高速ADCボードのアナログフロントエンドのひずみが大きく、悩んでいます。10MHzの正弦波をいれたところ、-30dBくらいのひずみが観測されています。

Db0_10m_100mv

時系列の波形で見てもひずんでいるのがわかるほどです。

Db0_10m_100mv_2

同じものを高速オシロでみてもひずみは見えているので、ADCの問題ではなく、アナログフロントエンドの問題か、発振器の問題であると考えられます。

Distort

アナログフロントエンドには、LMH5401IRMSTという高速OPアンプと、LMH6401IRMZTという可変ゲインアンプを使っていますが、可変ゲインアンプが怪しいと考えています。

もしかしたら信号発生器が悪いのかもしれません。

| | コメント (0)

2017.10.09

ZYNQのPSからのDDR3メモリアクセス速度

ZYNQ搭載のADCボード「Cosmo-Z」用の、汎用計測ファームウェア V2.0を開発しています。

いままでは、root権限でプログラムを動かして、/dev/memを開いてメインメモリを直接操作していたのですが、これでは一般ユーザが使えません。

そこで、デバイスドライバをちゃんと作って、一般ユーザの権限でもソフトウェアを使えるようにしようとしています。

Linuxのデバイスドライバでは、FPGAやDDR3メモリなどのリソースの物理アドレスがわかっていれば、

request_mem_region(物理アドレス, サイズ ,name);

でメモリマップI/Oの空間を予約して、

void *__iomem vaddr = ioremap(物理アドレス, サイズ);

で仮想アドレスを割り当てます。

割り当てられた仮想アドレスに対して読み書きすれば、指定した物理アドレスに値が読み書きされます。

ぶっちゃけ、まどろっこしいことせずに memcpy(vaddr, user_buf, 100); とやれば、DDR3メモリやFPGAのレジスタに書き込みができてしまうのですが、割り当てた物理アドレスに存在したリソースがページアウトされている可能性があるので、安全なプログラムとはいえません。

まぁ、OS管理外のDMA用メモリなのでページアウトされることはないのですが、カーネルではmemcpyは使ってはいけないことになっているようなので、正しく作らなければなりません。

カーネルモードで、ユーザバッファとの間でデータをコピーするには、copy_from_user()や、copy_to_user()といった関数を使います。

copy_to_userは、カーネルのバッファからユーザのバッファにデータをコピーする際に使います。

remain = copy_to_user(buf, p_dev->mapped_capture_addr + p_dev->offset, count);

この関数の戻り値は0なら成功です。0でなければ、コピーできなかったバイト数を返すという仕様なのですが、今日、実験しているときに0でない値が帰ってくることがありました

ネットを探しても、copy_to_userで失敗する人はそういません。原因は単純なミスでした。

newで確保したバッファのサイズ以上の転送をしようとしたのです。具体的には、

unsigned char buf = new unsigned char[4096];

で確保したバッファに対して16Mバイトくらいの転送をしようとしていたのです。

この単純なミスに気が付かずに、ページサイズの関係は原因かも、とか、copy_to_userのアセンブラソースを読んだりして8時間くらい無駄に悩んでいました。

sun

newでユーザメモリ空間に256Mバイトのバッファを確保し、物理アドレス0x20000000に対して転送した場合の速度を測りました。

Cosmozddr3

ZYNQのCPUからみれば、Linux管理下のメモリから読み出して、Linux管理外のメモリに転送するという、メモリ間の転送なのですが、書き込みは550~600MBytes/s、読み出しは200~240MByte/secという速度が出ました。

つまり、同じDDR3メモリから読み出して書いているので、実際にはこの2倍の速度で動いていることになるのでしょう。

なぜ読み書きで速度が異なるのかはわかりません。また、書き込む内容が全部ゼロよりも、乱数を書いておいたほうが読み出しが早くなる、という傾向がみられました。原因はわかりません。

| | コメント (0)

2017.10.06

ST39VF801CというROMのCFIは間違っている

あるお客様から、MITOUJTAGのフラッシュROMプログラマ機能でSST社のFlashROM、「ST39VF801C」への書き込みができないというご指摘がありました。

SSTは以前から標準規格を無視した仕様のICを出していて、JTAG通じたフラッシュメモリの書き込みでは問題が発見されていました。

例えば、

XILINXのアンサーによれば、「Flashwriter では、SST フラッシュ デバイスはサポートされていません 」と、あきらめられています。

そんな定評のあるSSTですが、ST39VF801Cも同様におかしなCFIをもっていました。

そこで、このROMを取り寄せ、変換基板と万能基板に乗せてSpartan-6ボードにつなぎ、バウンダリスキャンでフラッシュROMの端子を1つずつ操作をしてみました。

Sst_top

Sst_bot

JTAGバウンダリスキャンでCFIを読みだしてみると、ブロックが65536個あると表示されてしまいます。

Sst1_2

sun

データシートによれば、SST39V801Cのセクタは19個、ブロックは4個であるはずです。

Sst2

同じサイズのセクタの塊をブロックというのですが、内訳として、16Kセクタが1個、8Kセクタが2個、32Kセクタが1個、64Kセクタが15個の、4つのブロックでできています。

しかしながら、SST39V801CのCFIコードには、ブロックが5個あると記されています。

Sst3

この先にはCFIコードがなく、FFFFFFFF・・・が読み出されるので、そのまま読むと、変なことになってしまうのです。

さて、SST39V801CのCFIの間違いはこれだけではありません。

CFIのブロックの情報の上位16bitはセクタサイズ(256バイト単位)が、下位16bitはセクタの数が書かれているのですが、ここには実際に存在するセクタの数は-1された値が書かれることになっています。

したがって、16Kセクタが1個、8Kセクタが2個、32Kセクタが1個、64Kセクタが15個という構成であれば、0、1、0、14となっていなければなりませんが、このROMのブロック情報では・・

Sst4

となっていて、最後のブロックにセクタが16個あることになってしまいます。

このため、普通に解釈するとセクタ構成がおかしくなります。

Sst5

なので、SST39VF601Cの場合は最後のセクタの数を-1するような修正を加えなければなりません。

これでようやくセクタ情報が正しく表示されるようになりました。

Sst6

sun

なお、このROMは1ワード書くたびにプロテクトを解除しなければなりません。FUJITSUやAMD、Spansionに比べると、書き込み手順が長くなり、非常に遅くなります。

SSTのエンジニアには、規格をちゃんと理解してからICを作ってほしいと切に願います。

| | コメント (0)

2017.10.05

Pythonを使ってZYNQをコントロール

Cosmo-Zの2017年度アップグレードと称して、ファームウェアの大幅な改良を行っています。

最大の目的は、お客様がアプリを簡単に作れるようにすること。

その目的の達成のために、Cosmo-Zのさまざまな機能をPythonやEPCS、RT-MiddleWare、SKPIなどで操作できるようにしたいと考えています。

今日はまず、Cosmo-Zを操作するプログラムを、ライブラリと、アプリ層に分けました。

Cszapi1

上の図でcosmozapiというのがライブラリ、cszmainというのがテストアプリです。

cosmozapiの中ではZYNQのAXI Liteで作ったメモリマップレジスタに様々な機能にアクセスしています。

今回はスタティックライブラリ(.a)として作っていますが、意外とハマったのが、XILINXのSDKが作るデフォルトのライブラリプロジェクトではだめで、コンパイルオプションに-fpicを追加しなければなりません。

これをしないと、後でPython用のライブラリ(so)を作る際に、

/usr/bin/ld: ../libcosmozapi.a(cosmoz.o): relocation R_ARM_MOVW_ABS_NC against `a local symbol' can not be used when making a shared object; recompile with -fPIC
../libcosmozapi.a: could not read symbols: Bad value
collect2: ld returned 1 exit status

というエラーが出てしまいます。

次に、このライブラリをPythonから呼び出せるようにラッパすることです。

cosmozPy.c

#include <cosmoz.h>
#include "Python.h"

PyObject* cosmoz_open(PyObject* self, PyObject* args)
{
        BOOL status = csz_open(CSZ_CONNECTION_TYPE_DIRECT,NULL);
        return Py_BuildValue("i",status);
}

PyObject* cosmoz_firm(PyObject* self, PyObject* args)
{
        return Py_BuildValue("{s:s,s:s,s:s}",
                "date",csz_firmdate(),
                "description",csz_firmdescr(),
                "version",csz_firmver()
        );
}

PyObject* cosmoz_xadc(PyObject* self, PyObject* args)
{
        int x, y, g;
        double tempe,vccint,vccaux,vccddro;
        csz_xadc(&tempe,&vccint,&vccaux,&vccddro);
        return Py_BuildValue("{s:d,s:d,s:d,s:d}","vccint",vccint,"vccaux",vccaux,"vccddro",vccddro,"temp",tempe);
}

static PyMethodDef cosmozmethods[] = {
        {"open", cosmoz_open, METH_NOARGS},
        {"xadc", cosmoz_xadc, METH_NOARGS},
        {"firm", cosmoz_firm, METH_NOARGS},
        {NULL},
};

void initcosmoz()
{
        Py_InitModule("cosmoz", cosmozmethods);
}

これを以下のようにしてコンパイルします。なお、Py_InitModuleの第一引数のモジュール名と、最後の関数の「initモジュール名」と、出来上がったファイルのsoの前の名前は合わせておく必要があります。それ以外はファイル内でのみ参照されるものなので任意です。

gcc -fpic -o cosmoz.o -c cosmoz.c -I/usr/include/python2.7 -I.
gcc -shared cosmoz.o -lcosmozapi -L.. -o cosmoz.so

このプログラムをビルドするには、Phthon.hが必要なのですが、これはubuntuに最初は入っていなかったので、apt-get install python-devやapt-get install python3-devで入れました。

それからハマったのは

  • Py_InitModuleはPython2の関数なので、Python3では使えない
  • このプログラムはCで書くこと。C++だと関数の名前が修飾されてしまってリンカエラになる。extern "C"とかやればいいんだろうが、これくらいならCで書いたほうがかえって気持ちが良い。

C++で書くと、コンパイル時に以下のようなエラーが出ることがあります。

gcc -fpic -o cosmoz.o -c cosmoz.cc -I/usr/include/python2.7 -I.
cosmoz.cc:75:1: error: invalid conversion from ‘PyObject* (*)(PyObject*, PyObject*, PyObject*) {aka _object* (*)(_object*, _object*, _object*)}’ to ‘PyCFunction {aka _object* (*)(_object*, _object*)}’ [-fpermissive]

これは PyObject* func1(PyObject* self, PyObject* args, PyObject* kw) のように文字列を引数として受け取る(つまりMETH_KEYWORDS型)の関数を、PyMethodDefで列挙しているところで型が合わなくなるためです。PyCObject*かなにかに無理やりキャストすれば通りますが、それでも名前が修飾されてしまっているので、モジュールをロードして実行してみると、

root@cosmoz:/home/share/py# python cosmoz_server.py
Traceback (most recent call last):
  File "cosmoz_server.py", line 1, in 

    import cosmoz ImportError: dynamic module does not define init function (initcosmoz)

となって、「initモジュール名」の初期化関数が見当たらないとなってしまいます。

PythonのラッパはCで作りましょう。

sun

次に、Pythonのプログラムを作って動かしてみます。

cosmoz.py

import cosmoz

print('Cosmo-Z Open Status => ' + str(cosmoz.open()))
print("Firmware => " + str(cosmoz.firm()));
print("Get XADC => " + str(cosmoz.xadc()));

実行結果は

Cosmo-Z Open Status => 1
Firmware => {'date': 'Oct  5 2017 17:34:51', 'version': '2.0.0', 'description': 'Cosmo-Z Firmware Version 2 Pre-Alpha'}
Get XADC => {'temp': 59.06008300781252, 'vccaux': 1.811279296875, 'vccint': 0.990966796875, 'vccddro': 1.491943359375}

となりました。

Cで作ったプログラムがPythonで呼び出せたので、ちょっと感動しました。

sun

最後にこれをネットワーク経由で、Windowsマシンから呼び出せるようにしましょう。

まず、XML-RPCというサーバを動かします。

cosmoz_server.py

import cosmoz
import SimpleXMLRPCServer as xmlrpc_server
def open():
	return cosmoz.open()

def firm():
	return cosmoz.firm()

def xadc():
	return cosmoz.xadc()

server = xmlrpc_server.SimpleXMLRPCServer(('192.168.1.10', 21068))  
server.register_function(open)
server.register_function(firm)
server.register_function(xadc)
server.register_introspection_functions()
server.serve_forever()

これで192.168.1.10として待ち受けるサーバができます。このアドレスの書き方はよくわかりません。

そうして、Windowsマシンに入れたPythonで

import xmlrpclib as xmlrpc_client
client = xmlrpc_client.ServerProxy('http://cosmoz:21068')
print client.open()
print client.firm()
print client.xadc()

と打ってみると、

Py1

見事につながりました。

ただ、ちょっと遅いですね。

Wiresharkで見ると、Windows PC(192.168.1.2)がZYNQ(192.168.1.10)にPOSTのリクエストを送ってから応答が返るまで10秒かかっています。

Py3  

その間にはMDNSという見慣れない、なんとなく邪悪な感じのするマルチキャストが飛び交っています。

このZYNQ UbuntuはMDNSというプロトコルのパケットを送って、応答が返るまで次のTCPの返事を出さないようです。MDNSというのはよくわかりませんが、avahiというデーモンが何かをしているようなので、

/etc/init.d/avahi-daemon stop

で止めてしまいます。

すると、20ms弱で応答するようになりました。

Py2

一つの関数の呼び出しに20msは遅いかもしれませんが、これで良しとしましょう。

sun

わかったことをまとめますと、

  • XILINX SDKのスタティックライブラリのプロジェクトで作った.aをリンクしてsoを作ることはできない。コンパイルオプションに-fpicを付けること。
  • PythonのラッパはCで作る。
  • XML-RPCサーバを作るときには、SimpleXMLRPCServer(('192.168.1.10', 21068)) のように第一引数に自分のIPアドレスを入れるようだ。127.0.0.1では他のマシンからアクセスできなかった。
  • avahi-daemonを止めておかないとレスポンスが遅すぎる
  • ZYNQ上のUbuntuでPythonを動かし、XML-RPCを動かした場合の、1回のRPC呼び出しの所要時間は20ms弱だった

ということです。

実は、「Pythonなんて若いもんが使う軽量言語だろ、興味ないね。」と、今まで軽視して触りもしなかったのですが、勉強してみると、本格的なオブジェクト指向ができるのに記述が簡単で、非常に洗練されていて、ライブラリが充実した言語であることがわかりました。

Pythonを触って2日目でここまで来れましたが、もうC++&STLには戻れないですね。

| | コメント (0)

2017.10.04

1G ADCボードで波形が取れた

アルバイトさんが、開発中の1GSps ADCボードで波形が取れるようにしてくれました。

まずはシグナルジェネレータで作った20MHzの波形

Db3_sin20m

次は、100MHzの波形

Db3_sin100m

ちょっとひずみが大きい?

sun

400MHzの波形

Db3_sin400m

400MHzになると人間には正弦波には見えず、同じパターンが早く繰り返すようになりますが、FFTするときれいな正弦波として見えます。

sun

そして今度は速度を下げて、500kHzの波形

Db3_sin500k

上り坂と下り坂の部分にノイズのようなものが見えます。何でしょう?

最初はADCデータのデコードのミスかと思ったのですが、やはり何かあるようです。

Db3_sin500k_2

このグラフの描画はPythonを使っているそうです。Excelで描くのとちがってちょっとした演算が楽にできるので、私もPythonを勉強したいと思います。

| | コメント (0)

2017.10.01

Vivadoのサンプルデザインの使用方法

開発中の1GADCで、JESD204Bの初期化シーケンス(ILAS)の読み出しに成功しました。

Np1094_jesd

4つのGTPのチャネルで以下のような情報になっていました。

GTP0: 7C 1C 9C 00 00 03 07 01 08 01 0D 2F 23 00 00 00 35 00 7C
GTP1: 7C 1C 9C 00 00 02 07 01 08 01 0D 2F 23 00 00 00 34 00 7C
GTP2: 7C 1C 9C 00 00 00 07 01 08 01 0D 2F 23 00 00 00 32 00 7C
GTP3: 7C 1C 9C 00 00 01 07 01 08 01 0D 2F 23 00 00 00 33 00 7C

注目すべき点は0Dというところです。これはADCの分解能を示します。10進数で13ですが、+1されて14bitということになるのですが、ADS54J60は16bitのADCのはずです。

ADS54J60の下位2bitはサブADCのキャリブレーションに使われているので、ジャンクなデータしか出てきません。したがって、14bitの性能しかもっていないのですが、その意味で14bitを示しているのかもしれません。

sun

さて、このようなGTPの回路をどうやって作ったかということですが、GTPやGTXはリセット信号がたくさんあって、その順序などを守らないとうまく動いてくれませんでした。

ですが、最近のVivadoで作ったコアには、どうやらちょっとだけ簡単にしてくれる仕掛けがあるようなのです。

GTPのコアを作る際に、Include Shared Logic in example designにチェックを入れ、次のページでProtocolをJESD204に選びます。

Gtp1Gtp2

確信は持てませんが、おそらくOpen IP Example Designをして、

Ip1

次のダイアログで、サンプルが展開されるディレクトリを指定します。

Ip2_3

すると、そのディレクトリにVivadoのサンプルデザインのプロジェクトが生成されるというようです。

Ip3

このプロジェクトのトップデザインファイルは、gtwizard_0_ex.srcs\sources_1\imports\example_designの中にあるgtwizard_0_exdes.vhdというファイルなのですが、これの内容を注意深く自分のプロジェクトにコピペしてきます。

gtwizard_0_exdes.vhdがトップで、その下にgtwizard_0_support.vhdというファイルがあります。gtwizard_0_support.vhdはリセット信号などをいろいろやってくれるもののようです。

気になる点としては、soft_resetという信号がすべて'0'に固定されていてリセットを使っていない点でした。コードを読んでいくうちに、どうやらリセット信号をユーザが動かす必要はないことがわかりました。

    port map
    (
        soft_reset_tx_in                =>      soft_reset_i,
        soft_reset_rx_in                =>      soft_reset_i,
        DONT_RESET_ON_DATA_ERROR_IN     =>      tied_to_ground_i,
    Q0_CLK1_GTREFCLK_PAD_N_IN => Q0_CLK1_GTREFCLK_PAD_N_IN,
    Q0_CLK1_GTREFCLK_PAD_P_IN => Q0_CLK1_GTREFCLK_PAD_P_IN,
        GT0_DATA_VALID_IN               =>      gt0_track_data_i,
        GT1_DATA_VALID_IN               =>      gt1_track_data_i,
        GT2_DATA_VALID_IN               =>      gt2_track_data_i,
        GT3_DATA_VALID_IN               =>      gt3_track_data_i,

というポートがあるのですが、

DONT_RESET_ON_DATA_ERROR_IN     =>      tied_to_ground_i,

となっていて、GNDに固定されていることがわかります。この信号がLだと、データエラーのときにリセットしないことをしないので、つまりデータがエラーのときにリセットがかかるようになるようです。

そして、GT0_DATA_VALID_IN~GT3_DATA_VALID_INという信号がLならばエラーが発生していることを示すようです。これらの信号をL→Hと動かせばリセットがかかります。

前置きが長くなりましたが、Vivadoのexampleプロジェクトを使えば受信回路は自然に正しくリセットがかかるようです。

なお、GT0_DATA_VALID_INなどに与えられるgt0_track_data_iという信号は、gtwizard_0_GT_FRAME_CHECKというモジュールで作られています。これは、JESD204のフレームをチェックして、正しいかどうかを判断してくれるものなのでしょう。

ただ、フレームチェックモジュールの詳しい動作はわからいので、私はこれを使わずにJTAG経由でDATA_VALIDをH/Lできるようにしています。

sun

さて、逆に、自分でRESETやUSERRDYを動かそうとしても、うまくリセットがかかりませんでした。

そこで、サンプルデザインを解読してみたのですが、奥のほうにあるgtwizard_0_init.vhdの中で、以下のような処理をしていました。

Ip4

chipscope : if EXAMPLE_USE_CHIPSCOPE = 1 generate
    gt0_gttxreset_i                              <= GT0_GTTXRESET_IN or gt_gttxreset_t;
    gt0_gtrxreset_i                              <= GT0_GTRXRESET_IN or gt_gtrxreset_t;
    gt0_txuserrdy_i                              <= GT0_TXUSERRDY_IN and gt_txuserrdy_t;
    gt0_rxuserrdy_i                              <= GT0_RXUSERRDY_IN and gt_rxuserrdy_t;
    gt1_gttxreset_i                              <= GT1_GTTXRESET_IN or gt_gttxreset_t;
    gt1_gtrxreset_i                              <= GT1_GTRXRESET_IN or gt_gtrxreset_t;
    gt1_txuserrdy_i                              <= GT1_TXUSERRDY_IN and gt_txuserrdy_t;
    gt1_rxuserrdy_i                              <= GT1_RXUSERRDY_IN and gt_rxuserrdy_t;
    gt2_gttxreset_i                              <= GT2_GTTXRESET_IN or gt_gttxreset_t;
    gt2_gtrxreset_i                              <= GT2_GTRXRESET_IN or gt_gtrxreset_t;
    gt2_txuserrdy_i                              <= GT2_TXUSERRDY_IN and gt_txuserrdy_t;
    gt2_rxuserrdy_i                              <= GT2_RXUSERRDY_IN and gt_rxuserrdy_t;
    gt3_gttxreset_i                              <= GT3_GTTXRESET_IN or gt_gttxreset_t;
    gt3_gtrxreset_i                              <= GT3_GTRXRESET_IN or gt_gtrxreset_t;
    gt3_txuserrdy_i                              <= GT3_TXUSERRDY_IN and gt_txuserrdy_t;
    gt3_rxuserrdy_i                              <= GT3_RXUSERRDY_IN and gt_rxuserrdy_t;
end generate chipscope;

no_chipscope : if EXAMPLE_USE_CHIPSCOPE = 0 generate
gt0_gttxreset_i                              <= gt_gttxreset_t;
gt0_gtrxreset_i                              <= gt_gtrxreset_t;
gt0_txuserrdy_i                              <= gt_txuserrdy_t;
gt0_rxuserrdy_i                              <= gt_rxuserrdy_t;
gt1_gttxreset_i                              <= gt_gttxreset_t;
gt1_gtrxreset_i                              <= gt_gtrxreset_t;
gt1_txuserrdy_i                              <= gt_txuserrdy_t;
gt1_rxuserrdy_i                              <= gt_rxuserrdy_t;
gt2_gttxreset_i                              <= gt_gttxreset_t;
gt2_gtrxreset_i                              <= gt_gtrxreset_t;
gt2_txuserrdy_i                              <= gt_txuserrdy_t;
gt2_rxuserrdy_i                              <= gt_rxuserrdy_t;
gt3_gttxreset_i                              <= gt_gttxreset_t;
gt3_gtrxreset_i                              <= gt_gtrxreset_t;
gt3_txuserrdy_i                              <= gt_txuserrdy_t;
gt3_rxuserrdy_i                              <= gt_rxuserrdy_t;
end generate no_chipscope;

なんと、ユーザが与えたuserrdy信号やtxreset、rxresetはCHIPSCOPEを使わない状態では無効になっているようです。そして、代わりに_tが付いた信号が使われていました。

この_tがついたgt_txuserrdy_tはgtwizard_0_TX_STARTUP_FSMというモジュールで作られているようで、

gt_txresetfsm_i:  gtwizard_0_TX_STARTUP_FSM 

  generic map(
           EXAMPLE_SIMULATION       => EXAMPLE_SIMULATION,
           STABLE_CLOCK_PERIOD      => STABLE_CLOCK_PERIOD,           -- Period of the stable clock driving this state-machine, unit is [ns]
           RETRY_COUNTER_BITWIDTH   => 8, 
           TX_PLL0_USED             => TRUE ,                        -- the TX and RX Reset FSMs must 
           RX_PLL0_USED             => TRUE,                         -- share these two generic values
           PHASE_ALIGNMENT_MANUAL   => FALSE                 -- Decision if a manual phase-alignment is necessary or the automatic 
                                                                     -- is enough. For single-lane applications the automatic alignment is 
                                                                     -- sufficient              
             )     
    port map ( 
        STABLE_CLOCK                    =>      SYSCLK_IN,
        TXSYSCLKSEL                     =>      GT0_TXSYSCLKSEL_IN,
        TXUSERCLK                       =>      GT0_TXUSRCLK_IN,
        SOFT_RESET                      =>      SOFT_RESET_TX_IN,
        PLL0REFCLKLOST                  =>      tied_to_ground_i,
        PLL0LOCK                        =>      gt_pll0lock_i,
        PLL1REFCLKLOST                  =>      tied_to_ground_i,
        PLL1LOCK                        =>      gt_pll1lock_i,
        TXRESETDONE                     =>      gt_txresetdone_i,
        MMCM_LOCK                       =>      tied_to_vcc_i,
        GTTXRESET                       =>      gt_gttxreset_t,
        MMCM_RESET                      =>      open,
        PLL0_RESET                      =>      gt_tx_pll0reset_t,
        PLL1_RESET                      =>      gt_tx_pll1reset_t,
        TX_FSM_RESET_DONE               =>      GT_TX_FSM_RESET_DONE_OUT,
        TXUSERRDY                       =>      gt_txuserrdy_t,
        RUN_PHALIGNMENT                 =>      open,
        RESET_PHALIGNMENT               =>      open,
        PHALIGNMENT_DONE                =>      tied_to_vcc_i,
        RETRY_COUNTER                   =>      open
           );

と、ユーザからのuserrdy信号ではなく、内部でPLLのロックなどから作ったタイミングで作られているようです。

sun

結論をまとめると、

  • Vivadoのexample designを使う場合、JESD204のトランシーバの受信回路は、リセット信号は自動生成されたサンプルデザインにお任せすべき。
  • ユーザはgtwizard_0_exdes.vhdの内容を注意深くコピペしながら、サンプルデザインを自分の回路に取り込む。
  • resetやuserrdyを自分で操作する必要はない。(おそらく操作しても無視される)
  • DATA_VALID信号をL→Hにすると、受信回路にリセットがかかって、よろしくやってくれる。

ということのようです。

もうデータシート見ながらリセットの順番を考えなくてもよさそうです。ゆとりなサンプルデザインになりました。

| | コメント (0)

« 2017年9月 | トップページ | 2017年11月 »