feat: publish FreeRTOS C FC03 card

This commit is contained in:
2026-07-19 16:36:02 +02:00
commit 34d08ae979
203 changed files with 57403 additions and 0 deletions
+586
View File
@@ -0,0 +1,586 @@
{
"$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 E01E12", "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 E01E12 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 E01E12 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 E01E12",
"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"]
}