Files

1380 lines
45 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"$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": "E01E10: 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 E01E10 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 E01E10 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": "E01E10: 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 E01E10 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"
]
}