SIS3803を利用して、スケーラーの読み出しを試みた。

SIS3803について。

・立ち上げただけでは、カウントできない。Global Count Enableの状態にする必要がある
・馬場さんの用意した関数cmdvmeを使って、base addressの値をとってくると、0x00000000となっている。
・つぎに、cmdvme -lw 0x38383828 0x1とする。この作業で、global count enableの状態を作れる。再度base addressを見に行くと、0x00008000となっている。

これで準備完了。

実際の値の格納について。

・scalerの各チャンネルは、それぞれ固有のアドレスが与えられている。どこのチャンネルにいくつイベントが来たか、という情報は、そのチャンネルのアドレスにアクセスすればよい。
・読み出し方法は二通り。一つは読んだら全チャンネルクリアするモード、もうひとつはずーっと数え続けるモード。
・実験では、読んだ値を保持して欲しくない。わかんなくなるので。つまり、クリアするモードにすればいいように思う。
・ところが、それではだめ。これは今気づいたけど。

1チャネル読んだら、他の全部をクリアすることになるので、複数のチャネルを読むことができない。
forループとかでも、0ch,1ch,2ch....という順番で読んでしまうので、0chを読んだら、他の全てが0になってしまう。

結論的には、値を保持するモードで測って、全チャネル読み終わったら、clearすればいい。


実際にどうするか。

まずはwindowの定義をする。

startup.cで、
univ_init_window(ADDR,REGSIZE,A32,No);

とする。
これは、ADDR:base address,REGSIZE:使うレジスターのoffset(最大値),A32:32bitアドレスモード,No:モジュールの番号(これは勝手に決める。以降全てモジュールの番号でやり取りすることになる。)

さらに、SIS3803は値を保持する能力があるので、一度初期化する必要がある。

univ_map_write32(0x020,sc,SCAN);

base addressから数えて0x20のアドレスに、1を立ててあげると、それまでscalerが覚えていた値がクリアされる。

これをstartup.cの中に書いてあげることで、DAQを回した瞬間にスケーラーの値を0にできる。
ここで一つ注意事項。

univ_map_write32の二番目の引数(上の例では、scという変数)は、ポインタである必要がある。
ただの数字の1を入れても、セグメンテーション違反になって実行できない。
どうするか。
int *sc,a=1;
sc=&a;
このようにすれば問題ない。
なんかそんな回りくどいことしないで、
int *sc;
*sc = 1;
でいいじゃん。と思うだろうけど、
なんかこれじゃうまくいかなかった。

初期化はできた。
次に書き出しである。

sca.cというscaler用の関数の中に、
univ_map_read32という関数を書く。
これによって書き出す仕様になってる。

実際には、
int ii;
int *sc,a=1;
const int SCALERCH = 16;
int scr[SCALERCH];
sc=&a;
for(ii=0;ii univ_map_read32(0x280+ii*4,(long *)(data+BLKSIZE/2-(SCALERCH-ii)*2),SCAN);
}
univ_map_write32(0x020,sc,SCAN);

こんな感じ。
どういうことかというと、
まず、dataという物。これはポインタ。もっと言うと、dataという配列の第一要素(data[0])の先頭のアドレスを表すポインタ。
重要なのは、dataはunsigned shortで定義される。つまり2byteが最小単位。
BLKSIZEはbyte単位。
つまり、univ_map_read32の二番目の引数は、
dataの先頭から、BLKSIZE/2個(short単位で)進んだところ、つまり、ブロックの最終部分、さらにそこから、全ch-各ch×2個(short単位)戻ったところに、
scalerのbase addressから、0x280+各ch*4(チャネル毎のオフセットは0x4なので)のアドレスに格納された値をコピーする。
という意味。
わけわかんない?
これをすると、
dataのお尻から、64byte分、メモリ空間が確保され、そこに、4byteずつ、各チャネルのデータが順番に並ぶ。
このforループを抜けると、全チャネルの値をクリアするために、
startup.cに書いたのと同じようにuniv_map_write32関数が用意されている。

基本的には、これだけ。

最後に読み出し。
これはめんどくさいから、プログラムをそのまま乗っける
/*******************************/
/*analize to XXXX.rdf*/
/*read the v1190 output and scaler data*/
/*this program must be used in the environment we can use "ROOT"*/
/*******************************/


#include
#include
#include
#include
#include "TApplication.h"
#include "/home/gauge/work/ana/H_header/rootheader.h"

#define BLKSIZE 0x21000

const int SCALERCH = 16;
unsigned short buff[BLKSIZE/2];
unsigned int data[BLKSIZE/4];
unsigned int scr[SCALERCH];
const int MAXCH = 128;
// int 4byte
// short 2byte
// char 1byte

