LLC2_API
|
00001 /***************************************************************************** 00002 * 00003 * File: llcontrol-core.cpp 00004 * 00005 * $RCSfile: llcontrol-core.c,v $ 00006 * 00007 * Copyright (C) 2001 D-TACQ Solutions Ltd 00008 * not to be used without owner's permission 00009 * 00010 * Description: 00011 * application implementing the LOW LATENCY CONTROL feature 00012 * 00013 */ 00014 00015 00016 /** @file llcontrol-core.cpp module implements the core of llcontrol loop. 00017 * 00018 - mmapMboxes() - get app mapping for mailboxes 00019 00020 - mmapBuffer() - get dmabuff and phys addresses 00021 00022 - runSCM() - capture using soft trigger 00023 00024 - runECM() - capture using external clock 00025 <p> 00026 <h2>Linux 2.4 Host Only:</h2> 00027 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 00028 00029 Note on HOST MEMORY MAP: assumes bootcommand configured as follows, 00030 to allow a 16MB host area per card in at the top of memory. 00031 This host area will be slaved off the pci backplane 00032 00033 - mem=NNN restrict memory for Linux. Must allow Nx16MB + gap 00034 - acq32_big_buf_base=0xnnnn : informs driver of start of area 00035 - acq32_big_buf_len=0xxxxx : informs driver of length of area 00036 00037 eg 00038 00039 [dt100@cp605 dt100]$ cat /proc/cmdline mem=320M acq32.load_order=slotbased 00040 00041 acq32_big_buf_base=0x18000000 acq32_big_buf_len=0x08000000 00042 00043 For the purpose of LLCONTROL, each 16MB card are is used as follows: 00044 00045 - 0x000000 .. 0xefffff (15MB) - AI reception area (controlled by client app). 00046 - 0xf00000 .. 0xffffff (1MB) - message buffers (owned by target). 00047 00048 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 2.4 00049 00050 00051 This means that if the llcontrol app is using an incrementing memory 00052 strategy in host buffer for AI, this is subject to max 15MB 00053 (80K samples at 96 channels). 00054 00055 The message buffers are used for DAC output. 00056 The application reserves a Mesage Frame Address (MFA) from the target 00057 before copying data to slave memory. 00058 The MFA is an index into the message buffer memory. 00059 00060 </p> 00061 00062 <h2>Linux 2.6 Host: </h2> 00063 the host buffer is setup dynamically, but is LIMITED to 4MB, 00064 of which the top 1M is used for messaging. 00065 00066 */ 00067 00068 00069 00070 #include "local.h" 00071 00072 #include <assert.h> 00073 #include <stdio.h> 00074 #include <stdlib.h> 00075 00076 #include <errno.h> 00077 #include <fcntl.h> 00078 #include <sys/ioctl.h> 00079 #include <sys/mman.h> 00080 #include <sys/stat.h> 00081 #include <sys/time.h> 00082 #include <sys/types.h> 00083 #include <stdio.h> 00084 #include <unistd.h> 00085 00086 #include <popt.h> 00087 00088 #include "acq32ioctl.h" 00089 #include "acq32busprot.h" 00090 00091 #include "llif.h" 00092 #include "llprotocol.h" 00093 00094 00095 #include "llcontrol.h" 00096 #include "x86timer.h" 00097 00098 #define FLAVOR "ACQ196" 00099 00100 #include "llcontrol-core.h" 00101 00102 const char* core_ident = "$Revision: 1.1.4.33 $ B1102\n"; 00103 00104 int G_quit; 00105 00106 00107 00108 unsigned llcv2_hb_offset = 0; 00109 00110 00111 00112 00113 00114 void v2_updateTstats( 00115 u32 cmd, struct Card* card, struct TimingStats* tstats) 00116 /** updates timing stats from embedded host buffer data */ 00117 { 00118 u32* stats = getVaddr(card->buf, LLCV2_OFFSET_STATUS_HSBT); 00119 tstats->tinst = llv2_extend32(stats[BP_MB_LLC_TINST], 00120 stats[LLCV2_STATUS_TINST]); 00121 tstats->tprocess = LLC_GET_TCYCLE(stats[BP_MB_LLC_CSR]); 00122 } 00123 00124 00125 void (*updateTstats)(u32 cmd, struct Card* card, struct TimingStats* tstats) = 00126 v2_updateTstats; 00127 00128 00129 u32 card_v2_WaitDmaDone(struct Card* card) 00130 { 00131 return llv2WaitDmaDone(card->mbx, 00132 getVaddr(card->buf, LLCV2_OFFSET_STATUS_HSBT)); 00133 } 00134 00135 00136 00137 u32 (*waitDmaDone)(struct Card *c) = card_v2_WaitDmaDone; 00138 /**< virtual function to block until DMA done. 00139 * defaults to V1 mbox 00140 */ 00141 00142 00143 int user_abort = 0; /** @todo could be set by a signal */ 00144 00145 00146 00147 static int measureBridge(struct MU *mu) 00148 /** Run a test - measureBridge performance. */ 00149 { 00150 #define NMEASURE 20 00151 static struct { 00152 unsigned elapsed; 00153 u32 mbox; 00154 } stats[NMEASURE]; 00155 int im; 00156 unsigned sum = 0; 00157 00158 for (im = 0; im != NMEASURE; ++im){ 00159 INIT_TIMER; 00160 stats[im].mbox = getMbox(mu, 3); 00161 stats[im].elapsed = get_elapsed_microseconds(0); 00162 00163 usleep(10000); 00164 } 00165 00166 fprintf(stderr, "[%2s], %10s, %10s\n","id", "mbox3", "usecs"); 00167 for (im = 0; im != NMEASURE; ++im){ 00168 sum += stats[im].elapsed; 00169 00170 fprintf(stderr, "[%2d], 0x%08x, %10d\n", 00171 im, stats[im].mbox, stats[im].elapsed); 00172 } 00173 00174 fprintf(stderr, "mean %10s, %12.1f\n", "", (float)sum/NMEASURE); 00175 return 0; 00176 } 00177 00178 static int measureBridgeStats(struct TestDescription *td, struct MU *mu) 00179 { 00180 static struct { 00181 int min; 00182 int max; 00183 int sum; 00184 int samples; 00185 } stats = { 1000000, -1000000, 0, 0 }; 00186 00187 int im; 00188 int current; 00189 00190 if ( td->mask_ints ){ 00191 PRINTF(1)( "mask ints %s\n", td->mask_ints_mask ); 00192 FOREACHCARD{ 00193 acq200_setImask(EACHSLOT(td), td->mask_ints_mask); 00194 } 00195 } 00196 00197 for (im = 0; im != td->iterations; ++im){ 00198 INIT_TIMER; 00199 getMbox(mu, 3); 00200 00201 current = get_elapsed_microseconds(0); 00202 00203 if (current < stats.min){ 00204 stats.min = current; 00205 }else if (current > stats.max){ 00206 stats.max = current; 00207 } 00208 stats.sum += current; 00209 stats.samples++; 00210 } 00211 00212 if (td->mask_ints){ // @@todo refactor me please. 00213 char* enable_mask = new char[strlen(td->mask_ints_mask)+1]; 00214 int ic; 00215 00216 for (ic = 0; td->mask_ints_mask[ic]; ++ic){ 00217 enable_mask[ic] = '0'; /** enable all! */ 00218 } 00219 enable_mask[ic] = '\0'; 00220 FOREACHCARD{ 00221 acq200_setImask(EACHSLOT(td), enable_mask); 00222 } 00223 delete [] enable_mask; 00224 } 00225 00226 fprintf(stderr, "measureBridgeStats results from %d polls\n", 00227 stats.samples); 00228 fprintf(stderr, "%10s %d\n", "min", stats.min); 00229 fprintf(stderr, "%10s %d\n", "max", stats.max); 00230 fprintf(stderr, "%10s %.2f\n", "mean", (float)stats.sum/stats.samples); 00231 return 0; 00232 } 00233 00234 00235 00236 #include <signal.h> 00237 00238 static struct TestDescription* S_td; 00239 00240 static void quit_handler( int signum ) 00241 { 00242 struct TestDescription* td = S_td; 00243 00244 fprintf( stderr, "Quitting Time - mailboxes were\n" ); 00245 00246 FOREACHCARD{ 00247 fprintf(stderr, "0x%08x 0x%08x 0x%08x 0x%08x\n", 00248 getMbox(EACHMBX(td), 0), 00249 getMbox(EACHMBX(td), 1), 00250 getMbox(EACHMBX(td), 2), 00251 getMbox(EACHMBX(td), 3) ); 00252 } 00253 00254 FOREACHCARD{ 00255 showLastWrites(EACHMBX(S_td)); 00256 } 00257 00258 FOREACHCARD{ 00259 leaveLLC(EACHMBX(S_td)); 00260 } 00261 exit( signum ); 00262 } 00263 00264 void setupAbortHandler( struct TestDescription* td ) 00265 { 00266 static struct sigaction def_action = { { quit_handler } }; 00267 00268 S_td = td; // ugly global. use class var in C++ 00269 00270 sigaction( SIGINT, &def_action, 0 ); 00271 } 00272 00273 00274 00275 00276 static void monitor_handler( int signum ) 00277 { 00278 static u32 old_mboxes[4]; 00279 u32 new_mboxes[4]; 00280 int ibox; 00281 int changed = 0; 00282 00283 for ( ibox = 0; ibox != 4; ++ibox ){ 00284 new_mboxes[ibox] = getMbox( S_td->cards[0].mbx, ibox ); 00285 if ( new_mboxes[ibox] != old_mboxes[ibox] ){ 00286 changed = 1; 00287 } 00288 } 00289 00290 if ( changed || verbose > 2 ){ 00291 fprintf( stderr, "%8d ", S_td->iter ); 00292 for ( ibox = 0; ibox != 4; ++ibox ){ 00293 fprintf( stderr, "0x%08x%c ", 00294 new_mboxes[ibox], 00295 new_mboxes[ibox]!=old_mboxes[ibox]? '*': ' ' ); 00296 00297 old_mboxes[ibox] = new_mboxes[ibox]; 00298 } 00299 fprintf( stderr, "\n" ); 00300 } 00301 if ( G_quit ){ 00302 setupMonitor( 0 ); 00303 fprintf( stderr, "monitor quitting\n" ); 00304 } 00305 } 00306 00307 void setupMonitor( int millisec ) 00308 { 00309 static struct sigaction monitor_action = { { monitor_handler } }; 00310 static struct itimerval new_timer = {}; 00311 00312 new_timer.it_value.tv_usec = 00313 new_timer.it_interval.tv_usec = millisec*1000; 00314 00315 sigaction( SIGALRM, &monitor_action, 0 ); 00316 setitimer( ITIMER_REAL, &new_timer, 0 ); 00317 } 00318 00319 void initCardResource(struct Card* card) 00320 { 00321 card->mbx = mmapMbox(card->slot); 00322 card->buf = mmapBigBuffer(card->slot, ACQ196_BIGBUF_AREA); 00323 hbPrimeBuffer(card->buf); 00324 00325 get_cpu_clock_speed(); 00326 } 00327 00328 00329 static void debug_prompt(int icard, int ibuf, u32 addr) 00330 /** promnpt user to ensure pram set (temporary pre-update measure). */ 00331 { 00332 const char* value; 00333 switch(ibuf){ 00334 case LLCV2_INIT_AI_HSBT: 00335 value = "AI_target"; break; 00336 case LLCV2_INIT_AO_HSBS: 00337 value = "AO_src"; break; 00338 case LLCV2_INIT_DO_HSBS: 00339 value = "DO src"; break; 00340 case LLCV2_INIT_STATUS_HSBT: 00341 value = "STATUS_target";break; 00342 default: 00343 value = ""; 00344 } 00345 00346 fprintf(stderr, 00347 "V2: please ensure card %d has pram %s set 0x%08x\n", 00348 icard, value, addr); 00349 } 00350 00351 00352 static void sync_2v_updateTstats( 00353 u32 cmd, struct Card* card, struct TimingStats* tstats) 00354 /** updates timing stats from embedded host buffer data */ 00355 { 00356 #define SAMPLE_SIZE (96*2) /* WORKTODO */ 00357 u32* stats = getVaddr(card->buf, card->sync_2v_offset_status_hsbt); 00358 tstats->tinst = llv2_extend32(stats[LLC_SYNC2V_IN_MBOX2], 00359 stats[LLC_SYNC2V_IN_TINST]); 00360 tstats->tprocess = LLC_GET_TCYCLE(stats[LLC_SYNC2V_IN_MBOX0]); 00361 } 00362 00363 static u32 card_sync_2v_WaitDmaDone(struct Card* card) 00364 { 00365 return card->tlatch = llv2WaitDmaDone_2v(card->mbx, 00366 getVaddr(card->buf, card->sync_2v_offset_status_hsbt), 00367 card->tlatch 00368 ); 00369 } 00370 00371 #define BAR_FIFO 3 00372 00373 00374 static u32 getSlavePa(int slot) 00375 { 00376 char fname[128]; 00377 char line[80]; 00378 FILE *fp; 00379 int bar; 00380 u32 pa = 0; 00381 00382 sprintf(fname, "/dev/ao32cpci/ctrl/ao32cpci.%d/resource", slot); 00383 00384 fp = fopen(fname, "r"); 00385 if (!fp){ 00386 fprintf(stderr, "ERROR: failed to open \"%s\"\n", fname); 00387 exit(errno); 00388 } 00389 00390 for (bar = 0; fgets(line, 80, fp); ++bar){ 00391 if (bar == BAR_FIFO){ 00392 long long pa0, pa1, len; 00393 if (sscanf(line, 00394 "%Lx %Lx %Lx", &pa0, &pa1, &len) == 3){ 00395 pa = (u32)pa0; 00396 } 00397 } 00398 } 00399 fclose(fp); 00400 00401 if (pa == 0){ 00402 fprintf(stderr, 00403 "ERROR: failed to get sensible PA for %d\n", slot); 00404 } 00405 return pa; 00406 } 00407 00408 00409 static void setSlaveData(u32* aovec, void* src) 00410 /** create some data for AO32CPCI. 00411 AO32 gets AO16 data duplicated, DO64 gets a walking bit 00412 */ 00413 { 00414 static int ibit; 00415 unsigned long long DO_PAT = 1ULL<<ibit; 00416 00417 if (++ibit > 64){ 00418 ibit = 0; 00419 } 00420 00421 memcpy(aovec, src, 16*sizeof(short)); 00422 memcpy(aovec+8, src, 16*sizeof(short)); 00423 memcpy(aovec+16, &DO_PAT, sizeof(unsigned long long)); 00424 } 00425 00426 00427 00428 00429 void appEnterLLC_SYNC_2VAO32( 00430 int icard, struct MU *mu, struct TestDescription *td) 00431 /** set up LLCV2_INIT buffer and enter mode. 00432 * Buffer set up as 4K block at offset 0 00433 */ 00434 { 00435 u32* init_buf = getVaddr(EACHBUF(td), 0); 00436 u32 target_pa = getBusAddr(EACHBUF(td), 0); 00437 struct Card* card = &td->cards[icard]; 00438 int islave = 0; 00439 PRINTF(2)("appEnterLLC_SYNC_2V() va:%p pa:0x%08x %s slaves %d\n", 00440 init_buf, target_pa, MASTER? "MASTER": "",td->ao32_count); 00441 00442 00443 /** set up for single 4K buffer */ 00444 llcv2_hb_offset = 0; 00445 card->sync_2v_offset_status_hsbt = 00446 LLCV2_AI_HSBT + card->channels*sizeof(short); 00447 card->tlatch = 0; 00448 00449 /** uses V2 synchronization */ 00450 updateTstats = sync_2v_updateTstats; 00451 waitDmaDone = card_sync_2v_WaitDmaDone; 00452 00453 init_buf[LLCV2_INIT_MARKER] = LLCV2_INIT_MAGIC_AO32; 00454 init_buf[LLCV2_INIT_AI_HSBT] = target_pa + LLCV2_AI_HSBT; 00455 init_buf[LLCV2_INIT_AO_HSBS] = target_pa + LLCV2_AO_HSBS; 00456 00457 /* make a zero terminated list of slave pa's */ 00458 if (MASTER){ 00459 for (islave = 0; islave < td->ao32_count; ++islave){ 00460 init_buf[LLCV2_INIT_AO32PA0+islave] = 00461 getSlavePa(td->ao32_ids[islave]); 00462 } 00463 } 00464 init_buf[LLCV2_INIT_AO32PA0+islave] = 0; 00465 00466 enterLLC_SYNC_ECM( 00467 mu, td->clkpos, td->trpos, 00468 td->arg.divisor,td->internal_loopback, 00469 BP_FC_SET_LLCV2_INIT, 00470 target_pa ); 00471 } 00472 00473 00474 static int make_output_file( 00475 struct TestDescription* td, 00476 int slot, 00477 const char* _class) 00478 { 00479 if (td->outfname == 0){ 00480 return 0; // no file 00481 }else if ( td->outfname[0] == '-' ){ 00482 return 1; // use stdout 00483 }else{ 00484 char outfname[128]; 00485 int fd_out; 00486 00487 sprintf(outfname, "%s.%d.%s", td->outfname, slot, _class); 00488 fd_out = open( outfname, O_WRONLY|O_CREAT|O_TRUNC, 0666 ); 00489 00490 if (fd_out <= 0){ 00491 fprintf(stderr, "ERROR: failed to open file %s\n", 00492 outfname); 00493 exit( -1 ); 00494 } 00495 return fd_out; 00496 } 00497 } 00498 static void doWorkBufDataOutput(struct TestDescription *td) 00499 { 00500 FOREACHCARD{ 00501 int iter; 00502 int offset = 0; 00503 int fd_out = make_output_file(td, EACHSLOT(td), "userbuf"); 00504 00505 for (iter = 0; iter != td->iterations; ++iter){ 00506 write(fd_out, (char*)td->work_buf[icard]+offset, 00507 td_sample_size(td)*td->samples); 00508 offset += td_sample_size(td)*td->samples; 00509 } 00510 close(fd_out); 00511 } 00512 } 00513 static void doDmaBufDataOutput(struct TestDescription *td) 00514 { 00515 FOREACHCARD{ 00516 int iter; 00517 int offset = 0; 00518 int fd_out = make_output_file(td, EACHSLOT(td), "dmabuf"); 00519 00520 for (iter = 0; iter != td->iterations; ++iter){ 00521 write(fd_out, getVaddr(EACHBUF(td), offset), 00522 td_sample_size(td)*td->samples); 00523 if (td->overwrite){ 00524 break; 00525 } 00526 offset += td->sample_offset; 00527 00528 } 00529 close(fd_out); 00530 } 00531 } 00532 static int CLIP_LIMIT = 100000; 00533 00534 /** CLIP bogus times - not to try hide anything - big number spoils display */ 00535 #define CLIP(t) ((t) < 0? -1: (t) > CLIP_LIMIT? (-1): (t)) 00536 00537 static inline int extractTprocess(struct TimingStats *ts) 00538 /** if TP is available, USE IT, else calculate from tlatch, tinst 00539 * this is valid because either it is accurate IOP calc, or 00540 * worst case Host calc 00541 */ 00542 { 00543 return CLIP(ts->tinst) - CLIP(ts->tlatch); 00544 } 00545 00546 static void dumpTimingStats( struct TestDescription* td, FILE* fp ) 00547 { 00548 #define HFMT "%8s, %10s, %10s, %8s, %8s, %6s, %4s, " 00549 #define DFMT "%8d, %10d, %10d, %8d, %8d, %6d, %4d, " 00550 00551 int iter; 00552 00553 u32 old_tlatch[MAXCARDS] = {}; 00554 00555 FOREACHCARD{ 00556 fprintf(fp, "%60s: %d", "CARD", EACHSLOT(td)); 00557 } 00558 fprintf(fp, "\n"); 00559 FOREACHCARD{ 00560 int it; 00561 fprintf(fp, HFMT, 00562 "iter", "tinst", "tlatch", "tprocess", "tclock", 00563 "hpol","tpol"); 00564 00565 if (td->tlog < 2) continue; /** brief report */ 00566 00567 for (it = 0; it != MAXTEST; ++it){ 00568 fprintf(fp, "%6d,", it); 00569 } 00570 } 00571 fprintf(fp, "\n"); 00572 00573 for (iter = 0; iter != td->iterations; ++iter){ 00574 00575 if ( iter && td->stats_buf[0][iter].iter == 0 ){ 00576 fprintf(fp, "\n"); 00577 break; 00578 }else 00579 00580 FOREACHCARD{ 00581 struct TimingStats *ts = &td->stats_buf[icard][iter]; 00582 int tclock = ts->tlatch - old_tlatch[icard]; 00583 int it; 00584 int tp1 = 0; 00585 int tp2 = 0; 00586 00587 fprintf(fp, DFMT, 00588 ts->iter, 00589 CLIP(ts->tinst), 00590 CLIP(ts->tlatch), 00591 extractTprocess(ts), 00592 CLIP(tclock), 00593 ts->hb_poll, 00594 ts->target_poll); 00595 old_tlatch[icard] = ts->tlatch; 00596 00597 if (td->tlog < 2) continue; /** brief report */ 00598 00599 for (it = 0; it != MAXTEST; ++it){ 00600 int tp = ts->test_points[it]; 00601 if (tp){ 00602 if (tp1 == 0){ 00603 tp1 = tp; 00604 }else if (tp > tp2){ 00605 tp2 = tp; 00606 } 00607 } 00608 fprintf(fp, "%6d,", tp); 00609 } 00610 fprintf(fp, " T:%3d, ", tp2 - tp1); 00611 } 00612 00613 fprintf(fp, "\n"); 00614 } 00615 #undef HFMT 00616 #undef DFMT 00617 #undef CLIP 00618 } 00619 00620 00621 static void dumpTimingStatsBin(struct TestDescription* td, const char *bfile) 00622 { 00623 FILE* fp = fopen(bfile, "w"); 00624 00625 u32 tclock; 00626 int iter; 00627 u32 old_tinst[MAXCARDS] = {}; 00628 00629 assert(fp); 00630 00631 for ( iter = 0; iter != td->iterations; ++iter ){ 00632 FOREACHCARD{ 00633 struct TimingStats *ts = &td->stats_buf[icard][iter]; 00634 00635 fwrite(&ts, sizeof(struct TimingStats), 1, fp); 00636 tclock = ts->tinst - old_tinst[icard]; 00637 fwrite(&tclock, sizeof(u32), 1, fp); 00638 old_tinst[icard] = ts->tinst; 00639 } 00640 } 00641 fclose(fp); 00642 } 00643 00644 void doPostShotAnalysis(struct TestDescription *td) 00645 { 00646 if ( td->tlog ){ 00647 if (td->tlog_binfile != 0){ 00648 dumpTimingStatsBin(td, td->tlog_binfile); 00649 }else{ 00650 FILE *fp = fopen("/tmp/llcontrol.tstats", "w"); 00651 dumpTimingStats(td, fp); 00652 fclose(fp); 00653 printf("timing stats stored in /tmp/llcontrol.tstats\n"); 00654 // dumpTimingStats(td, stdout); 00655 } 00656 } 00657 if ( td->outfname != 0 ){ 00658 doDmaBufDataOutput(td); 00659 if (td->do_work){ 00660 doWorkBufDataOutput(td); 00661 } 00662 } 00663 } 00664 00665 00666 00667 00668 void updateTimingStats( 00669 struct TimingStats* buffer, int iter, struct TimingStats* tstats) 00670 { 00671 struct TimingStats* cursor = &buffer[iter]; 00672 00673 memcpy(cursor, tstats, sizeof(struct TimingStats)); 00674 cursor->iter = iter; 00675 } 00676 00677 00678