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について。
・立ち上げただけでは、カウントできない。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_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,"
fprintf(stdout,"
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
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
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,"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を使いきる仕様にしてみようかなと。
無駄が多いけど。
そんな感じ。
とりあえず今日はここまで。

