{ "$schema": "../../../tools/card-layouts/schemas/card-source.schema.json", "schema": "esc-card-source.v1", "card": { "id": "mpabi-freertos-c-06-software-timers", "series": "freertos-c", "series_title": "FreeRTOS C", "number": "06", "count": "15", "slug": "software-timers", "title": "Software timers i timer daemon", "topic": "one-shot, auto-reload, command queue i callback", "project": "Freestanding C nad FreeRTOS", "subject": "Informatyka", "level": "Rok 2 · L06 · RV32I/Hazard3", "revision_date": "2026-07-19T00:00:00+02:00", "status": "Gotowa", "version": "v00.01", "uuid": "40613f0f-6688-53f0-8c20-e87c6ef73c4e", "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": "Application → command queue → daemon", "status": "enabled", "target_label": "fig:a1-context" }, { "id": "A2", "label": "STRUCTURE", "subtitle": "Struktura", "status": "unavailable", "reason": "Timer_t i TimerTag są pokazane dokładniej w A3 i A7." }, { "id": "A3", "label": "DISPATCH", "subtitle": "TimerHandle_t, pvTimerID i callback", "status": "enabled", "target_label": "fig:a3-dispatch" }, { "id": "A4", "label": "APPLICATION", "subtitle": "Eksperyment", "status": "unavailable", "reason": "One-shot i auto-reload są kompletne w A5." }, { "id": "A5", "label": "FLOW", "subtitle": "E01–E10: commands i expiry", "status": "enabled", "target_label": "fig:a5-flow" }, { "id": "A6", "label": "STATE", "subtitle": "Inactive, active, callback, reload", "status": "enabled", "target_label": "fig:a6-state" }, { "id": "A7", "label": "RUNTIME", "subtitle": "Daemon TCB, callback stack i tick", "status": "enabled", "target_label": "fig:a7-runtime" }, { "id": "A8", "label": "PATTERNS", "subtitle": "Wzorce", "status": "unavailable", "reason": "Wzorzec callback adapter jest już jawny w A3; karta skupia się na semantyce daemon task." } ], "debug_strategies": { "code.timer-contract": { "id": "code.timer-contract", "kind": "code", "action": "open", "expected_observations": [ "daemon ma priorytet 3 i własny stack", "jeden callback rozróżnia dwa IDs" ], "assertions": [ "callback nie blokuje", "TimerHandle_t nie jest application task handle" ], "evidence_fields": [ "code_ref", "config", "symbol" ], "prerequisites": [ "źródło FC06" ], "layout": [ "FreeRTOSConfig", "TimerTag", "callback" ], "commands": [ "rg -n 'configTIMER|TimerTag|fc06_timer_callback' include src" ] }, "run.commands": { "id": "run.commands", "kind": "run", "action": "replay", "expected_observations": [ "pdPASS oznacza enqueue", "callback następuje później" ], "assertions": [ "zero block time", "queue length 8" ], "evidence_fields": [ "event", "accepted", "tick", "PC", "timestamp" ], "prerequisites": [ "Hazard3 online", "czysta RAM" ], "layout": [ "controller", "timer queue", "daemon", "active lists" ], "commands": [ "print g_fc06.start_accepted", "print g_fc06.reset_accepted", "print g_fc06.change_accepted" ] }, "run.callback": { "id": "run.callback", "kind": "run", "action": "replay", "expected_observations": [ "callback wykonuje daemon", "ID wskazuje TimerTag" ], "assertions": [ "daemon != controller", "callback SP != controller SP" ], "evidence_fields": [ "timer kind", "daemon handle", "SP", "tick" ], "prerequisites": [ "timer active" ], "layout": [ "callback source", "pvTimerID", "daemon TCB", "SP" ], "commands": [ "print g_fc06.daemon_seen", "print g_fc06.callback_sp", "print g_fc06.auto_tick" ] }, "run.pass": { "id": "run.pass", "kind": "run", "action": "replay", "expected_observations": [ "auto 2/4/7", "one-shot 6 raz" ], "assertions": [ "10 records", "digest stabilny" ], "evidence_fields": [ "PASS", "timeline", "digest", "ELF hash", "timestamp" ], "prerequisites": [ "controller_done=1" ], "layout": [ "timeline", "counts", "digest" ], "commands": [ "print g_fc06", "print g_fc06_pass" ] } }, "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": "Software timers i timer daemon", "url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/ab188300-f03d-5ddb-8316-b9534876a5bc", "repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-software-timers", "url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84", "url_domain": "mpabi.pl", "doc_uuid": "ab188300-f03d-5ddb-8316-b9534876a5bc", "revision": "v00.01", "issued_on": "2026-07-19T00:00:00+02:00", "series": "FREERTOS-C-06", "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 przyjęcie komendy timera od jej wykonania przez timer daemon oraz bada callback na osobnym stosie taska systemowego.", "scope_title": "Zakres karty", "scope_content_tex": "Controller uruchamia one-shot i auto-reload, resetuje one-shot przed pierwotnym terminem, po dwóch callbackach zmienia okres auto z 2 na 3 ticki i zatrzymuje go po trzecim. Callback rozpoznaje timer przez pvTimerID i zapisuje wyłącznie bounded evidence.", "scope_table": { "headers": [ "Lekcja", "Task", "Najważniejsza idea", "Priorytet", "Status", "Version" ], "rows": [ { "chapter": "FC06", "task": "Task01", "idea_tex": "one-shot, auto-reload, command queue i callback", "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": { "FC06.EN01": { "bloom_level": "Analiza", "label": "Model timer service", "text": "Uczeń rozdziela command queue, active list, daemon task i callback.", "assessment_criteria": [ "FC06.KW01" ] }, "FC06.EK01": { "bloom_level": "Zastosowanie", "label": "Replay one-shot i auto-reload", "text": "Uczeń odtwarza E01–E10 i wiąże command acceptance, expiry tick, TimerTag, daemon TCB i stack.", "assessment_criteria": [ "FC06.KW01" ] } }, "assessment_criteria": { "FC06.KW01": { "text": "Program kończy się PASS; one-shot=6 raz; auto=2/4/7; komendy są accepted; daemon i controller mają różne handles/SP; digest jest deterministyczny.", "learning_effects": [ "FC06.EN01", "FC06.EK01" ] } }, "educational_requirements": { "FC06.WE01": { "text": "Zaprojektowanie i zbadanie deterministycznego eksperymentu software timers.", "label": "one-shot, auto-reload, command queue i callback", "learning_effects": [ "FC06.EN01", "FC06.EK01" ], "learning_tree": { "schema": "we-learning-tree.v1", "policy": "Każda aktywna kotwica wskazuje kod lub checkpoint.", "ogolne": [ { "effect_ref": "FC06.EN01", "display": "EN LOCAL RTOS.06", "source": "LOCAL", "official": "RTOS", "local": "06", "kind": "EN", "tree_id": "FC06.WE01.OG.LOCAL.RTOS.06", "text": "Uczeń rozdziela command queue, active list, daemon task i callback.", "kw": [ { "criterion_ref": "FC06.KW01", "display": "KW LOCAL RTOS.06", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "06", "text": "Łączy kod, diagram i zweryfikowany dowód." } ] } ], "zawodowe": [ { "effect_ref": "FC06.EK01", "display": "EK LOCAL RTOS.06", "source": "LOCAL", "official": "RTOS", "local": "06", "kind": "EK", "tree_id": "FC06.WE01.TECH.LOCAL.RTOS.06", "text": "Uczeń odtwarza E01–E10 i wiąże command acceptance, expiry tick, TimerTag, daemon TCB i stack.", "kw": [ { "criterion_ref": "FC06.KW01", "display": "KW LOCAL RTOS.06", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "06", "text": "Łączy kod, diagram i zweryfikowany dowód." } ] } ] } } }, "debug_checkpoints": { "schema": "stem-debug-checkpoints.v1", "semantics": "deterministic-replay", "artifact": { "source": "src/tasks/task01_software_timers.c", "source_git_blob": "c024c7e4d5e9209ed6d11aab2645b8983d89532b", "elf": "build/task01_software_timers/prog.elf", "hazard3_elf_sha256": "8c7b6da680ecaba4a0447ff223bcfd6f3266cd7c059f23f3e0503d71bc155b9d", "hazard3_image": "build/task01_software_timers/prog.bin", "hazard3_image_sha256": "93a38d315b3b709aee047ad2d556d1e3c8e183a614507283a152a5117d1a6133" }, "targets": { "hazard3-sim": { "adapter": "hazard3-reset-load-replay.v1", "baseline": "restart-backend", "clean_ram": true } }, "items": { "task01.config": { "event_id": "E01", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 1" }, "verify": { "expressions": [ { "expr": "g_fc06_event_count", "equals": 1 }, { "expr": "g_fc06.one_shot == 0", "equals": 1 } ] } }, "task01.started": { "event_id": "E02", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 2" }, "verify": { "expressions": [ { "expr": "g_fc06.start_accepted", "equals": 1 }, { "expr": "g_fc06.auto_count", "equals": 0 } ] } }, "task01.reset": { "event_id": "E03", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 3" }, "verify": { "expressions": [ { "expr": "g_fc06.reset_accepted", "equals": 1 }, { "expr": "g_fc06.one_shot_count", "equals": 0 } ] } }, "task01.auto-1": { "event_id": "E04", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 4" }, "verify": { "expressions": [ { "expr": "g_fc06.auto_count", "equals": 1 }, { "expr": "g_fc06.auto_tick[0]", "equals": 2 } ] } }, "task01.auto-2": { "event_id": "E05", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 5" }, "verify": { "expressions": [ { "expr": "g_fc06.auto_count", "equals": 2 }, { "expr": "g_fc06.auto_tick[1]", "equals": 4 } ] } }, "task01.period-change": { "event_id": "E06", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 6" }, "verify": { "expressions": [ { "expr": "g_fc06.change_accepted", "equals": 1 }, { "expr": "g_fc06.auto_count", "equals": 2 } ] } }, "task01.one-shot": { "event_id": "E07", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 7" }, "verify": { "expressions": [ { "expr": "g_fc06.one_shot_count", "equals": 1 }, { "expr": "g_fc06.one_shot_tick", "equals": 6 } ] } }, "task01.auto-3": { "event_id": "E08", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 8" }, "verify": { "expressions": [ { "expr": "g_fc06.auto_count", "equals": 3 }, { "expr": "g_fc06.auto_tick[2]", "equals": 7 } ] } }, "task01.stopped": { "event_id": "E09", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 9" }, "verify": { "expressions": [ { "expr": "g_fc06.stop_accepted", "equals": 1 }, { "expr": "g_fc06.controller_done", "equals": 1 } ] } }, "task01.pass": { "event_id": "E10", "stop": { "symbol": "fc06_checkpoint_committed", "offset": 0, "condition": "$a0 == 10" }, "verify": { "expressions": [ { "expr": "g_fc06_pass", "equals": 1 }, { "expr": "g_fc06_event_count", "equals": 10 }, { "expr": "g_fc06.daemon_seen != g_fc06.controller", "equals": 1 } ] } } } }, "sections": [ { "title": "A1 — Context", "order": 10, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Task aplikacyjny nie wywołuje callbacku. Wysyła komendę do kolejki, a timer daemon aktualizuje listy i później uruchamia callback.", "assets": [ { "path": "assets/a1-context.png", "html_path": "assets/a1-context.svg", "source_path": "assets/a1-context.puml", "caption": "A1 CONTEXT — controller, command queue, daemon i callback.", "label": "fig:a1-context", "alt": "Warstwy software timer service.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "app": 1, "queue": 1, "daemon": 1, "lists": 1, "callback": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc06-a1-context", "title": "A1 · granice timer service", "task": { "id": "task01", "label": "Task01 · software timers" }, "block": { "id": "a1", "label": "A1 CONTEXT", "description": "Dwie płaszczyzny: application command i daemon execution." }, "phases": [ { "id": "context", "label": "COMMAND / DAEMON / CALLBACK", "steps": [ { "id": "app", "number": 1, "label": "application submits commands", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.commands", "svg_label": "01", "code_ref": "src/tasks/task01_software_timers.c:122", "snapshot_ref": "task01.started", "description": "API zwraca stan wysłania komendy.", "evidence": "start_accepted=1." }, { "id": "queue", "number": 2, "label": "timer queue decouples execution", "mode": "CODE", "strategy_ref": "code.timer-contract", "svg_label": "02", "code_ref": "include/FreeRTOSConfig.h:29", "description": "Długość 8 ogranicza pending commands.", "evidence": "configTIMER_QUEUE_LENGTH." }, { "id": "daemon", "number": 3, "label": "daemon owns command processing", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.callback", "svg_label": "03", "code_ref": "src/tasks/task01_software_timers.c:77", "snapshot_ref": "task01.auto-1", "description": "Callback widzi daemon handle.", "evidence": "daemon priority 3." }, { "id": "lists", "number": 4, "label": "active lists hold expiry", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.commands", "svg_label": "04", "code_ref": "src/tasks/task01_software_timers.c:131", "snapshot_ref": "task01.reset", "description": "Reset przesuwa one-shot.", "evidence": "brak callbacku w tick 5." }, { "id": "callback", "number": 5, "label": "callback records only", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.callback", "svg_label": "05", "code_ref": "src/tasks/task01_software_timers.c:68", "snapshot_ref": "task01.one-shot", "description": "Krótka funkcja na daemon stack.", "evidence": "one_shot_count=1." } ] } ] } } ] }, { "title": "A3 — Dispatch", "order": 30, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Callback otrzymuje nieprzezroczysty TimerHandle_t. pvTimerGetTimerID odzyskuje jawny kontekst aplikacyjny bez globalnej tablicy rozgałęzień po adresach.", "assets": [ { "path": "assets/a3-dispatch.png", "html_path": "assets/a3-dispatch.svg", "source_path": "assets/a3-dispatch.puml", "caption": "A3 DISPATCH — jeden callback i dwa typowane IDs.", "label": "fig:a3-dispatch", "alt": "Mechanizm dispatch software timera.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "handle": 1, "timer": 1, "callback": 1, "tag": 1, "evidence": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc06-a3-dispatch", "title": "A3 · callback dispatch", "task": { "id": "task01", "label": "Task01 · software timers" }, "block": { "id": "a3", "label": "A3 DISPATCH", "description": "Handle → Timer_t → callback → TimerTag." }, "phases": [ { "id": "dispatch", "label": "HANDLE / ID / CALLBACK", "steps": [ { "id": "handle", "number": 1, "label": "opaque handle identifies Timer_t", "mode": "CODE", "strategy_ref": "code.timer-contract", "svg_label": "01", "code_ref": "include/task01_software_timers.h:37", "description": "Aplikacja nie czyta pól Timer_t.", "evidence": "TimerHandle_t." }, { "id": "timer", "number": 2, "label": "kernel stores callback and ID", "mode": "CODE", "strategy_ref": "code.timer-contract", "svg_label": "02", "code_ref": "src/tasks/task01_software_timers.c:249", "description": "xTimerCreate wiąże funkcję z TimerTag.", "evidence": "one-shot i auto." }, { "id": "callback", "number": 3, "label": "daemon invokes shared callback", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.callback", "svg_label": "03", "code_ref": "src/tasks/task01_software_timers.c:68", "snapshot_ref": "task01.auto-1", "description": "Wywołanie jest synchroniczne w daemon task.", "evidence": "PC w callback, daemon SP." }, { "id": "tag", "number": 4, "label": "pvTimerID restores kind", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.callback", "svg_label": "04", "code_ref": "src/tasks/task01_software_timers.c:70", "snapshot_ref": "task01.one-shot", "description": "Marker chroni kontrakt context.", "evidence": "ONE_SHOT versus AUTO." }, { "id": "evidence", "number": 5, "label": "callback commits bounded record", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.callback", "svg_label": "05", "code_ref": "src/tasks/task01_software_timers.c:89", "snapshot_ref": "task01.auto-3", "description": "Payload to expiry tick.", "evidence": "E08 tick 7." } ] } ] } } ] }, { "title": "A5 — Flow", "order": 40, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "Komenda i callback są osobnymi momentami. Oś 2/4/6/7 wynika z okresów, resetu i change period.", "assets": [ { "path": "assets/a5-flow.png", "html_path": "assets/a5-flow.svg", "source_path": "assets/a5-flow.puml", "caption": "A5 FLOW — one-shot reset i auto period change.", "label": "fig:a5-flow", "alt": "Sekwencja controller, daemon i timers.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "config": 1, "start": 1, "reset": 1, "auto1": 1, "auto2": 1, "change": 1, "one": 1, "auto3": 1, "stop": 1, "pass": 1 } }, "interactive": { "kind": "uml-sequence", "storage_key": "fc06-a5-flow", "title": "A5 · timer replay", "task": { "id": "task01", "label": "Task01 · software timers" }, "block": { "id": "a5", "label": "A5 FLOW", "description": "E01–E10: enqueue, process, expiry i PASS." }, "phases": [ { "id": "flow", "label": "COMMANDS / EXPIRY / PASS", "steps": [ { "id": "config", "number": 1, "label": "configuration committed", "mode": "RUN", "event_id": "E01", "strategy_ref": "run.commands", "svg_label": "01", "code_ref": "src/tasks/task01_software_timers.c:247", "snapshot_ref": "task01.config", "description": "Timer objects jeszcze nie istnieją.", "evidence": "event count 1." }, { "id": "start", "number": 2, "label": "two starts accepted", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.commands", "svg_label": "02", "code_ref": "src/tasks/task01_software_timers.c:122", "snapshot_ref": "task01.started", "description": "Daemon przetwarza dwa commands.", "evidence": "start_accepted=1." }, { "id": "reset", "number": 3, "label": "one-shot reset at tick 1", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.commands", "svg_label": "03", "code_ref": "src/tasks/task01_software_timers.c:131", "snapshot_ref": "task01.reset", "description": "Expiry przesuwa się z 5 do 6.", "evidence": "reset_accepted=1." }, { "id": "auto1", "number": 4, "label": "auto callback at tick 2", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.callback", "svg_label": "04", "code_ref": "src/tasks/task01_software_timers.c:87", "snapshot_ref": "task01.auto-1", "description": "Pierwszy period=2.", "evidence": "auto_count=1." }, { "id": "auto2", "number": 5, "label": "auto callback at tick 4", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.callback", "svg_label": "05", "code_ref": "src/tasks/task01_software_timers.c:87", "snapshot_ref": "task01.auto-2", "description": "Drugi period=2.", "evidence": "auto_count=2." }, { "id": "change", "number": 6, "label": "period changed to 3", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.commands", "svg_label": "06", "code_ref": "src/tasks/task01_software_timers.c:147", "snapshot_ref": "task01.period-change", "description": "Nowy termin liczony od change command.", "evidence": "change_accepted=1." }, { "id": "one", "number": 7, "label": "one-shot callback at tick 6", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.callback", "svg_label": "07", "code_ref": "src/tasks/task01_software_timers.c:96", "snapshot_ref": "task01.one-shot", "description": "Fires exactly once.", "evidence": "count=1, tick=6." }, { "id": "auto3", "number": 8, "label": "auto callback at tick 7", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.callback", "svg_label": "08", "code_ref": "src/tasks/task01_software_timers.c:87", "snapshot_ref": "task01.auto-3", "description": "Nowy okres wynosi 3.", "evidence": "4→7." }, { "id": "stop", "number": 9, "label": "auto stop accepted", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.commands", "svg_label": "09", "code_ref": "src/tasks/task01_software_timers.c:163", "snapshot_ref": "task01.stopped", "description": "Controller kończy po trzech callbackach.", "evidence": "controller_done=1." }, { "id": "pass", "number": 10, "label": "verifier commits PASS", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.pass", "svg_label": "10", "code_ref": "src/tasks/task01_software_timers.c:194", "snapshot_ref": "task01.pass", "description": "Timeline, handles, stacks i digest.", "evidence": "PASS=1." } ] } ] } } ] }, { "title": "A6 — State", "order": 50, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "pdPASS oznacza pending command, nie natychmiastową zmianę active list. Auto-reload wraca do ACTIVE, one-shot do INACTIVE.", "assets": [ { "path": "assets/a6-state.png", "html_path": "assets/a6-state.svg", "source_path": "assets/a6-state.puml", "caption": "A6 STATE — command pending, active, callback i reload.", "label": "fig:a6-state", "alt": "Maszyna stanów software timera.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "inactive": 1, "active": 1, "pending": 1, "callback": 1, "reload": 1, "stopped": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc06-a6-state", "title": "A6 · timer lifecycle", "task": { "id": "task01", "label": "Task01 · software timers" }, "block": { "id": "a6", "label": "A6 STATE", "description": "Stan logiczny timera i pending command." }, "phases": [ { "id": "state", "label": "INACTIVE / ACTIVE / CALLBACK", "steps": [ { "id": "inactive", "number": 1, "label": "new timer is inactive", "mode": "CODE", "strategy_ref": "code.timer-contract", "svg_label": "01", "code_ref": "src/tasks/task01_software_timers.c:249", "description": "Create nie startuje timera.", "evidence": "Brak expiry przed command." }, { "id": "active", "number": 2, "label": "processed start makes active", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.commands", "svg_label": "02", "code_ref": "src/tasks/task01_software_timers.c:122", "snapshot_ref": "task01.started", "description": "Daemon dodaje timer do active list.", "evidence": "Commands accepted." }, { "id": "pending", "number": 3, "label": "reset/change travel through queue", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.commands", "svg_label": "03", "code_ref": "src/tasks/task01_software_timers.c:147", "snapshot_ref": "task01.period-change", "description": "Stan przejściowy jest asynchroniczny.", "evidence": "pdPASS przed kolejnym expiry." }, { "id": "callback", "number": 4, "label": "expiry invokes callback", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.callback", "svg_label": "04", "code_ref": "src/tasks/task01_software_timers.c:68", "snapshot_ref": "task01.one-shot", "description": "Callback działa w daemon task.", "evidence": "tick 6." }, { "id": "reload", "number": 5, "label": "auto timer reloads", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.callback", "svg_label": "05", "code_ref": "src/tasks/task01_software_timers.c:80", "snapshot_ref": "task01.auto-3", "description": "One-shot nie wraca do active list.", "evidence": "auto_count=3." }, { "id": "stopped", "number": 6, "label": "stop removes active timer", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.commands", "svg_label": "06", "code_ref": "src/tasks/task01_software_timers.c:163", "snapshot_ref": "task01.stopped", "description": "Brak czwartego callbacku.", "evidence": "stop_accepted=1." } ] } ] } } ] }, { "title": "A7 — Runtime", "order": 60, "content_kind": "prose", "asset_page_mode": "one-per-page", "page_orientation": "landscape", "content_tex": "A7 dowodzi, że callback nie jest kontynuacją controller task. Jego PC i SP należą do timer daemon, a TimerTag pozostaje application context.", "assets": [ { "path": "assets/a7-runtime.png", "html_path": "assets/a7-runtime.svg", "source_path": "assets/a7-runtime.puml", "caption": "A7 RUNTIME — timeline, Timer_t, daemon handle i stos.", "label": "fig:a7-runtime", "alt": "Runtime software timer callback.", "kind": "diagram", "width": 1.0, "page_grid": { "columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": { "exp": 1, "controller": 1, "daemon": 1, "one": 1, "auto": 1, "events": 1 } }, "interactive": { "kind": "uml-class", "storage_key": "fc06-a7-runtime", "title": "A7 · daemon runtime", "task": { "id": "task01", "label": "Task01 · software timers" }, "block": { "id": "a7", "label": "A7 RUNTIME", "description": "Controller i callback mają różne TCB oraz stosy." }, "phases": [ { "id": "runtime", "label": "TCB / STACK / TIMER / TICK", "steps": [ { "id": "exp", "number": 1, "label": "experiment owns bounded results", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.pass", "svg_label": "01", "code_ref": "src/tasks/task01_software_timers.c:194", "snapshot_ref": "task01.pass", "description": "Wynik nie zależy od prywatnych list.", "evidence": "timeline i counts." }, { "id": "controller", "number": 2, "label": "controller submits commands", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.commands", "svg_label": "02", "code_ref": "src/tasks/task01_software_timers.c:110", "snapshot_ref": "task01.period-change", "description": "Priority 2 i controller_sp.", "evidence": "current TCB=controller." }, { "id": "daemon", "number": 3, "label": "daemon executes callbacks", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.callback", "svg_label": "03", "code_ref": "src/tasks/task01_software_timers.c:77", "snapshot_ref": "task01.one-shot", "description": "Priority 3 i callback_sp.", "evidence": "daemon_seen != controller." }, { "id": "one", "number": 4, "label": "one-shot expires once", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.callback", "svg_label": "04", "code_ref": "src/tasks/task01_software_timers.c:94", "snapshot_ref": "task01.one-shot", "description": "Reset ustala tick 6.", "evidence": "count=1." }, { "id": "auto", "number": 5, "label": "auto timer owns changing period", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.callback", "svg_label": "05", "code_ref": "src/tasks/task01_software_timers.c:80", "snapshot_ref": "task01.auto-3", "description": "Period change zachowuje ten sam handle.", "evidence": "2/4/7." }, { "id": "events", "number": 6, "label": "ten records form digest", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.pass", "svg_label": "06", "code_ref": "src/tasks/task01_software_timers.c:208", "snapshot_ref": "task01.pass", "description": "Marker każdego rekordu jest zapisywany na końcu.", "evidence": "stable digest." } ] } ] } } ] }, { "title": "Task01 — software timers", "order": 90, "content_kind": "tasks", "task_refs": [ "task01" ] } ], "tasks": { "task01": { "title": "One-shot, auto-reload i timer daemon", "uuid": "ab188300-f03d-5ddb-8316-b9534876a5bc", "prompt_tex": "Przejdź po A1, A3, A5, A6 i A7. Odtwórz E01–E10 i rozdziel moment enqueue command od callback execution.", "criterion": "Program kończy się PASS; one-shot=6 raz; auto=2/4/7; komendy są accepted; daemon i controller mają różne handles/SP; digest jest deterministyczny.", "conclusion_tex": "Software timer jest obsługiwany przez daemon task. API wysyła komendę, aktywna lista przechowuje expiry, a callback działa później na stosie daemon task.", "flow": [ { "kind": "block", "id": "model", "title": "A — model timer service", "content_tex": "Przewidź oś czasu i właściciela każdego wywołania.", "steps": [ { "id": "commands", "title": "Rozdziel command i callback.", "content_tex": "Wskaż dwa różne momenty dowodowe." }, { "id": "timeline", "title": "Przewidź 2/4/6/7.", "content_tex": "Uwzględnij reset i change period." } ] }, { "kind": "block", "id": "replay", "title": "B — replay Hazard3", "content_tex": "Zbadaj controller i daemon w osobnych layoutach.", "steps": [ { "id": "enqueue", "title": "Zbadaj E02/E03/E06.", "content_tex": "Sprawdź accepted flags i command source." }, { "id": "callbacks", "title": "Zbadaj E04/E05/E07/E08.", "content_tex": "Porównaj TimerTag, PC, SP i tick." }, { "id": "pass", "title": "Zbadaj E09/E10.", "content_tex": "Sprawdź brak czwartego callbacku i digest." } ] }, { "kind": "exercise", "id": "late-command", "title": "Ćwiczenie — command queue pressure", "prompt_tex": "Zmniejsz configTIMER_QUEUE_LENGTH do 1 i przygotuj kontrolowany burst dwóch komend z block time 0. Obsłuż pdFAIL bez utraty poprzedniego stanu timera.", "evidence_tex": "Snapshot queue pressure, jawny expected failure i deterministyczny final state.", "criterion": "Uczeń nie interpretuje pdFAIL jako wykonanej komendy i nie blokuje callbacku.", "based_on": [ "model", "replay" ] } ], "educational_requirement_refs": [ "FC06.WE01" ], "learning_effect_refs": [ "FC06.EN01", "FC06.EK01" ], "assessment_criterion_ref": "FC06.KW01" } }, "tasks_order": [ "task01" ] }