int main(int argc, char *argv[]){
TApplication theApp("App",&argc,argv);

gROOT->SetStyle("Plain");
gStyle->SetOptStat("neuioMR");
gStyle->SetPalette(1);

FILE *fd;
int mp, sz, datasz, i,ii;
int tdcdata,header;
int trigger=0;
unsigned int tdcCannel;
char filename[20];
char rootname[20];
int dataI;
int edge;
unsigned int sum[SCALERCH] = {0};
int dmanumber=0;
switch (argc) {
case 1:
fprintf(stdout," ./vr4 filenumber\n");
fprintf(stdout," ./vr4 0000\n ");
exit(1);
break;
case 2:
sprintf(filename,"%s.rdf",argv[1]);
if((fd = fopen(filename, "r")) == NULL){
printf("Can't open %s\n", filename);
exit(1);
}
break;
}

TNtuple *nt = new TNtuple("nt","TDCdata","N:Edge:Cannel:data");
while(!feof(fd)){
fread(buff, 2, BLKSIZE/2, fd);

if(buff[0] != 0) continue; // skip to next block

mp = 4;

while(buff[mp] != 0xffff){
sz = buff[mp] & 0x7fff;
memcpy((char *)data, (char *)(buff+mp+5), (sz-5)*2);
datasz = (sz-5)/2; // in integer unit
for(i=0;i tdcdata=data[i] & 0xf8000000;
header=data[i] & 0x40000000;
if(header==0x40000000){
trigger++;
}
if(tdcdata!=0)continue;
tdcCannel=((data[i] & 0x07f80000) >> 19);
if((tdcCannel >> 7) == 0){
dataI=data[i] & 0x0007ffff;
edge=0; //for rising
}
else if((tdcCannel >> 7) == 1){
tdcCannel = tdcCannel & 0x0000007f;
dataI=data[i] & 0x0007ffff;
edge=1; //for faling
}
nt->Fill(trigger,edge,tdcCannel,dataI);
}
memcpy((char *)scr,(char *)(buff+BLKSIZE/2-(2*SCALERCH)),4*SCALERCH);
for(ii=0;ii sum[ii] += scr[ii];
if(scr[ii]!=0) fprintf(stdout,"src[%d]=%d\n",ii,scr[ii]);
}
dmanumber++;
mp += sz;
}
}
fclose(fd);
sprintf(rootname,"%s.root",argv[1]);
TFile *rootfile = new TFile(rootname,"RECREATE","tdcData");
nt->Write();
rootfile->Close();
for(ii=0;ii fprintf(stdout,"scaler channel[%d] = %d\n",ii,sum[ii]);
}
fprintf(stdout,"event number = %d\n",trigger);
fprintf(stdout,"dmanumber = %d\n",dmanumber);
return 0;
}

重要なのは、memcpyのところ。
まず、scalerのデータ用配列を用意しとく。ここではint scr[SCALERCH];で定義してある。
データが格納された部分をsrc配列に詰めていく。
memcpyはbyte単位でやり取りするので、
memcpy((char *)scr,(char *)(buff+BLKSIZE/2-(2*SCALERCH)),4*SCALERCH);
引数一つ目、
scr配列の先頭1byte分のアドレス
引数二つ目
buff:これも配列。fread(buff,2,BLKSIZE/2,fd);で、fdファイルポインタの先頭から、2byteずつ(buffはunsigned shortなので)、BLKSIZE/2(これもshortサイズに合わせてる)個buffの先頭からコピーしている。
これはC言語の知識だけど、配列を宣言すると、例えばintだったら、配列の先頭から4byteずつ、配列の要素分のメモリ空間が用意される。
つまり、buffの先頭からfreadでコピーすると、各要素が2byteずつ、全部でBLKSIZE/2個、すなわちBLKSIZEバイトのデータが格納される。
これとほとんど同じで、
buffの先頭から、BLKSIZE/2個(short 単位)行ったところ(データのお尻)から、2*SCALERCH個(これもshort)戻ったところの、1byte(charなので)分のアドレス、という意味になる。
引数三つめ
4*SCALERCHというのは、scaler用に確保したメモリ空間の大きさ。これはDAQ側で設定している。memcpyはbyte単位で書かないとややこしくなるので、scrがintだから、4をかけて、byteに変換している。

これをやると、scr配列の各要素に、ch0ならscr[0],ch1ならscr[1]とか、言った感じでデータをこぴーできる。

はい、
長くなったけどこんなところです。

