root/trunk/libffado/tests/test-timestampedbuffer.cpp

Revision 1763, 19.6 kB (checked in by ppalmers, 14 years ago)

Merged revisions 1536,1541,1544-1546,1549,1554-1562,1571,1579-1581,1618,1632,1634-1635,1661,1677-1679,1703-1704,1715,1720-1723,1743-1745,1755 via svnmerge from
svn+ssh://ffadosvn@ffado.org/ffado/branches/libffado-2.0

Also fix remaining format string warnings.

Line 
1 /*
2  * Copyright (C) 2005-2008 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <argp.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <endian.h>
33
34 #include <signal.h>
35 #include "src/debugmodule/debugmodule.h"
36
37 #include "libutil/ByteSwap.h"
38
39 #include "src/libieee1394/cycletimer.h"
40
41 #include "src/libutil/TimestampedBuffer.h"
42 #include "libutil/Time.h"
43
44 #include <pthread.h>
45
46 using namespace Util;
47
48 class TimestampedBufferTestClient
49     : public TimestampedBufferClient {
50 public:
51     bool processReadBlock(char *data, unsigned int nevents, unsigned int offset) {return true;};
52     bool processWriteBlock(char *data, unsigned int nevents, unsigned int offset) {return true;};
53
54     void setVerboseLevel(int l) {setDebugLevel(l);};
55 private:
56     DECLARE_DEBUG_MODULE;
57 };
58
59 IMPL_DEBUG_MODULE( TimestampedBufferTestClient, TimestampedBufferTestClient, DEBUG_LEVEL_VERBOSE );
60
61 DECLARE_GLOBAL_DEBUG_MODULE;
62
63 int run;
64 // Program documentation.
65 static char doc[] = "FFADO -- Timestamped buffer test\n\n";
66
67 // A description of the arguments we accept.
68 static char args_doc[] = "";
69
70
71 struct arguments
72 {
73     short verbose;
74     uint64_t wrap_at;
75     uint64_t frames_per_packet;
76     uint64_t events_per_frame;
77     float rate;
78     uint64_t total_cycles;
79     uint64_t buffersize;
80     uint64_t start_at_cycle;
81 };
82
83 // The options we understand.
84 static struct argp_option options[] = {
85     {"verbose",     'v',    "n",    0,  "Verbose level" },
86     {"wrap",        'w',    "n",    0,  "Wrap at (ticks) (3072000)" },
87     {"fpp",        'f',    "n",    0,  "Frames per packet (8)" },
88     {"epf",        'e',    "n",    0,  "Events per frame (10)" },
89     {"rate",        'r',    "n",    0,  "Rate (ticks/frame) (512.0)" },
90     {"cycles",        'c',    "n",    0,  "Total cycles to run (2000)" },
91     {"buffersize",        'b',    "n",    0,  "Buffer size (in frames) (1024)" },
92     {"startcycle",        's',    "n",    0,  "Start at cycle (0)" },
93     { 0 }
94 };
95
96 //-------------------------------------------------------------
97
98 // Parse a single option.
99 static error_t
100 parse_opt( int key, char* arg, struct argp_state* state )
101 {
102     // Get the input argument from `argp_parse', which we
103     // know is a pointer to our arguments structure.
104     struct arguments* arguments = ( struct arguments* ) state->input;
105     char* tail;
106
107     errno = 0;
108     switch (key) {
109         case 'v':
110             if (arg) {
111                 arguments->verbose = strtoll( arg, &tail, 0 );
112                 if ( errno ) {
113                     fprintf( stderr, "Could not parse 'verbose' argument\n" );
114                     return ARGP_ERR_UNKNOWN;
115                 }
116             } else {
117                 if ( errno ) {
118                     fprintf( stderr, "Could not parse 'verbose' argument\n" );
119                     return ARGP_ERR_UNKNOWN;
120                 }
121             }
122             break;
123         case 'w':
124             if (arg) {
125                 arguments->wrap_at = strtoll( arg, &tail, 0 );
126                 if ( errno ) {
127                     fprintf( stderr, "Could not parse 'wrap' argument\n" );
128                     return ARGP_ERR_UNKNOWN;
129                 }
130             } else {
131                 if ( errno ) {
132                     fprintf( stderr, "Could not parse 'wrap' argument\n" );
133                     return ARGP_ERR_UNKNOWN;
134                 }
135             }
136             break;
137         case 'f':
138             if (arg) {
139                 arguments->frames_per_packet = strtoll( arg, &tail, 0 );
140                 if ( errno ) {
141                     fprintf( stderr, "Could not parse 'fpp' argument\n" );
142                     return ARGP_ERR_UNKNOWN;
143                 }
144             } else {
145                 if ( errno ) {
146                     fprintf( stderr, "Could not parse 'fpp' argument\n" );
147                     return ARGP_ERR_UNKNOWN;
148                 }
149             }
150             break;
151         case 'e':
152             if (arg) {
153                 arguments->events_per_frame = strtoll( arg, &tail, 0 );
154                 if ( errno ) {
155                     fprintf( stderr, "Could not parse 'epf' argument\n" );
156                     return ARGP_ERR_UNKNOWN;
157                 }
158             } else {
159                 if ( errno ) {
160                     fprintf( stderr, "Could not parse 'epf' argument\n" );
161                     return ARGP_ERR_UNKNOWN;
162                 }
163             }
164             break;
165         case 'c':
166             if (arg) {
167                 arguments->total_cycles = strtoll( arg, &tail, 0 );
168                 if ( errno ) {
169                     fprintf( stderr, "Could not parse 'cycles' argument\n" );
170                     return ARGP_ERR_UNKNOWN;
171                 }
172             } else {
173                 if ( errno ) {
174                     fprintf( stderr, "Could not parse 'cycles' argument\n" );
175                     return ARGP_ERR_UNKNOWN;
176                 }
177             }
178             break;
179         case 's':
180             if (arg) {
181                 arguments->start_at_cycle = strtoll( arg, &tail, 0 );
182                 if ( errno ) {
183                     fprintf( stderr, "Could not parse 'startcycle' argument\n" );
184                     return ARGP_ERR_UNKNOWN;
185                 }
186             } else {
187                 if ( errno ) {
188                     fprintf( stderr, "Could not parse 'startcycle' argument\n" );
189                     return ARGP_ERR_UNKNOWN;
190                 }
191             }
192             break;
193         case 'b':
194             if (arg) {
195                 arguments->buffersize = strtoll( arg, &tail, 0 );
196                 if ( errno ) {
197                     fprintf( stderr, "Could not parse 'buffersize' argument\n" );
198                     return ARGP_ERR_UNKNOWN;
199                 }
200             } else {
201                 if ( errno ) {
202                     fprintf( stderr, "Could not parse 'buffersize' argument\n" );
203                     return ARGP_ERR_UNKNOWN;
204                 }
205             }
206             break;
207         case 'r':
208             if (arg) {
209                 arguments->rate = strtof( arg, &tail );
210                 if ( errno ) {
211                     fprintf( stderr, "Could not parse 'rate' argument\n" );
212                     return ARGP_ERR_UNKNOWN;
213                 }
214             } else {
215                 if ( errno ) {
216                     fprintf( stderr, "Could not parse 'rate' argument\n" );
217                     return ARGP_ERR_UNKNOWN;
218                 }
219             }
220             break;
221        default:
222             return ARGP_ERR_UNKNOWN;
223     }
224     return 0;
225 }
226
227 // Our argp parser.
228 static struct argp argp = { options, parse_opt, args_doc, doc };
229
230
231 static void sighandler (int sig)
232 {
233         run = 0;
234 }
235
236 int main(int argc, char *argv[])
237 {
238
239     TimestampedBuffer *t=NULL;
240     TimestampedBufferTestClient *c=NULL;
241
242     struct arguments arguments;
243
244     // Default values.
245     arguments.verbose        = 0;
246     arguments.wrap_at = 3072000LLU; // 1000 cycles
247     arguments.frames_per_packet = 8;
248     arguments.events_per_frame = 10;
249     arguments.rate = 512.0;
250     arguments.total_cycles = 2000;
251     arguments.buffersize = 1024;
252     arguments.start_at_cycle = 0;
253
254     // Parse our arguments; every option seen by `parse_opt' will
255     // be reflected in `arguments'.
256     if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) {
257         fprintf( stderr, "Could not parse command line\n" );
258         exit(1);
259     }
260
261     setDebugLevel(arguments.verbose);
262
263     run=1;
264
265     signal (SIGINT, sighandler);
266     signal (SIGPIPE, sighandler);
267
268     c=new TimestampedBufferTestClient();
269
270     if(!c) {
271         debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBufferTestClient\n");
272         exit(1);
273     }
274     c->setVerboseLevel(arguments.verbose);
275
276     t=new TimestampedBuffer(c);
277
278     if(!t) {
279         debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBuffer\n");
280         delete c;
281         exit(1);
282     }
283     t->setVerboseLevel(arguments.verbose);
284
285     // Setup the buffer
286     t->setBufferSize(arguments.buffersize);
287     t->setEventSize(sizeof(int));
288     t->setEventsPerFrame(arguments.events_per_frame);
289
290     t->setUpdatePeriod(arguments.frames_per_packet);
291     t->setNominalRate(arguments.rate);
292
293     t->setWrapValue(arguments.wrap_at);
294
295     t->prepare();
296
297     SleepRelativeUsec(1000);
298
299     debugOutput(DEBUG_LEVEL_NORMAL, "Start setBufferHeadTimestamp test...\n");
300     {
301         bool pass=true;
302         uint64_t time=arguments.start_at_cycle*3072;
303         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
304
305         // initialize the timestamp
306         uint64_t timestamp=time;
307         if (timestamp >= arguments.wrap_at) {
308             // here we need a modulo because start_at_cycle can be large
309             timestamp %= arguments.wrap_at;
310         }
311
312         // account for the fact that there is offset,
313         // and that setBufferHeadTimestamp doesn't take offset
314         // into account
315         uint64_t timestamp2=timestamp;
316         if (timestamp2>=arguments.wrap_at) {
317             timestamp2-=arguments.wrap_at;
318         }
319
320         t->setBufferHeadTimestamp(timestamp2);
321
322         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
323         if (timestamp >= arguments.wrap_at) {
324             timestamp -= arguments.wrap_at;
325         }
326
327         // write some packets
328         for (unsigned int i=0;i<20;i++) {
329             t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
330             timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
331             if (timestamp >= arguments.wrap_at) {
332                 timestamp -= arguments.wrap_at;
333             }
334         }
335
336         for(unsigned int cycle=arguments.start_at_cycle;
337             cycle < arguments.start_at_cycle+arguments.total_cycles;
338             cycle++) {
339                 ffado_timestamp_t ts_head_tmp;
340                 uint64_t ts_head;
341                 signed int fc_head;
342
343                 t->setBufferHeadTimestamp(timestamp);
344                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
345                 ts_head=(uint64_t)ts_head_tmp;
346
347                 if (timestamp != ts_head) {
348                     debugError(" cycle %4u error: %011"PRIu64" != %011"PRIu64"\n",
349                                cycle, timestamp, ts_head);
350                     pass=false;
351                 }
352
353                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
354                 if (timestamp >= arguments.wrap_at) {
355                     timestamp -= arguments.wrap_at;
356                 }
357
358             // simulate the cycle timer clock in ticks
359             time += 3072;
360             if (time >= arguments.wrap_at) {
361                 time -= arguments.wrap_at;
362             }
363
364             // allow for the messagebuffer thread to catch up
365             SleepRelativeUsec(200);
366
367             if(!run) break;
368         }
369
370         if(!pass) {
371             debugError("Test failed, exiting...\n");
372
373             delete t;
374             delete c;
375
376             return -1;
377
378         }
379     }
380
381
382
383     debugOutput(DEBUG_LEVEL_NORMAL, "Start read/write test...\n");
384     {
385         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
386         int dummyframe_out[arguments.events_per_frame*arguments.frames_per_packet];
387
388         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
389             dummyframe_in[i]=i;
390         }
391
392         uint64_t time=arguments.start_at_cycle*3072;
393
394         // initialize the timestamp
395         uint64_t timestamp=time;
396         if (timestamp >= arguments.wrap_at) {
397             // here we need a modulo because start_at_cycle can be large
398             timestamp %= arguments.wrap_at;
399         }
400         t->setBufferTailTimestamp(timestamp);
401
402         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
403         if (timestamp >= arguments.wrap_at) {
404             timestamp -= arguments.wrap_at;
405         }
406
407         for(unsigned int cycle=arguments.start_at_cycle;
408             cycle < arguments.start_at_cycle+arguments.total_cycles;
409             cycle++) {
410
411             // simulate the rate adaptation
412             int64_t diff=(time%arguments.wrap_at)-timestamp;
413
414             if (diff>(int64_t)arguments.wrap_at/2) {
415                 diff -= arguments.wrap_at;
416             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
417                 diff += arguments.wrap_at;
418             }
419
420             debugOutput(DEBUG_LEVEL_NORMAL,
421                         "Simulating cycle %d @ time=%011"PRIu64", diff=%"PRId64"\n",
422                         cycle, time, diff);
423
424             if(diff > 0) {
425                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
426                 uint64_t ts_head, ts_tail;
427                 signed int fc_head, fc_tail;
428
429                 // write one packet
430                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
431
432                 // read the buffer head timestamp
433                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
434                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
435                 ts_head=(uint64_t)ts_head_tmp;
436                 ts_tail=(uint64_t)ts_tail_tmp;
437                 debugOutput(DEBUG_LEVEL_NORMAL,
438                         " TS after write: HEAD: %011"PRIu64", FC=%04u\n",
439                         ts_head,fc_head);
440                 debugOutput(DEBUG_LEVEL_NORMAL,
441                         "                 TAIL: %011"PRIu64", FC=%04u\n",
442                         ts_tail,fc_tail);
443
444                 // read one packet
445                 t->readFrames(arguments.frames_per_packet, (char *)&dummyframe_out);
446
447                 // read the buffer head timestamp
448                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
449                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
450                 ts_head=(uint64_t)ts_head_tmp;
451                 ts_tail=(uint64_t)ts_tail_tmp;
452                 debugOutput(DEBUG_LEVEL_NORMAL,
453                         " TS after write: HEAD: %011"PRIu64", FC=%04u\n",
454                         ts_head,fc_head);
455                 debugOutput(DEBUG_LEVEL_NORMAL,
456                         "                 TAIL: %011"PRIu64", FC=%04u\n",
457                         ts_tail,fc_tail);
458
459                 // check
460                 bool pass=true;
461                 for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
462                     pass = pass && (dummyframe_in[i]==dummyframe_out[i]);
463                 }
464                 if (!pass) {
465                     debugOutput(DEBUG_LEVEL_NORMAL, "write/read check for cycle %d failed\n",cycle);
466                 }
467
468                 // update the timestamp
469                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
470                 if (timestamp >= arguments.wrap_at) {
471                     timestamp -= arguments.wrap_at;
472                 }
473             }
474
475             // simulate the cycle timer clock in ticks
476             time += 3072;
477             if (time >= arguments.wrap_at) {
478                 time -= arguments.wrap_at;
479             }
480
481             // allow for the messagebuffer thread to catch up
482             SleepRelativeUsec(200);
483
484             if(!run) break;
485         }
486     }
487
488     // second run, now do block processing
489     debugOutput(DEBUG_LEVEL_NORMAL, "Start block read test...\n");
490     {
491         unsigned int blocksize=32;
492         int dummyframe_out_block[arguments.events_per_frame*arguments.frames_per_packet*blocksize];
493         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
494
495         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
496             dummyframe_in[i]=i;
497         }
498
499         uint64_t time=arguments.start_at_cycle*3072;
500
501         // initialize the timestamp
502         uint64_t timestamp=time;
503         if (timestamp >= arguments.wrap_at) {
504             // here we need a modulo because start_at_cycle can be large
505             timestamp %= arguments.wrap_at;
506         }
507         t->setBufferTailTimestamp(timestamp);
508
509         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
510         if (timestamp >= arguments.wrap_at) {
511             timestamp -= arguments.wrap_at;
512         }
513
514         for(unsigned int cycle=arguments.start_at_cycle;
515             cycle < arguments.start_at_cycle+arguments.total_cycles;
516             cycle++) {
517
518             // simulate the rate adaptation
519             int64_t diff=(time%arguments.wrap_at)-timestamp;
520
521             if (diff>(int64_t)arguments.wrap_at/2) {
522                 diff -= arguments.wrap_at;
523             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
524                 diff += arguments.wrap_at;
525             }
526
527             debugOutput(DEBUG_LEVEL_NORMAL,
528                         "Simulating cycle %d @ time=%011"PRIu64", diff=%"PRId64"\n",
529                         cycle, time, diff);
530
531             if(diff>0) {
532                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
533                 uint64_t ts_head, ts_tail;
534                 signed int fc_head, fc_tail;
535
536                 // write one packet
537                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
538
539                 // read the buffer head timestamp
540                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
541                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
542                 ts_head=(uint64_t)ts_head_tmp;
543                 ts_tail=(uint64_t)ts_tail_tmp;
544                 debugOutput(DEBUG_LEVEL_NORMAL,
545                         " TS after write: HEAD: %011"PRIu64", FC=%04u\n",
546                         ts_head,fc_head);
547                 debugOutput(DEBUG_LEVEL_NORMAL,
548                         "                 TAIL: %011"PRIu64", FC=%04u\n",
549                         ts_tail,fc_tail);
550
551                 if (fc_head > (signed int)blocksize) {
552                     debugOutput(DEBUG_LEVEL_NORMAL,"Reading one block (%u frames)\n",blocksize);
553
554                     // read one block
555                     t->readFrames(blocksize, (char *)&dummyframe_out_block);
556
557                     // read the buffer head timestamp
558                     t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
559                     t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
560                     ts_head=(uint64_t)ts_head_tmp;
561                     ts_tail=(uint64_t)ts_tail_tmp;
562                     debugOutput(DEBUG_LEVEL_NORMAL,
563                             " TS after read: HEAD: %011"PRIu64", FC=%04u\n",
564                             ts_head,fc_head);
565                     debugOutput(DEBUG_LEVEL_NORMAL,
566                             "                TAIL: %011"PRIu64", FC=%04u\n",
567                             ts_tail,fc_tail);
568                 }
569
570                 // update the timestamp
571                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
572                 if (timestamp >= arguments.wrap_at) {
573                     timestamp -= arguments.wrap_at;
574                 }
575             }
576
577             // simulate the cycle timer clock in ticks
578             time += 3072;
579             if (time >= arguments.wrap_at) {
580                 time -= arguments.wrap_at;
581             }
582
583             // allow for the messagebuffer thread to catch up
584             SleepRelativeUsec(200);
585
586             if(!run) break;
587         }
588     }
589
590     delete t;
591     delete c;
592
593     return EXIT_SUCCESS;
594 }
595
596
Note: See TracBrowser for help on using the browser.