chiark / gitweb /
realtime: allow suppression of use of the PICs watchdog feature
[trains.git] / hostside / startup.c
1 /*
2  * realtime
3  * startup state machine
4  */
5
6 #include "realtime.h"
7
8 const char *const stastatelist[]= DEFINE_STASTATE_DATA;
9 StartupState sta_state;
10
11 static TimeoutEvent sta_toev= { .pclass="startup", .pinst="sta" };
12
13 static PicInsn piob;
14 static void sta_goto(StartupState new_state);
15
16 /*---------- ping ----------*/
17
18 static int pong_seq;
19 static TimeoutEvent ping_toev= { .pinst="ping" };
20
21 static void timedout_ping(TimeoutEvent *toev) {
22   assert(sta_state >= Sta_Ping);
23   sta_goto(Sta_Off);
24 }
25
26 static void timefor_ping(TimeoutEvent *toev) {
27   enco_pic_ping(&piob, pong_seq ^ PING_PONG_PATTERN);
28   serial_transmit(&piob);
29   ping_toev.callback= timedout_ping;
30   ping_toev.pclass= "startup";
31   toev_start(&ping_toev);
32 }
33
34 static void initial_ping(void) {
35   struct timeval now;
36   
37   mgettimeofday(&now);
38   pong_seq= (now.tv_sec & 0x1fU) << 5; /* bottom 5bi of secs: period 32s */
39   pong_seq |= (now.tv_usec >> 15);      /* top 5bi of 20bi us: res.~2^15us */
40   ping_toev.duration=  300;
41   timefor_ping(0);
42 }
43
44 /*---------- watchdog ----------*/
45
46 static TimeoutEvent watchdog_toev= {
47   .pclass=toev_fast_pclass,
48   .pinst="watchdog",
49   .duration= UNMARGIN_WATCHDOG - MARGIN_WATCHDOG,
50 };
51 static PicInsn watchdog_piob;
52
53 static void watchdog_transmit(TimeoutEvent *toev) {
54   toev_start(&watchdog_toev);
55   serial_transmit(&watchdog_piob);
56 }
57
58 static void watchdog_start(void) {
59   watchdog_toev.callback= watchdog_transmit;
60   enco_pic_watchdog(&watchdog_piob, UNMARGIN_WATCHDOG_16);
61   watchdog_transmit(0);
62 }
63
64 static void watchdog_stop(void) {
65   if (!watchdog_toev.running) return;
66   toev_stop(&watchdog_toev);
67   enco_pic_watchdog(&watchdog_piob, 0);
68   serial_transmit(&watchdog_piob);
69 }
70
71 /*---------- main startup algorithm ----------*/
72
73 static void timedout_onward(TimeoutEvent *toev) {
74   assert(sta_state != Sta_Run);
75   if (sta_state == Sta_Settling) {
76     enco_pic_off(&piob);
77     serial_transmit(&piob);
78   }
79   sta_goto(sta_state == Sta_Flush ? Sta_Ping : sta_state + 1);
80 }
81
82 static void sta_startup_manual(void) {
83   waggle_startup_manual();
84   retransmit_start();
85   ouprintf("stastate %s\n", stastatelist[sta_state]);
86 }
87
88 void sta_startup(void) {
89   if (sta_state == Sta_Manual) sta_startup_manual();
90   else sta_goto(Sta_Flush);
91 }
92
93 void sta_finalising_done(void) { sta_goto(Sta_Run); }
94
95 static void sta_goto(StartupState new_state) {
96   toev_stop(&sta_toev);
97   sta_toev.callback= timedout_onward;
98   sta_toev.duration= -1;
99
100   if (new_state < Sta_Ping) {
101     toev_stop(&ping_toev);
102   } else if (new_state == Sta_Ping) {
103     initial_ping();
104   } else {
105     assert(sta_state >= Sta_Ping);
106   }
107
108   switch (new_state) {
109   case Sta_Flush:      sta_toev.duration=   300;   break;
110   case Sta_Off:                                    break;
111   case Sta_Manual:                                 abort();
112   case Sta_Ping:                                   break;
113   case Sta_Settling:   sta_toev.duration=   750;   break;
114   case Sta_Resolving:  sta_toev.duration=   500;   break;
115   case Sta_Finalising:                             break;
116   case Sta_Run:                                    break;
117   case Sta_Crashed: abort();
118   }
119
120   if (new_state < Sta_Run)
121     choreographers_all_abandon();
122   if (new_state < Sta_Finalising) {
123     safety_abandon_run();
124     motions_all_abandon();
125   }
126
127   piob.l= 0;
128   switch (new_state) {
129   case Sta_Flush:                                                  break;
130   case Sta_Off:   if (sta_state > Sta_Ping) enco_pic_off(&piob);   break;
131   case Sta_Manual:                                                 abort();
132   case Sta_Ping:                                                   break;
133   case Sta_Settling:   waggle_settle();     enco_pic_off(&piob);   break;
134   case Sta_Resolving:
135     resolve_begin();
136     points_turning_on();
137     adjuncts_start_xmit();
138     enco_pic_on(&piob);
139     break;
140   case Sta_Finalising:
141     if (resolve_complete() <0)
142       /* in this case, we get stuck - user has to power cycle the layout */
143       return;
144     /* resolve_motioncheck will move us to Run eventually, we hope */
145     break;
146   case Sta_Run:
147     if (!simulate)
148       persist_install();
149     retransmit_start();
150     break;
151   case Sta_Crashed: abort();
152   }
153   if (piob.l) serial_transmit(&piob);
154
155   if (new_state >= Sta_Run && !disable_watchdog) watchdog_start();
156   else watchdog_stop();
157      
158   toev_start(&sta_toev);
159   sta_state= new_state;
160
161   /* notify various people: */
162   ouprintf("stastate %s\n", stastatelist[sta_state]);
163   /* ... add others here. */
164   if (sta_state == Sta_Finalising) resolve_motioncheck();
165 }   
166
167 void serial_moredata(PicInsn *buf) {
168   const PicInsnInfo *pii;
169   int obj, v, suppress;
170   Byte *ep;
171
172   /* Called when more data is received from PICs.
173    * On entry, buf->l is amount of data available.
174    * Does one of the following:
175    *  - determines that there is no complete message; sets buf->l = 0
176    *  - finds and handles one PicInsn message, sets buf->l = message length
177    *  - handles some series of bytes structured some other way,
178    *       sets buf->l to the numer of bytes handled.
179    */
180   assert(buf->l > 0);
181   
182   if (sta_state == Sta_Flush) {
183     ouhex("picioh in junk", buf->d, buf->l);
184     toev_start(&sta_toev);
185     return; /* junk absolutely everything */
186   }
187   if (PICMSG_AAARGH_P(buf->d[0])) {
188     ouhex("picioh in aaargh", buf->d, buf->l);
189     die("PIC sent us AAARGH!");
190   }
191   if (PICMSG_HELLO_P(buf->d[0])) {
192     ouhex("picioh in hello", buf->d, 1);
193     if (sta_state != Sta_Manual)
194       sta_goto(Sta_Flush);
195     buf->l= 1;
196     return;
197   }
198   if (sta_state == Sta_Off) {
199     ouhex("picioh in off", buf->d, 1);
200     buf->l= 1;
201     return;
202   }
203
204   assert(sta_state >= Sta_Manual);
205   /* So, we are expecting properly formed messages. */
206   
207   for (ep= buf->d; ep < buf->d + buf->l; ep++)
208     if (!(*ep & 0x80u))
209       goto found_end;
210
211   if (buf->l == sizeof(buf->d)) {
212     ouhex("picioh in toolong", buf->d, buf->l);
213     die("PIC sent packet too long");
214   }
215   buf->l= 0; /* message not yet finished, so consume nothing */
216   return;
217
218  found_end:
219   /* Aha! */
220   buf->l= ep - buf->d + 1;
221   picinsn_decode(buf, pic_reply_infos, &pii, &obj, &v);
222   suppress= pii && pii->noiselevel > picio_send_noise;
223
224   if (!suppress && picio_send_noise >= 2)
225     ouhex_nosim("picioh in msg", buf->d, buf->l);
226
227   if (simlog_full || sta_state < Sta_Settling ||
228       !((pii->opcode==PICMSG_NMRADONE && obj==1) ||
229         (pii->opcode==PICMSG_PONG && obj==pong_seq)))
230     simlog_serial(buf->d, buf->l);
231   
232   if (!pii) { ouprintf("picio in unknown\n"); return; }
233   if (!suppress)
234     oupicio("in",pii,obj,v,ouprintf_only);
235   pii->input_fn(pii,buf,obj);
236 }
237
238 void on_pic_nul(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
239   /* layout turned off, probably */
240   if (sta_state == Sta_Manual) return;
241   enco_pic_off(&piob);
242   serial_transmit(&piob);
243   sta_goto(Sta_Flush);
244   return;
245 }
246
247 void on_pic_pong(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
248   if (sta_state == Sta_Manual)
249     return;
250
251   if (objnum != pong_seq) {
252     if (objnum == (pong_seq^PING_PONG_PATTERN))
253       die("PIC connection is looped back (ping %#05x bounced)", objnum);
254     die("PIC sent wrong ping response (%#05x, wanted %#05x)", objnum, pong_seq);
255   }
256
257   ping_toev.duration= 1000;
258   ping_toev.callback= timefor_ping;
259   ping_toev.pclass= toev_fast_pclass;
260   toev_start(&ping_toev);
261
262   if (sta_state == Sta_Ping)
263     sta_goto(Sta_Settling);
264 }
265
266 void on_pic_fault(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
267   if (sta_state <= Sta_Ping) return;
268   die("FAULT");
269 }
270
271 void on_pic_wtimeout(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
272   if (sta_state <= Sta_Settling) return;
273   die("microcontrollers' watchdog timer triggered\n");
274 }
275
276 void on_pic_hello(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
277   { abort(); }
278 void on_pic_aaargh(const PicInsnInfo *pii, const PicInsn *pi, int objnum)
279   { abort(); }
280 void on_pic_spurious(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
281   ouprintf("warning spurious %d : spurious short circuit (fault)"
282            " detection interrupts\n", objnum);
283 }
284 void on_pic_pointed(const PicInsnInfo *pii, const PicInsn *pi, int objnum) { }
285 void on_pic_retriable(const PicInsnInfo *pii, const PicInsn *pi, int objnum){}
286
287 static int coalescing_detects;
288
289 static void detect_report_now(Segment *seg) {
290   ouprintf_only("detect %s %d\n", seg->i->pname, seg->detect_actual);
291   seg->detect_reported= seg->detect_actual;
292 }
293
294 static SegmentNum on_pic_detect_prep(int detyn, int objnum) {
295   SegmentNum segn;
296
297   if (objnum >= info_segmentmaplen ||
298       (segn= info_segmentmap[objnum]) < 0)
299     die("PIC sent detect%d @#%#x not in map",detyn,objnum);
300
301   Segment *seg= &segments[segn];
302   seg->detect_actual= detyn;
303
304   if (!(picio_send_noise <= 1 &&
305         seg->owner &&
306         seg->det_ignore)) {
307     if (cmdi.out.total >= 1024) coalescing_detects= 1;
308
309     if (coalescing_detects &&
310         seg->det_ignore &&
311         seg->detect_reported) {
312       assert(seg->detect_flaps < INT_MAX);
313       seg->detect_flaps++;
314     } else {
315       detect_report_now(seg);
316     }
317   }
318
319   return segn;
320 }
321
322 void cmdi_output_bufferempty(OutBufferChain *obc) {
323   SEG_IV;
324   
325   if (!coalescing_detects) return;
326   coalescing_detects= 0;
327
328   FOR_SEG {
329     if (seg->detect_flaps) {
330       ouprintf_only("detect-flaps %s %d\n", segi->pname, seg->detect_flaps);
331       seg->detect_flaps= 0;
332     }
333     if (seg->detect_actual != seg->detect_reported) {
334       detect_report_now(seg);
335     }
336   }
337 }
338
339 void on_pic_detect1(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
340   SegmentNum segn;
341   Segment *seg;
342   const char *pname;
343   
344   segn= on_pic_detect_prep(1,objnum);
345   seg= &segments[segn];
346   pname= info_segments[segn].pname;
347   
348   switch (sta_state) {
349   case Sta_Flush:
350   case Sta_Off:
351   case Sta_Manual:
352   case Sta_Ping:
353   case Sta_Settling:
354     break;
355   case Sta_Resolving:
356     seg->res_detect= 1;
357     break;
358   case Sta_Finalising:
359     if (!seg->res_detect) {
360       ouprintf("resolution new-detection-in-finalising @%s\n", pname);
361       sta_goto(Sta_Settling);
362     }
363     break;
364   case Sta_Run:
365     safety_notify_detection(seg);
366     break;
367   case Sta_Crashed: abort();
368   }
369 }
370
371 void on_pic_detect0(const PicInsnInfo *pii, const PicInsn *pi, int objnum) {
372   on_pic_detect_prep(0,objnum);
373 }
374
375 void choreographers_all_abandon(void) { }