実はまだ未完成で、scalerからの読み出しの値がなんか変。
たぶんこれは、memcpyして、用意した変数にscrの値を足してるんだけど、
おそらくその場所がよくない。
もともとは、DMA一回につき、scalerのデータもくっつけるつもりだったんだけど、
じつはそうなっていなくて、
almost fullでTDCのbufferにthresholdを設けて、それを超えたらCPUにinterruptをかけ、DMA転送を行うのだが、
DMA一回でbuffer内の全てのデータを送れるというわけではない。
一回のDMA転送容量と、その回数を決めてあげることで、buffer内に多くのデータをためても送れるようにする、というのが今後の展開。
その中で、scalerの値を見に行くのは、1blockの中で、どこまでデータが来たら、読みに行く、という別のthresholdが用意されているので、
DMA一回につき、毎回scalerを読んでるわけじゃない。
scalerを読んでる印みたいなものがあれば、
ある程度正しく値を持ってこれると思うんだけど、まだよくわかんない。

それと、DMAの話もうまく行ってない。
試しにalmost fullのthresholdを31kword(TDCの定義。ここではlong)ぐらいにして、
DMAサイズを適当に1/4ぐらいにして、4回DMAさせる使用にしてみた。
そうすると、なんやらおかしくて、
うまく転送ができない。
TDC buffがthresholdを超えてから、一回目の転送が始まるんだけど、一回目と二回目の転送の時間間隔が異常に大きい。
そのため、その時間の間にbuffがいっぱいになって、わけわかんなくなってなぜかネットワークが落ちるという事態にもなった。
これはなんか怖くていろいろチャレンジできません。
次回は、
blockについてるthresholdをめっちゃ小さくして、4096word一回のDMAでblockを使いきる仕様にしてみようかなと。
無駄が多いけど。

そんな感じ。

とりあえず今日はここまで。






SIS3803とは,ココ にもある通り、VME規格のSCALERである。

シグナル総数をカウントして、それを表示してくれる。


写真I


Mott Polarimeterのブログ


写真II

Mott Polarimeterのブログ




さて、VMIVMEからアクセスするためには、ドライバをインストールした後、SIS3803をVMEクレートに取り付け、

# cmdvme -lr 0x38383800
00000000

と3803のベースアドレスにアクセスする。

すると、写真IIの一番右のモジュールの左上のランプがチカッと光る。
アクセスは出来ているみたい。

さらにテスト

# cmdvme -lw 0x38383800 0x1 //Uランプが点灯
# cmdvme -lw 0x38383800 0x100 //Uランプが消灯  

アクセス成功だ。

それでは、実際に簡単な回路を作り測定できるか検証してみる。

まずinput許可の信号を出さなければいけないが、これは

# cmdvme -lw 0x38383828 1

と打つと、Sランプがつく。global count enableの状態にしているのだ。最後の3ケタに028を足して、bitを立たせてあげる。

ちなみにdisableの状態にしたい場合は

# cmdvme -lw 0x3838382C 1

これでデータ非取得モードになる。ちなみに、enable状態にした状態だと、

# cmdvme -lr 0x38383800
00008000

ステータスコードから参照すると、15Bit目が立っているので、Global Count Enableであると確認できる。
これで、1chにclockからはきだされるNIMをさし、1chのデータを呼び出す。address mapによると、ベースアドレス+280が0ch、+2BCが15chなので、1chは0chから4つ後、すなわち+284をすればよい。

# cmdvme -lr 0x38383a84
001212c1
# cmdvme -lr 0x38383a84
00122c60
# cmdvme -lr 0x38383a84
001237c8
# cmdvme -lr 0x38383a84
00123f70
# cmdvme -lr 0x38383a84
0012464e
# cmdvme -lr 0x38383a84
00124bc6

カウント取れてるっぽいね。どういう値を意味するのかはこれから解析する。
v1190aのoutputフォーマットを読み解く。
0000000 0001 0000 0000 0000 0000 0000 0000 0000
0000020 0000 0000 5552 2d4e 3030 3333 2020 5453
0000040 5241 2054 3e3d 3220 3a33 3735 353a 2037
0000060 5320 4f54 2050 3e3d 5820 3a58 5858 583a
0000100 2058 2020 7250 6e69 2074 3e2d 5820 3a58
0000120 5858 583a 2058 3020 2d36 7041 2d72 3031
0000140 0000 0000 7274 6769 6567 2072 6564 616c
0000160 2079 7531 2073 2020 2020 2020 2020 2020
0000200 2020 2020 2020 2020 2020 2020 2020 2020
*
0000400 2020 2020 0000 0000 0000 0000 0000 0000
0000420 0000 0000 0000 0000 0000 0000 0000 0000
*
0410000 0000 0000 0000 0000 8805 0001 0001 0802
0410020 0001 000a 4000 0319 0800 0002 1800 0319
0410040 0900 0002 1900 0319 0a00 0002 1a00 0319
0410060 0b00 0002 1b00 014a 8400 002a 4000 1d9b
0410100 0800 1002 1800 1d9b 0900 1002 1900 1d9b
0410120 0a00 1002 1a00 1d9b 0b00 1002 1b00 014a
0410140 8400 004a 4000 281d 0800 2002 1800 281d
0410160 0900 2002 1900 281d 0a00 2002 1a00 281d
0410200 0b00 2002 1b00 014a 8400 006a 4000 329f
0410220 0800 3002 1800 329f 0900 3002 1900 329f



