Files

87 lines
20 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.
{
"card":{"number":"06","slug":"software-timers","title":"Software timers i timer daemon","topic":"one-shot, auto-reload, command queue i callback","status":"Gotowa","version":"v00.01"},
"front":{
"goal":"Uczeń rozdziela przyjęcie komendy timera od jej wykonania przez timer daemon oraz bada callback na osobnym stosie taska systemowego.",
"scope":"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."
},
"viewpoints":[
{"id":"A1","label":"CONTEXT","status":"enabled","subtitle":"Application → command queue → daemon"},
{"id":"A2","label":"STRUCTURE","status":"unavailable","subtitle":"Struktura","reason":"Timer_t i TimerTag są pokazane dokładniej w A3 i A7."},
{"id":"A3","label":"DISPATCH","status":"enabled","subtitle":"TimerHandle_t, pvTimerID i callback"},
{"id":"A4","label":"APPLICATION","status":"unavailable","subtitle":"Eksperyment","reason":"One-shot i auto-reload są kompletne w A5."},
{"id":"A5","label":"FLOW","status":"enabled","subtitle":"E01E10: commands i expiry"},
{"id":"A6","label":"STATE","status":"enabled","subtitle":"Inactive, active, callback, reload"},
{"id":"A7","label":"RUNTIME","status":"enabled","subtitle":"Daemon TCB, callback stack i tick"},
{"id":"A8","label":"PATTERNS","status":"unavailable","subtitle":"Wzorce","reason":"Wzorzec callback adapter jest już jawny w A3; karta skupia się na semantyce daemon task."}
],
"artifact":{"source":"src/tasks/task01_software_timers.c","elf":"build/task01_software_timers/prog.elf","image":"build/task01_software_timers/prog.bin"},
"strategies":[
{"id":"code.timer-contract","kind":"code","prerequisites":["źródło FC06"],"layout":["FreeRTOSConfig","TimerTag","callback"],"commands":["rg -n 'configTIMER|TimerTag|fc06_timer_callback' include src"],"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"]},
{"id":"run.commands","kind":"run","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"],"expected_observations":["pdPASS oznacza enqueue","callback następuje później"],"assertions":["zero block time","queue length 8"],"evidence_fields":["event","accepted","tick","PC","timestamp"]},
{"id":"run.callback","kind":"run","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"],"expected_observations":["callback wykonuje daemon","ID wskazuje TimerTag"],"assertions":["daemon != controller","callback SP != controller SP"],"evidence_fields":["timer kind","daemon handle","SP","tick"]},
{"id":"run.pass","kind":"run","prerequisites":["controller_done=1"],"layout":["timeline","counts","digest"],"commands":["print g_fc06","print g_fc06_pass"],"expected_observations":["auto 2/4/7","one-shot 6 raz"],"assertions":["10 records","digest stabilny"],"evidence_fields":["PASS","timeline","digest","ELF hash","timestamp"]}
],
"checkpoints":{
"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":[
{"id":"A1","label":"CONTEXT","title":"Context","order":10,"orientation":"landscape","description":"Dwie płaszczyzny: application command i daemon execution.","content_tex":"Task aplikacyjny nie wywołuje callbacku. Wysyła komendę do kolejki, a timer daemon aktualizuje listy i później uruchamia callback.","assets":[{"stem":"a1-context","title":"A1 · granice timer service","caption":"A1 CONTEXT — controller, command queue, daemon i callback.","label":"fig:a1-context","alt":"Warstwy software timer service.","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."}
]}]}]},
{"id":"A3","label":"DISPATCH","title":"Dispatch","order":30,"orientation":"landscape","description":"Handle → Timer_t → callback → TimerTag.","content_tex":"Callback otrzymuje nieprzezroczysty \\texttt{TimerHandle\\_t}. \\texttt{pvTimerGetTimerID} odzyskuje jawny kontekst aplikacyjny bez globalnej tablicy rozgałęzień po adresach.","assets":[{"stem":"a3-dispatch","title":"A3 · callback dispatch","caption":"A3 DISPATCH — jeden callback i dwa typowane IDs.","label":"fig:a3-dispatch","alt":"Mechanizm dispatch software timera.","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."}
]}]}]},
{"id":"A5","label":"FLOW","title":"Flow","order":40,"orientation":"landscape","description":"E01E10: enqueue, process, expiry i PASS.","content_tex":"Komenda i callback są osobnymi momentami. Oś 2/4/6/7 wynika z okresów, resetu i change period.","assets":[{"stem":"a5-flow","diagram_kind":"sequence","title":"A5 · timer replay","caption":"A5 FLOW — one-shot reset i auto period change.","label":"fig:a5-flow","alt":"Sekwencja controller, daemon i timers.","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."}
]}]}]},
{"id":"A6","label":"STATE","title":"State","order":50,"orientation":"landscape","description":"Stan logiczny timera i pending command.","content_tex":"pdPASS oznacza pending command, nie natychmiastową zmianę active list. Auto-reload wraca do ACTIVE, one-shot do INACTIVE.","assets":[{"stem":"a6-state","title":"A6 · timer lifecycle","caption":"A6 STATE — command pending, active, callback i reload.","label":"fig:a6-state","alt":"Maszyna stanów software timera.","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."}
]}]}]},
{"id":"A7","label":"RUNTIME","title":"Runtime","order":60,"orientation":"landscape","description":"Controller i callback mają różne TCB oraz stosy.","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":[{"stem":"a7-runtime","title":"A7 · daemon runtime","caption":"A7 RUNTIME — timeline, Timer_t, daemon handle i stos.","label":"fig:a7-runtime","alt":"Runtime software timer callback.","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."}
]}]}]}
],
"learning":{"model_label":"Model timer service","model":"Uczeń rozdziela command queue, active list, daemon task i callback.","replay_label":"Replay one-shot i auto-reload","replay":"Uczeń odtwarza E01E10 i wiąże command acceptance, expiry tick, TimerTag, daemon TCB i stack.","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.","requirement":"Zaprojektowanie i zbadanie deterministycznego eksperymentu software timers."},
"task":{"short_label":"software timers","title":"One-shot, auto-reload i timer daemon","prompt_tex":"Przejdź po A1, A3, A5, A6 i A7. Odtwórz E01E10 i rozdziel moment enqueue command od callback execution.","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 \\texttt{configTIMER\\_QUEUE\\_LENGTH} do 1 i przygotuj kontrolowany burst dwóch komend z block time 0. Obsłuż \\texttt{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"]}
]}
}