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

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/

| | コメント (0)

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月 | トップページ