一行目から
*まではコメント文
0001で始まるのがヘッダー。

2020の連続はよくわかんね。

とりあえず、二個目のアスタリスク以降がデータ。
五個目、8805からデータが始まる。
8805は、DMAのサイズを表している。
でも一番上位のbitは1になるようになっているため、本当のsizeは
0805になる。
そのあとの0001,0001は順番?だと思う。

次、0802はセグメント(bbはそう呼ぶ)のサイズ。
後ろの0001は順番。

いよいよ。000a 4000からデータスタート。
それでもまずは、TDCのヘッダーが入る。
000aと4000。
ちなみにこれらは、後ろが上位ビットになっている。
つまり
0x4000000aが正解。
さて、
こいつら見にくくね?
ってことで、

0x4000000a  これがグローバルヘッダー
0x0800008b  これはTDC Header
0x18000002 TDC Trailer
0x0900008b TDC Header:上との違いは、TDCチップの番号。全4つ。
0x19000002  一個上のHeaderのTrailer
0x0a00008b
0x1a000002
0x0b00008b
0x030032ca ここがデータ。Headerの後ろにTrailerが来ていない。
0x031032ad
0x0700333c
0x071036ea
0x03003373
0x070033c6
0x03003815
0x03003aa3
0x1b00000a  ここにTrailerがあるので、ここまでだとわかる。
0x8400024a  これがGlobal Headerに対する、Trailer。ここまで。
0x4000002a  次のデータ。
0x08001b0d
0x18001002
0x09001b0d
0x19001002
0x0a001b0d
0x1a001002
0x0b001b0d
0x030032c2
0x031032a3
0x07003333
0x071036e0
0x03003368
0x070033bc
0x030037f6
0x03003a9a
0x1b00100a
0x8400024a


そこで疑問。
最初のHeader(TDCのデータより上にあるやつ。8505とか)は、
DMAのたびに入る模様。
なので、これをscanすることで、DMA一回分のデータ量が分かったりする。
じゃぁglobal headerはなんなんだ?
これがevent数という解釈なのか?
なぞ。

もうひとつ。
現在は、clockをtriggerに入れて、同じ信号をNIM ECLで分けてからTDCにinputしている。
一つデータを取っている間は、
次のTRIGGERが来てもデータを取らないようになっているのか。
また同じchに一つのtime window内に信号が複数来た場合はどうなるのか、後ろは見ないのか。

たぶん、上の話は、4000がtriggerの区切りになっている気がする。
下の話は、v1190.shをみて判断するべきかと。

今日のところはこんなところで。

あと、bb scriptを書いとく。
生データから、上の0xffffffffとかを表示するスクリプト。

////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#define BLKSIZE 0x21000

unsigned short buff[BLKSIZE/2];
unsigned int data[BLKSIZE/4];

// int 4byte
// short 2byte
// char 1byte

int main(int argc, char *argv[]){
FILE *fd;
int mp, sz, datasz, i;

if((fd = fopen(argv[1], "r")) == NULL){
printf("Can't open %s\n", argv[1]);
exit(1);
}

while(!feof(fd)){
fread(buff, 2, BLKSIZE/2, fd);

if(buff[0] != 0) continue; // skip to next block

mp = 4;

while(buff[mp] != 0xffff){
sz = buff[mp] & 0x7fff;
printf("evtsize = %d\n", sz);
memcpy((char *)data, (char *)(buff+mp+5), (sz-5)*2);
//buff[mp+3]--buff[mp+sz-1]

datasz = (sz-5)/2; // in integer unit
for(i=0;i printf("0x%08x\n", data[i]);
}


mp += sz;
}


}



fclose(fd);


return 0;
}

/*
8 8 0 5
1000 1000 0000 0101

7 f f f
0111 1111 1111 1111


0000 1000 0000 0101
1111 1111 1111 1111

*/

///////////////////////////////////////////////////////////