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

Revision 783, 19.5 kB (checked in by ppalmers, 16 years ago)

cleanup time/wait/sleep code

Line 
1 /*
2  * Copyright (C) 2005-2007 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 3 of the License, or
12  * (at your option) any later version.
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 <netinet/in.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     switch (key) {
108         case 'v':
109             if (arg) {
110                 arguments->verbose = strtoll( arg, &tail, 0 );
111                 if ( errno ) {
112                     fprintf( stderr, "Could not parse 'verbose' argument\n" );
113                     return ARGP_ERR_UNKNOWN;
114                 }
115             } else {
116                 if ( errno ) {
117                     fprintf( stderr, "Could not parse 'verbose' argument\n" );
118                     return ARGP_ERR_UNKNOWN;
119                 }
120             }
121             break;
122         case 'w':
123             if (arg) {
124                 arguments->wrap_at = strtoll( arg, &tail, 0 );
125                 if ( errno ) {
126                     fprintf( stderr, "Could not parse 'wrap' argument\n" );
127                     return ARGP_ERR_UNKNOWN;
128                 }
129             } else {
130                 if ( errno ) {
131                     fprintf( stderr, "Could not parse 'wrap' argument\n" );
132                     return ARGP_ERR_UNKNOWN;
133                 }
134             }
135             break;
136         case 'f':
137             if (arg) {
138                 arguments->frames_per_packet = strtoll( arg, &tail, 0 );
139                 if ( errno ) {
140                     fprintf( stderr, "Could not parse 'fpp' argument\n" );
141                     return ARGP_ERR_UNKNOWN;
142                 }
143             } else {
144                 if ( errno ) {
145                     fprintf( stderr, "Could not parse 'fpp' argument\n" );
146                     return ARGP_ERR_UNKNOWN;
147                 }
148             }
149             break;
150         case 'e':
151             if (arg) {
152                 arguments->events_per_frame = strtoll( arg, &tail, 0 );
153                 if ( errno ) {
154                     fprintf( stderr, "Could not parse 'epf' argument\n" );
155                     return ARGP_ERR_UNKNOWN;
156                 }
157             } else {
158                 if ( errno ) {
159                     fprintf( stderr, "Could not parse 'epf' argument\n" );
160                     return ARGP_ERR_UNKNOWN;
161                 }
162             }
163             break;
164         case 'c':
165             if (arg) {
166                 arguments->total_cycles = strtoll( arg, &tail, 0 );
167                 if ( errno ) {
168                     fprintf( stderr, "Could not parse 'cycles' argument\n" );
169                     return ARGP_ERR_UNKNOWN;
170                 }
171             } else {
172                 if ( errno ) {
173                     fprintf( stderr, "Could not parse 'cycles' argument\n" );
174                     return ARGP_ERR_UNKNOWN;
175                 }
176             }
177             break;
178         case 's':
179             if (arg) {
180                 arguments->start_at_cycle = strtoll( arg, &tail, 0 );
181                 if ( errno ) {
182                     fprintf( stderr, "Could not parse 'startcycle' argument\n" );
183                     return ARGP_ERR_UNKNOWN;
184                 }
185             } else {
186                 if ( errno ) {
187                     fprintf( stderr, "Could not parse 'startcycle' argument\n" );
188                     return ARGP_ERR_UNKNOWN;
189                 }
190             }
191             break;
192         case 'b':
193             if (arg) {
194                 arguments->buffersize = strtoll( arg, &tail, 0 );
195                 if ( errno ) {
196                     fprintf( stderr, "Could not parse 'buffersize' argument\n" );
197                     return ARGP_ERR_UNKNOWN;
198                 }
199             } else {
200                 if ( errno ) {
201                     fprintf( stderr, "Could not parse 'buffersize' argument\n" );
202                     return ARGP_ERR_UNKNOWN;
203                 }
204             }
205             break;
206         case 'r':
207             if (arg) {
208                 arguments->rate = strtof( arg, &tail );
209                 if ( errno ) {
210                     fprintf( stderr, "Could not parse 'rate' argument\n" );
211                     return ARGP_ERR_UNKNOWN;
212                 }
213             } else {
214                 if ( errno ) {
215                     fprintf( stderr, "Could not parse 'rate' argument\n" );
216                     return ARGP_ERR_UNKNOWN;
217                 }
218             }
219             break;
220        default:
221             return ARGP_ERR_UNKNOWN;
222     }
223     return 0;
224 }
225
226 // Our argp parser.
227 static struct argp argp = { options, parse_opt, args_doc, doc };
228
229
230 static void sighandler (int sig)
231 {
232         run = 0;
233 }
234
235 int main(int argc, char *argv[])
236 {
237
238     TimestampedBuffer *t=NULL;
239     TimestampedBufferTestClient *c=NULL;
240
241     struct arguments arguments;
242
243     // Default values.
244     arguments.verbose        = 0;
245     arguments.wrap_at = 3072000LLU; // 1000 cycles
246     arguments.frames_per_packet = 8;
247     arguments.events_per_frame = 10;
248     arguments.rate = 512.0;
249     arguments.total_cycles = 2000;
250     arguments.buffersize = 1024;
251     arguments.start_at_cycle = 0;
252
253     // Parse our arguments; every option seen by `parse_opt' will
254     // be reflected in `arguments'.
255     if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) {
256         fprintf( stderr, "Could not parse command line\n" );
257         exit(1);
258     }
259
260     setDebugLevel(arguments.verbose);
261
262     run=1;
263
264     signal (SIGINT, sighandler);
265     signal (SIGPIPE, sighandler);
266
267     c=new TimestampedBufferTestClient();
268
269     if(!c) {
270         debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBufferTestClient\n");
271         exit(1);
272     }
273     c->setVerboseLevel(arguments.verbose);
274
275     t=new TimestampedBuffer(c);
276
277     if(!t) {
278         debugOutput(DEBUG_LEVEL_NORMAL, "Could not create TimestampedBuffer\n");
279         delete c;
280         exit(1);
281     }
282     t->setVerboseLevel(arguments.verbose);
283
284     t->init();
285
286     // Setup the buffer
287     t->setBufferSize(arguments.buffersize);
288     t->setEventSize(sizeof(int));
289     t->setEventsPerFrame(arguments.events_per_frame);
290
291     t->setUpdatePeriod(arguments.frames_per_packet);
292     t->setNominalRate(arguments.rate);
293
294     t->setWrapValue(arguments.wrap_at);
295
296     t->setTickOffset(10000);
297
298     t->prepare();
299
300     SleepRelativeUsec(1000);
301
302     debugOutput(DEBUG_LEVEL_NORMAL, "Start setBufferHeadTimestamp test...\n");
303     {
304         bool pass=true;
305         uint64_t time=arguments.start_at_cycle*3072;
306         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
307
308         // initialize the timestamp
309         uint64_t timestamp=time;
310         if (timestamp >= arguments.wrap_at) {
311             // here we need a modulo because start_at_cycle can be large
312             timestamp %= arguments.wrap_at;
313         }
314
315         // account for the fact that there is offset,
316         // and that setBufferHeadTimestamp doesn't take offset
317         // into account
318         uint64_t timestamp2=timestamp+t->getTickOffset();
319         if (timestamp2>=arguments.wrap_at) {
320             timestamp2-=arguments.wrap_at;
321         }
322
323         t->setBufferHeadTimestamp(timestamp2);
324
325         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
326         if (timestamp >= arguments.wrap_at) {
327             timestamp -= arguments.wrap_at;
328         }
329
330         // write some packets
331         for (unsigned int i=0;i<20;i++) {
332             t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
333             timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
334             if (timestamp >= arguments.wrap_at) {
335                 timestamp -= arguments.wrap_at;
336             }
337         }
338
339         for(unsigned int cycle=arguments.start_at_cycle;
340             cycle < arguments.start_at_cycle+arguments.total_cycles;
341             cycle++) {
342                 ffado_timestamp_t ts_head_tmp;
343                 uint64_t ts_head;
344                 signed int fc_head;
345
346                 t->setBufferHeadTimestamp(timestamp);
347                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
348                 ts_head=(uint64_t)ts_head_tmp;
349
350                 if (timestamp != ts_head) {
351                     debugError(" cycle %4u error: %011llu != %011llu\n",
352                         timestamp, ts_head);
353                         pass=false;
354                 }
355
356                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
357                 if (timestamp >= arguments.wrap_at) {
358                     timestamp -= arguments.wrap_at;
359                 }
360
361             // simulate the cycle timer clock in ticks
362             time += 3072;
363             if (time >= arguments.wrap_at) {
364                 time -= arguments.wrap_at;
365             }
366
367             // allow for the messagebuffer thread to catch up
368             SleepRelativeUsec(200);
369
370             if(!run) break;
371         }
372
373         if(!pass) {
374             debugError("Test failed, exiting...\n");
375
376             delete t;
377             delete c;
378
379             return -1;
380
381         }
382     }
383
384
385
386     debugOutput(DEBUG_LEVEL_NORMAL, "Start read/write test...\n");
387     {
388         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
389         int dummyframe_out[arguments.events_per_frame*arguments.frames_per_packet];
390
391         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
392             dummyframe_in[i]=i;
393         }
394
395         uint64_t time=arguments.start_at_cycle*3072;
396
397         // initialize the timestamp
398         uint64_t timestamp=time;
399         if (timestamp >= arguments.wrap_at) {
400             // here we need a modulo because start_at_cycle can be large
401             timestamp %= arguments.wrap_at;
402         }
403         t->setBufferTailTimestamp(timestamp);
404
405         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
406         if (timestamp >= arguments.wrap_at) {
407             timestamp -= arguments.wrap_at;
408         }
409
410         for(unsigned int cycle=arguments.start_at_cycle;
411             cycle < arguments.start_at_cycle+arguments.total_cycles;
412             cycle++) {
413
414             // simulate the rate adaptation
415             int64_t diff=(time%arguments.wrap_at)-timestamp;
416
417             if (diff>(int64_t)arguments.wrap_at/2) {
418                 diff -= arguments.wrap_at;
419             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
420                 diff += arguments.wrap_at;
421             }
422
423             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
424
425             if(diff>0) {
426                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
427                 uint64_t ts_head, ts_tail;
428                 signed int fc_head, fc_tail;
429
430                 // write one packet
431                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
432
433                 // read the buffer head timestamp
434                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
435                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
436                 ts_head=(uint64_t)ts_head_tmp;
437                 ts_tail=(uint64_t)ts_tail_tmp;
438                 debugOutput(DEBUG_LEVEL_NORMAL,
439                         " TS after write: HEAD: %011llu, FC=%04u\n",
440                         ts_head,fc_head);
441                 debugOutput(DEBUG_LEVEL_NORMAL,
442                         "                 TAIL: %011llu, FC=%04u\n",
443                         ts_tail,fc_tail);
444
445                 // read one packet
446                 t->readFrames(arguments.frames_per_packet, (char *)&dummyframe_out);
447
448                 // read the buffer head timestamp
449                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
450                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
451                 ts_head=(uint64_t)ts_head_tmp;
452                 ts_tail=(uint64_t)ts_tail_tmp;
453                 debugOutput(DEBUG_LEVEL_NORMAL,
454                         " TS after write: HEAD: %011llu, FC=%04u\n",
455                         ts_head,fc_head);
456                 debugOutput(DEBUG_LEVEL_NORMAL,
457                         "                 TAIL: %011llu, FC=%04u\n",
458                         ts_tail,fc_tail);
459
460                 // check
461                 bool pass=true;
462                 for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
463                     pass = pass && (dummyframe_in[i]==dummyframe_out[i]);
464                 }
465                 if (!pass) {
466                     debugOutput(DEBUG_LEVEL_NORMAL, "write/read check for cycle %d failed\n",cycle);
467                 }
468
469                 // update the timestamp
470                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
471                 if (timestamp >= arguments.wrap_at) {
472                     timestamp -= arguments.wrap_at;
473                 }
474             }
475
476             // simulate the cycle timer clock in ticks
477             time += 3072;
478             if (time >= arguments.wrap_at) {
479                 time -= arguments.wrap_at;
480             }
481
482             // allow for the messagebuffer thread to catch up
483             SleepRelativeUsec(200);
484
485             if(!run) break;
486         }
487     }
488
489     // second run, now do block processing
490     debugOutput(DEBUG_LEVEL_NORMAL, "Start block read test...\n");
491     {
492         unsigned int blocksize=32;
493         int dummyframe_out_block[arguments.events_per_frame*arguments.frames_per_packet*blocksize];
494         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
495
496         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
497             dummyframe_in[i]=i;
498         }
499
500         uint64_t time=arguments.start_at_cycle*3072;
501
502         // initialize the timestamp
503         uint64_t timestamp=time;
504         if (timestamp >= arguments.wrap_at) {
505             // here we need a modulo because start_at_cycle can be large
506             timestamp %= arguments.wrap_at;
507         }
508         t->setBufferTailTimestamp(timestamp);
509
510         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
511         if (timestamp >= arguments.wrap_at) {
512             timestamp -= arguments.wrap_at;
513         }
514
515         for(unsigned int cycle=arguments.start_at_cycle;
516             cycle < arguments.start_at_cycle+arguments.total_cycles;
517             cycle++) {
518
519             // simulate the rate adaptation
520             int64_t diff=(time%arguments.wrap_at)-timestamp;
521
522             if (diff>(int64_t)arguments.wrap_at/2) {
523                 diff -= arguments.wrap_at;
524             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
525                 diff += arguments.wrap_at;
526             }
527
528             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
529
530             if(diff>0) {
531                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
532                 uint64_t ts_head, ts_tail;
533                 signed int fc_head, fc_tail;
534
535                 // write one packet
536                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
537
538                 // read the buffer head timestamp
539                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
540                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
541                 ts_head=(uint64_t)ts_head_tmp;
542                 ts_tail=(uint64_t)ts_tail_tmp;
543                 debugOutput(DEBUG_LEVEL_NORMAL,
544                         " TS after write: HEAD: %011llu, FC=%04u\n",
545                         ts_head,fc_head);
546                 debugOutput(DEBUG_LEVEL_NORMAL,
547                         "                 TAIL: %011llu, FC=%04u\n",
548                         ts_tail,fc_tail);
549
550                 if (fc_head > (signed int)blocksize) {
551                     debugOutput(DEBUG_LEVEL_NORMAL,"Reading one block (%u frames)\n",blocksize);
552
553                     // read one block
554                     t->readFrames(blocksize, (char *)&dummyframe_out_block);
555
556                     // read the buffer head timestamp
557                     t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
558                     t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
559                     ts_head=(uint64_t)ts_head_tmp;
560                     ts_tail=(uint64_t)ts_tail_tmp;
561                     debugOutput(DEBUG_LEVEL_NORMAL,
562                             " TS after read: HEAD: %011llu, FC=%04u\n",
563                             ts_head,fc_head);
564                     debugOutput(DEBUG_LEVEL_NORMAL,
565                             "                TAIL: %011llu, FC=%04u\n",
566                             ts_tail,fc_tail);
567                 }
568
569                 // update the timestamp
570                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
571                 if (timestamp >= arguments.wrap_at) {
572                     timestamp -= arguments.wrap_at;
573                 }
574             }
575
576             // simulate the cycle timer clock in ticks
577             time += 3072;
578             if (time >= arguments.wrap_at) {
579                 time -= arguments.wrap_at;
580             }
581
582             // allow for the messagebuffer thread to catch up
583             SleepRelativeUsec(200);
584
585             if(!run) break;
586         }
587     }
588
589     delete t;
590     delete c;
591
592     return EXIT_SUCCESS;
593 }
594
595
Note: See TracBrowser for help on using the browser.