ICARUS OS (Intelligent Cooperative Architecture for Real-time Unified Systems) 0.1.0
Preemptive Real-Time Operating System for ARM Cortex-M7
Loading...
Searching...
No Matches
display.c
Go to the documentation of this file.
1
32#include "bsp/display.h"
33#include "icarus/kernel.h"
34#include "icarus/scheduler.h"
35#include <string.h>
36#include <inttypes.h>
37
38/* ============================================================================
39 * ANSI ESCAPE CODE MACROS
40 * ========================================================================= */
41
49#define ANSI_GOTO(row, col) (void)printf("\033[%d;%dH", (row), (col))
50
52#define ANSI_SHOW_CURSOR() (void)printf("\033[?25h")
53
55#define ANSI_HIDE_CURSOR() (void)printf("\033[?25l")
56
58#define ANSI_RESET "\033[0m"
59
61#define ANSI_BOLD "\033[1m"
62
64#define ANSI_DIM "\033[2m"
65
67#define ANSI_GREEN "\033[32m"
68
70#define ANSI_YELLOW "\033[33m"
71
73#define ANSI_MAGENTA "\033[35m"
74
76#define ANSI_CYAN "\033[36m"
77
79#define ANSI_WHITE "\033[37m"
80
82#define ANSI_BG_GREEN "\033[42m"
83
85#define ANSI_BG_MAGENTA "\033[45m"
86
/* End of ANSI_CODES */
88
89/* ============================================================================
90 * MESSAGE HISTORY IMPLEMENTATION
91 * ========================================================================= */
92
105 if (hist == NULL) {
106 return;
107 }
108 (void)memset(hist, 0, sizeof(msg_history_t));
109}
110
127void msg_history_add(msg_history_t* hist, const uint8_t* data, uint8_t len,
128 uint8_t source_id, bool is_send) {
129 if ((hist == NULL) || (data == NULL) || (len == 0u)) {
130 return;
131 }
132
133 // Clamp length to max
134 uint8_t clamped_len = len;
135 if (clamped_len > (uint8_t)MSG_HISTORY_MAX_BYTES) {
136 clamped_len = (uint8_t)MSG_HISTORY_MAX_BYTES;
137 }
138
139 // Add entry at head position
140 msg_history_entry_t* entry = &hist->entries[hist->head];
141 (void)memcpy(entry->data, data, clamped_len);
142 entry->len = clamped_len;
143 entry->source_id = source_id;
144 entry->is_send = is_send;
145
146 // Advance head (circular)
147 hist->head = (uint8_t)((uint8_t)(hist->head + 1u) % (uint8_t)MSG_HISTORY_LEN);
148 if (hist->count < (uint8_t)MSG_HISTORY_LEN) {
149 hist->count++;
150 }
151}
152
174void display_render_msg_history(uint8_t row, uint8_t col, msg_history_t* hist, const char* label) {
175 if (hist == NULL) {
176 return;
177 }
178
179 // Header - use ASCII for compatibility
180 ANSI_GOTO(row, col);
181 (void)printf(ANSI_CYAN ANSI_BOLD "+---%s---+" ANSI_RESET, label);
182
183 // Calculate starting index (oldest entry)
184 uint8_t start_idx;
185 if (hist->count < (uint8_t)MSG_HISTORY_LEN) {
186 start_idx = 0u;
187 } else {
188 start_idx = hist->head;
189 }
190
191 // Render each history entry
192 for (uint8_t i = 0u; i < (uint8_t)MSG_HISTORY_LEN; i++) {
193 int goto_row = (int)row + 1 + (int)i;
194 ANSI_GOTO(goto_row, col);
195
196 if (i < hist->count) {
197 uint8_t idx = (uint8_t)((start_idx + i) % (uint8_t)MSG_HISTORY_LEN);
198 msg_history_entry_t* entry = &hist->entries[idx];
199
200 if (entry->is_send) {
201 (void)printf(ANSI_GREEN "|>P%d:", entry->source_id);
202 } else {
203 (void)printf(ANSI_MAGENTA "|<C%d:", entry->source_id);
204 }
205
206 if (entry->len == 1u) {
207 (void)printf("%3d" ANSI_RESET "|", entry->data[0]);
208 } else {
209 for (uint8_t j = 0u; (j < entry->len) && (j < 3u); j++) {
210 (void)printf("%02X", entry->data[j]);
211 }
212 (void)printf(ANSI_RESET "|");
213 }
214 } else {
215 (void)printf("| |");
216 }
217 }
218
219 // Footer
220 {
221 int footer_row = (int)row + (int)MSG_HISTORY_LEN + 1;
222 ANSI_GOTO(footer_row, col);
223 }
224 (void)printf(ANSI_CYAN "+---------+" ANSI_RESET);
225}
226
247void display_render_bar(uint8_t row, const char* task_name, uint32_t elapsed_ticks, uint32_t period_ticks) {
248 // Guard against division by zero
249 uint32_t period = period_ticks;
250 if (period == 0u) {
251 period = 1u;
252 }
253
254 // Clamp elapsed to period to avoid overflow
255 uint32_t elapsed = elapsed_ticks;
256 if (elapsed > period) {
257 elapsed = period;
258 }
259
260 // Calculate filled portion (0 to BAR_WIDTH)
261 // cppcheck-suppress zerodivcond
262 // period is guaranteed > 0 by guard above, but cppcheck can't track this
263 uint32_t filled = (elapsed * (uint32_t)BAR_WIDTH) / period;
264 if (filled > (uint32_t)BAR_WIDTH) {
265 filled = (uint32_t)BAR_WIDTH;
266 }
267
268 // Move cursor to start of this task's row
269 ANSI_GOTO(row, 1);
270
271 // Print task name and opening bracket
272 (void)printf("[%s] ", (task_name != NULL) ? task_name : "unknown");
273
274 // Render bar: filled portion, then empty portion
275 for (uint32_t i = 0u; i < (uint32_t)BAR_WIDTH; i++) {
276 if (i < filled) {
277 (void)printf("█"); // Full block for filled
278 } else {
279 (void)printf("─"); // Horizontal line for empty
280 }
281 }
282
283 // Print period in ticks (right-aligned)
284 (void)printf(" %4" PRIu32 " / %4" PRIu32 " ticks", elapsed, period);
285
286 // Clear to end of line to avoid leftover characters
287 (void)printf("\033[K");
288}
289
306void display_render_banner(uint8_t row, const char* task_name, bool is_on) {
307 // Move cursor to start of this task's row
308 ANSI_GOTO(row, 1);
309
310 if (is_on) {
311 // Show banner when LED is on
312 (void)printf("[%s] ", task_name);
313 // Print a visual indicator (filled blocks)
314 for (uint32_t i = 0u; i < ((uint32_t)BAR_WIDTH - 20u); i++) {
315 (void)printf("★");
316 }
317 (void)printf(" [%s]", task_name);
318 // Clear to end of line to remove any leftover characters
319 (void)printf("\033[K");
320 } else {
321 // Clear the entire line when LED is off
322 (void)printf("\033[K"); // Clear from cursor to end of line
323 }
324}
325
352void display_render_vbar(uint8_t start_row, uint8_t col, uint32_t count, uint32_t max_count) {
353 uint32_t mc = max_count;
354 if (mc == 0u) {
355 mc = 1u;
356 }
357 uint32_t cnt = count;
358 if (cnt > mc) {
359 cnt = mc;
360 }
361
362 uint32_t filled_rows = (cnt * (uint32_t)VBAR_HEIGHT) / mc;
363 if (filled_rows > (uint32_t)VBAR_HEIGHT) {
364 filled_rows = (uint32_t)VBAR_HEIGHT;
365 }
366
367 // Label
368 {
369 int label_row = (int)start_row - 1;
370 ANSI_GOTO(label_row, col);
371 }
372 (void)printf(ANSI_CYAN "SEM" ANSI_RESET);
373
374 // Top border
375 ANSI_GOTO(start_row, col);
376 (void)printf("+---+");
377
378 // Bar rows (fill from bottom)
379 for (uint8_t i = 0u; i < (uint8_t)VBAR_HEIGHT; i++) {
380 int bar_row = (int)start_row + 1 + (int)i;
381 ANSI_GOTO(bar_row, col);
382 uint8_t row_from_bottom = (uint8_t)((uint8_t)VBAR_HEIGHT - 1u) - i;
383 if ((uint32_t)row_from_bottom < filled_rows) {
384 (void)printf(ANSI_GREEN "|###|" ANSI_RESET);
385 } else {
386 (void)printf("| |");
387 }
388 }
389
390 // Bottom border
391 {
392 int bottom_row = (int)start_row + (int)VBAR_HEIGHT + 1;
393 ANSI_GOTO(bottom_row, col);
394 }
395 (void)printf("+---+");
396
397 // Count label
398 {
399 int count_row = (int)start_row + (int)VBAR_HEIGHT + 2;
400 ANSI_GOTO(count_row, col);
401 }
402 (void)printf("%2" PRIu32 "/%2" PRIu32, cnt, mc);
403}
404
431void display_render_pipe(uint8_t start_row, uint8_t col, const char* label,
432 uint8_t count, uint8_t max_count,
433 uint8_t last_sent, uint8_t last_recv,
434 bool show_sent, bool show_recv) {
435 // Guard against division by zero
436 uint8_t mc = max_count;
437 if (mc == 0u) {
438 mc = 1u;
439 }
440 uint8_t cnt = count;
441 if (cnt > mc) {
442 cnt = mc;
443 }
444
445 // Calculate fill percentage for horizontal bar
446 uint8_t bar_width = 8u;
447 uint8_t filled = (uint8_t)((uint8_t)(cnt * bar_width) / mc);
448
449 // Draw label
450 ANSI_GOTO(start_row, col);
451 (void)printf("\033[36m%s\033[0m", label); // Cyan label
452
453 // Draw queue visualization: [████────]
454 {
455 int q_row = (int)start_row + 1;
456 ANSI_GOTO(q_row, col);
457 }
458 (void)printf("[");
459 for (uint8_t i = 0u; i < bar_width; i++) {
460 if (i < filled) {
461 (void)printf("\033[33m█\033[0m"); // Yellow filled
462 } else {
463 (void)printf("─");
464 }
465 }
466 (void)printf("]");
467
468 // Draw count
469 {
470 int cnt_row = (int)start_row + 2;
471 ANSI_GOTO(cnt_row, col);
472 }
473 (void)printf("%2d/%2d", cnt, mc);
474
475 // Draw sent indicator with arrow animation
476 {
477 int sent_row = (int)start_row + 3;
478 ANSI_GOTO(sent_row, col);
479 }
480 if (show_sent) {
481 (void)printf("\033[32m→%3d\033[0m", last_sent); // Green arrow + value
482 } else {
483 (void)printf(" ");
484 }
485
486 // Draw received indicator
487 {
488 int recv_row = (int)start_row + 4;
489 ANSI_GOTO(recv_row, col);
490 }
491 if (show_recv) {
492 (void)printf("\033[35m←%3d\033[0m", last_recv); // Magenta arrow + value
493 } else {
494 (void)printf(" ");
495 }
496}
497
516void display_render_producer(uint8_t row, const char* task_name,
517 uint32_t elapsed_ticks, uint32_t period_ticks,
518 uint8_t msg_value, bool show_msg) {
519 // Guard against division by zero
520 uint32_t period = period_ticks;
521 if (period == 0u) {
522 period = 1u;
523 }
524 uint32_t elapsed = elapsed_ticks;
525 if (elapsed > period) {
526 elapsed = period;
527 }
528
529 uint32_t filled = (elapsed * (uint32_t)BAR_WIDTH) / period;
530 if (filled > (uint32_t)BAR_WIDTH) {
531 filled = (uint32_t)BAR_WIDTH;
532 }
533
534 ANSI_GOTO(row, 1);
535
536 // Task name with producer color (green)
537 (void)printf("\033[32m[%s]\033[0m ", (task_name != NULL) ? task_name : "unknown");
538
539 // Progress bar
540 for (uint32_t i = 0u; i < (uint32_t)BAR_WIDTH; i++) {
541 if (i < filled) {
542 (void)printf("\033[32m█\033[0m"); // Green filled
543 } else {
544 (void)printf("─");
545 }
546 }
547
548 // Timing info
549 (void)printf(" %4" PRIu32 "/%4" PRIu32, elapsed, period);
550
551 // Message indicator with animation
552 if (show_msg) {
553 // Flash effect: show arrow and value
554 (void)printf(" \033[32;1m→[%3d]\033[0m", msg_value);
555 } else {
556 (void)printf(" ");
557 }
558
559 (void)printf("\033[K");
560}
561
580void display_render_consumer(uint8_t row, const char* task_name,
581 uint32_t elapsed_ticks, uint32_t period_ticks,
582 uint8_t msg_value, bool show_msg) {
583 // Guard against division by zero
584 uint32_t period = period_ticks;
585 if (period == 0u) {
586 period = 1u;
587 }
588 uint32_t elapsed = elapsed_ticks;
589 if (elapsed > period) {
590 elapsed = period;
591 }
592
593 uint32_t filled = (elapsed * (uint32_t)BAR_WIDTH) / period;
594 if (filled > (uint32_t)BAR_WIDTH) {
595 filled = (uint32_t)BAR_WIDTH;
596 }
597
598 ANSI_GOTO(row, 1);
599
600 // Task name with consumer color (magenta)
601 (void)printf("\033[35m[%s]\033[0m ", (task_name != NULL) ? task_name : "unknown");
602
603 // Progress bar
604 for (uint32_t i = 0u; i < (uint32_t)BAR_WIDTH; i++) {
605 if (i < filled) {
606 (void)printf("\033[35m█\033[0m"); // Magenta filled
607 } else {
608 (void)printf("─");
609 }
610 }
611
612 // Timing info
613 (void)printf(" %4" PRIu32 "/%4" PRIu32, elapsed, period);
614
615 // Message indicator with animation
616 if (show_msg) {
617 // Flash effect: show arrow and value
618 (void)printf(" \033[35;1m←[%3d]\033[0m", msg_value);
619 } else {
620 (void)printf(" ");
621 }
622
623 (void)printf("\033[K");
624}
625
654void display_init(void) {
655 static uint8_t initialized = 0u;
656 if (initialized != 0u) {
657 return;
658 }
659 initialized = 1u;
660
661 // Clear screen and move to top
662 (void)printf("\033[2J"); // Clear entire screen
663 ANSI_HIDE_CURSOR(); // Hide cursor
665
666 // // Print header
667 // printf("ICARUS OS v0.0.1 — Preemptive Kernel Scheduler Demo - ARMv7E-M");
668 // ANSI_GOTO(ROW_SEPARATOR, 1);
669 // printf("************************************************\n");
670
671 // ─────────────────────────────────────────────
672 // ICARUS GUI-style header with CORRECT ASCII logo
673 // ─────────────────────────────────────────────
674
675 // Top border
677 (void)printf("\033[2K");
678 (void)printf("┌──────────────────────────────────────────────────────────────┐");
679
680 // ICARUS logo line 1
681 ANSI_GOTO(ROW_HEADER + 1, 1);
682 (void)printf("\033[2K");
683 (void)printf("│ ██╗ ██████╗ █████╗ ██████╗ ██╗ ██╗ ██████╗ │");
684
685 // ICARUS logo line 2
686 ANSI_GOTO(ROW_HEADER + 2, 1);
687 (void)printf("\033[2K");
688 (void)printf("│ ██║██╔════╝ ██╔══██╗██╔══██╗██║ ██║██╔════╝ │");
689
690 // ICARUS logo line 3
691 ANSI_GOTO(ROW_HEADER + 3, 1);
692 (void)printf("\033[2K");
693 (void)printf("│ ██║██║ ███████║██████╔╝██║ ██║╚█████╗ │");
694
695 // ICARUS logo line 4
696 ANSI_GOTO(ROW_HEADER + 4, 1);
697 (void)printf("\033[2K");
698 (void)printf("│ ██║██║ ██╔══██║██╔══██╗██║ ██║ ╚═══██╗ │");
699
700 // ICARUS logo line 5
701 ANSI_GOTO(ROW_HEADER + 5, 1);
702 (void)printf("\033[2K");
703 (void)printf("│ ██║╚██████╗ ██║ ██║██║ ██║╚██████╔╝██████╔╝ │");
704
705 // ICARUS logo line 6
706 ANSI_GOTO(ROW_HEADER + 6, 1);
707 (void)printf("\033[2K");
708 (void)printf("│ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ │");
709
710 // Subtitle / metadata
711 ANSI_GOTO(ROW_HEADER + 7, 1);
712 (void)printf("\033[2K");
713 (void)printf("│ Preemptive Kernel • ARMv7E-M • STM32H750 │");
714
715 // Bottom border
717 (void)printf("\033[2K");
718 (void)printf("└──────────────────────────────────────────────────────────────┘");
719
720 // TEST: Simple message after header
721 (void)printf("\r\nDTCM Protection Active - System Running\r\n");
722 return; // TEMPORARY: Skip task iteration to isolate issue
723
724
725 // Initialize heartbeat row (if enabled)
726#if ENABLE_HEARTBEAT_VISUALIZATION
727 display_render_banner(ROW_HEARTBEAT, "heartbeat", false); // Start with banner off
728#endif
729
730 // Initialize task rows with empty bars using task names from TCB
731 // Note: This assumes tasks are registered in order and we skip system tasks
732 // System tasks are registered first (ICARUS_KEEPALIVE_TASK, ICARUS_HEARTBEAT_TASK)
733 // User tasks start from index 2
734
735 uint8_t num_tasks = os_get_num_created_tasks();
736 uint8_t user_task_count = 0u;
737
738 for (uint8_t i = 0u; (i < num_tasks) && (user_task_count < 3u); i++) {
739 // Get task name via SVC (safe with DTCM priv-only)
740 const char* name = os_get_task_name(i);
741
742 if ((name != NULL) && (name[0] != '\0')) {
743 // Skip system tasks (they start with "ICARUS_")
744 if (strncmp(name, "ICARUS_", 7) != 0) {
745 // This is a user task
746 uint8_t disp_row = (uint8_t)((uint8_t)ROW_TASK_A + user_task_count);
747 uint32_t period = (uint32_t)TASK_A_PERIOD_TICKS;
748 if (user_task_count == 1u) {
749 period = (uint32_t)TASK_B_PERIOD_TICKS;
750 } else if (user_task_count == 2u) {
751 period = (uint32_t)TASK_C_PERIOD_TICKS;
752 } else {
753 /* keep default */
754 }
755
756 display_render_bar(disp_row, name, 0u, period);
757 user_task_count++;
758 }
759 }
760 }
761
762 // Move cursor below display area to avoid interference
763#if ENABLE_HEARTBEAT_VISUALIZATION
764 ANSI_GOTO(ROW_TASK_C + 2, 1);
765#else
766 ANSI_GOTO(ROW_TASK_C + 2, 1); // Same position since rows shift when heartbeat is disabled
767#endif
768}
void display_render_pipe(uint8_t start_row, uint8_t col, const char *label, uint8_t count, uint8_t max_count, uint8_t last_sent, uint8_t last_recv, bool show_sent, bool show_recv)
Render a message queue (pipe) visualization panel.
Definition display.c:431
void display_render_msg_history(uint8_t row, uint8_t col, msg_history_t *hist, const char *label)
Render message history as a rolling window display.
Definition display.c:174
void msg_history_add(msg_history_t *hist, const uint8_t *data, uint8_t len, uint8_t source_id, bool is_send)
Add a message to the history buffer.
Definition display.c:127
void display_render_banner(uint8_t row, const char *task_name, bool is_on)
Render a flashing banner indicator (heartbeat display)
Definition display.c:306
void display_init(void)
Initialize terminal display subsystem.
Definition display.c:654
void msg_history_init(msg_history_t *hist)
Initialize a message history buffer.
Definition display.c:104
void display_render_bar(uint8_t row, const char *task_name, uint32_t elapsed_ticks, uint32_t period_ticks)
Render a horizontal progress bar for task visualization.
Definition display.c:247
void display_render_vbar(uint8_t start_row, uint8_t col, uint32_t count, uint32_t max_count)
Render a vertical bar showing semaphore fill level.
Definition display.c:352
void display_render_producer(uint8_t row, const char *task_name, uint32_t elapsed_ticks, uint32_t period_ticks, uint8_t msg_value, bool show_msg)
Render a producer task progress bar with message indicator.
Definition display.c:516
void display_render_consumer(uint8_t row, const char *task_name, uint32_t elapsed_ticks, uint32_t period_ticks, uint8_t msg_value, bool show_msg)
Render a consumer task progress bar with message indicator.
Definition display.c:580
#define VBAR_HEIGHT
Definition display.h:43
#define ROW_TASK_C
Definition display.h:32
#define TASK_A_PERIOD_TICKS
Definition display.h:50
#define ROW_SEPARATOR
Definition display.h:27
#define TASK_C_PERIOD_TICKS
Definition display.h:52
#define TASK_B_PERIOD_TICKS
Definition display.h:51
#define MSG_HISTORY_LEN
Definition display.h:134
#define ROW_HEARTBEAT
Definition display.h:29
#define BAR_WIDTH
Definition display.h:39
#define MSG_HISTORY_MAX_BYTES
Definition display.h:135
#define ROW_TASK_A
Definition display.h:30
#define ROW_HEADER
Definition display.h:26
#define ANSI_HIDE_CURSOR()
Hide terminal cursor.
Definition display.c:55
#define ANSI_BOLD
Bold text attribute.
Definition display.c:61
#define ANSI_MAGENTA
Magenta foreground color.
Definition display.c:73
#define ANSI_CYAN
Cyan foreground color.
Definition display.c:76
#define ANSI_GREEN
Green foreground color.
Definition display.c:67
#define ANSI_GOTO(row, col)
Move cursor to specified row and column (1-indexed)
Definition display.c:49
#define ANSI_RESET
Reset all text attributes.
Definition display.c:58
ICARUS Kernel Core - State and Initialization.
ICARUS Scheduler - Task Scheduling and Timing.
const char * os_get_task_name(uint8_t task_idx)
Get task name by index (safe from unprivileged mode)
Definition svc.c:1123
uint8_t os_get_num_created_tasks(void)
Get number of created tasks (safe from unprivileged mode)
Definition svc.c:1145
Message history entry for rolling window display.
Definition display.h:140
uint8_t data[4]
Definition display.h:141
bool is_send
Definition display.h:144
uint8_t len
Definition display.h:142
uint8_t source_id
Definition display.h:143
Message history buffer for a pipe.
Definition display.h:150
uint8_t count
Definition display.h:153
msg_history_entry_t entries[8]
Definition display.h:151
uint8_t head
Definition display.h:152