{ "$schema": "../../../tools/card-layouts/schemas/card-source.schema.json", "schema": "esc-card-source.v1", "card": { "id": "mpabi-freertos-c-07-isr-semaphore", "series": "freertos-c", "series_title": "FreeRTOS C", "number": "07", "count": "15", "slug": "isr-semaphore", "title": "Przerwanie zewnętrzne i semaphore FromISR", "topic": "Hazard3 MEI, xISRStack, xSemaphoreGiveFromISR i yield", "project": "Freestanding C nad FreeRTOS", "subject": "Informatyka", "level": "Rok 2 · L07 · RV32I/Hazard3", "revision_date": "2026-07-19T00:00:00+02:00", "status": "Gotowa", "version": "v00.01", "uuid": "530724e2-6774-5b17-9912-b3f5c243c15d", "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": "Testbench → Hazard3 → port → task", "status": "enabled", "target_label": "fig:a1-context" }, { "id": "A2", "label": "STRUCTURE", "subtitle": "Struktura", "status": "unavailable", "reason": "Obiekty runtime i ich relacje są pełniej pokazane w A7." }, { "id": "A3", "label": "DISPATCH", "subtitle": "Vector, trap, FromISR i context switch", "status": "enabled", "target_label": "fig:a3-dispatch" }, { "id": "A4", "label": "APPLICATION", "subtitle": "Eksperyment", "status": "unavailable", "reason": "Konkretny handoff jest kompletny w A5." }, { "id": "A5", "label": "FLOW", "subtitle": "E01–E09: trigger do PASS", "status": "enabled", "target_label": "fig:a5-flow" }, { "id": "A6", "label": "STATE", "subtitle": "Semaphore empty/given/taken i task state", "status": "enabled", "target_label": "fig:a6-state" }, { "id": "A7", "label": "RUNTIME", "subtitle": "Waiter, stimulus i xISRStack", "status": "enabled", "target_label": "fig:a7-runtime" }, { "id": "A8", "label": "PATTERNS", "subtitle": "Wzorce", "status": "unavailable", "reason": "Deferred interrupt handoff jest tu mechanizmem wykonania A3/A5, nie osobnym wzorcem aplikacyjnym." } ], "debug_strategies": { "code.irq-contract": { "id": "code.irq-contract", "kind": "code", "action": "open", "expected_observations": [ "external vector trafia do pełnego trap path", "ISR API ma osobny suffix" ], "assertions": [ "source clear poprzedza give", "port ma 256-word ISR stack" ], "evidence_fields": [ "code_ref", "symbol", "ABI" ], "prerequisites": [ "źródło FC07" ], "layout": [ "vector bridge", "FreeRTOS trap", "application ISR", "config" ], "commands": [ "rg -n 'isr_external_irq|application_interrupt|GiveFromISR|SET_IRQ' src include" ] }, "run.trap": { "id": "run.trap", "kind": "run", "action": "replay", "expected_observations": [ "mcause=0x8000000b", "ISR SP różny od task SP", "source=0" ], "assertions": [ "real MEI", "full context saved" ], "evidence_fields": [ "mcause", "SP", "PC", "IRQ register", "timestamp" ], "prerequisites": [ "Hazard3 online", "IRQ5 enabled", "czysta RAM" ], "layout": [ "mcause", "mepc", "xISRStack", "source register" ], "commands": [ "print/x g_fc07.mcause", "print/x g_fc07.isr_sp", "print g_fc07.irq_source_after_clear" ] }, "run.handoff": { "id": "run.handoff", "kind": "run", "action": "replay", "expected_observations": [ "xHPW=pdTRUE", "E07 poprzedza E08" ], "assertions": [ "waiter value widzi stimulus_after=0", "stimulus po resume widzi waiter_ran=1" ], "evidence_fields": [ "state", "actor", "value", "PC/SP" ], "prerequisites": [ "waiter Blocked na binary semaphore" ], "layout": [ "semaphore", "xHPW", "waiter/stimulus TCB", "event order" ], "commands": [ "print g_fc07.higher_priority_task_woken", "print g_fc07_events" ] }, "run.pass": { "id": "run.pass", "kind": "run", "action": "replay", "expected_observations": [ "PASS=1", "nine sequential markers" ], "assertions": [ "three distinct stack contexts", "digest stable" ], "evidence_fields": [ "PASS", "digest", "source blob", "ELF hash", "timestamp" ], "prerequisites": [ "E01–E08 committed" ], "layout": [ "nine records", "stack separation", "digest" ], "commands": [ "print g_fc07_pass", "print g_fc07.digest" ] } }, "template": "templates/karta-klasyczna.json", "title_block": { "category": "KARTA PRACY · INFORMATYKA", "prepared_by": "M. Pabiszczak", "prepared_on": "2026-07-19T00:00:00+02:00", "title": "Przerwanie zewnętrzne i semaphore FromISR", "url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/a6da1075-8d1f-5615-b0e6-0fc33e59e1e3", "repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-isr-semaphore", "url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84", "url_domain": "mpabi.pl", "doc_uuid": "a6da1075-8d1f-5615-b0e6-0fc33e59e1e3", "revision": "v00.01", "issued_on": "2026-07-19T00:00:00+02:00", "series": "FREERTOS-C-07", "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ń rozdziela task context od ISR context, używa API FromISR i dowodzi natychmiastowego przełączenia do wyższego taska.", "scope_title": "Zakres karty", "scope_content_tex": "Prawdziwe wejście IRQ5 testbencha wywołuje machine external interrupt. Port zapisuje pełny kontekst i przechodzi na \\texttt{xISRStack}. ISR czyści źródło, daje binary semaphore i wywołuje \\texttt{portYIELD\\_FROM\\_ISR}. Waiter priority 3 działa przed powrotem do stimulus priority 2.", "scope_table": { "headers": [ "Lekcja", "Task", "Najważniejsza idea", "Priorytet", "Status", "Version" ], "rows": [ { "chapter": "FC07", "task": "Task01", "idea_tex": "Hazard3 MEI, xISRStack, xSemaphoreGiveFromISR i yield", "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 runtime." }, { "id": "ogolne", "label": "OG", "side": "right", "tree": "WE -> EN -> KW", "description": "Model FreeRTOS C." } ] }, "learning_effects": { "FC07.EN01": { "bloom_level": "Analiza", "label": "Model ISR handoff", "text": "Uczeń rozdziela hardware source, trap port, xISRStack, FromISR API i task scheduling.", "assessment_criteria": [ "FC07.KW01" ] }, "FC07.EK01": { "bloom_level": "Zastosowanie", "label": "Replay realnego MEI", "text": "Uczeń odtwarza E01–E09 i wiąże mcause, source clear, xHPW, trzy SP oraz kolejność context restore.", "assessment_criteria": [ "FC07.KW01" ] } }, "assessment_criteria": { "FC07.KW01": { "text": "PASS; mcause=MEI; source=0 przed give; xHPW=1; waiter E07 przed stimulus E08; trzy różne stosy; deterministyczny digest.", "learning_effects": [ "FC07.EN01", "FC07.EK01" ] } }, "educational_requirements": { "FC07.WE01": { "text": "Zaprojektowanie i zbadanie bezpiecznego handoff z prawdziwego przerwania do taska FreeRTOS.", "label": "Hazard3 MEI, xISRStack, xSemaphoreGiveFromISR i yield", "learning_effects": [ "FC07.EN01", "FC07.EK01" ], "learning_tree": { "schema": "we-learning-tree.v1", "policy": "Każda aktywna kotwica wskazuje kod lub checkpoint.", "ogolne": [ { "effect_ref": "FC07.EN01", "display": "EN LOCAL RTOS.07", "source": "LOCAL", "official": "RTOS", "local": "07", "kind": "EN", "tree_id": "FC07.WE01.OG.LOCAL.RTOS.07", "text": "Uczeń rozdziela hardware source, trap port, xISRStack, FromISR API i task scheduling.", "kw": [ { "criterion_ref": "FC07.KW01", "display": "KW LOCAL RTOS.07", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "07", "text": "Łączy kod, diagram i zweryfikowany dowód." } ] } ], "zawodowe": [ { "effect_ref": "FC07.EK01", "display": "EK LOCAL RTOS.07", "source": "LOCAL", "official": "RTOS", "local": "07", "kind": "EK", "tree_id": "FC07.WE01.TECH.LOCAL.RTOS.07", "text": "Uczeń odtwarza E01–E09 i wiąże mcause, source clear, xHPW, trzy SP oraz kolejność context restore.", "kw": [ { "criterion_ref": "FC07.KW01", "display": "KW LOCAL RTOS.07", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "07", "text": "Łączy kod, diagram i zweryfikowany dowód." } ] } ] } } }, "debug_checkpoints": { "schema": "stem-debug-checkpoints.v1", "semantics": "deterministic-replay", "artifact": { "source": "src/tasks/task01_isr_semaphore.c", "source_git_blob": "be3f48ea00d0fa32f09c9124bad9e123105708d9", "elf": "build/task01_isr_semaphore/prog.elf", "hazard3_elf_sha256": "9dbe2d28af41000340e99e33d86890272544f807682e99eecc47f2b0f9e022e5", "hazard3_image": "build/task01_isr_semaphore/prog.bin", "hazard3_image_sha256": "ac9dca42435e9e3fc523a310d12f985212b95e3a70a9c89a61b646036d675754" }, "targets": { "hazard3-sim": { "adapter": "hazard3-reset-load-replay.v1", "baseline": "restart-backend", "clean_ram": true } }, "items": { "task01.config": { "event_id": "E01", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 1" }, "verify": { "expressions": [ { "expr": "g_fc07_event_count", "equals": 1 }, { "expr": "g_fc07.semaphore == 0", "equals": 1 } ] } }, "task01.waiter-block": { "event_id": "E02", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 2" }, "verify": { "expressions": [ { "expr": "g_fc07.waiter_sp != 0", "equals": 1 }, { "expr": "g_fc07.waiter_ran", "equals": 0 } ] } }, "task01.before-trigger": { "event_id": "E03", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 3" }, "verify": { "expressions": [ { "expr": "g_fc07.stimulus_sp != 0", "equals": 1 }, { "expr": "g_fc07.stimulus_after", "equals": 0 } ] } }, "task01.isr-entry": { "event_id": "E04", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 4" }, "verify": { "expressions": [ { "expr": "g_fc07.mcause", "equals": 2147483659 }, { "expr": "g_fc07.isr_sp != g_fc07.stimulus_sp", "equals": 1 } ] } }, "task01.source-clear": { "event_id": "E05", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 5" }, "verify": { "expressions": [ { "expr": "g_fc07.irq_source_after_clear", "equals": 0 }, { "expr": "g_fc07.waiter_ran", "equals": 0 } ] } }, "task01.give-yield": { "event_id": "E06", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 6" }, "verify": { "expressions": [ { "expr": "g_fc07.higher_priority_task_woken", "equals": 1 }, { "expr": "g_fc07.stimulus_after", "equals": 0 } ] } }, "task01.waiter-runs": { "event_id": "E07", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 7" }, "verify": { "expressions": [ { "expr": "g_fc07.waiter_ran", "equals": 1 }, { "expr": "g_fc07.stimulus_after", "equals": 0 } ] } }, "task01.stimulus-resumes": { "event_id": "E08", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 8" }, "verify": { "expressions": [ { "expr": "g_fc07.waiter_ran", "equals": 1 }, { "expr": "g_fc07.stimulus_after", "equals": 1 } ] } }, "task01.pass": { "event_id": "E09", "stop": { "symbol": "fc07_checkpoint_committed", "offset": 0, "condition": "$a0 == 9" }, "verify": { "expressions": [ { "expr": "g_fc07_pass", "equals": 1 }, { "expr": "g_fc07_event_count", "equals": 9 }, { "expr": "g_fc07_events[6].actor", "equals": 2 } ] } } } }, "sections": [ { "title": "A1 — Context", "order": 10, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Źródło testbencha, kontroler Hazard3, port FreeRTOS, ISR aplikacji i waiter są osobnymi warstwami odpowiedzialności.", "assets": [ { "path": "assets/a1-context.png", "html_path": "assets/a1-context.svg", "source_path": "assets/a1-context.puml", "caption": "A1 CONTEXT — IRQ5 do high-priority waitera.", "label": "fig:a1-context", "alt": "Warstwy realnego external IRQ.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "source": 1, "hazard": 1, "port": 1, "isr": 1, "waiter": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc07-a1-context", "title": "A1 · granice interrupt path", "task": { "id": "task01", "label": "Task01 · ISR semaphore" }, "block": { "id": "a1", "label": "A1 CONTEXT", "description": "Od fizycznej linii IRQ do odblokowanego taska." }, "phases": [ { "id": "context", "label": "SOURCE / CPU / PORT / ISR / TASK", "steps": [ { "id": "source", "number": 1, "label": "testbench drives a real IRQ line", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.trap", "svg_label": "01", "code_ref": "src/tasks/task01_isr_semaphore.c:156", "snapshot_ref": "task01.before-trigger", "description": "MMIO store ustawia bit wejścia CPU.", "evidence": "IRQ mask bit 5." }, { "id": "hazard", "number": 2, "label": "Hazard3 raises machine external interrupt", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.trap", "svg_label": "02", "code_ref": "src/tasks/task01_isr_semaphore.c:103", "snapshot_ref": "task01.isr-entry", "description": "To nie jest tick hook.", "evidence": "mcause=MEI." }, { "id": "port", "number": 3, "label": "port saves full task context", "mode": "CODE", "strategy_ref": "code.irq-contract", "svg_label": "03", "code_ref": "src/common/hazard3_freertos_traps.S:24", "description": "Vector skacze do official trap handler.", "evidence": "xISRStack." }, { "id": "isr", "number": 4, "label": "application ISR clears and gives", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "04", "code_ref": "src/tasks/task01_isr_semaphore.c:99", "snapshot_ref": "task01.give-yield", "description": "API FromISR ustawia xHPW.", "evidence": "source=0, xHPW=1." }, { "id": "waiter", "number": 5, "label": "higher waiter consumes semaphore", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.handoff", "svg_label": "05", "code_ref": "src/tasks/task01_isr_semaphore.c:135", "snapshot_ref": "task01.waiter-runs", "description": "Context restore wybiera waiter TCB.", "evidence": "stimulus_after=0." } ] } ] } } ] }, { "title": "A3 — Dispatch", "order": 30, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Samo vTaskSwitchContext w zwykłym handlerze nie wystarcza. Port musi najpierw zapisać pełny kontekst przerwanego taska, przejść na ISR stack i dopiero potem odtworzyć kontekst wybranego taska.", "assets": [ { "path": "assets/a3-dispatch.png", "html_path": "assets/a3-dispatch.svg", "source_path": "assets/a3-dispatch.puml", "caption": "A3 DISPATCH — vector, trap, FromISR i restore.", "label": "fig:a3-dispatch", "alt": "Dispatch external IRQ w porcie FreeRTOS.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "vector": 1, "trap": 1, "stack": 1, "app": 1, "give": 1, "yield": 1, "restore": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc07-a3-dispatch", "title": "A3 · external interrupt dispatch", "task": { "id": "task01", "label": "Task01 · ISR semaphore" }, "block": { "id": "a3", "label": "A3 DISPATCH", "description": "Pełny context save umożliwia yield z ISR." }, "phases": [ { "id": "dispatch", "label": "VECTOR / SAVE / ISR / RESTORE", "steps": [ { "id": "vector", "number": 1, "label": "external vector enters port", "mode": "CODE", "strategy_ref": "code.irq-contract", "svg_label": "01", "code_ref": "src/common/hazard3_freertos_traps.S:24", "description": "Strong symbol zastępuje weak handler testbencha.", "evidence": "isr_external_irq." }, { "id": "trap", "number": 2, "label": "trap saves full context", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.trap", "svg_label": "02", "code_ref": "vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S:356", "snapshot_ref": "task01.isr-entry", "description": "Interrupted SP jest zapisany w TCB.", "evidence": "mepc/mstatus/registers." }, { "id": "stack", "number": 3, "label": "handler runs on xISRStack", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.trap", "svg_label": "03", "code_ref": "src/tasks/task01_isr_semaphore.c:104", "snapshot_ref": "task01.isr-entry", "description": "ISR SP różni się od obu tasków.", "evidence": "isr_sp." }, { "id": "app", "number": 4, "label": "application handler owns device protocol", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.trap", "svg_label": "04", "code_ref": "src/tasks/task01_isr_semaphore.c:107", "snapshot_ref": "task01.source-clear", "description": "Najpierw clear source.", "evidence": "register=0." }, { "id": "give", "number": 5, "label": "FromISR unblocks waiter", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "05", "code_ref": "src/tasks/task01_isr_semaphore.c:111", "snapshot_ref": "task01.give-yield", "description": "Binary semaphore staje się available.", "evidence": "xHPW=1." }, { "id": "yield", "number": 6, "label": "yield selects waiter TCB", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "06", "code_ref": "src/tasks/task01_isr_semaphore.c:118", "snapshot_ref": "task01.give-yield", "description": "Switch następuje przed mret.", "evidence": "vTaskSwitchContext." }, { "id": "restore", "number": 7, "label": "port restores selected task", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.handoff", "svg_label": "07", "code_ref": "src/tasks/task01_isr_semaphore.c:135", "snapshot_ref": "task01.waiter-runs", "description": "CPU nie wraca od razu do stimulus.", "evidence": "E07 before E08." } ] } ] } } ] }, { "title": "A5 — Flow", "order": 40, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Sekwencja dowodzi source-clear-before-give oraz waiter-before-interrupted-task.", "assets": [ { "path": "assets/a5-flow.png", "html_path": "assets/a5-flow.svg", "source_path": "assets/a5-flow.puml", "caption": "A5 FLOW — od Blocked przez ISR do natychmiastowego waitera.", "label": "fig:a5-flow", "alt": "Sekwencja ISR semaphore handoff.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "config": 1, "wait": 1, "trigger": 1, "entry": 1, "clear": 1, "give": 1, "waiter": 1, "resume": 1, "pass": 1 } }, "interactive": { "kind": "uml-sequence", "storage_key": "fc07-a5-flow", "title": "A5 · IRQ handoff replay", "task": { "id": "task01", "label": "Task01 · ISR semaphore" }, "block": { "id": "a5", "label": "A5 FLOW", "description": "Jeden realny handoff E01–E09." }, "phases": [ { "id": "flow", "label": "TRIGGER / ISR / YIELD / PASS", "steps": [ { "id": "config", "number": 1, "label": "IRQ contract configured", "mode": "RUN", "event_id": "E01", "strategy_ref": "run.trap", "svg_label": "01", "code_ref": "src/tasks/task01_isr_semaphore.c:238", "snapshot_ref": "task01.config", "description": "IRQ5 i priority są jawne.", "evidence": "event count 1." }, { "id": "wait", "number": 2, "label": "waiter reaches empty take", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.handoff", "svg_label": "02", "code_ref": "src/tasks/task01_isr_semaphore.c:130", "snapshot_ref": "task01.waiter-block", "description": "Następnie przechodzi do Blocked.", "evidence": "waiter_ran=0." }, { "id": "trigger", "number": 3, "label": "stimulus commits before trigger", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.trap", "svg_label": "03", "code_ref": "src/tasks/task01_isr_semaphore.c:155", "snapshot_ref": "task01.before-trigger", "description": "Kontrolowany punkt przed MMIO.", "evidence": "stimulus_after=0." }, { "id": "entry", "number": 4, "label": "MEI enters xISRStack", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.trap", "svg_label": "04", "code_ref": "src/tasks/task01_isr_semaphore.c:103", "snapshot_ref": "task01.isr-entry", "description": "Prawdziwy async trap.", "evidence": "mcause and SP." }, { "id": "clear", "number": 5, "label": "ISR clears hardware source", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.trap", "svg_label": "05", "code_ref": "src/tasks/task01_isr_semaphore.c:107", "snapshot_ref": "task01.source-clear", "description": "Zapobiega retrigger.", "evidence": "source=0." }, { "id": "give", "number": 6, "label": "give requests a switch", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "06", "code_ref": "src/tasks/task01_isr_semaphore.c:111", "snapshot_ref": "task01.give-yield", "description": "Wyższy task stał się Ready.", "evidence": "xHPW=pdTRUE." }, { "id": "waiter", "number": 7, "label": "waiter runs before stimulus", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.handoff", "svg_label": "07", "code_ref": "src/tasks/task01_isr_semaphore.c:135", "snapshot_ref": "task01.waiter-runs", "description": "Natychmiastowy efekt yield.", "evidence": "stimulus_after=0." }, { "id": "resume", "number": 8, "label": "interrupted stimulus resumes", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.handoff", "svg_label": "08", "code_ref": "src/tasks/task01_isr_semaphore.c:158", "snapshot_ref": "task01.stimulus-resumes", "description": "Dopiero po zakończeniu waitera.", "evidence": "waiter_ran=1." }, { "id": "pass", "number": 9, "label": "verifier commits PASS", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.pass", "svg_label": "09", "code_ref": "src/tasks/task01_isr_semaphore.c:186", "snapshot_ref": "task01.pass", "description": "Sprawdza order, stacks i cause.", "evidence": "PASS=1, digest." } ] } ] } } ] }, { "title": "A6 — State", "order": 50, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "portrait", "content_tex": "GiveFromISR zmienia obiekt synchronizacyjny i stan waitera, lecz natychmiastowe wykonanie wymaga dodatkowego yield request.", "assets": [ { "path": "assets/a6-state.png", "html_path": "assets/a6-state.svg", "source_path": "assets/a6-state.puml", "caption": "A6 STATE — empty, given, switch i taken.", "label": "fig:a6-state", "alt": "Maszyna stanu semaphore handoff.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": { "empty": 1, "pending": 1, "given": 1, "switch": 1, "taken": 1, "resumed": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc07-a6-state", "title": "A6 · ISR handoff state", "task": { "id": "task01", "label": "Task01 · ISR semaphore" }, "block": { "id": "a6", "label": "A6 STATE", "description": "Semaphore state i scheduler decision." }, "phases": [ { "id": "state", "label": "SEMAPHORE / TASK / SWITCH", "steps": [ { "id": "empty", "number": 1, "label": "empty semaphore blocks waiter", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.handoff", "svg_label": "01", "code_ref": "src/tasks/task01_isr_semaphore.c:131", "snapshot_ref": "task01.waiter-block", "description": "Waiter trafia na wait list.", "evidence": "Blocked." }, { "id": "pending", "number": 2, "label": "IRQ interrupts stimulus", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.trap", "svg_label": "02", "code_ref": "src/tasks/task01_isr_semaphore.c:99", "snapshot_ref": "task01.isr-entry", "description": "Stimulus context jest zachowany.", "evidence": "mepc and SP." }, { "id": "given", "number": 3, "label": "give makes waiter Ready", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "03", "code_ref": "src/tasks/task01_isr_semaphore.c:111", "snapshot_ref": "task01.give-yield", "description": "Semaphore nie pozostaje full.", "evidence": "xHPW=true." }, { "id": "switch", "number": 4, "label": "yield requests context switch", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "04", "code_ref": "src/tasks/task01_isr_semaphore.c:118", "snapshot_ref": "task01.give-yield", "description": "pxCurrentTCB wybiera waitera.", "evidence": "vTaskSwitchContext." }, { "id": "taken", "number": 5, "label": "waiter consumes token", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.handoff", "svg_label": "05", "code_ref": "src/tasks/task01_isr_semaphore.c:131", "snapshot_ref": "task01.waiter-runs", "description": "Take wraca pdPASS.", "evidence": "waiter Running." }, { "id": "resumed", "number": 6, "label": "stimulus eventually resumes", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.handoff", "svg_label": "06", "code_ref": "src/tasks/task01_isr_semaphore.c:158", "snapshot_ref": "task01.stimulus-resumes", "description": "Oryginalny mepc nie ginie.", "evidence": "E08 after E07." } ] } ] } } ] }, { "title": "A7 — Runtime", "order": 60, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "portrait", "content_tex": "ISR stack nie należy do żadnego taska. Port zapisuje interrupted task stack, wykonuje ISR na xISRStack i odtwarza stack wybranego waitera.", "assets": [ { "path": "assets/a7-runtime.png", "html_path": "assets/a7-runtime.svg", "source_path": "assets/a7-runtime.puml", "caption": "A7 RUNTIME — stimulus TCB, xISRStack i waiter TCB.", "label": "fig:a7-runtime", "alt": "Runtime trzech kontekstów stosu.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": { "waiter": 1, "stimulus": 1, "isr": 1, "sem": 1, "evidence": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc07-a7-runtime", "title": "A7 · stack and context evidence", "task": { "id": "task01", "label": "Task01 · ISR semaphore" }, "block": { "id": "a7", "label": "A7 RUNTIME", "description": "Trzy stosy i dwa TCB w jednym trap replay." }, "phases": [ { "id": "runtime", "label": "TCB / ISR STACK / RESTORE", "steps": [ { "id": "waiter", "number": 1, "label": "waiter owns priority-3 stack", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.handoff", "svg_label": "01", "code_ref": "src/tasks/task01_isr_semaphore.c:129", "snapshot_ref": "task01.waiter-block", "description": "SP zapisany przed take.", "evidence": "waiter_sp." }, { "id": "stimulus", "number": 2, "label": "stimulus owns interrupted stack", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.trap", "svg_label": "02", "code_ref": "src/tasks/task01_isr_semaphore.c:153", "snapshot_ref": "task01.before-trigger", "description": "Priority 2.", "evidence": "stimulus_sp." }, { "id": "isr", "number": 3, "label": "trap switches to xISRStack", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.trap", "svg_label": "03", "code_ref": "src/tasks/task01_isr_semaphore.c:104", "snapshot_ref": "task01.isr-entry", "description": "ISR nie zużywa task stack.", "evidence": "isr_sp distinct." }, { "id": "sem", "number": 4, "label": "semaphore links ISR and waiter", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.handoff", "svg_label": "04", "code_ref": "src/tasks/task01_isr_semaphore.c:111", "snapshot_ref": "task01.give-yield", "description": "Only FromISR API in ISR.", "evidence": "xHPW." }, { "id": "evidence", "number": 5, "label": "event order proves selected return", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.pass", "svg_label": "05", "code_ref": "src/tasks/task01_isr_semaphore.c:195", "snapshot_ref": "task01.pass", "description": "E07 waiter, E08 stimulus.", "evidence": "nine committed records." } ] } ] } } ] }, { "title": "Task01 — ISR semaphore", "order": 90, "content_kind": "tasks", "task_refs": [ "task01" ] } ], "tasks": { "task01": { "title": "External IRQ → FromISR semaphore → high-priority task", "uuid": "a6da1075-8d1f-5615-b0e6-0fc33e59e1e3", "prompt_tex": "Przejdź po A1, A3, A5, A6 i A7. Odtwórz E01–E09 i udowodnij source-clear-before-give oraz waiter-before-stimulus.", "criterion": "PASS; mcause=MEI; source=0 przed give; xHPW=1; waiter E07 przed stimulus E08; trzy różne stosy; deterministyczny digest.", "conclusion_tex": "ISR działa na osobnym stacku i używa FromISR API. \\texttt{xHigherPriorityTaskWoken} jest informacją dla portu; dopiero \\texttt{portYIELD\\_FROM\\_ISR} powoduje restore kontekstu wyższego taska przed powrotem do przerwanego kodu.", "flow": [ { "kind": "block", "id": "contract", "title": "A — kontrakt przerwania", "content_tex": "Rozdziel source, vector, port, ISR i task.", "steps": [ { "id": "order", "title": "Ustal kolejność ISR.", "content_tex": "Najpierw clear, potem give, na końcu yield." }, { "id": "predict", "title": "Przewidź context restore.", "content_tex": "Wskaż, czy pierwszy wykona się waiter czy stimulus." } ] }, { "kind": "block", "id": "replay", "title": "B — replay Hazard3", "content_tex": "Każdy RUN zaczyna się od czystej RAM i wyłączonego źródła.", "steps": [ { "id": "trap", "title": "Zbadaj E03–E05.", "content_tex": "Zapisz \\texttt{mcause}, \\texttt{mepc}, \\texttt{isr\\_sp} i source state." }, { "id": "handoff", "title": "Zbadaj E06–E08.", "content_tex": "Porównaj xHPW i kolejność tasków." }, { "id": "pass", "title": "Zbadaj E09.", "content_tex": "Sprawdź trzy stosy, markers i digest." } ] }, { "kind": "exercise", "id": "no-yield", "title": "Ćwiczenie — brak yield request", "prompt_tex": "W kontrolowanym wariancie pomiń \\texttt{portYIELD\\_FROM\\_ISR}, zachowując GiveFromISR. Zmierz, kiedy waiter zacznie działać i wskaż następny legalny scheduling point.", "evidence_tex": "Dwa timestampowane ślady E07/E08 z yield i bez yield oraz opis różnicy.", "criterion": "Uczeń nie myli odblokowania taska z natychmiastowym wykonaniem i przywraca poprawny yield w wersji finalnej.", "based_on": [ "contract", "replay" ] } ], "educational_requirement_refs": [ "FC07.WE01" ], "learning_effect_refs": [ "FC07.EN01", "FC07.EK01" ], "assessment_criterion_ref": "FC07.KW01" } }, "tasks_order": [ "task01" ] }