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

Revision 1146, 19.4 kB (checked in by ppalmers, 15 years ago)

reset errno when parsing arguments. fixes #107.

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: %011llu != %011llu\n",
349                         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, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
421
422             if(diff>0) {
423                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
424                 uint64_t ts_head, ts_tail;
425                 signed int fc_head, fc_tail;
426
427                 // write one packet
428                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
429
430                 // read the buffer head timestamp
431                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
432                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
433                 ts_head=(uint64_t)ts_head_tmp;
434                 ts_tail=(uint64_t)ts_tail_tmp;
435                 debugOutput(DEBUG_LEVEL_NORMAL,
436                         " TS after write: HEAD: %011llu, FC=%04u\n",
437                         ts_head,fc_head);
438                 debugOutput(DEBUG_LEVEL_NORMAL,
439                         "                 TAIL: %011llu, FC=%04u\n",
440                         ts_tail,fc_tail);
441
442                 // read one packet
443                 t->readFrames(arguments.frames_per_packet, (char *)&dummyframe_out);
444
445                 // read the buffer head timestamp
446                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
447                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
448                 ts_head=(uint64_t)ts_head_tmp;
449                 ts_tail=(uint64_t)ts_tail_tmp;
450                 debugOutput(DEBUG_LEVEL_NORMAL,
451                         " TS after write: HEAD: %011llu, FC=%04u\n",
452                         ts_head,fc_head);
453                 debugOutput(DEBUG_LEVEL_NORMAL,
454                         "                 TAIL: %011llu, FC=%04u\n",
455                         ts_tail,fc_tail);
456
457                 // check
458                 bool pass=true;
459                 for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
460                     pass = pass && (dummyframe_in[i]==dummyframe_out[i]);
461                 }
462                 if (!pass) {
463                     debugOutput(DEBUG_LEVEL_NORMAL, "write/read check for cycle %d failed\n",cycle);
464                 }
465
466                 // update the timestamp
467                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
468                 if (timestamp >= arguments.wrap_at) {
469                     timestamp -= arguments.wrap_at;
470                 }
471             }
472
473             // simulate the cycle timer clock in ticks
474             time += 3072;
475             if (time >= arguments.wrap_at) {
476                 time -= arguments.wrap_at;
477             }
478
479             // allow for the messagebuffer thread to catch up
480             SleepRelativeUsec(200);
481
482             if(!run) break;
483         }
484     }
485
486     // second run, now do block processing
487     debugOutput(DEBUG_LEVEL_NORMAL, "Start block read test...\n");
488     {
489         unsigned int blocksize=32;
490         int dummyframe_out_block[arguments.events_per_frame*arguments.frames_per_packet*blocksize];
491         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
492
493         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
494             dummyframe_in[i]=i;
495         }
496
497         uint64_t time=arguments.start_at_cycle*3072;
498
499         // initialize the timestamp
500         uint64_t timestamp=time;
501         if (timestamp >= arguments.wrap_at) {
502             // here we need a modulo because start_at_cycle can be large
503             timestamp %= arguments.wrap_at;
504         }
505         t->setBufferTailTimestamp(timestamp);
506
507         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
508         if (timestamp >= arguments.wrap_at) {
509             timestamp -= arguments.wrap_at;
510         }
511
512         for(unsigned int cycle=arguments.start_at_cycle;
513             cycle < arguments.start_at_cycle+arguments.total_cycles;
514             cycle++) {
515
516             // simulate the rate adaptation
517             int64_t diff=(time%arguments.wrap_at)-timestamp;
518
519             if (diff>(int64_t)arguments.wrap_at/2) {
520                 diff -= arguments.wrap_at;
521             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
522                 diff += arguments.wrap_at;
523             }
524
525             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
526
527             if(diff>0) {
528                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
529                 uint64_t ts_head, ts_tail;
530                 signed int fc_head, fc_tail;
531
532                 // write one packet
533                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
534
535                 // read the buffer head timestamp
536                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
537                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
538                 ts_head=(uint64_t)ts_head_tmp;
539                 ts_tail=(uint64_t)ts_tail_tmp;
540                 debugOutput(DEBUG_LEVEL_NORMAL,
541                         " TS after write: HEAD: %011llu, FC=%04u\n",
542                         ts_head,fc_head);
543                 debugOutput(DEBUG_LEVEL_NORMAL,
544                         "                 TAIL: %011llu, FC=%04u\n",
545                         ts_tail,fc_tail);
546
547                 if (fc_head > (signed int)blocksize) {
548                     debugOutput(DEBUG_LEVEL_NORMAL,"Reading one block (%u frames)\n",blocksize);
549
550                     // read one block
551                     t->readFrames(blocksize, (char *)&dummyframe_out_block);
552
553                     // read the buffer head timestamp
554                     t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
555                     t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
556                     ts_head=(uint64_t)ts_head_tmp;
557                     ts_tail=(uint64_t)ts_tail_tmp;
558                     debugOutput(DEBUG_LEVEL_NORMAL,
559                             " TS after read: HEAD: %011llu, FC=%04u\n",
560                             ts_head,fc_head);
561                     debugOutput(DEBUG_LEVEL_NORMAL,
562                             "                TAIL: %011llu, FC=%04u\n",
563                             ts_tail,fc_tail);
564                 }
565
566                 // update the timestamp
567                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
568                 if (timestamp >= arguments.wrap_at) {
569                     timestamp -= arguments.wrap_at;
570                 }
571             }
572
573             // simulate the cycle timer clock in ticks
574             time += 3072;
575             if (time >= arguments.wrap_at) {
576                 time -= arguments.wrap_at;
577             }
578
579             // allow for the messagebuffer thread to catch up
580             SleepRelativeUsec(200);
581
582             if(!run) break;
583         }
584     }
585
586     delete t;
587     delete c;
588
589     return EXIT_SUCCESS;
590 }
591
592
Note: See TracBrowser for help on using the browser.