]> Pileus Git - ~andy/csm213a-hw/blob - hw2/main.cpp
82f28f75a3d4e62bd722cd69459bab3120969cb1
[~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  *   local: drift compensated wall-clock time
35  *   world: nanoseconds in world time world
36  *   valid: local timestamp at valid valid
37  */
38 //uint64_t time_to_local(uint64_t world, uint64_t valid)
39 //{
40 //      uint64_t now =
41 //      local =  + (stamp);
42 //}
43
44 /**
45  * Generate time stamp for an async event:
46  *   time:  drift compensated wall-clock time
47  *   stamp: event timestamp from PIT Module
48  */
49 uint64_t time_to_world(uint64_t local)
50 {
51         uint64_t elapsed = local - time_last_local;
52         return time_last_world + elapsed;
53 }
54
55 /**
56  * Compensate the Real-Time-Clock oscillator for
57  * temperature and drift errors. Called at 1Hz and
58  * synchronous to the RTC 1Hz output.
59  */
60 void time_rtc_comp(void)
61 {
62         // todo
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_init(uint64_t local, uint64_t world)
72 {
73         sirq_printf("initialize clocks: %d -> %d\r\n",
74                         (int)(local/NSEC_PER_SEC),
75                         (int)(world/NSEC_PER_SEC));
76
77         time_last_local = local;
78         time_last_world = world;
79 }
80
81 /**
82  * Synchronize the timer internal state with updates
83  * from an external time sync message.
84  *   local: our internal timestamp for the event
85  *   world: reference timestamp from the other device
86  */
87 void time_ext_sync(uint64_t local, uint64_t world)
88 {
89         sirq_printf("syncing clocks: %d -> %d\r\n",
90                         (int)(local/NSEC_PER_SEC),
91                         (int)(world/NSEC_PER_SEC));
92
93         time_last_local = local;
94         time_last_world = world;
95 }
96
97 /************************
98  * Serial I/O functions *
99  ************************/
100
101 typedef struct {
102         int      index;
103         int      state;
104         uint8_t  buffer[256];
105 } parser_t;
106
107 const  uint64_t serial_sync_delay = NSEC_PER_SEC * 2; // 1hz
108 static uint64_t serial_sync_due   = 0;
109
110 static tdma_t  *serial_tdma_rcv   = NULL;
111 static tdma_t  *serial_tdma_xmt   = NULL;
112
113 static uint64_t serial_prev_local = 0;
114 static uint64_t serial_prev_seq   = 0;
115
116 static uint64_t serial_xmt_local  = 0;
117 static uint64_t serial_xmt_seq    = 0;
118
119 /**
120  * Output time sync message
121  */
122 void serial_send_sync(sirq_t *port, uint64_t now)
123 {
124         if (serial_sync_due == 0 || now < serial_sync_due)
125                 return; // not ready
126
127         // Calculate world time
128         uint64_t world = time_to_world(serial_xmt_local);
129
130         // Message data
131         header_t   head;
132         sync_msg_t body;
133
134         // Transmit sync message
135         head.header = MSG_HEADER;
136         head.msgid  = MSG_ID_SYNC;
137         head.length = sizeof(body);
138         head.cksum  = 0; // todo
139
140         body.seq          = serial_xmt_seq;
141         body.time.seconds = world / NSEC_PER_SEC;
142         body.time.nanosec = world % NSEC_PER_SEC;
143
144         sirq_write(port, &head, sizeof(head));
145         sirq_write(port, &body, sizeof(body));
146
147         serial_xmt_seq  += 1;
148         serial_sync_due  = 0;
149
150         // Debug
151         sirq_printf("sync msg transmit\r\n");
152
153         // save transmit time
154         for (int i = 0; i < 1000; i++)
155                 asm("nop");
156         int valid = tdma_stamp(serial_tdma_xmt, &serial_xmt_local);
157         if (!valid)
158                 sirq_printf("missing sync transmit time\r\n");
159         tdma_reset(serial_tdma_xmt);
160 }
161
162 /**
163  * Output external event received message
164  *   event: id of the received event
165  *   time:  compensated timestamp of the event
166  */
167 void serial_send_event(uint16_t event, uint64_t local)
168 {
169         uint64_t world = time_to_world(local);
170
171         ntime_t time = {};
172         time.seconds = (uint32_t)(world / NSEC_PER_SEC);
173         time.nanosec = (uint32_t)(world % NSEC_PER_SEC);
174
175         sirq_printf("event received - %08x:%08x - %u.%u\r\n",
176                 (uint32_t)(local >> 32), (uint32_t)local,
177                 time.seconds, time.nanosec);
178         // todo
179 }
180
181 /**
182  * Handle sync message
183  */
184 void serial_handle_sync(sync_msg_t *msg)
185 {
186         // Read receive timestamp for next time sync message
187         uint64_t current = 0;
188         int valid = tdma_stamp(serial_tdma_rcv, &current);
189         if (!valid)
190                 sirq_printf("missing sync receive time\r\n");
191         tdma_reset(serial_tdma_rcv);
192
193         // Lookup times
194         uint64_t world = ((uint64_t)msg->time.seconds) * NSEC_PER_SEC
195                        + ((uint64_t)msg->time.nanosec);
196
197         // Initialize
198         if (msg->seq == 0) {
199                 uint64_t local = tdma_time();
200                 time_ext_init(local, world);
201         }
202
203         // Valid times timestamp
204         if (serial_prev_seq == (msg->seq-1)) {
205                 uint64_t local = serial_prev_local;
206                 time_ext_sync(local, world);
207         }
208
209         // Queue transmit to other board
210         serial_sync_due   = tdma_time() + serial_sync_delay;
211
212         // Update states
213         serial_prev_local = current;
214         serial_prev_seq   = msg->seq;
215 }
216
217 /**
218  * Handle event message
219  */
220 void serial_handle_event(event_msg_t *msg)
221 {
222 }
223
224 /**
225  * Deliver message
226  */
227 void serial_deliver(int msgid, void *body)
228 {
229         switch (msgid) {
230                 case MSG_ID_SYNC:
231                         sirq_printf("received sync msg\r\n");
232                         serial_handle_sync((sync_msg_t*)body);
233                         break;
234                 case MSG_ID_EVENT:
235                         sirq_printf("received event msg\r\n");
236                         serial_handle_event((event_msg_t*)body);
237                         break;
238         }
239 }
240
241 /**
242  * Process serial receive messages
243  */
244 void serial_receive(parser_t *parser, int byte)
245 {
246         //sirq_printf("serial_receive - %02x\r\n", byte);
247
248         // Lookup pointers
249         header_t *head = (header_t*)parser->buffer;
250         void     *body = (void*)(head+1);
251         const int max_length = sizeof(parser->buffer)-sizeof(header_t);
252
253         // Process uart messages
254         parser->buffer[parser->index++] = byte;
255         switch (parser->state) {
256                 case 0: // Search
257                         if (parser->index == sizeof(uint16_t)) {
258                                 if (head->header == MSG_HEADER) {
259                                         parser->state = 1;
260                                 } else {
261                                         parser->buffer[0] = parser->buffer[1];
262                                         parser->index = 1;
263                                 }
264                         }
265                         break;
266                 case 1: // Header
267                         if (parser->index == sizeof(header_t)) {
268                                 if (head->length <= max_length &&
269                                     head->msgid  <= MSG_MAXID) {
270                                         parser->state = 2;
271                                 } else {
272                                         parser->index = 0;
273                                         parser->state = 0;
274                                 }
275                         }
276                         break;
277                 case 2: // Data
278                         if (parser->index == (int)sizeof(header_t)+head->length) {
279                                 serial_deliver(head->msgid, body);
280                                 parser->index = 0;
281                                 parser->state = 0;
282                         }
283                         break;
284         }
285 }
286
287 /********************
288  * Data definitions *
289  ********************/
290
291 // LEDs
292 DigitalOut led1(LED1);
293 DigitalOut led2(LED2);
294
295 // Message Parsers
296 parser_t   parser_dbg;
297 parser_t   parser_bbb;
298 parser_t   parser_mbed;
299
300 // Serial IRQ
301 sirq_t    *sirq_dbg;
302 sirq_t    *sirq_bbb;
303 sirq_t    *sirq_mbed;
304
305 // Timer DMA
306 tdma_t    *tdma_evt;
307 tdma_t    *tdma_rcv;
308 tdma_t    *tdma_xmt;
309
310 /*********
311  * Tasks *
312  *********/
313
314 void task_serial(uint64_t local, uint64_t world)
315 {
316         while (sirq_ready(sirq_dbg)) {
317                 //sirq_printf("serial recv - dbg\r\n");
318                 serial_receive(&parser_dbg,  sirq_getc(sirq_dbg));
319         }
320
321         while (sirq_ready(sirq_bbb)) {
322                 //sirq_printf("serial recv - bbb\r\n");
323                 serial_receive(&parser_bbb,  sirq_getc(sirq_bbb));
324         }
325
326         while (sirq_ready(sirq_mbed)) {
327                 //sirq_printf("serial recv - mbed\r\n");
328                 serial_receive(&parser_mbed, sirq_getc(sirq_mbed));
329         }
330 }
331
332 void task_events(uint64_t local, uint64_t world)
333 {
334         uint64_t event = 0;
335
336 #ifdef VERBOSE
337         if (tdma_stamp(tdma_evt, &event)) {
338                 sirq_printf("event received - evt\r\n");
339         if (tdma_stamp(tdma_rcv, &event))
340                 sirq_printf("event received - rcv\r\n");
341         if (tdma_stamp(tdma_xmt, &event))
342                 sirq_printf("event received - xmt\r\n");
343 #endif
344
345         if (tdma_stamp(tdma_evt, &event))
346                 serial_send_event(0, event);
347         tdma_reset(tdma_evt);
348 }
349
350 void task_sync(uint64_t local, uint64_t world)
351 {
352         serial_send_sync(sirq_bbb, local);
353 }
354
355 void task_leds(uint64_t local, uint64_t world)
356 {
357         static uint32_t which = 0;
358         led1 = (which == 0);
359         led2 = (which == 1);
360         which ^= 1;
361 }
362
363 void task_debug(uint64_t local, uint64_t world)
364 {
365         //tdma_debug(tdma_rcv);
366         sirq_printf("background - %6d.%02d -> %d.%02d\r\n",
367                         (uint32_t)(local / NSEC_PER_SEC),
368                         (uint32_t)(local % NSEC_PER_SEC / 10000000),
369                         (uint32_t)(world / NSEC_PER_SEC),
370                         (uint32_t)(world % NSEC_PER_SEC / 10000000));
371 }
372
373 /********
374  * Main *
375  ********/
376
377 #define N_ELEM(x) (sizeof(x) / sizeof((x)[0]))
378
379 extern void test_main(void);
380 extern serial_t stdio_uart;
381
382 static struct {
383         void (*task)(uint64_t, uint64_t);
384         uint64_t period;
385         uint64_t due;
386 } tasks[] = {
387         { task_serial, 0          }, // always
388         { task_events, 1000000000 }, // always
389         { task_sync,   0          }, // always
390         { task_leds,   100000000  }, // 10hz
391         { task_debug,  1000000000 }, // 1hz
392 };
393
394 void background(void)
395 {
396         // Debugging
397         uint64_t local = tdma_time();
398         uint64_t world = time_to_world(local);
399
400         // Run the scheduler
401         for (unsigned i = 0; i < N_ELEM(tasks); i++) {
402                 if (local >= tasks[i].due) {
403                         tasks[i].task(local, world);
404                         tasks[i].due += tasks[i].period;
405                 }
406         }
407 }
408
409 int main(int argc, char **argv)
410 {
411         tdma_init();
412
413         // Open serial ports
414         sirq_dbg   = sirq_open(SIRQ_UART0, USBTX, USBRX, 115200); // to pc
415         sirq_bbb   = sirq_open(SIRQ_UART1, PTD3,  PTD2,  115200); // to bbb
416         sirq_mbed  = sirq_open(SIRQ_UART2, PTE0,  PTE1,  115200); // to mbed
417
418
419         // Setup timers
420         tdma_evt   = tdma_open(TDMA_CHAN0, 3, PTC9,  PullDown); // async event
421
422         // mbed time sync
423         tdma_rcv   = tdma_open(TDMA_CHAN2, 3, PTD2,  PullUp);   // time sync rcv
424         tdma_xmt   = tdma_open(TDMA_CHAN3, 3, PTD3,  PullUp);   // time sync xmt
425
426         // host time sync
427         //tdma_rcv   = tdma_open(TDMA_CHAN2, 2, USBRX, PullUp); // time sync rcv
428         //tdma_xmt   = tdma_open(TDMA_CHAN3, 2, USBTX, PullUp); // time sync xmt
429
430         // Serial timestamping
431         serial_tdma_rcv = tdma_rcv;
432         serial_tdma_xmt = tdma_xmt;
433
434         // Run background loop
435         printf("hello");
436         while (true)
437                 background();
438
439         // Run tests
440         //test_main();
441
442         return 0;
443 }