LLC2_API
llcontrol-core.cpp
Go to the documentation of this file.
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