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

Revision 907, 19.4 kB (checked in by ppalmers, 16 years ago)

fix concurrency issue

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 <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     // Setup the buffer
285     t->setBufferSize(arguments.buffersize);
286     t->setEventSize(sizeof(int));
287     t->setEventsPerFrame(arguments.events_per_frame);
288
289     t->setUpdatePeriod(arguments.frames_per_packet);
290     t->setNominalRate(arguments.rate);
291
292     t->setWrapValue(arguments.wrap_at);
293
294     t->prepare();
295
296     SleepRelativeUsec(1000);
297
298     debugOutput(DEBUG_LEVEL_NORMAL, "Start setBufferHeadTimestamp test...\n");
299     {
300         bool pass=true;
301         uint64_t time=arguments.start_at_cycle*3072;
302         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
303
304         // initialize the timestamp
305         uint64_t timestamp=time;
306         if (timestamp >= arguments.wrap_at) {
307             // here we need a modulo because start_at_cycle can be large
308             timestamp %= arguments.wrap_at;
309         }
310
311         // account for the fact that there is offset,
312         // and that setBufferHeadTimestamp doesn't take offset
313         // into account
314         uint64_t timestamp2=timestamp;
315         if (timestamp2>=arguments.wrap_at) {
316             timestamp2-=arguments.wrap_at;
317         }
318
319         t->setBufferHeadTimestamp(timestamp2);
320
321         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
322         if (timestamp >= arguments.wrap_at) {
323             timestamp -= arguments.wrap_at;
324         }
325
326         // write some packets
327         for (unsigned int i=0;i<20;i++) {
328             t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
329             timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
330             if (timestamp >= arguments.wrap_at) {
331                 timestamp -= arguments.wrap_at;
332             }
333         }
334
335         for(unsigned int cycle=arguments.start_at_cycle;
336             cycle < arguments.start_at_cycle+arguments.total_cycles;
337             cycle++) {
338                 ffado_timestamp_t ts_head_tmp;
339                 uint64_t ts_head;
340                 signed int fc_head;
341
342                 t->setBufferHeadTimestamp(timestamp);
343                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
344                 ts_head=(uint64_t)ts_head_tmp;
345
346                 if (timestamp != ts_head) {
347                     debugError(" cycle %4u error: %011llu != %011llu\n",
348                         timestamp, ts_head);
349                         pass=false;
350                 }
351
352                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
353                 if (timestamp >= arguments.wrap_at) {
354                     timestamp -= arguments.wrap_at;
355                 }
356
357             // simulate the cycle timer clock in ticks
358             time += 3072;
359             if (time >= arguments.wrap_at) {
360                 time -= arguments.wrap_at;
361             }
362
363             // allow for the messagebuffer thread to catch up
364             SleepRelativeUsec(200);
365
366             if(!run) break;
367         }
368
369         if(!pass) {
370             debugError("Test failed, exiting...\n");
371
372             delete t;
373             delete c;
374
375             return -1;
376
377         }
378     }
379
380
381
382     debugOutput(DEBUG_LEVEL_NORMAL, "Start read/write test...\n");
383     {
384         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
385         int dummyframe_out[arguments.events_per_frame*arguments.frames_per_packet];
386
387         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
388             dummyframe_in[i]=i;
389         }
390
391         uint64_t time=arguments.start_at_cycle*3072;
392
393         // initialize the timestamp
394         uint64_t timestamp=time;
395         if (timestamp >= arguments.wrap_at) {
396             // here we need a modulo because start_at_cycle can be large
397             timestamp %= arguments.wrap_at;
398         }
399         t->setBufferTailTimestamp(timestamp);
400
401         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
402         if (timestamp >= arguments.wrap_at) {
403             timestamp -= arguments.wrap_at;
404         }
405
406         for(unsigned int cycle=arguments.start_at_cycle;
407             cycle < arguments.start_at_cycle+arguments.total_cycles;
408             cycle++) {
409
410             // simulate the rate adaptation
411             int64_t diff=(time%arguments.wrap_at)-timestamp;
412
413             if (diff>(int64_t)arguments.wrap_at/2) {
414                 diff -= arguments.wrap_at;
415             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
416                 diff += arguments.wrap_at;
417             }
418
419             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
420
421             if(diff>0) {
422                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
423                 uint64_t ts_head, ts_tail;
424                 signed int fc_head, fc_tail;
425
426                 // write one packet
427                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
428
429                 // read the buffer head timestamp
430                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
431                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
432                 ts_head=(uint64_t)ts_head_tmp;
433                 ts_tail=(uint64_t)ts_tail_tmp;
434                 debugOutput(DEBUG_LEVEL_NORMAL,
435                         " TS after write: HEAD: %011llu, FC=%04u\n",
436                         ts_head,fc_head);
437                 debugOutput(DEBUG_LEVEL_NORMAL,
438                         "                 TAIL: %011llu, FC=%04u\n",
439                         ts_tail,fc_tail);
440
441                 // read one packet
442                 t->readFrames(arguments.frames_per_packet, (char *)&dummyframe_out);
443
444                 // read the buffer head timestamp
445                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
446                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
447                 ts_head=(uint64_t)ts_head_tmp;
448                 ts_tail=(uint64_t)ts_tail_tmp;
449                 debugOutput(DEBUG_LEVEL_NORMAL,
450                         " TS after write: HEAD: %011llu, FC=%04u\n",
451                         ts_head,fc_head);
452                 debugOutput(DEBUG_LEVEL_NORMAL,
453                         "                 TAIL: %011llu, FC=%04u\n",
454                         ts_tail,fc_tail);
455
456                 // check
457                 bool pass=true;
458                 for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
459                     pass = pass && (dummyframe_in[i]==dummyframe_out[i]);
460                 }
461                 if (!pass) {
462                     debugOutput(DEBUG_LEVEL_NORMAL, "write/read check for cycle %d failed\n",cycle);
463                 }
464
465                 // update the timestamp
466                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
467                 if (timestamp >= arguments.wrap_at) {
468                     timestamp -= arguments.wrap_at;
469                 }
470             }
471
472             // simulate the cycle timer clock in ticks
473             time += 3072;
474             if (time >= arguments.wrap_at) {
475                 time -= arguments.wrap_at;
476             }
477
478             // allow for the messagebuffer thread to catch up
479             SleepRelativeUsec(200);
480
481             if(!run) break;
482         }
483     }
484
485     // second run, now do block processing
486     debugOutput(DEBUG_LEVEL_NORMAL, "Start block read test...\n");
487     {
488         unsigned int blocksize=32;
489         int dummyframe_out_block[arguments.events_per_frame*arguments.frames_per_packet*blocksize];
490         int dummyframe_in[arguments.events_per_frame*arguments.frames_per_packet];
491
492         for (unsigned int i=0;i<arguments.events_per_frame*arguments.frames_per_packet;i++) {
493             dummyframe_in[i]=i;
494         }
495
496         uint64_t time=arguments.start_at_cycle*3072;
497
498         // initialize the timestamp
499         uint64_t timestamp=time;
500         if (timestamp >= arguments.wrap_at) {
501             // here we need a modulo because start_at_cycle can be large
502             timestamp %= arguments.wrap_at;
503         }
504         t->setBufferTailTimestamp(timestamp);
505
506         timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
507         if (timestamp >= arguments.wrap_at) {
508             timestamp -= arguments.wrap_at;
509         }
510
511         for(unsigned int cycle=arguments.start_at_cycle;
512             cycle < arguments.start_at_cycle+arguments.total_cycles;
513             cycle++) {
514
515             // simulate the rate adaptation
516             int64_t diff=(time%arguments.wrap_at)-timestamp;
517
518             if (diff>(int64_t)arguments.wrap_at/2) {
519                 diff -= arguments.wrap_at;
520             } else if (diff<(-(int64_t)arguments.wrap_at)/2){
521                 diff += arguments.wrap_at;
522             }
523
524             debugOutput(DEBUG_LEVEL_NORMAL, "Simulating cycle %d @ time=%011llu, diff=%lld\n",cycle,time,diff);
525
526             if(diff>0) {
527                 ffado_timestamp_t ts_head_tmp, ts_tail_tmp;
528                 uint64_t ts_head, ts_tail;
529                 signed int fc_head, fc_tail;
530
531                 // write one packet
532                 t->writeFrames(arguments.frames_per_packet, (char *)&dummyframe_in, timestamp);
533
534                 // read the buffer head timestamp
535                 t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
536                 t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
537                 ts_head=(uint64_t)ts_head_tmp;
538                 ts_tail=(uint64_t)ts_tail_tmp;
539                 debugOutput(DEBUG_LEVEL_NORMAL,
540                         " TS after write: HEAD: %011llu, FC=%04u\n",
541                         ts_head,fc_head);
542                 debugOutput(DEBUG_LEVEL_NORMAL,
543                         "                 TAIL: %011llu, FC=%04u\n",
544                         ts_tail,fc_tail);
545
546                 if (fc_head > (signed int)blocksize) {
547                     debugOutput(DEBUG_LEVEL_NORMAL,"Reading one block (%u frames)\n",blocksize);
548
549                     // read one block
550                     t->readFrames(blocksize, (char *)&dummyframe_out_block);
551
552                     // read the buffer head timestamp
553                     t->getBufferHeadTimestamp(&ts_head_tmp, &fc_head);
554                     t->getBufferTailTimestamp(&ts_tail_tmp, &fc_tail);
555                     ts_head=(uint64_t)ts_head_tmp;
556                     ts_tail=(uint64_t)ts_tail_tmp;
557                     debugOutput(DEBUG_LEVEL_NORMAL,
558                             " TS after read: HEAD: %011llu, FC=%04u\n",
559                             ts_head,fc_head);
560                     debugOutput(DEBUG_LEVEL_NORMAL,
561                             "                TAIL: %011llu, FC=%04u\n",
562                             ts_tail,fc_tail);
563                 }
564
565                 // update the timestamp
566                 timestamp += (uint64_t)(arguments.rate * arguments.frames_per_packet);
567                 if (timestamp >= arguments.wrap_at) {
568                     timestamp -= arguments.wrap_at;
569                 }
570             }
571
572             // simulate the cycle timer clock in ticks
573             time += 3072;
574             if (time >= arguments.wrap_at) {
575                 time -= arguments.wrap_at;
576             }
577
578             // allow for the messagebuffer thread to catch up
579             SleepRelativeUsec(200);
580
581             if(!run) break;
582         }
583     }
584
585     delete t;
586     delete c;
587
588     return EXIT_SUCCESS;
589 }
590
591
Note: See TracBrowser for help on using the browser.