{ "$schema": "../../../tools/card-layouts/schemas/card-source.schema.json", "schema": "esc-card-source.v1", "card": { "id": "mpabi-freertos-c-03-scheduler", "series": "freertos-c", "series_title": "FreeRTOS C", "number": "03", "count": "15", "slug": "tick-priority-preemption-timeslicing", "title": "Tick, priorytety, wywłaszczenie i time slicing", "topic": "Scheduler FreeRTOS obserwowany w C i Hazard3", "project": "Freestanding C nad FreeRTOS", "subject": "Informatyka", "level": "Rok 2 · L03 · RV32I/Hazard3", "revision_date": "2026-07-18T00:00:00+02:00", "status": "Gotowa", "version": "v00.01", "uuid": "c4bf7933-9a39-4968-b40e-158bafbb3eea", "author": "M. Pabiszczak", "year": "2026" }, "generated": { "tex": "doc/generated/main.tex", "html": "web/index.html", "html_css": "web/style.css", "html_tree_inspector": false, "react_app": true }, "render_dictionary": false, "viewpoints": [ {"id": "A1", "label": "CONTEXT", "subtitle": "Timer → tick ISR → scheduler → taski", "status": "enabled", "target_label": "fig:a1-context"}, {"id": "A2", "label": "STRUCTURE", "subtitle": "Konteksty i ograniczone rekordy dowodowe", "status": "enabled", "target_label": "fig:a2-structure"}, {"id": "A3", "label": "DISPATCH", "subtitle": "Callback i context", "status": "unavailable", "reason": "Mechanizm TaskFunction_t i void* został udowodniony w FC02 i w FC03 nie ulega zmianie."}, {"id": "A4", "label": "APPLICATION", "subtitle": "Cztery taski i kontrakt priorytetów", "status": "enabled", "target_label": "fig:a4-application"}, {"id": "A5", "label": "FLOW", "subtitle": "Deterministyczny przebieg E01–E12", "status": "enabled", "target_label": "fig:a5-flow-a"}, {"id": "A6", "label": "STATE", "subtitle": "Ready, Running, Deleted i zmiana priorytetu", "status": "enabled", "target_label": "fig:a6-state"}, {"id": "A7", "label": "RUNTIME", "subtitle": "Tick, CSRs, TCB, stosy i ślady", "status": "enabled", "target_label": "fig:a7-runtime"}, {"id": "A8", "label": "PATTERNS", "subtitle": "Wzorce aplikacyjne", "status": "unavailable", "reason": "Polityka schedulera jest konfiguracją kernela, a nie wzorcem projektowym aplikacji."} ], "debug_strategies": { "code.tick-boundary": { "id": "code.tick-boundary", "kind": "code", "action": "open", "prerequisites": ["repozytorium FC03", "zweryfikowany source blob"], "layout": ["read-only source", "granica timer/port/kernel/aplikacja"], "commands": ["rg -n 'configTICK_RATE_HZ|vApplicationTickHook|xTaskIncrementTick' include src vendor/FreeRTOS-Kernel"], "expected_observations": ["tick jest okazją do planowania", "aplikacja zapisuje tylko ograniczony rekord"], "assertions": ["tick hook nie drukuje i nie blokuje", "aplikacja nie zależy od pxCurrentTCB"], "evidence_fields": ["code_ref", "source_git_blob", "kotwica UML"] }, "code.scheduler-data": { "id": "code.scheduler-data", "kind": "code", "action": "open", "prerequisites": ["freestanding C11", "include/task01_scheduler.h"], "layout": ["konteksty C", "priorytety", "bufory śladów"], "commands": ["rg -n 'PeerContext|HighProbeContext|g_switch_task|g_preemption_order' include src"], "expected_observations": ["taski mają jawne konteksty", "rekordy mają stałą pojemność"], "assertions": ["high ma kontrakt 0→3", "verifier ma priorytet 1"], "evidence_fields": ["layout pól", "stałe priorytetów", "pojemność śladu"] }, "run.prepare": { "id": "run.prepare", "kind": "run", "action": "replay", "prerequisites": ["Hazard3 online", "czysta RAM", "aktualny ELF"], "layout": ["source", "konteksty", "uchwyty", "priorytety"], "commands": ["print g_high_probe", "print g_peer_a", "print g_peer_b", "print g_verifier"], "expected_observations": ["high jest Ready przy priorytecie 0", "peery mają priorytet 2, verifier 1"], "assertions": ["wszystkie uchwyty są ważne przed startem schedulera", "publiczne API zwraca oczekiwane priorytety"], "evidence_fields": ["event_id", "handle", "priority", "timestamp"] }, "run.tick": { "id": "run.tick", "kind": "run", "action": "replay", "prerequisites": ["scheduler uruchomiony", "tick hook włączony"], "layout": ["tick hook", "CSR", "ISR stack", "przerwany task"], "commands": ["print g_tick_hook_count", "print/x g_first_tick_mcause", "print/x g_first_tick_mepc", "print/x g_first_tick_sp"], "expected_observations": ["mcause oznacza machine-timer interrupt", "mepc wskazuje przerwany kod"], "assertions": ["checkpoint ticka jest jednorazowy", "sink ISR nie korzysta z task-context API"], "evidence_fields": ["tick", "mcause", "mepc", "ISR SP", "timestamp"] }, "run.timeslice": { "id": "run.timeslice", "kind": "run", "action": "replay", "prerequisites": ["dwa CPU-bound taski priority=2", "time slicing włączony"], "layout": ["pętle peerów", "task-change trace", "ticki"], "commands": ["print g_switch_task", "print g_switch_tick", "print g_aba_task", "print g_peer_changes"], "expected_observations": ["zatwierdzony ślad zawiera A→B→A", "ticki są niemalejące"], "assertions": ["zapisywana jest jedna pozycja na obserwowaną zmianę taska", "dowód nie zakłada równego udziału CPU"], "evidence_fields": ["triplet A/B/A", "ticks", "change count", "timestamp"] }, "run.preempt": { "id": "run.preempt", "kind": "run", "action": "replay", "prerequisites": ["A→B→A wykryte", "high Ready przy priorytecie 0"], "layout": ["call site vTaskPrioritySet", "high task", "markery kolejności"], "commands": ["print g_preemption_order", "print g_high_probe", "info registers pc sp a0 a1"], "expected_observations": ["high uruchamia się przy priorytecie 3", "caller nie zapisuje after przed HIGH"], "assertions": ["projekcja markerów to before/HIGH/after", "verifier jeszcze nie działa"], "evidence_fields": ["marker projection", "priority", "PC", "SP", "timestamp"] }, "run.state": { "id": "run.state", "kind": "run", "action": "replay", "prerequisites": ["kanoniczny checkpoint FC03"], "layout": ["kontekst aplikacyjny", "publiczny stan taska", "source"], "commands": ["print g_peer_a.phase", "print g_peer_b.phase", "print g_high_probe.phase", "print g_peers_finished"], "expected_observations": ["Ready nie oznacza Running", "każdy task czyści własny publiczny handle"], "assertions": ["high i peery kończą się przed verifierem", "brak suspend/resume w eksperymencie"], "evidence_fields": ["phase", "handle", "event_id", "PC"] }, "run.runtime": { "id": "run.runtime", "kind": "run", "action": "replay", "prerequisites": ["Hazard3/GDB online", "symbole ELF"], "layout": ["timer/CSR", "handle/TCB", "task stack", "committed evidence"], "commands": ["print g_peer_a.created_handle", "print g_peer_a.tcb_address", "print g_peer_a.stack_low", "print g_peer_a.stack_high", "info registers sp pc"], "expected_observations": ["handle/TCB i stack są rozdzielone", "SP należy do stosu wybranego taska"], "assertions": ["created_handle == tcb_address", "źródło, ELF i obraz są identyfikowalne"], "evidence_fields": ["handle/TCB", "stack range", "SP", "PC", "hash"] }, "run.pass": { "id": "run.pass", "kind": "run", "action": "replay", "prerequisites": ["high i oba peery zakończone", "verifier wybrany"], "layout": ["konteksty", "ślady", "końcowe asercje"], "commands": ["print g_scheduler_pass", "print g_aba_task", "print g_preemption_order", "print g_tick_hook_count"], "expected_observations": ["A/B/A i before/HIGH/after są obecne", "program kończy się PASS"], "assertions": ["PASS=1", "g_peers_finished=2", "tick hook count>=2"], "evidence_fields": ["PASS", "traces", "ticks", "source blob", "image hash", "timestamp"] } }, "template": "templates/karta-klasyczna.json", "title_block": { "category": "KARTA PRACY · INFORMATYKA", "prepared_by": "M. Pabiszczak", "prepared_on": "2026-07-18T00:00:00+02:00", "title": "Tick, priorytety, wywłaszczenie i time slicing", "url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2214a13f-34c6-40a1-a561-70a00ec98285", "repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-scheduler", "url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84", "url_domain": "mpabi.pl", "doc_uuid": "2214a13f-34c6-40a1-a561-70a00ec98285", "revision": "v00.01", "issued_on": "2026-07-18T00:00:00+02:00", "series": "FREERTOS-C-03", "document_type": "karta pracy", "tool": "card-layouts", "show_qr": true, "show_repository_qr": true, "height_cm": 2.6, "repeat_on_every_page": true, "replace_front_matter": true }, "front_page_scope": { "title": "Cel karty", "content_tex": "Uczeń rozróżnia wybór najwyższego priorytetu od time slicing między równymi priorytetami. Odtwarza tick oraz natychmiastowe wywłaszczenie po zmianie priorytetu i wiąże ślad aplikacji ze stanem Hazard3/GDB.", "scope_title": "Zakres karty", "scope_content_tex": "Dwa CPU-bound taski o priorytecie 2 tworzą ślad A-B-A. Peer A podnosi Ready task z priorytetu 0 do 3; HIGH musi wykonać się przed powrotem vTaskPrioritySet. Verifier o priorytecie 1 publikuje PASS dopiero po zakończeniu wyższych tasków. Poza zakresem są delay, kolejki, semafory, ISR-safe API i C++.", "scope_table": { "headers": ["Lekcja", "Task", "Najważniejsza idea", "Priorytet", "Status", "Version"], "rows": [{"chapter": "FC03", "task": "Task01", "idea_tex": "Tick, priority selection, preemption i time slicing", "priority": "główny", "status": "ready", "version": "v00.01", "key": true}] } }, "side_margin_tree_layout": { "columns": [ {"id": "zawodowe", "label": "TECH", "side": "left", "tree": "WE -> EK -> KW", "description": "Dowody schedulera w Hazard3."}, {"id": "ogolne", "label": "OG", "side": "right", "tree": "WE -> EN -> KW", "description": "Model czasu i priorytetów."} ] }, "learning_effects": { "FC03.EN01": {"bloom_level": "Analiza", "label": "Model schedulera", "text": "Uczeń odróżnia priorytet, Ready/Running, tick i time slicing.", "assessment_criteria": ["FC03.KW01"]}, "FC03.EK01": {"bloom_level": "Zastosowanie", "label": "Replay wywłaszczenia", "text": "Uczeń odtwarza E01–E12 i dowodzi A-B-A oraz before-HIGH-after.", "assessment_criteria": ["FC03.KW01"]} }, "assessment_criteria": { "FC03.KW01": {"text": "Program kończy się PASS; ślad zawiera A-B-A, markery before-HIGH-after, poprawny machine-timer mcause i zgodne zakresy stosów.", "learning_effects": ["FC03.EN01", "FC03.EK01"]} }, "educational_requirements": { "FC03.WE01": { "text": "Zaprojektowanie i zbadanie kontrolowanego eksperymentu schedulera FreeRTOS w C.", "label": "FreeRTOS scheduler experiment", "learning_effects": ["FC03.EN01", "FC03.EK01"], "learning_tree": { "schema": "we-learning-tree.v1", "policy": "Każda aktywna kotwica UML wskazuje kod lub zweryfikowany checkpoint.", "ogolne": [{ "effect_ref": "FC03.EN01", "display": "EN LOCAL RTOS.03", "source": "LOCAL", "official": "RTOS", "local": "03", "kind": "EN", "tree_id": "FC03.WE01.OG.LOCAL.RTOS.03", "text": "Analizuje reguły wyboru i stany tasków.", "kw": [{"criterion_ref": "FC03.KW01", "display": "KW LOCAL RTOS.03", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "03", "text": "Rozdziela priorytet od time slicing."}] }], "zawodowe": [{ "effect_ref": "FC03.EK01", "display": "EK LOCAL DBG.03", "source": "LOCAL", "official": "DBG", "local": "03", "kind": "EK", "tree_id": "FC03.WE01.TECH.LOCAL.DBG.03", "text": "Odtwarza scheduler w Hazard3/GDB.", "kw": [{"criterion_ref": "FC03.KW01", "display": "KW LOCAL DBG.03", "source": "LOCAL", "kind": "KW", "official": "DBG", "local": "03", "text": "Łączy checkpoint, UML, rejestry i pamięć."}] }] } } }, "debug_checkpoints": { "schema": "stem-debug-checkpoints.v1", "semantics": "deterministic-replay", "artifact": { "source": "src/tasks/task01_scheduler.c", "source_git_blob": "34b18bfc69ac51f2ead2cd38ea02f2a5005ac802", "elf": "build/task01_scheduler/prog.elf", "hazard3_elf_sha256": "47a2f8aa02953de388a52d03cba99b25e750309f11d46894ee8a31872a285496", "hazard3_image": "build/task01_scheduler/prog.bin", "hazard3_image_sha256": "59b2cc89a674f6b04896c269a0d25f75a4e7759ad6c236887b0a49843ed2cda6" }, "targets": { "hazard3-sim": {"adapter": "hazard3-reset-load-replay.v1", "baseline": "restart-backend", "clean_ram": true} }, "items": { "task01.config": { "event_id": "E01", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 1"}, "verify": {"expressions": [{"expr": "g_last_checkpoint", "equals": 1}, {"expr": "g_high_probe.handle == 0", "equals": 1}]} }, "task01.high-dormant": { "event_id": "E02", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 2 && $a1 == &g_high_probe"}, "verify": {"expressions": [{"expr": "g_high_probe.handle != 0", "equals": 1}, {"expr": "g_high_probe.phase", "equals": 1}]} }, "task01.priorities": { "event_id": "E03", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 3 && $a1 == &g_high_probe"}, "verify": {"expressions": [{"expr": "g_high_probe.observed_initial_priority", "equals": 0}, {"expr": "g_peer_a.observed_priority", "equals": 2}, {"expr": "g_peer_b.observed_priority", "equals": 2}, {"expr": "g_verifier.observed_priority", "equals": 1}]} }, "task01.scheduler": { "event_id": "E04", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 4"}, "verify": {"expressions": [{"expr": "g_peer_a.handle != 0", "equals": 1}, {"expr": "g_peer_b.handle != 0", "equals": 1}, {"expr": "g_high_probe.handle != 0", "equals": 1}, {"expr": "g_verifier.handle != 0", "equals": 1}]} }, "task01.peer-entry": { "event_id": "E05", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 5"}, "verify": {"expressions": [{"expr": "g_peer_a.iterations + g_peer_b.iterations >= 0", "equals": 1}]} }, "task01.first-tick": { "event_id": "E06", "stop": {"symbol": "task01_tick_checkpoint_committed", "offset": 0, "condition": "g_tick_hook_count == 1"}, "verify": {"expressions": [{"expr": "g_tick_hook_count", "equals": 1}, {"expr": "g_first_tick_mcause", "equals": 2147483655}, {"expr": "g_first_tick_sp != 0", "equals": 1}, {"expr": "g_first_tick_mepc != 0", "equals": 1}]} }, "task01.timeslice": { "event_id": "E07", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 7 && $a1 == &g_peer_a"}, "verify": {"expressions": [{"expr": "g_aba_seen", "equals": 1}, {"expr": "g_aba_task[0]", "equals": 0}, {"expr": "g_aba_task[1]", "equals": 1}, {"expr": "g_aba_task[2]", "equals": 0}, {"expr": "g_peer_changes >= 2", "equals": 1}]} }, "task01.before-raise": { "event_id": "E08", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 8 && $a1 == &g_peer_a"}, "verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 1}, {"expr": "g_preemption_order[0]", "equals": 1}, {"expr": "g_peer_after_raise", "equals": 0}]} }, "task01.high-preempts": { "event_id": "E09", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 9 && $a1 == &g_high_probe"}, "verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 2}, {"expr": "g_preemption_order[1]", "equals": 2}, {"expr": "g_high_probe.observed_priority", "equals": 3}, {"expr": "g_peer_after_raise", "equals": 0}]} }, "task01.after-raise": { "event_id": "E10", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 10 && $a1 == &g_peer_a"}, "verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 3}, {"expr": "g_preemption_order[2]", "equals": 3}, {"expr": "g_stop_peers", "equals": 1}]} }, "task01.peers-finished": { "event_id": "E11", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 11"}, "verify": {"expressions": [{"expr": "g_peers_finished", "equals": 2}, {"expr": "g_peer_a.handle == 0", "equals": 1}, {"expr": "g_peer_b.handle == 0", "equals": 1}]} }, "task01.pass": { "event_id": "E12", "stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 12 && $a1 == &g_verifier"}, "verify": {"expressions": [{"expr": "g_scheduler_pass", "equals": 1}, {"expr": "g_peers_finished", "equals": 2}, {"expr": "g_aba_seen", "equals": 1}, {"expr": "g_preemption_order_count", "equals": 3}, {"expr": "g_tick_hook_count >= 2", "equals": 1}]} } } }, "sections": [ { "title": "A1 — Context", "order": 10, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "portrait", "content_tex": "Timer Hazard3 wywołuje port RV32I, kernel podejmuje decyzję planistyczną, a aplikacja zapisuje ograniczony dowód.", "assets": [{ "path": "assets/a1-context.png", "html_path": "assets/a1-context.svg", "source_path": "assets/a1-context.puml", "caption": "A1 CONTEXT — timer, port, scheduler i taski aplikacji.", "label": "fig:a1-context", "alt": "Pionowy kontekst ticka i schedulera FreeRTOS na Hazard3.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": {"timer": 1, "port": 1, "scheduler": 1, "application": 1}}, "interactive": { "kind": "uml-class", "storage_key": "fc03-a1-context", "title": "A1 · granice ticka i schedulera", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a1-context", "label": "A1 CONTEXT", "description": "Kierunek wykonania i odpowiedzialności."}, "phases": [{"id": "tick-boundary", "label": "TICK / SCHEDULER", "steps": [ {"id": "timer", "number": 1, "label": "mtime reaches mtimecmp", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "01", "svg_target": "Timer", "code_ref": "include/FreeRTOSConfig.h:4", "description": "Hazard3 generuje machine-timer interrupt.", "evidence": "Adresy mtime i mtimecmp pochodzą z konfiguracji portu."}, {"id": "port", "number": 2, "label": "RV32I port handles the tick", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "02", "svg_target": "Port", "code_ref": "vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S:1", "description": "Port przełącza domenę ISR i wchodzi do kernela.", "evidence": "Tick hook działa w kontekście przerwania."}, {"id": "scheduler", "number": 3, "label": "scheduler selects a Ready task", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "03", "svg_target": "Scheduler", "code_ref": "vendor/FreeRTOS-Kernel/tasks.c:1", "description": "Priorytet wybiera klasę, time slicing rotuje równych.", "evidence": "Reguły pozostają własnością kernela."}, {"id": "application", "number": 4, "label": "application records evidence", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "04", "svg_target": "Application", "code_ref": "src/tasks/task01_scheduler.c:116", "description": "Aplikacja zapisuje tylko bounded evidence.", "evidence": "Brak drukowania, alokacji i blokowania w tick hook."} ]}] } }] }, { "title": "A2 — Structure", "order": 20, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Jawne konteksty C przechowują publiczne uchwyty i pomiary; dwa ograniczone bufory dowodzą niezależnie time slicing oraz wywłaszczenia.", "assets": [{ "path": "assets/a2-structure.png", "html_path": "assets/a2-structure.svg", "source_path": "assets/a2-structure.puml", "caption": "A2 STRUCTURE — konteksty tasków i rekordy śladów.", "label": "fig:a2-structure", "alt": "Struktury peer, high probe, verifier i dwa rekordy dowodowe.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"peers": 1, "probe": 1, "verifier": 1, "switch-trace": 1, "order-trace": 1}}, "interactive": { "kind": "uml-class", "storage_key": "fc03-a2-structure", "title": "A2 · dane eksperymentu", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a2-structure", "label": "A2 STRUCTURE", "description": "Jawne dane aplikacji, nie kopia prywatnych struktur kernela."}, "phases": [{"id": "contexts-traces", "label": "CONTEXTS / TRACES", "steps": [ {"id": "peers", "number": 1, "label": "two equal-priority peer contexts", "mode": "CODE", "strategy_ref": "code.scheduler-data", "svg_label": "01", "svg_target": "Peers", "code_ref": "include/task01_scheduler.h:23", "description": "Peery dzielą typ, ale mają osobne context, handle i stosy.", "evidence": "configured_priority=2 dla obu."}, {"id": "probe", "number": 2, "label": "high probe priority contract 0 to 3", "mode": "CODE", "strategy_ref": "code.scheduler-data", "svg_label": "02", "svg_target": "Probe", "code_ref": "include/task01_scheduler.h:43", "description": "Probe jest Ready, lecz początkowo poniżej verifiera.", "evidence": "initial_priority=0, target_priority=3."}, {"id": "verifier", "number": 3, "label": "verifier is deliberately lowest runnable task", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "03", "svg_target": "Verify", "code_ref": "src/tasks/task01_scheduler.c:519", "snapshot_ref": "task01.priorities", "description": "Verifier ma priorytet 1.", "evidence": "Nie może opublikować PASS przed zakończeniem peerów."}, {"id": "switch-trace", "number": 4, "label": "task-change trace stores id and tick", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "04", "svg_target": "SwitchTrace", "code_ref": "src/tasks/task01_scheduler.c:172", "snapshot_ref": "task01.timeslice", "description": "Powtórzenia w tej samej obserwowanej zmianie nie są logowane.", "evidence": "Zatwierdzony podciąg A/B/A."}, {"id": "order-trace", "number": 5, "label": "preemption markers are separate", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.preempt", "svg_label": "05", "svg_target": "OrderTrace", "code_ref": "src/tasks/task01_scheduler.c:305", "snapshot_ref": "task01.after-raise", "description": "Marker projection nie udaje pełnego task trace.", "evidence": "before/HIGH/after."} ]}] } }] }, { "title": "A4 — Application", "order": 30, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Cztery taski tworzą kontrolowany eksperyment: dwa równorzędne peery, probe podnoszony do priorytetu 3 i verifier uruchamiany na końcu.", "assets": [{ "path": "assets/a4-application.png", "html_path": "assets/a4-application.svg", "source_path": "assets/a4-application.puml", "caption": "A4 APPLICATION — topologia tasków i inwarianty priorytetów.", "label": "fig:a4-application", "alt": "Dwa peery, high probe i verifier w kontrolowanym eksperymencie.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"peer-a": 1, "peer-b": 1, "high": 1, "verifier": 1}}, "interactive": { "kind": "uml-class", "storage_key": "fc03-a4-application", "title": "A4 · topologia eksperymentu", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a4-application", "label": "A4 APPLICATION", "description": "Konkretny przypadek użycia schedulera."}, "phases": [{"id": "priority-topology", "label": "TASKS / PRIORITIES", "steps": [ {"id": "peer-a", "number": 1, "label": "peer A runs CPU-bound at priority 2", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.timeslice", "svg_label": "01", "svg_target": "PeerA", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Peer A rejestruje zmiany i inicjuje raise.", "evidence": "Własny context, TCB i stos."}, {"id": "peer-b", "number": 2, "label": "peer B competes at equal priority", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "02", "svg_target": "PeerB", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.timeslice", "description": "B jest konieczny do podciągu A/B/A.", "evidence": "Oba peery wykonują pracę."}, {"id": "high", "number": 3, "label": "peer A raises high probe to 3", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "03", "svg_target": "High", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "High preemptuje caller i self-delete.", "evidence": "Marker HIGH poprzedza after_raise."}, {"id": "verifier", "number": 4, "label": "verifier runs last at priority 1", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "04", "svg_target": "Verifier", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Verifier publikuje końcowy dowód.", "evidence": "PASS dopiero po self-delete tasków o priorytecie 2/3."} ]}] } }] }, { "title": "A5 — Flow", "order": 40, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "spread_group": "fc03-scheduler-flow", "spread_label": "FC03 · scheduler experiment", "spread_columns": 2, "content_tex": "E01--E12 tworzą jeden logiczny przebieg rozłożony na dwie poziome strony.", "assets": [ { "path": "assets/a5-flow-a.png", "html_path": "assets/a5-flow-a.svg", "source_path": "assets/a5-flow-a.puml", "caption": "A5 część 1/2 — konfiguracja, start, pierwszy tick i A-B-A.", "label": "fig:a5-flow-a", "alt": "Pierwsza połowa sekwencji eksperymentu schedulera.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"config": 1, "high-ready": 1, "priorities": 1, "scheduler": 1, "peer-entry": 1, "first-tick": 1, "timeslice": 1}}, "interactive": { "kind": "uml-sequence", "storage_key": "fc03-a5-flow-a", "title": "część 1/2 · przygotowanie i time slicing", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Jeden przebieg E01–E12 i wspólni uczestnicy."}, "phases": [{"id": "prepare-timeslice", "label": "PREPARE / TIME SLICE", "steps": [ {"id": "config", "number": 1, "label": "scheduler configuration committed", "mode": "RUN", "event_id": "E01", "strategy_ref": "run.prepare", "svg_label": "01", "code_ref": "src/tasks/task01_scheduler.c:486", "snapshot_ref": "task01.config", "description": "Eksperyment zaczyna się z czystą RAM.", "evidence": "One core, preemption, time slicing i tick hook są w kodzie konfiguracji."}, {"id": "high-ready", "number": 2, "label": "high probe Ready at priority 0", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.prepare", "svg_label": "02", "code_ref": "src/tasks/task01_scheduler.c:488", "snapshot_ref": "task01.high-dormant", "description": "High istnieje, lecz nie może jeszcze wyprzedzić verifiera.", "evidence": "handle != NULL, phase=READY."}, {"id": "priorities", "number": 3, "label": "public API confirms 0,2,2,1", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "03", "code_ref": "src/tasks/task01_scheduler.c:527", "snapshot_ref": "task01.priorities", "description": "Priorytety są odczytywane przez publiczne API.", "evidence": "high=0, A=2, B=2, verifier=1."}, {"id": "scheduler", "number": 4, "label": "vTaskStartScheduler", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.prepare", "svg_label": "04", "code_ref": "src/tasks/task01_scheduler.c:545", "snapshot_ref": "task01.scheduler", "description": "Ostatni checkpoint na startup stack.", "evidence": "Wszystkie handle są ważne."}, {"id": "peer-entry", "number": 5, "label": "first equal-priority peer runs", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.timeslice", "svg_label": "05", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Pierwszy peer przechodzi do Running.", "evidence": "SP należy do jego task stack."}, {"id": "first-tick", "number": 6, "label": "first machine-timer tick observed", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "06", "code_ref": "src/tasks/task01_scheduler.c:116", "snapshot_ref": "task01.first-tick", "description": "ISR-safe sink przechwytuje pierwszy tick.", "evidence": "mcause=0x80000007, mepc i ISR SP."}, {"id": "timeslice", "number": 7, "label": "trace contains A to B to A", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "07", "code_ref": "src/tasks/task01_scheduler.c:290", "snapshot_ref": "task01.timeslice", "description": "Checkpoint emituje peer A po wykryciu podciągu.", "evidence": "A/B/A i niemalejące ticki."} ]}] } }, { "path": "assets/a5-flow-b.png", "html_path": "assets/a5-flow-b.svg", "source_path": "assets/a5-flow-b.puml", "caption": "A5 część 2/2 — priority raise, natychmiastowe wywłaszczenie i PASS.", "label": "fig:a5-flow-b", "alt": "Druga połowa sekwencji eksperymentu schedulera.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"before-raise": 1, "high-preempts": 1, "after-raise": 1, "peers-finished": 1, "pass": 1}}, "interactive": { "kind": "uml-sequence", "storage_key": "fc03-a5-flow-b", "title": "część 2/2 · preemption i zakończenie", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Kontynuacja tych samych tasków i rekordów."}, "phases": [{"id": "preempt-finish", "label": "PREEMPT / FINISH", "steps": [ {"id": "before-raise", "number": 8, "label": "peer A commits before_raise", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.preempt", "svg_label": "08", "code_ref": "src/tasks/task01_scheduler.c:299", "snapshot_ref": "task01.before-raise", "description": "Pierwszy marker poprzedza vTaskPrioritySet.", "evidence": "order={before}, after flag=0."}, {"id": "high-preempts", "number": 9, "label": "high runs before call returns", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "09", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "Zmiana 0→3 czyni high najwyższym Ready taskiem.", "evidence": "order={before,HIGH}, caller after flag=0."}, {"id": "after-raise", "number": 10, "label": "peer A commits after_raise and stop", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.preempt", "svg_label": "10", "code_ref": "src/tasks/task01_scheduler.c:303", "snapshot_ref": "task01.after-raise", "description": "Caller wraca dopiero po self-delete high.", "evidence": "order={before,HIGH,after}, stop=1."}, {"id": "peers-finished", "number": 11, "label": "both peers clear handles and self-delete", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "11", "code_ref": "src/tasks/task01_scheduler.c:325", "snapshot_ref": "task01.peers-finished", "description": "Każdy peer kończy własny lifetime taska.", "evidence": "g_peers_finished=2, oba handle=NULL."}, {"id": "pass", "number": 12, "label": "low-priority verifier publishes PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "12", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Verifier sprawdza wszystkie niezależne dowody.", "evidence": "PASS=1 i poprawny exit code."} ]}] } } ] }, { "title": "A6 — State", "order": 50, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Priorytet wybiera najwyższy Ready task; time slicing dotyczy tylko równych priorytetów. Stan diagnostyczny aplikacji nie zastępuje stanu kernela.", "assets": [{ "path": "assets/a6-state.png", "html_path": "assets/a6-state.svg", "source_path": "assets/a6-state.puml", "caption": "A6 STATE — przejścia high probe, peerów i verifiera.", "label": "fig:a6-state", "alt": "Stany Ready, Running i Deleted oraz zmiana priorytetu high probe.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"high-low": 1, "high-ready": 1, "high-run": 1, "high-deleted": 1, "peers": 1, "peers-done": 1, "verify-ready": 1, "verify-run": 1}}, "interactive": { "kind": "uml-class", "storage_key": "fc03-a6-state", "title": "A6 · priorytet i stan", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a6-state", "label": "A6 STATE", "description": "Ready nie oznacza Running."}, "phases": [ {"id": "high-probe", "label": "HIGH PROBE", "steps": [ {"id": "high-low", "number": 1, "label": "high Ready at priority 0", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.state", "svg_label": "01", "svg_target": "HighLow", "code_ref": "src/tasks/task01_scheduler.c:497", "snapshot_ref": "task01.high-dormant", "description": "Task istnieje, ale nie jest wybrany.", "evidence": "phase=READY, priority=0."}, {"id": "high-ready", "number": 2, "label": "priority changes from 0 to 3", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.preempt", "svg_label": "02", "svg_target": "HighReady", "code_ref": "src/tasks/task01_scheduler.c:300", "snapshot_ref": "task01.before-raise", "description": "API zmienia pozycję taska w polityce wyboru.", "evidence": "Caller zapisał before marker."}, {"id": "high-run", "number": 3, "label": "high selected and Running", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "03", "svg_target": "HighRun", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "Najwyższy Ready task wywłaszcza caller.", "evidence": "priority=3, PC/SP należą do high."}, {"id": "high-deleted", "number": 4, "label": "high self-deletes", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.state", "svg_label": "04", "code_ref": "src/tasks/task01_scheduler.c:366", "snapshot_ref": "task01.after-raise", "description": "High czyści handle przed vTaskDelete(NULL).", "evidence": "Caller może wrócić i zapisać after."} ]}, {"id": "peers-verifier", "label": "PEERS / VERIFIER", "steps": [ {"id": "peers", "number": 5, "label": "peers alternate Ready and Running", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "05", "svg_target": "Peers", "code_ref": "src/tasks/task01_scheduler.c:279", "snapshot_ref": "task01.timeslice", "description": "Tick rotuje równorzędne peery.", "evidence": "A/B/A."}, {"id": "peers-done", "number": 6, "label": "both peers complete and delete", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "06", "code_ref": "src/tasks/task01_scheduler.c:325", "snapshot_ref": "task01.peers-finished", "description": "Stop request nie usuwa taska z zewnątrz.", "evidence": "Każdy peer self-delete."}, {"id": "verify-ready", "number": 7, "label": "verifier waits as lower Ready task", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "07", "svg_target": "VerifyReady", "code_ref": "src/tasks/task01_scheduler.c:519", "snapshot_ref": "task01.priorities", "description": "Priorytet 1 przegrywa z peerami 2.", "evidence": "Verifier nie wykonuje się przed stop."}, {"id": "verify-run", "number": 8, "label": "verifier runs and publishes PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "08", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Po usunięciu wyższych tasków verifier staje się najwyższy.", "evidence": "PASS=1."} ]} ] } }] }, { "title": "A7 — Runtime", "order": 60, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Dowód runtime łączy timer CSRs, ISR stack, Ready lists obserwowane w debuggerze, TCB/stosy tasków oraz aplikacyjne rekordy commit.", "assets": [{ "path": "assets/a7-runtime.png", "html_path": "assets/a7-runtime.svg", "source_path": "assets/a7-runtime.puml", "caption": "A7 RUNTIME — timer, ISR, scheduler, taski i końcowy dowód.", "label": "fig:a7-runtime", "alt": "Runtime evidence od timer CSRs do śladu aplikacji.", "kind": "diagram", "width": 1.0, "page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"timer": 1, "isr": 1, "kernel": 1, "peer": 1, "high": 1, "evidence": 1}}, "interactive": { "kind": "uml-class", "storage_key": "fc03-a7-runtime", "title": "A7 · mapa dowodów runtime", "task": {"id": "task01", "label": "Task01 · scheduler experiment"}, "block": {"id": "a7-runtime", "label": "A7 RUNTIME", "description": "Kernel internals są tylko obserwacją debuggera."}, "phases": [ {"id": "timer-cpu", "label": "TIMER / CPU", "steps": [ {"id": "timer", "number": 1, "label": "timer CSRs define the interrupt", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "01", "svg_target": "Timer", "code_ref": "include/FreeRTOSConfig.h:4", "snapshot_ref": "task01.first-tick", "description": "Mtime/mtimecmp prowadzą do machine-timer trap.", "evidence": "mcause=0x80000007."}, {"id": "isr", "number": 2, "label": "ISR evidence captures tick PC and SP", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "02", "svg_target": "ISR", "code_ref": "src/tasks/task01_scheduler.c:116", "snapshot_ref": "task01.first-tick", "description": "Mepc opisuje przerwany task, SP domenę ISR.", "evidence": "Niezerowe mepc/SP i one-shot checkpoint."}, {"id": "kernel", "number": 3, "label": "Ready lists are debugger-only evidence", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.runtime", "svg_label": "03", "svg_target": "Kernel", "code_ref": "vendor/FreeRTOS-Kernel/tasks.c:1", "snapshot_ref": "task01.timeslice", "description": "Aplikacja nie odczytuje pxCurrentTCB.", "evidence": "Publiczne rekordy wystarczają do PASS."} ]}, {"id": "tasks-evidence", "label": "TASKS / EVIDENCE", "steps": [ {"id": "peer", "number": 4, "label": "peer handle TCB stack PC and SP", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.runtime", "svg_label": "04", "svg_target": "Peer", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Context, TCB i stack są osobnymi obszarami.", "evidence": "created_handle==tcb_address i SP w stack range."}, {"id": "high", "number": 5, "label": "high runs at changed priority", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "05", "svg_target": "High", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "PC/SP i run_tick identyfikują wybrany task.", "evidence": "priority=3 i marker HIGH."}, {"id": "evidence", "number": 6, "label": "committed evidence binds source and ELF", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "06", "svg_target": "Evidence", "code_ref": "src/tasks/task01_scheduler.c:450", "snapshot_ref": "task01.pass", "description": "Końcowy snapshot wiąże wynik z artefaktami.", "evidence": "event_id, source blob, ELF/image hash, PASS i timestamp."} ]} ] } }] }, {"title": "Task01 — Scheduler experiment", "order": 70, "content_kind": "tasks", "task_refs": ["task01"]} ], "tasks": { "task01": { "title": "Kontrolowany eksperyment schedulera FreeRTOS", "uuid": "2214a13f-34c6-40a1-a561-70a00ec98285", "prompt_tex": "Przejdź po A1, A2, A4, A5, A6 i A7. Dla kroków RUN użyj Enter lub F2 i udowodnij oddzielnie time slicing A-B-A oraz natychmiastowe wywłaszczenie before-HIGH-after.", "criterion": "Program wypisuje PASS; trace zawiera A-B-A; marker projection to before-HIGH-after; pierwszy tick ma machine-timer mcause; TCB i stack są spójne.", "conclusion_tex": "Priorytet wybiera najwyższy Ready task. Time slicing rotuje wyłącznie taski o tym samym priorytecie, a uczynienie wyższego taska Ready może wywłaszczyć caller przed powrotem API.", "flow": [ { "kind": "block", "id": "scheduler-model", "title": "A — model ticka i wyboru taska", "content_tex": "Najpierw odczytaj A1, A2 i A4 oraz zapisz przewidywaną kolejność.", "steps": [ {"id": "read-priorities", "title": "Odczytaj kontrakt priorytetów.", "content_tex": "Wyjaśnij, dlaczego high=0, peery=2, verifier=1 nie powodują przedwczesnego uruchomienia high ani verifiera."}, {"id": "predict-traces", "title": "Przewidź dwa ślady.", "content_tex": "Rozdziel task-change trace A-B-A od marker projection before-HIGH-after."} ] }, { "kind": "block", "id": "hazard3-replay", "title": "B — deterministyczny replay E01–E12", "content_tex": "Każdy RUN replay zaczyna się od czystej RAM i zweryfikowanego obrazu.", "steps": [ {"id": "follow-tick", "title": "Zbadaj pierwszy tick.", "content_tex": "Na E06 zapisz mcause, mepc, ISR SP i tick."}, {"id": "follow-timeslice", "title": "Zbadaj A-B-A.", "content_tex": "Na E07 zapisz triplet tasków, ticki i liczbę obserwowanych zmian."}, {"id": "follow-preemption", "title": "Zbadaj natychmiastowe wywłaszczenie.", "content_tex": "Porównaj E08, E09 i E10; pokaż, że HIGH wykonał się przed after_raise."}, {"id": "finish-pass", "title": "Zamknij dowód.", "content_tex": "Na E11 sprawdź self-delete peerów, a na E12 końcowe inwarianty i PASS."} ] }, { "kind": "exercise", "id": "no-timeslicing-bounded-observation", "title": "Ćwiczenie — bounded observation bez time slicing", "prompt_tex": "Ustaw configUSE_TIME_SLICING=0, nie zmieniając dwóch CPU-bound peerów o priorytecie 2. Uruchom tylko ograniczoną obserwację debuggera; nie oczekuj PASS. Zapisz, który peer pozostaje Running, jaki jest tick count i dlaczego drugi może być głodzony. Następnie przywróć konfigurację.", "evidence_tex": "Timestampowany snapshot PC/SP, tick count i obu liczników iteracji oraz krótkie wyjaśnienie expected noncompletion.", "criterion": "Uczeń nie interpretuje braku PASS jako błędu harnessu i poprawnie wiąże głodzenie z brakiem rotacji równych priorytetów.", "based_on": ["scheduler-model", "hazard3-replay"] } ], "educational_requirement_refs": ["FC03.WE01"], "learning_effect_refs": ["FC03.EN01", "FC03.EK01"], "assessment_criterion_ref": "FC03.KW01" } }, "tasks_order": ["task01"] }