Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : /*
17 : * main loop scheduler for ArduPilot
18 : * Author: Andrew Tridgell, January 2013
19 : *
20 : */
21 :
22 : #include "AP_Scheduler_config.h"
23 :
24 : #if AP_SCHEDULER_ENABLED
25 :
26 : #include "AP_Scheduler.h"
27 :
28 : #include <AP_HAL/AP_HAL.h>
29 : #include <AP_Param/AP_Param.h>
30 : #include <AP_Vehicle/AP_Vehicle.h>
31 : #include <AP_Logger/AP_Logger.h>
32 : #include <AP_InertialSensor/AP_InertialSensor.h>
33 : #include <AP_InternalError/AP_InternalError.h>
34 : #include <AP_Common/ExpandingString.h>
35 : #include <AP_HAL/SIMState.h>
36 : #include <AP_Vehicle/AP_Vehicle_Type.h>
37 :
38 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
39 : #include <SITL/SITL.h>
40 : #endif
41 : #include <stdio.h>
42 :
43 : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_ArduSub)
44 : #define SCHEDULER_DEFAULT_LOOP_RATE 400
45 : #else
46 : #define SCHEDULER_DEFAULT_LOOP_RATE 50
47 : #endif
48 :
49 : #define debug(level, fmt, args...) do { if ((level) <= _debug.get()) { hal.console->printf(fmt, ##args); }} while (0)
50 :
51 : extern const AP_HAL::HAL& hal;
52 :
53 : const AP_Param::GroupInfo AP_Scheduler::var_info[] = {
54 : // @Param: DEBUG
55 : // @DisplayName: Scheduler debug level
56 : // @Description: Set to non-zero to enable scheduler debug messages. When set to show "Slips" the scheduler will display a message whenever a scheduled task is delayed due to too much CPU load. When set to ShowOverruns the scheduled will display a message whenever a task takes longer than the limit promised in the task table.
57 : // @Values: 0:Disabled,2:ShowSlips,3:ShowOverruns
58 : // @User: Advanced
59 : AP_GROUPINFO("DEBUG", 0, AP_Scheduler, _debug, 0),
60 :
61 : // @Param: LOOP_RATE
62 : // @DisplayName: Scheduling main loop rate
63 : // @Description: This controls the rate of the main control loop in Hz. This should only be changed by developers. This only takes effect on restart. Values over 400 are considered highly experimental.
64 : // @Range: 50 400
65 : // @RebootRequired: True
66 : // @Units: Hz
67 : // @User: Advanced
68 : AP_GROUPINFO("LOOP_RATE", 1, AP_Scheduler, _loop_rate_hz, SCHEDULER_DEFAULT_LOOP_RATE),
69 :
70 : // @Param: OPTIONS
71 : // @DisplayName: Scheduling options
72 : // @Description: This controls optional aspects of the scheduler.
73 : // @Bitmask: 0:Enable per-task perf info
74 : // @User: Advanced
75 : AP_GROUPINFO("OPTIONS", 2, AP_Scheduler, _options, 0),
76 :
77 : AP_GROUPEND
78 : };
79 :
80 : // constructor
81 2098 : AP_Scheduler::AP_Scheduler()
82 : {
83 2098 : if (_singleton) {
84 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
85 0 : AP_HAL::panic("Too many schedulers");
86 : #endif
87 0 : return;
88 : }
89 2098 : _singleton = this;
90 :
91 2098 : AP_Param::setup_object_defaults(this, var_info);
92 : }
93 :
94 : /*
95 : * Get the AP_Scheduler singleton
96 : */
97 : AP_Scheduler *AP_Scheduler::_singleton;
98 811726726 : AP_Scheduler *AP_Scheduler::get_singleton()
99 : {
100 811726726 : return _singleton;
101 : }
102 :
103 : // initialise the scheduler
104 2094 : void AP_Scheduler::init(const AP_Scheduler::Task *tasks, uint8_t num_tasks, uint32_t log_performance_bit)
105 : {
106 : // grab the semaphore before we start anything
107 2094 : _rsem.take_blocking();
108 :
109 : // only allow 50 to 2000 Hz
110 2094 : if (_loop_rate_hz < 50) {
111 0 : _loop_rate_hz.set(50);
112 2094 : } else if (_loop_rate_hz > 2000) {
113 0 : _loop_rate_hz.set(2000);
114 : }
115 2094 : _last_loop_time_s = 1.0 / _loop_rate_hz;
116 :
117 : // These variables are initialized only here in scheduler::init()
118 : // These are also defensively initialized in the getter functions to catch initialization order issues.
119 2094 : _loop_period_us = 1000000UL / _loop_rate_hz;
120 2094 : _loop_period_s = 1.0f / _loop_rate_hz;
121 2094 : _active_loop_rate_hz = _loop_rate_hz;
122 :
123 2094 : _vehicle_tasks = tasks;
124 2094 : _num_vehicle_tasks = num_tasks;
125 :
126 : #if AP_VEHICLE_ENABLED
127 2094 : AP_Vehicle* vehicle = AP::vehicle();
128 2094 : if (vehicle != nullptr) {
129 2094 : vehicle->get_common_scheduler_tasks(_common_tasks, _num_common_tasks);
130 : }
131 : #endif
132 :
133 2094 : _num_tasks = _num_vehicle_tasks + _num_common_tasks;
134 :
135 2094 : _last_run = NEW_NOTHROW uint16_t[_num_tasks];
136 2094 : _tick_counter = 0;
137 :
138 : // setup initial performance counters
139 2094 : perf_info.set_loop_rate(get_loop_rate_hz());
140 2094 : perf_info.reset();
141 :
142 2094 : if (_options & uint8_t(Options::RECORD_TASK_INFO)) {
143 0 : perf_info.allocate_task_info(_num_tasks);
144 : }
145 :
146 2094 : _log_performance_bit = log_performance_bit;
147 :
148 : // sanity check the task lists to ensure the priorities are
149 : // never decrease
150 2094 : uint8_t old = 0;
151 63913 : for (uint8_t i=0; i<_num_common_tasks; i++) {
152 61819 : if (_common_tasks[i].priority < old){
153 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
154 0 : break;
155 : }
156 61819 : old = _common_tasks[i].priority;
157 : }
158 2094 : old = 0;
159 104016 : for (uint8_t i=0; i<_num_vehicle_tasks; i++) {
160 101922 : if (_vehicle_tasks[i].priority < old) {
161 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
162 0 : break;
163 : }
164 101922 : old = _vehicle_tasks[i].priority;
165 : }
166 2094 : }
167 :
168 : // one tick has passed
169 41686253 : void AP_Scheduler::tick(void)
170 : {
171 41686253 : _tick_counter++;
172 41686253 : _tick_counter32++;
173 41686253 : }
174 :
175 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
176 : /*
177 : fill stack with NaN so we can catch use of uninitialised stack
178 : variables in SITL
179 : */
180 1133018659 : static void fill_nanf_stack(void)
181 : {
182 : float v[1024];
183 1133018659 : fill_nanf(v, ARRAY_SIZE(v));
184 1133018659 : }
185 : #endif
186 :
187 : /*
188 : run one tick
189 : this will run as many scheduler tasks as we can in the specified time
190 : */
191 41686253 : void AP_Scheduler::run(uint32_t time_available)
192 : {
193 41686253 : uint32_t run_started_usec = AP_HAL::micros();
194 41686253 : uint32_t now = run_started_usec;
195 :
196 41686253 : uint8_t vehicle_tasks_offset = 0;
197 41686253 : uint8_t common_tasks_offset = 0;
198 :
199 3460287610 : for (uint8_t i=0; i<_num_tasks; i++) {
200 : // determine which of the common task / vehicle task to run
201 3418602988 : bool run_vehicle_task = false;
202 3418602988 : if (vehicle_tasks_offset < _num_vehicle_tasks &&
203 2495953403 : common_tasks_offset < _num_common_tasks) {
204 : // still have entries on both lists; compare the
205 : // priorities. In case of a tie the vehicle-specific
206 : // entry wins.
207 2495953403 : const Task &vehicle_task = _vehicle_tasks[vehicle_tasks_offset];
208 2495953403 : const Task &common_task = _common_tasks[common_tasks_offset];
209 2495953403 : if (vehicle_task.priority <= common_task.priority) {
210 2198469542 : run_vehicle_task = true;
211 : }
212 3418602988 : } else if (vehicle_tasks_offset < _num_vehicle_tasks) {
213 : // out of common tasks to run
214 0 : run_vehicle_task = true;
215 922649585 : } else if (common_tasks_offset < _num_common_tasks) {
216 : // out of vehicle tasks to run
217 922649585 : run_vehicle_task = false;
218 : } else {
219 : // this is an error; the outside loop should have terminated
220 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
221 0 : break;
222 : }
223 :
224 3418602988 : const AP_Scheduler::Task &task = run_vehicle_task ? _vehicle_tasks[vehicle_tasks_offset] : _common_tasks[common_tasks_offset];
225 3418602988 : if (run_vehicle_task) {
226 2198469542 : vehicle_tasks_offset++;
227 : } else {
228 1220133446 : common_tasks_offset++;
229 : }
230 :
231 3418602988 : if (task.priority > MAX_FAST_TASK_PRIORITIES) {
232 2887601052 : const uint16_t dt = _tick_counter - _last_run[i];
233 : // we allow 0 to mean loop rate
234 2887601052 : uint32_t interval_ticks = (is_zero(task.rate_hz) ? 1 : _loop_rate_hz / task.rate_hz);
235 2887601052 : if (interval_ticks < 1) {
236 58891883 : interval_ticks = 1;
237 : }
238 2887601052 : if (dt < interval_ticks) {
239 : // this task is not yet scheduled to run again
240 2285581515 : continue;
241 : }
242 : // this task is due to run. Do we have enough time to run it?
243 602019537 : _task_time_allowed = task.max_time_micros;
244 :
245 602019537 : if (dt >= interval_ticks*2) {
246 2158 : perf_info.task_slipped(i);
247 : }
248 :
249 602019537 : if (dt >= interval_ticks*max_task_slowdown) {
250 : // we are going beyond the maximum slowdown factor for a
251 : // task. This will trigger increasing the time budget
252 0 : task_not_achieved++;
253 : }
254 :
255 602019537 : if (_task_time_allowed > time_available) {
256 : // not enough time to run this task. Continue loop -
257 : // maybe another task will fit into time remaining
258 2814 : continue;
259 : }
260 : } else {
261 531001936 : _task_time_allowed = get_loop_period_us();
262 : }
263 :
264 : // run it
265 1133018659 : _task_time_started = now;
266 1133018659 : hal.util->persistent_data.scheduler_task = i;
267 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
268 1133018659 : fill_nanf_stack();
269 : #endif
270 1133018659 : task.function();
271 1133017028 : hal.util->persistent_data.scheduler_task = -1;
272 :
273 : // record the tick counter when we ran. This drives
274 : // when we next run the event
275 1133017028 : _last_run[i] = _tick_counter;
276 :
277 : // work out how long the event actually took
278 1133017028 : now = AP_HAL::micros();
279 1133017028 : uint32_t time_taken = now - _task_time_started;
280 1133017028 : bool overrun = false;
281 1133017028 : if (time_taken > _task_time_allowed) {
282 223 : overrun = true;
283 : // the event overran!
284 223 : debug(3, "Scheduler overrun task[%u-%s] (%u/%u)\n",
285 : (unsigned)i,
286 : task.name,
287 : (unsigned)time_taken,
288 : (unsigned)_task_time_allowed);
289 : }
290 :
291 1133017028 : perf_info.update_task_info(i, time_taken, overrun);
292 :
293 1133017028 : if (time_taken >= time_available) {
294 : /*
295 : we are out of time, but we need to keep walking the task
296 : table in case there is another fast loop task after this
297 : task, plus we need to update the accouting so we can
298 : work out if we need to allocate extra time for the loop
299 : (lower the loop rate)
300 : Just set time_available to zero, which means we will
301 : only run fast tasks after this one
302 : */
303 214 : time_available = 0;
304 : } else {
305 1133016814 : time_available -= time_taken;
306 : }
307 : }
308 :
309 : // update number of spare microseconds
310 41684622 : _spare_micros += time_available;
311 :
312 41684622 : _spare_ticks++;
313 41684622 : if (_spare_ticks == 32) {
314 2602209 : _spare_ticks /= 2;
315 2602209 : _spare_micros /= 2;
316 : }
317 41684622 : }
318 :
319 : /*
320 : return number of micros until the current task reaches its deadline
321 : */
322 186924818 : uint16_t AP_Scheduler::time_available_usec(void) const
323 : {
324 186924818 : uint32_t dt = AP_HAL::micros() - _task_time_started;
325 186924818 : if (dt > _task_time_allowed) {
326 99 : return 0;
327 : }
328 186924719 : return _task_time_allowed - dt;
329 : }
330 :
331 : /*
332 : calculate load average as a number from 0 to 1
333 : */
334 1135604 : float AP_Scheduler::load_average()
335 : {
336 : // return 1 if filtered main loop rate is 5% below the configured rate
337 1135604 : if (get_filtered_loop_rate_hz() < get_loop_rate_hz() * 0.95) {
338 222 : return 1.0;
339 : }
340 1135382 : if (_spare_ticks == 0) {
341 717 : return 0.0f;
342 : }
343 1134665 : const uint32_t loop_us = get_loop_period_us();
344 1134665 : const uint32_t used_time = loop_us - (_spare_micros/_spare_ticks);
345 1134665 : return constrain_float(used_time / (float)loop_us, 0, 1);
346 : }
347 :
348 41686254 : void AP_Scheduler::loop()
349 : {
350 : // wait for an INS sample
351 41686254 : hal.util->persistent_data.scheduler_task = -3;
352 41686254 : _rsem.give();
353 41686254 : AP::ins().wait_for_sample();
354 41686253 : _rsem.take_blocking();
355 41686253 : hal.util->persistent_data.scheduler_task = -1;
356 :
357 41686253 : _loop_sample_time_us = AP_HAL::micros64();
358 41686253 : const uint32_t sample_time_us = uint32_t(_loop_sample_time_us);
359 :
360 41686253 : if (_loop_timer_start_us == 0) {
361 2086 : _loop_timer_start_us = sample_time_us;
362 2086 : _last_loop_time_s = get_loop_period_s();
363 : } else {
364 41684167 : _last_loop_time_s = (sample_time_us - _loop_timer_start_us) * 1.0e-6;
365 : }
366 :
367 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
368 : {
369 : /*
370 : for testing low CPU conditions we can add an optional delay in SITL
371 : */
372 41686253 : auto *sitl = AP::sitl();
373 41686253 : uint32_t loop_delay_us = sitl? sitl->loop_delay.get() : 1000U;
374 41686253 : hal.scheduler->delay_microseconds(loop_delay_us);
375 : }
376 : #endif
377 :
378 : // tell the scheduler one tick has passed
379 41686253 : tick();
380 :
381 : // run all the tasks that are due to run. Note that we only
382 : // have to call this once per loop, as the tasks are scheduled
383 : // in multiples of the main loop tick. So if they don't run on
384 : // the first call to the scheduler they won't run on a later
385 : // call until scheduler.tick() is called again
386 41686253 : const uint32_t loop_us = get_loop_period_us();
387 41686253 : uint32_t now = AP_HAL::micros();
388 41686253 : uint32_t time_available = 0;
389 41686253 : const uint32_t loop_tick_us = now - sample_time_us;
390 41686253 : if (loop_tick_us < loop_us) {
391 : // get remaining time available for this loop
392 41686253 : time_available = loop_us - loop_tick_us;
393 : }
394 :
395 : // add in extra loop time determined by not achieving scheduler tasks
396 41686253 : time_available += extra_loop_us;
397 :
398 : // run the tasks
399 41686253 : run(time_available);
400 :
401 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
402 : // move result of AP_HAL::micros() forward:
403 41684622 : hal.scheduler->delay_microseconds(1);
404 : #endif
405 :
406 41684622 : if (task_not_achieved > 0) {
407 : // add some extra time to the budget
408 0 : extra_loop_us = MIN(extra_loop_us+100U, 5000U);
409 0 : task_not_achieved = 0;
410 0 : task_all_achieved = 0;
411 41684622 : } else if (extra_loop_us > 0) {
412 0 : task_all_achieved++;
413 0 : if (task_all_achieved > 50) {
414 : // we have gone through 50 loops without a task taking too
415 : // long. CPU pressure has eased, so drop the extra time we're
416 : // giving each loop
417 0 : task_all_achieved = 0;
418 : // we are achieving all tasks, slowly lower the extra loop time
419 0 : extra_loop_us = MAX(0U, extra_loop_us-50U);
420 : }
421 : }
422 :
423 : // check loop time
424 41684622 : perf_info.check_loop_time(sample_time_us - _loop_timer_start_us);
425 :
426 41684622 : _loop_timer_start_us = sample_time_us;
427 :
428 : #if AP_SIM_ENABLED && CONFIG_HAL_BOARD != HAL_BOARD_SITL
429 : hal.simstate->update();
430 : #endif
431 41684622 : }
432 :
433 : #if HAL_LOGGING_ENABLED
434 19414 : void AP_Scheduler::update_logging()
435 : {
436 19414 : if (debug_flags()) {
437 0 : perf_info.update_logging();
438 : }
439 38828 : if (_log_performance_bit != (uint32_t)-1 &&
440 19414 : AP::logger().should_log(_log_performance_bit)) {
441 18657 : Log_Write_Performance();
442 : }
443 19414 : perf_info.set_loop_rate(get_loop_rate_hz());
444 19414 : perf_info.reset();
445 : // dynamically update the per-task perf counter
446 19414 : if (!(_options & uint8_t(Options::RECORD_TASK_INFO)) && perf_info.has_task_info()) {
447 1 : perf_info.free_task_info();
448 19413 : } else if ((_options & uint8_t(Options::RECORD_TASK_INFO)) && !perf_info.has_task_info()) {
449 1 : perf_info.allocate_task_info(_num_tasks);
450 : }
451 19414 : }
452 :
453 : // Write a performance monitoring packet
454 18657 : void AP_Scheduler::Log_Write_Performance()
455 : {
456 18657 : uint64_t rtc = 0;
457 : #if AP_RTC_ENABLED
458 18657 : UNUSED_RESULT(AP::rtc().get_utc_usec(rtc));
459 : #endif
460 :
461 18657 : const AP_HAL::Util::PersistentData &pd = hal.util->persistent_data;
462 18657 : struct log_Performance pkt = {
463 : LOG_PACKET_HEADER_INIT(LOG_PERFORMANCE_MSG),
464 18657 : time_us : AP_HAL::micros64(),
465 37314 : loop_rate : (uint16_t)(get_filtered_loop_rate_hz() + 0.5f),
466 18657 : num_long_running : perf_info.get_num_long_running(),
467 18657 : num_loops : perf_info.get_num_loops(),
468 18657 : max_time : perf_info.get_max_time(),
469 18657 : mem_avail : hal.util->available_memory(),
470 37314 : load : (uint16_t)(load_average() * 1000),
471 18657 : internal_error_last_line : AP::internalerror().last_error_line(),
472 18657 : internal_errors : AP::internalerror().errors(),
473 18657 : internal_error_count : AP::internalerror().count(),
474 18657 : spi_count : pd.spi_count,
475 18657 : i2c_count : pd.i2c_count,
476 18657 : i2c_isr_count : pd.i2c_isr_count,
477 18657 : extra_loop_us : extra_loop_us,
478 : rtc : rtc,
479 55971 : };
480 18657 : AP::logger().WriteCriticalBlock(&pkt, sizeof(pkt));
481 18657 : }
482 : #endif // HAL_LOGGING_ENABLED
483 :
484 : // display task statistics as text buffer for @SYS/tasks.txt
485 2 : void AP_Scheduler::task_info(ExpandingString &str)
486 : {
487 : // a header to allow for machine parsers to determine format
488 2 : str.printf("TasksV2\n");
489 :
490 : // dynamically enable statistics collection
491 2 : if (!(_options & uint8_t(Options::RECORD_TASK_INFO))) {
492 0 : _options.set(_options | uint8_t(Options::RECORD_TASK_INFO));
493 0 : return;
494 : }
495 :
496 2 : if (perf_info.get_task_info(0) == nullptr) {
497 0 : return;
498 : }
499 :
500 : // baseline the total time taken by all tasks
501 2 : float total_time = 1.0f;
502 174 : for (uint8_t i = 0; i < _num_tasks + 1; i++) {
503 172 : const AP::PerfInfo::TaskInfo* ti = perf_info.get_task_info(i);
504 172 : if (ti != nullptr && ti->tick_count > 0) {
505 170 : total_time += ti->elapsed_time_us;
506 : }
507 : }
508 :
509 2 : uint8_t vehicle_tasks_offset = 0;
510 2 : uint8_t common_tasks_offset = 0;
511 :
512 172 : for (uint8_t i = 0; i < _num_tasks; i++) {
513 170 : const AP::PerfInfo::TaskInfo* ti = perf_info.get_task_info(i);
514 : const char *task_name;
515 :
516 : // determine which of the common task / vehicle task to run
517 170 : bool run_vehicle_task = false;
518 170 : if (vehicle_tasks_offset < _num_vehicle_tasks &&
519 128 : common_tasks_offset < _num_common_tasks) {
520 : // still have entries on both lists; compare the
521 : // priorities. In case of a tie the vehicle-specific
522 : // entry wins.
523 128 : const Task &vehicle_task = _vehicle_tasks[vehicle_tasks_offset];
524 128 : const Task &common_task = _common_tasks[common_tasks_offset];
525 128 : if (vehicle_task.priority <= common_task.priority) {
526 112 : run_vehicle_task = true;
527 : }
528 170 : } else if (vehicle_tasks_offset < _num_vehicle_tasks) {
529 : // out of common tasks to run
530 0 : run_vehicle_task = true;
531 42 : } else if (common_tasks_offset < _num_common_tasks) {
532 : // out of vehicle tasks to run
533 42 : run_vehicle_task = false;
534 : } else {
535 : // this is an error; the outside loop should have terminated
536 0 : INTERNAL_ERROR(AP_InternalError::error_t::flow_of_control);
537 0 : return;
538 : }
539 :
540 170 : if (run_vehicle_task) {
541 112 : task_name = _vehicle_tasks[vehicle_tasks_offset++].name;
542 : } else {
543 58 : task_name = _common_tasks[common_tasks_offset++].name;
544 : }
545 :
546 170 : ti->print(task_name, total_time, str);
547 : }
548 : }
549 :
550 : namespace AP {
551 :
552 624451872 : AP_Scheduler &scheduler()
553 : {
554 624451872 : return *AP_Scheduler::get_singleton();
555 : }
556 :
557 : };
558 :
559 : #endif // AP_SCHEDULER_ENABLED
|