]> Pileus Git - ~andy/csm213a-hw/blob - hw2/main.cpp
Factor out message parser
[~andy/csm213a-hw] / hw2 / main.cpp
1 #include "messages.h"
2
3 #include "mbed.h"
4 #include "serial_irq.h"
5 #include "serial_dma.h"
6 #include "timer_dma.h"
7
8 /**
9  * Mode of operation:
10  *   Devices 1 and 2 synchronize clocks using serial messages.
11  *
12  *   1. Each serial message timestamped using the hardware timer capture
13  *      registers in both the sender and receiver.
14  *   2. The sender transmits the send timestamp during the next time-sync
15  *      message.
16  *   3. The receiver then compares the senders timestamp with it's own
17  *      timestamp for the corresponding messages and calculates an offset.
18  *   4. The offset is used to compensate the receivers local clock.
19  *
20  *   Time synchronization is performed in both directions.
21  */
22
23 /*******************
24  * Timer functions *
25  *******************/
26
27 #define NSEC_PER_SEC 1000000000ULL
28
29 uint64_t time_last_local; // timestamp at last time sync
30 uint64_t time_last_world; // offset at last time sync
31
32 /**
33  * Generate time stamp for an async event:
34  *   time:  drift compensated wall-clock time
35  *   stamp: event timestamp from PIT Module
36  */
37 uint64_t time_to_world(uint64_t local)
38 {
39         uint64_t elapsed = local - time_last_local;
40         return time_last_world + elapsed;
41 }
42
43 uint64_t time_to_local(uint64_t world)
44 {
45         uint64_t elapsed = world - time_last_world;
46         return time_last_local + elapsed;
47 }
48
49 /**
50  * Synchronize the timer internal state with updates
51  * from an external time sync message.
52  *   local: our internal timestamp for the event
53  *   world: reference timestamp from the other device
54  */
55 void time_ext_init(uint64_t local, uint64_t world)
56 {
57         sirq_printf("initialize clocks: %d -> %d\r\n",
58                         (int)(local/NSEC_PER_SEC),
59                         (int)(world/NSEC_PER_SEC));
60
61         time_last_local = local;
62         time_last_world = world;
63 }
64
65 /**
66  * Synchronize the timer internal state with updates
67  * from an external time sync message.
68  *   local: our internal timestamp for the event
69  *   world: reference timestamp from the other device
70  */
71 void time_ext_sync(uint64_t local, uint64_t world)
72 {
73         uint64_t guess = time_to_world(local);
74
75         time_last_local = local;
76         time_last_world = (guess/2) + (world/2);
77         //time_last_world = (guess * 3 / 4) + (world * 1 / 4);
78         //time_last_world =
79         //      (guess - (        guess / 2)) +
80         //      (world - (world - world / 2));
81         //time_last_world =
82         //      (guess - (guess - guess / 4)) +
83         //      (world - (        world / 4));
84
85         world = time_last_world;
86
87 //#ifdef VERBOSE
88 #if 1
89         uint64_t error = world > guess ? world - guess :
90                          guess > world ? guess - world : 0;
91         int      ahead = guess > world;
92         sirq_printf("syncing clocks: %6d=%d.%04u -> %d.%04u (err: %s%ld.%09lu)\r\n",
93                         (int)((local / NSEC_PER_SEC)),
94                         (int)((guess / NSEC_PER_SEC)),
95                         (int)((guess % NSEC_PER_SEC)/(NSEC_PER_SEC/10000)),
96                         (int)((world / NSEC_PER_SEC)),
97                         (int)((world % NSEC_PER_SEC)/(NSEC_PER_SEC/10000)),
98                         ahead ? "-" : " ",
99                         (int32_t )(error / (int64_t)NSEC_PER_SEC),
100                         (uint32_t)(error % (int64_t)NSEC_PER_SEC));
101 #endif
102 //#endif
103 }
104
105 void time_printf(const char *label, uint64_t local)
106 {
107         uint64_t world = time_to_world(local);
108         sirq_printf("%s -- %d.%09u -> %d.%09u\r\n",
109                         label,
110                         (int)(local / NSEC_PER_SEC),
111                         (int)(local % NSEC_PER_SEC),
112                         (int)(world / NSEC_PER_SEC),
113                         (int)(world % NSEC_PER_SEC));
114 }
115
116 /*********************
117  * Signal generation *
118  *********************/
119
120 // for 50 Mhz clock  50/1000 = 1/20  (PLL/2)
121
122 // for 48 Mhz clock  48/1000 = 6/125 (FLL)
123 // for 24 Mhz clock, 24/1000 = 3/125
124 // for 12 Mhz clock, 12/1000 = 3/250
125 // for  6 Mhz clock,  6/1000 = 3/500
126 // for  3 Mhz clock,  3/1000 = 3/1000
127
128 #define EMIT_PS 1
129
130 //#if EMIT_PS == 0
131 //#define EMIT_CLOCKS(nsec) ((uint16_t)((nsec)   / 20))
132 //#define EMIT_NSEC(clocks) ((uint16_t)((clocks) * 20))
133
134 #if EMIT_PS == 0
135 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 6 / 125))
136 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 125 / 6))
137 #elif EMIT_PS == 1
138 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 125))
139 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 125 / 3))
140 #elif EMIT_PS == 2
141 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 250))
142 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 250 / 3))
143 #elif EMIT_PS == 3
144 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 500))
145 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 500 / 3))
146 #elif EMIT_PS == 4
147 #define EMIT_CLOCKS(nsec) ((uint32_t)((nsec)   * 3 / 1000))
148 #define EMIT_NSEC(clocks) ((uint32_t)((clocks) * 1000 / 3))
149 #endif
150
151 static uint32_t *emit_pcr    = 0; // transmit pin name
152
153 static uint64_t  emit_start  = 0; // transmit start time (world time)
154 static uint64_t  emit_period = 0; // transmit period
155 static uint64_t  emit_due    = 0; // next transmit (world time)
156
157 static uint32_t  emit_slack  = 0; // how far ahead we need to schedule, in us
158 static uint32_t  emit_worst  = 0; // worst-case latency in task table
159
160 void emit_init(int alt, PinName pin, PinMode mode)
161 {
162         // Find pin
163         emit_pcr = (uint32_t*)(PORTA_BASE + pin);
164
165         // Enable clocks
166         SIM->SCGC6            |= SIM_SCGC6_TPM1_MASK;
167         SIM->SOPT2            |= SIM_SOPT2_TPMSRC(1);
168
169         // Reset PLL Source
170         //SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK;
171
172         // Debug print on SOPT2
173         // -- mbed may set PLLFLL when configuring UART0
174         // SOPT2: u0src=1 tpmsrc=1 USBSRC PLL/2 clkos=0 rtcos
175         sirq_printf("SOPT2: u0src=%d tpmsrc=%d %s %s clkos=%d %s\r\n",
176                 (SIM->SOPT2 & SIM_SOPT2_UART0SRC_MASK)     >> SIM_SOPT2_UART0SRC_SHIFT,
177                 (SIM->SOPT2 & SIM_SOPT2_TPMSRC_MASK)       >> SIM_SOPT2_TPMSRC_SHIFT,
178                 (SIM->SOPT2 & SIM_SOPT2_UART0SRC_MASK)     ? "USBSRC" : "usbsrc",
179                 (SIM->SOPT2 & SIM_SOPT2_PLLFLLSEL_MASK)    ? "PLL/2"  : "FLL",
180                 (SIM->SOPT2 & SIM_SOPT2_CLKOUTSEL_MASK)    >> SIM_SOPT2_CLKOUTSEL_SHIFT,
181                 (SIM->SOPT2 & SIM_SOPT2_RTCCLKOUTSEL_MASK) ? "RTCOS"  : "rtcos");
182
183         // Set pin mode
184         emit_pcr[0]            = PORT_PCR_ISF_MASK
185                                | PORT_PCR_MUX(alt)
186                                | mode;
187
188         // Setup Timer/PWM Module
189         TPM1->SC               = TPM_SC_TOF_MASK;
190         TPM1->CNT              = TPM_CNT_COUNT(0);
191         TPM1->MOD              = TPM_MOD_MOD(0xFFFF);
192
193         TPM1->CONTROLS[0].CnSC = TPM_CnSC_CHF_MASK    // clear flag
194                                | TPM_CnSC_MSB_MASK    // set output highon match,
195                                | TPM_CnSC_ELSB_MASK   // cleared on overflow
196                                | TPM_CnSC_ELSA_MASK;  // ..
197
198         TPM1->STATUS           = TPM_STATUS_CH0F_MASK
199                                | TPM_STATUS_TOF_MASK;
200
201         TPM1->CONF             = TPM_CONF_CSOO_MASK;
202 }
203
204 void emit_enable(uint64_t start, uint64_t period)
205 {
206         const int slack_clocks = 0x4000; // tune based on emit_worst
207
208         emit_start  = start;
209         emit_period = period;
210         emit_due    = start + period;
211
212         emit_slack  = EMIT_NSEC(slack_clocks);
213
214         time_printf("emit scheduled", emit_due);
215 }
216
217 void emit_schedule(uint64_t when)
218 {
219         uint64_t local  = time_to_local(when) * 3 / 125;
220         uint32_t width  = EMIT_CLOCKS(10000);
221
222         // Disable timer
223         TPM1->SC               = TPM_SC_TOF_MASK;
224
225         __disable_irq();
226
227         uint64_t now    = ((uint64_t)~PIT->LTMR64H << 32)
228                         | ((uint64_t)~PIT->LTMR64L);
229         uint32_t delta  = local - now;
230         uint32_t start  = delta >> (EMIT_PS-1); // convert to clocks
231         uint32_t stop   = start + width;        // end time
232
233         // Set transmit time
234         TPM1->CONTROLS[0].CnV  = start;
235         TPM1->MOD              = TPM_MOD_MOD(stop);
236
237         // Start the timer
238         TPM1->SC               = TPM_SC_TOF_MASK
239                                | TPM_SC_PS(EMIT_PS)
240                                | TPM_SC_CMOD(1);
241
242         __enable_irq();
243
244         // Test
245         //int64_t  cnv = TPM1->CONTROLS[0].CnV;
246         //int64_t  mod = TPM1->MOD;
247         //int64_t  due = local - tdma_time();
248         //sirq_printf("%6d -- cnv=%04x mod=%04x due=%04x start=%04x\r\n",
249         //              (int)(cnv - EMIT_CLOCKS(due)),
250         //              (int)cnv, (int)mod,
251         //              (int)EMIT_CLOCKS(due), EMIT_CLOCKS(start));
252
253         // Clock testing
254         //uint32_t test_tpm0 =  TPM1->CNT;
255         //uint32_t test_pit0 = ~PIT->CHANNEL[0].CVAL;
256         //for (int i = 0; i < 100; i++)
257         //      asm("nop");
258         //uint32_t test_tpm1 =  TPM1->CNT;
259         //uint32_t test_pit1 = ~PIT->CHANNEL[0].CVAL;
260
261         //uint32_t test_tpm  = test_tpm1 - test_tpm0;
262         //uint32_t test_pit  = test_pit1 - test_pit0;
263         //sirq_printf("pit/tpm: %d - tpm=%08x/%08x=%d pit=%08x/%08x=%d\r\n",
264         //              test_tpm - test_pit,
265         //              test_tpm0, test_tpm1, test_tpm,
266         //              test_pit0, test_pit1, test_pit);
267
268         // Debug output
269         //time_printf("emitting event", when);
270 }
271
272 void emit_transmit(uint64_t local, uint64_t world)
273 {
274         static uint64_t prev = 0;
275
276         // Record how how much time we have to reschedule
277         if (prev && (local-prev) > emit_worst)
278                 emit_worst = (local-prev);
279         prev = local;
280
281         // Schedule task if needed
282         if (emit_due && emit_period &&
283             world+emit_slack > emit_due) {
284                 emit_schedule(emit_due);
285                 emit_due += emit_period;
286         }
287 }
288
289 /************************
290  * Serial I/O functions *
291  ************************/
292
293 static uint32_t serial_device_id   = 0;
294
295 const  uint64_t serial_sync_delay  = NSEC_PER_SEC / 100;
296 static uint64_t serial_sync_due    = 0;
297
298 static tdma_t  *serial_tdma_rcv    = NULL;
299 static tdma_t  *serial_tdma_xmt    = NULL;
300
301 /**
302  * Convert world to local time
303  */
304 uint64_t serial_read_time(ntime_t time)
305 {
306         return ((uint64_t)time.seconds) * NSEC_PER_SEC
307              + ((uint64_t)time.nanosec);
308 }
309
310 ntime_t serial_write_time(uint64_t time)
311 {
312         ntime_t buf = {};
313         buf.seconds = time / NSEC_PER_SEC;
314         buf.nanosec = time % NSEC_PER_SEC;
315         return buf;
316 }
317
318 int serial_time_stamp(tdma_t *port, uint64_t *local, uint64_t *world,
319                 const char *msg)
320 {
321         int valid = tdma_stamp(port, local);
322         *world = time_to_world(*local);
323
324         if (!valid)
325                 sirq_printf("%s -- missing\r\n", msg);
326         //else
327         //      time_printf(msg, current);
328
329         return valid;
330 }
331
332 /**
333  * Output initialization message init message
334  */
335 void serial_send_init(uint16_t device, uint64_t local)
336 {
337 }
338
339 /**
340  * Output time sync message
341  */
342 void serial_send_sync(sirq_t *port, uint64_t now)
343 {
344         if (serial_sync_due == 0 || now < serial_sync_due)
345                 return; // not ready
346
347         // Message data
348         header_t   head;
349         sync_msg_t body;
350
351         // Write header
352         head.header = MSG_HEADER;
353         head.msgid  = MSG_ID_SYNC;
354         head.length = sizeof(body);
355         head.cksum  = 0; // todo
356
357         tdma_stop(serial_tdma_rcv, 0);
358         tdma_start(serial_tdma_xmt);
359
360         sirq_write(port, &head, sizeof(head));
361
362         tdma_stop(serial_tdma_xmt, 100);
363         tdma_start(serial_tdma_rcv);
364
365         // Save transmit time
366         uint64_t local = 0, world = 0;
367         serial_time_stamp(serial_tdma_xmt, &local, &world,
368                         "sync time transmit");
369
370         // Debug output
371         //sirq_printf("sync time transmit\r\n");
372         //time_printf("  local", local);
373         //time_printf("  world", world);
374
375         // Write body with updated time and send
376         body.time = serial_write_time(world);
377
378         sirq_write(port, &body, sizeof(body));
379
380         // Queue next transmit time
381         serial_sync_due  = 0;
382 }
383
384 /**
385  * Output external event received message
386  *   event: id of the received event
387  *   time:  compensated timestamp of the event
388  */
389 void serial_send_event(sirq_t *port, uint16_t event, uint64_t local)
390 {
391         //time_printf("event received", local);
392
393         // Convert timestamp
394         uint64_t world = time_to_world(local);
395         ntime_t  ltime = serial_write_time(local);
396         ntime_t  wtime = serial_write_time(world);
397
398         // Message data
399         header_t    head = {};
400         event_msg_t body = {};
401
402         // Transmit sync message
403         head.header = MSG_HEADER;
404         head.msgid  = MSG_ID_EVENT;
405         head.length = sizeof(body);
406         head.cksum  = 0; // todo
407
408         body.device = serial_device_id;
409         body.event  = event;
410         body.world  = wtime;
411         body.local  = ltime;
412
413         // Transmit message to BBB
414         sirq_write(port, &head, sizeof(head));
415         sirq_write(port, &body, sizeof(body));
416 }
417
418 /**
419  * Handle init message
420  */
421 void serial_handle_init(int msgid, init_msg_t *msg)
422 {
423         sirq_printf("initialize: %s %s %s %s %s\r\n",
424                 msg->valid & MSG_VALID_DEVICE ? "DEV"    : "dev",
425                 msg->valid & MSG_VALID_START  ? "START"  : "start",
426                 msg->valid & MSG_VALID_PERIOD ? "PERIOD" : "period",
427                 msg->valid & MSG_VALID_WORLD  ? "WORLD"  : "world",
428                 msg->valid & MSG_VALID_SYNC   ? "SYNC"   : "sync");
429         sirq_printf("  dev    -- %d\r\n", msg->device);
430         time_printf("  start ", serial_read_time(msg->start));
431         time_printf("  period", serial_read_time(msg->period));
432         time_printf("  world ", serial_read_time(msg->world));
433
434         if (msg->valid & MSG_VALID_DEVICE)
435                 serial_device_id = msg->device;
436
437         if (msg->valid & MSG_VALID_START ||
438             msg->valid & MSG_VALID_PERIOD) {
439                 uint64_t start  = serial_read_time(msg->start);
440                 uint64_t period = serial_read_time(msg->period);
441                 emit_enable(start, period);
442         }
443
444         if (msg->valid & MSG_VALID_WORLD) {
445                 uint64_t world = serial_read_time(msg->world);
446                 uint64_t local = tdma_time();
447                 time_ext_init(local, world);
448         }
449
450         if (msg->valid & MSG_VALID_SYNC)
451                 serial_sync_due = tdma_time() + serial_sync_delay;
452 }
453
454 /**
455  * Handle sync message
456  */
457 void serial_handle_sync(int msgid, sync_msg_t *msg)
458 {
459         // Read receive timestamp
460         uint64_t local = 0, world = 0;
461         serial_time_stamp(serial_tdma_rcv, &local, &world,
462                         "sync time receive ");
463         tdma_stop(serial_tdma_rcv, 0);
464
465         // Lookup reference time from message
466         uint64_t reference = serial_read_time(msg->time);
467
468         // Debug output
469         //sirq_printf("sync time receive\r\n");
470         //time_printf("  local", local);
471         //time_printf("  world", world);
472         //time_printf("  ref  ", reference);
473
474         // Synchronize the clocks
475         time_ext_sync(local, reference);
476
477         // Queue transmit to other board
478         serial_sync_due   = tdma_time() + serial_sync_delay;
479 }
480
481 /**
482  * Handle event message
483  */
484 void serial_handle_event(int msgid, event_msg_t *msg)
485 {
486 }
487
488 /********************
489  * Data definitions *
490  ********************/
491
492 // LEDs
493 DigitalOut led1(LED1);
494 DigitalOut led2(LED2);
495
496 // Message Parsers
497 parser_t   parser_dbg;
498 parser_t   parser_bbb;
499 parser_t   parser_mbed;
500
501 // Serial IRQ
502 sirq_t    *sirq_dbg;
503 sirq_t    *sirq_bbb;
504 sirq_t    *sirq_mbed;
505
506 // Timer DMA
507 tdma_t    *tdma_evt;
508 tdma_t    *tdma_rcv;
509 tdma_t    *tdma_xmt;
510
511 /*********
512  * Tasks *
513  *********/
514
515 void task_serial(uint64_t local, uint64_t world)
516 {
517         while (sirq_ready(sirq_dbg)) {
518                 //sirq_printf("serial recv - dbg\r\n");
519                 msg_receive(&parser_dbg,  sirq_getc(sirq_dbg));
520         }
521
522         while (sirq_ready(sirq_bbb)) {
523                 //sirq_printf("serial recv - bbb\r\n");
524                 msg_receive(&parser_bbb,  sirq_getc(sirq_bbb));
525         }
526
527         while (sirq_ready(sirq_mbed)) {
528                 //sirq_printf("serial recv - mbed\r\n");
529                 msg_receive(&parser_mbed, sirq_getc(sirq_mbed));
530         }
531 }
532
533 void task_events(uint64_t local, uint64_t world)
534 {
535         uint64_t event = 0;
536
537 #ifdef VERBOSE
538         if (tdma_stamp(tdma_evt, &event)) {
539                 sirq_printf("event received - evt\r\n");
540         if (tdma_stamp(tdma_rcv, &event))
541                 sirq_printf("event received - rcv\r\n");
542         if (tdma_stamp(tdma_xmt, &event))
543                 sirq_printf("event received - xmt\r\n");
544 #endif
545
546         if (tdma_stamp(tdma_evt, &event))
547                 serial_send_event(sirq_bbb, 0, event);
548         tdma_stop(tdma_evt, 0);
549         tdma_start(tdma_evt);
550 }
551
552 void task_sync(uint64_t local, uint64_t world)
553 {
554         serial_send_sync(sirq_mbed, local);
555 }
556
557 void task_leds(uint64_t local, uint64_t world)
558 {
559         static uint32_t which = 0;
560         led1 = (which == 0);
561         led2 = (which == 1);
562         which ^= 1;
563 }
564
565 void task_emit(uint64_t local, uint64_t world)
566 {
567         emit_transmit(local, world);
568 }
569
570 void task_debug(uint64_t local, uint64_t world)
571 {
572         //tdma_debug(tdma_rcv);
573         //tdma_debug(tdma_xmt);
574
575         //sirq_debug(sirq_mbed);
576
577         serial_send_event(sirq_bbb, 1, local);
578
579 #ifdef VERBOSE
580         sirq_printf("background - %6u.%02u -> %u.%02u\r\n",
581                         (uint32_t)(local / NSEC_PER_SEC),
582                         (uint32_t)(local % NSEC_PER_SEC / 10000000),
583                         (uint32_t)(world / NSEC_PER_SEC),
584                         (uint32_t)(world % NSEC_PER_SEC / 10000000));
585 #endif
586 }
587
588 /********
589  * Main *
590  ********/
591
592 #define N_ELEM(x) (sizeof(x) / sizeof((x)[0]))
593
594 extern void test_main(void);
595 extern serial_t stdio_uart;
596
597 static struct {
598         void (*task)(uint64_t, uint64_t);
599         uint64_t period;
600         uint64_t due;
601 } tasks[] = {
602         { task_serial, 0          }, // always
603         { task_events, 0          }, // always -- testing
604         { task_sync,   0          }, // always
605         { task_emit,   0          }, // always
606         { task_leds,   100000000  }, // 10hz
607         { task_debug,  1000000000 }, // 1hz
608 };
609
610 void background(void)
611 {
612         // Debugging
613         uint64_t local = tdma_time();
614         uint64_t world = time_to_world(local);
615
616         // Run the scheduler
617         for (unsigned i = 0; i < N_ELEM(tasks); i++) {
618                 if (local >= tasks[i].due) {
619                         tasks[i].task(local, world);
620                         tasks[i].due += tasks[i].period;
621                 }
622         }
623 }
624
625 int main(int argc, char **argv)
626 {
627         tdma_init();
628
629         // Open serial ports
630         sirq_dbg   = sirq_open(SIRQ_UART0, USBTX, USBRX, 115200); // to pc
631         sirq_bbb   = sirq_open(SIRQ_UART1, PTE0,  PTE1,  115200); // to bbb
632         sirq_mbed  = sirq_open(SIRQ_UART2, PTD3,  PTD2,  115200); // to mbed
633
634         // Setup timers
635         tdma_evt   = tdma_open(TDMA_CHAN0, 3, PTC9,  PullDown); // async event
636         tdma_rcv   = tdma_open(TDMA_CHAN2, 3, PTD2,  PullUp);   // time sync rcv
637         tdma_xmt   = tdma_open(TDMA_CHAN3, 3, PTD3,  PullUp);   // time sync xmt
638
639         // Register messages
640         msg_register(&parser_dbg,  MSG_ID_INIT,  (handler_t)serial_handle_init);
641         msg_register(&parser_dbg,  MSG_ID_SYNC,  (handler_t)serial_handle_sync);
642         msg_register(&parser_dbg,  MSG_ID_EVENT, (handler_t)serial_handle_event);
643         msg_register(&parser_bbb,  MSG_ID_INIT,  (handler_t)serial_handle_init);
644         msg_register(&parser_mbed, MSG_ID_SYNC,  (handler_t)serial_handle_sync);
645
646         // start timers
647         tdma_start(tdma_evt);
648         tdma_start(tdma_rcv);
649         tdma_start(tdma_xmt);
650
651         // Serial timestamping
652         serial_tdma_rcv = tdma_rcv;
653         serial_tdma_xmt = tdma_xmt;
654
655         // Setup event generation
656         emit_init(3, PTE20, PullDown);
657
658         // Run background loop
659         while (true)
660                 background();
661
662         // Run tests
663         //test_main();
664
665         return 0;
666 }